UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

292 lines (291 loc) 11.4 kB
// SPDX-License-Identifier: Apache-2.0 import GirafeSingleton from '../../base/GirafeSingleton.js'; import GroupLayer from '../../models/layers/grouplayer.js'; import Layer from '../../models/layers/layer.js'; import ThemeLayer from '../../models/layers/themelayer.js'; class LayerManager extends GirafeSingleton { layerClones = []; layerIdToClone = new Map(); get state() { return this.context.stateManager.state; } initializeSingleton() { this.context.stateManager.subscribe(/layers\.layersList\..*\.activeState/, (_oldActive, _newActive, layer) => this.onLayerToggled(layer)); this.context.stateManager.subscribe('layers.layersList', (oldLayers, newLayers) => this.onLayersListChanged(oldLayers, newLayers)); this.context.stateManager.subscribe(/layers\.layersList\..*\.children/, (oldChildren, newChildren) => this.onChildrenListChanged(oldChildren, newChildren)); } onLayersListChanged(oldLayers, newLayers) { let addedLayers = newLayers; if (oldLayers) { addedLayers = newLayers.filter((newLayer) => !oldLayers.find((oldLayer) => oldLayer.treeItemId === newLayer.treeItemId)); } this.layerClones.push(...addedLayers); this.fillLayerIdToClone(addedLayers); this.activateDefaultLayers(addedLayers); this.highlightNewLayers(addedLayers); } fillLayerIdToClone(layers) { for (const layer of layers) { this.layerIdToClone.set(layer.treeItemId, layer); if (layer instanceof GroupLayer || layer instanceof ThemeLayer) { this.fillLayerIdToClone(layer.children); } } } onChildrenListChanged(oldChildren, newChildren) { // If we added a new group to the list of layers // Then we activate the layers that should be activated by default const addedLayers = newChildren.filter((newChild) => !oldChildren.find((oldChild) => oldChild.treeItemId === newChild.treeItemId)); this.fillLayerIdToClone(addedLayers); this.activateDefaultLayers(addedLayers); } getTreeItem(treeItemId) { // The object is not in the list of active layers any more. // We look in the list of clones const treeItem = this.layerIdToClone.get(treeItemId); if (treeItem) { return treeItem; } throw new Error(`BaseLayer ${treeItemId} not found !`); } getTreeItemByLayerName(layerName) { return this.getFlattenedLayerTree(this.state.layers.layersList).find((l) => l.name === layerName) ?? null; } getFlattenedLayerTree(layers) { const allLayers = []; for (const layer of layers) { allLayers.push(layer); if (layer instanceof GroupLayer || layer instanceof ThemeLayer) { allLayers.push(...this.getFlattenedLayerTree(layer.children)); } } return allLayers; } activateIfDefaultChecked(layer) { if (layer.isDefaultChecked) { this.toggle(layer, 'on'); } } toggle(layer, state) { if (layer instanceof GroupLayer || layer instanceof ThemeLayer) { this.toggleGroupOrTheme(layer, state); } else if (layer instanceof Layer) { this.toggleLayer(layer, state); } } onLayerToggled(layer) { if (layer instanceof GroupLayer || layer instanceof ThemeLayer) { // Toggle parents if necessary this.manageExclusiveGroups(layer); this.toggleParent(layer); // Toggle childs if necessary if (layer.activeState !== 'semi') { this.toggleChilds(layer, layer.activeState); } } else if (layer instanceof Layer) { // Hide the legend when the layer is deactivated (if configured so) if (this.isLayerWithLegend(layer) && this.context.configManager.Config.treeview.hideLegendWhenLayerIsDeactivated) { if (layer.active) { layer.isLegendExpanded = layer.wasLegendExpanded; } else { layer.wasLegendExpanded = layer.isLegendExpanded; layer.isLegendExpanded = false; } } // Toggle parents if necessary this.manageExclusiveGroups(layer); this.toggleParent(layer); } this.manageDisclaimer(layer); } manageDisclaimer(layer) { if (layer.active && layer.disclaimer) { this.state.infobox.elements.push({ id: layer.treeItemId, text: layer.disclaimer, type: 'info' }); } if (layer.inactive && layer.disclaimer) { const index = this.state.infobox.elements.findIndex((el) => el.id === layer.treeItemId); if (index >= 0) { this.state.infobox.elements.splice(index, 1); } } } toggleLayer(layer, state) { if (!(layer instanceof Layer)) { throw new Error('This method should only be called on leafs layers, not on groups'); } let newState; if (state) { newState = state; } else if (layer.activeState === 'off') { newState = 'on'; } else { newState = 'off'; } if (layer.activeState != newState) { console.debug(`Setting Layer ${layer.name} to ${newState}`); this.getTreeItem(layer.treeItemId).activeState = newState; } } toggleGroupOrTheme(groupOrTheme, state) { let newState; if (state) { newState = state; } else if (groupOrTheme.activeState === 'off') { newState = 'on'; } else { newState = 'off'; } if (groupOrTheme.activeState != newState) { console.debug(`Setting Group ${groupOrTheme.name} to ${newState}`); this.getTreeItem(groupOrTheme.treeItemId).activeState = newState; } } toggleParent(layer) { if (layer.parent) { if ((layer.parent instanceof GroupLayer && layer.parent.isExclusiveGroup) || (layer.parent instanceof ThemeLayer && layer.parent.isExclusiveTheme)) { if (this.isAnyChildActive(layer.parent)) { this.toggleGroupOrTheme(layer.parent, 'on'); } else { this.toggleGroupOrTheme(layer.parent, 'off'); } } else if (this.areAllChildrenActive(layer.parent)) { this.toggleGroupOrTheme(layer.parent, 'on'); } else if (this.areAllChildrenInactive(layer.parent)) { this.toggleGroupOrTheme(layer.parent, 'off'); } else { this.toggleGroupOrTheme(layer.parent, 'semi'); } } } toggleChilds(group, state) { if (group instanceof GroupLayer && group.active && group.isExclusiveGroup && group.children.length >= 1) { // We activate a group, and this group is an exclusive group. // If there isn't any active child yet, we activate the first one if (!this.isAnyChildActive(group)) { this.toggle(group.children[0], state); } } else { // In all other cases, we activate/deactivate all children for (const child of group.children) { this.toggle(child, state); } } } manageExclusiveGroups(layer) { // This method manages the case of exclusives groups: // If we have activate a layer, and if the parent group is defined as "exclusive" // It means only 1 child can be activated at the same time. // Therefore, we have to deactivate all other childs for the parent group. if ((layer.active || ((layer instanceof GroupLayer || layer instanceof ThemeLayer) && layer.semiActive)) && layer.parent != null && ((layer.parent instanceof GroupLayer && layer.parent.isExclusiveGroup) || (layer.parent instanceof ThemeLayer && layer.parent.isExclusiveTheme))) { // Deactivate all other layers for (const child of layer.parent.children) { const otherLayer = this.getTreeItem(child.treeItemId); if (otherLayer.treeItemId !== layer.treeItemId && (otherLayer.active || ((otherLayer instanceof GroupLayer || otherLayer instanceof ThemeLayer) && otherLayer.semiActive))) { this.toggle(otherLayer, 'off'); } } } } areAllChildrenActive(groupOrTheme) { let allActive = true; for (const child of groupOrTheme.children) { if (!child.active) { allActive = false; } } return allActive; } areAllChildrenInactive(groupOrTheme) { let allInactive = true; for (const child of groupOrTheme.children) { if (!child.inactive) { allInactive = false; } } return allInactive; } isAnyChildActive(groupOrTheme) { for (const child of groupOrTheme.children) { if (child.active) { return true; } if (child instanceof GroupLayer && child.semiActive) { // A semi active group is considered as an active child in this case return true; } } return false; } setError(layer, error) { layer.hasError = true; layer.errorMessage = error; console.warn(layer.errorMessage); } unsetError(layer) { layer.hasError = false; layer.errorMessage = null; } isLayerWithLegend(layer) { return layer.isLegendExpanded !== undefined; } isLayerWithFilter(layer) { return layer.filter !== undefined; } getSortedLayers(layers) { const orderedLayers = layers.slice().sort((l1, l2) => { return l1.order - l2.order; }); return orderedLayers ?? []; } activateDefaultLayers(layers) { for (const layer of layers) { // Activate the layer by default this.activateIfDefaultChecked(layer); // Manage the legend visibility if (layer instanceof Layer && !layer.active && this.context.configManager.Config.treeview.hideLegendWhenLayerIsDeactivated && this.isLayerWithLegend(layer)) { // Hide Legend layer.isLegendExpanded = false; layer.wasLegendExpanded = true; } // Continue recursively if (layer instanceof GroupLayer || layer instanceof ThemeLayer) { this.activateDefaultLayers(layer.children); } } } highlightNewLayers(layers) { // Do not highlight at application start if (this.state.application.isReady) { for (const layer of layers) { layer.isHighlighted = true; } } } } export default LayerManager;