@geogirafe/lib-geoportal
Version:
GeoGirafe is a flexible application to build online geoportals.
442 lines (441 loc) • 18 kB
JavaScript
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;
}
}