UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

442 lines (441 loc) 18 kB
import GroupLayer from '../../../models/layers/grouplayer.js'; import ThemeLayer from '../../../models/layers/themelayer.js'; import { isTimeAwareLayer } from '../../../models/layers/timeawarelayer.js'; import LayerWms from '../../../models/layers/layerwms.js'; import ThemeLayerExternal from '../../../models/layers/themelayerexternal.js'; import LayerWmsExternal from '../../../models/layers/layerwmsexternal.js'; import LayerWmtsExternal from '../../../models/layers/layerwmtsexternal.js'; import ServerOgc from '../../../models/serverogc.js'; import LayerWmts from '../../../models/layers/layerwmts.js'; import LayerVectorTiles from '../../../models/layers/layervectortiles.js'; import LayerCog from '../../../models/layers/layercog.js'; import LayerOsm from '../../../models/layers/layerosm.js'; import LayerXYZ from '../../../models/layers/layerxyz.js'; import { deserializeWfsFilter, serializeWfsFilter } from './wfsfilterserializer.js'; export default class LayersConfigSerializer { context; constructor(context) { this.context = context; } get state() { return this.context.stateManager.state; } get preferNames() { return this.context.configManager.Config.share?.preferNames ?? false; } brainSerialize(layersConfig) { return this.serialize(layersConfig.layersList); } serialize(layers) { const sharedLayers = this.getSerializedLayerTree(layers); return JSON.stringify(sharedLayers); } brainDeserialize(str) { const deserializedLayers = this.deserialize(str); // Remove all existing layers for (const layer of this.context.stateManager.state.layers.layersList) { this.context.layerManager.toggle(layer, 'off'); } this.state.layers.layersList = []; for (const deserializedLayer of deserializedLayers) { this.state.layers.layersList.push(deserializedLayer); } } deserialize(str) { const sharedState = JSON.parse(str); return this.getDeserializedLayerTree(sharedState); } getSerializedLayerTree(layers) { const sharedLayers = []; for (const layer of layers) { try { const sharedLayer = this.getSerializedLayer(layer); sharedLayers.push(sharedLayer); } catch { console.warn(`Cannot serialize layer with id ${layer.id} and name ${layer.name}. Skipping it.`); } } return sharedLayers; } getSerializedLayer(layer) { if (layer instanceof ThemeLayerExternal) { return this.getExternalSerializedTheme(layer); } if (layer instanceof LayerWmsExternal || layer instanceof LayerWmtsExternal) { return this.getExternalSerializedLayer(layer); } if (layer instanceof ThemeLayer || layer instanceof GroupLayer) { return this.getInternalSerializedGroupOrTheme(layer); } return this.getInternalSerializedLayer(layer); } getExternalSerializedTheme(theme) { const sharedChildren = []; for (const child of theme.children) { const sharedChild = this.getSerializedLayer(child); sharedChildren.push(sharedChild); } return { name: theme.name, order: theme.order, checked: Number(theme.active), isExpanded: Number(theme.isExpanded), children: sharedChildren }; } getExternalSerializedLayer(layer) { const sharedLayer = { order: layer.order, checked: Number(layer.active), isExpanded: Number(layer.isLegendExpanded), opacity: layer.opacity, swiped: layer.swiped }; if (layer instanceof LayerWmtsExternal) { sharedLayer.wmts = { name: layer.name, url: layer.url, layer: layer.layer }; } else if (layer instanceof LayerWmsExternal) { sharedLayer.wms = { name: layer.layers, title: layer.name, url: layer.ogcServer.url, serverType: layer.ogcServer?.type ?? 'other' }; } return sharedLayer; } getInternalSerializedGroupOrTheme(group) { const originalTheme = this.context.themesHelper.findBaseLayerById(group.id); const sharedChildren = []; const removedChildrenIds = []; const removedChildrenNames = []; for (const originalChild of originalTheme.children) { const index = group.children.findIndex((el) => el.id === originalChild.id); if (index >= 0) { // Element was found => it is still in the list const sharedChild = this.getSerializedLayer(group.children[index]); sharedChildren.push(sharedChild); } else { // Element is not in the list any more, and therefore should not be shared or restored removedChildrenIds.push(originalChild.id); removedChildrenNames.push(originalChild.name); } } return { id: group.id, order: group.order, checked: Number(group.active), isExpanded: Number(group.isExpanded), timeRestriction: isTimeAwareLayer(group) ? group.timeRestriction : undefined, children: sharedChildren, excludedChildrenIds: removedChildrenIds, name: group.name, type: this.getInternalLayerType(group), excludedChildrenNames: removedChildrenNames }; } getInternalLayerType(layer) { if (layer instanceof ThemeLayer) { return 'theme'; } if (layer instanceof GroupLayer) { return 'group'; } if (layer instanceof LayerWms) { return 'wms'; } if (layer instanceof LayerWmts) { return 'wmts'; } if (layer instanceof LayerVectorTiles) { return 'vt'; } if (layer instanceof LayerCog) { return 'cog'; } if (layer instanceof LayerOsm) { return 'osm'; } if (layer instanceof LayerXYZ) { return 'xyz'; } throw new Error(`The layer ${layer.name} has an unknown type and cannot be shared.`); } getInternalSerializedLayer(layer) { return { id: layer.id, order: layer.order, checked: Number(layer.active), isExpanded: Number(layer.isLegendExpanded), opacity: layer.opacity, swiped: layer.swiped, filter: layer instanceof LayerWms && this.context.layerManager.isLayerWithFilter(layer) && layer.hasFilter ? serializeWfsFilter(layer.filter) : undefined, timeRestriction: isTimeAwareLayer(layer) ? layer.timeRestriction : undefined, name: layer.name, type: this.getInternalLayerType(layer) }; } getDeserializedLayerTree(sharedLayers) { const layersList = []; for (const sharedLayer of sharedLayers) { let layer; if ('id' in sharedLayer) { // Id attribute found => we are on an internal layer if (this.preferNames && sharedLayer.name) { layer = this.findBaseLayerByName(sharedLayer.name, sharedLayer.type); } else { layer = this.findBaseLayerById(sharedLayer.id); } if (layer) { this.deserializeInternalObject(layer, sharedLayer); } else { console.warn(`Cannot find layer with id ${sharedLayer.id} in the available layers`); } } else { layer = this.deserializeExternalObject(sharedLayer); } if (layer) { layersList.push(layer); } } return layersList; } deserializeInternalObject(layer, sharedLayer) { if (layer instanceof ThemeLayer) { this.deserializeInternalTheme(layer, sharedLayer); } else if (layer instanceof GroupLayer) { this.deserializeInternalGroup(layer, sharedLayer); } else { this.deserializeInternalLayer(layer, sharedLayer); } return layer; } deserializeInternalTheme(theme, sharedTheme) { theme.order = sharedTheme.order; theme.isDefaultChecked = Boolean(sharedTheme.checked); theme.isExpanded = Boolean(sharedTheme.isExpanded); this.removeUnnecessaryChilds(theme, sharedTheme); this.checkUnknownLayers(sharedTheme, theme); } deserializeInternalGroup(group, sharedGroup) { group.order = sharedGroup.order; group.isDefaultChecked = Boolean(sharedGroup.checked); group.isExpanded = Boolean(sharedGroup.isExpanded); this.removeUnnecessaryChilds(group, sharedGroup); this.checkUnknownLayers(sharedGroup, group); if (isTimeAwareLayer(group)) { group.timeRestriction = sharedGroup.timeRestriction; } } deserializeInternalLayer(layer, sharedLayer) { layer.order = sharedLayer.order; layer.isDefaultChecked = Boolean(sharedLayer.checked); if (sharedLayer.opacity) { layer.opacity = sharedLayer.opacity; } if (sharedLayer.swiped) { layer.swiped = sharedLayer.swiped; } if (this.context.layerManager.isLayerWithLegend(layer)) { layer.isLegendExpanded = Boolean(sharedLayer.isExpanded); } if (sharedLayer.filter && layer instanceof LayerWms) { layer.filter = deserializeWfsFilter(sharedLayer.filter); } if (isTimeAwareLayer(layer)) { layer.timeRestriction = sharedLayer.timeRestriction; } } checkUnknownLayers(sharedLayer, originalLayer) { // If some layers are present in the shared state but cannot be found in the current list of available layers // It probably means that the layers are private ones or that the layer has been delete. // Add an infobox for this. for (const sharedChild of sharedLayer.children) { if (sharedChild.checked === 1) { const originalChild = originalLayer.children.find((c) => c.id == sharedChild.id); if (!originalChild) { this.context.errorManager.pushMessage('unknown-layers-cannot-be-added', 'Some layer could not be added to the layer-tree. This is either because you do not have the rights for it, or because this layer does not exist anymore.', 'warning'); } } } } removeUnnecessaryChilds(originalLayer, sharedLayer) { let reorder = false; for (let i = originalLayer.children.length - 1; i >= 0; i--) { const child = originalLayer.children[i]; const serializedChild = this.preferNames ? sharedLayer.children.find((l) => l.name == child.name) : sharedLayer.children.find((l) => l.id == child.id); if (serializedChild) { this.deserializeInternalObject(child, serializedChild); } else { // This child exists in the original layer, but not in the shared state. // => If it is present in the x list, it was explicitely removed // And we can remove it from the current object const explicitlyRemoved = this.preferNames ? sharedLayer.excludedChildrenNames.find((name) => name == child.name) : sharedLayer.excludedChildrenIds.find((id) => id == child.id); if (explicitlyRemoved) { originalLayer.children.splice(i, 1); console.debug(`Layer ${child.name} was removed from initial state`); } else { // Otherwise it is a new layer. We do not remove it // But we have to set the right order for it. // In this case we have to reorder all the layers at this level // In order to keep the order defined in the initial group console.debug(`Layer ${child.name} will be added to the treeview because it is new`); console.debug(`Layer ${originalLayer.name} needs a reorering of its children`); reorder = true; } } } if (reorder) { this.reorderChildren(originalLayer); } } reorderChildren(originalLayer) { console.debug(`Reordering childs for layer ${originalLayer.name}`); let order = 1; for (const child of originalLayer.children) { child.order = order++; } } findBaseLayerById(layerId) { for (const theme of Object.values(this.state.themes._allThemes)) { const layer = this.findLayerRecursiveById(theme, layerId); if (layer) { return layer; } } return null; } findLayerRecursiveById(layer, layerId) { if (layer.id === layerId) { // When deserializing the layer, we clone it, // otherwise the following operation will also // affect the layer referenced in other themes const foundLayer = layer.clone(); return foundLayer; } // Else, we call recursively on the children if (layer instanceof GroupLayer || layer instanceof ThemeLayer) { for (const childLayer of layer.children) { const foundChild = this.findLayerRecursiveById(childLayer, layerId); if (foundChild) { return foundChild; } } } return null; } findBaseLayerByName(layerName, layerType) { for (const theme of Object.values(this.state.themes._allThemes)) { const layer = this.findLayerRecursiveByName(theme, layerName, layerType); if (layer) { return layer; } } return null; } findLayerRecursiveByName(layer, layerName, layerType) { if (layer.name === layerName && this.getInternalLayerType(layer) === layerType) { // When deserializing the layer, we clone it, // otherwise the following operation will also // affect the layer referenced in other themes const foundLayer = layer.clone(); return foundLayer; } // Else, we call recursively on the children if (layer instanceof GroupLayer || layer instanceof ThemeLayer) { for (const childLayer of layer.children) { const foundChild = this.findLayerRecursiveByName(childLayer, layerName, layerType); if (foundChild) { return foundChild; } } } return null; } deserializeExternalObject(sharedLayer) { let layer; if ('children' in sharedLayer) { layer = this.deserializeExternalTheme(sharedLayer); } else if ('wms' in sharedLayer) { layer = this.deserializeExternalWmsLayer(sharedLayer); } else if ('wmts' in sharedLayer) { layer = this.deserializeExternalWmtsLayer(sharedLayer); } else { throw new Error('Unsupport external layer type'); } return layer; } deserializeExternalTheme(sharedTheme) { const theme = new ThemeLayerExternal(sharedTheme.name); theme.order = sharedTheme.order; theme.isDefaultChecked = Boolean(sharedTheme.checked); theme.isExpanded = Boolean(sharedTheme.isExpanded); for (const sharedChild of sharedTheme.children) { const child = this.deserializeExternalObject(sharedChild); child.parent = theme; theme.children.push(child); } return theme; } deserializeExternalWmsLayer(sharedLayer) { if (!sharedLayer.wms) { throw new Error('Some informations are missing to deserialize this WMS Layer'); } const server = new ServerOgc('external', { url: sharedLayer.wms.url, type: sharedLayer.wms.serverType, wfsSupport: true, urlWfs: sharedLayer.wms.url, imageType: 'image/png' }); const layer = new LayerWmsExternal(sharedLayer.wms.title, sharedLayer.wms.name, server); layer.order = sharedLayer.order; layer.isDefaultChecked = Boolean(sharedLayer.checked); layer.isLegendExpanded = Boolean(sharedLayer.isExpanded); if (sharedLayer.opacity) { layer.opacity = sharedLayer.opacity; } if (sharedLayer.swiped) { layer.swiped = sharedLayer.swiped; } return layer; } deserializeExternalWmtsLayer(sharedLayer) { if (!sharedLayer.wmts) { throw new Error('Some informations are missing to deserialize this WMTS Layer'); } const layer = new LayerWmtsExternal(sharedLayer.wmts.name, sharedLayer.wmts.url, sharedLayer.wmts.layer); layer.order = sharedLayer.order; layer.isDefaultChecked = Boolean(sharedLayer.checked); layer.isLegendExpanded = Boolean(sharedLayer.isExpanded); if (sharedLayer.opacity) { layer.opacity = sharedLayer.opacity; } if (sharedLayer.swiped) { layer.swiped = sharedLayer.swiped; } return layer; } }