UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

247 lines (246 loc) 10.4 kB
// SPDX-License-Identifier: Apache-2.0 import WMTS, { optionsFromCapabilities } from 'ol/source/WMTS.js'; import WMTSCapabilities from 'ol/format/WMTSCapabilities.js'; import TileLayer from 'ol/layer/Tile.js'; import SelectionParam from '../../../models/selectionparam.js'; import LayerWms from '../../../models/layers/layerwms.js'; import ImageWMS from 'ol/source/ImageWMS.js'; import ImageLayer from 'ol/layer/Image.js'; // TODO REG : Move to core, because we have a cross-dependency issue with the extlayers component class WmtsManager { map; stateManager; wmtsCapabilitiesByServer = {}; wmtsPromisesByServer = {}; wmtsAbortsByLayer = {}; wmtsLayers = {}; basemapLayers = {}; get state() { return this.stateManager.state; } constructor(map, stateManager) { this.map = map; this.stateManager = stateManager; } removeAllBasemapLayers() { Object.values(this.basemapLayers).forEach((basemap) => { this.map.removeLayer(basemap.olayer); }); this.basemapLayers = {}; } async addLayer(layer) { await this.addLayerInternal(layer, false); this.manageLayerOptions(layer); } addBasemapLayer(basemap) { this.addLayerInternal(basemap, true); } async addLayerInternal(layer, isBasemap) { console.debug(`Adding WMTS Layer ${layer.name} : UniqueID=${layer.layerUniqueId}`); const abortController = new AbortController(); this.wmtsAbortsByLayer[layer.layerUniqueId] = abortController; const capabilities = await this.getWmtsCapabilities(layer.url); if (abortController.signal.aborted) { // The layer was removed during the async loading of Capabilities. // => Do not add the layer console.debug(`Aborting WMTS Layer ${layer.name} : UniqueID=${layer.layerUniqueId}`); return; } // Remove the abort controller from the list. delete this.wmtsAbortsByLayer[layer.layerUniqueId]; console.debug(`Displaying WMTS Layer ${layer.name} : UniqueID=${layer.layerUniqueId}`); let olayer = layer._olayer; if (!olayer) { const options = optionsFromCapabilities(capabilities, { layer: layer.layer, projection: this.state.projection }); if (options === null) { console.warn('Cannot create WMTS layer for layer ' + layer.layer); return; } // Set the right dimensions for (const key in layer.dimensions) { if (key in options.dimensions) { // Update value options.dimensions[key] = layer.dimensions[key]; } else { console.warn('A dimension ' + key + ' was defined for the WMTS layer ' + layer.layer + ' but the server does not seem to accept it.'); } } olayer = new TileLayer({ opacity: layer.opacity, source: new WMTS(options) }); this.enrichWmtsLayerFromCapabilities(layer, olayer, capabilities); layer._olayer = olayer; } // Set zindex for this new layer // (The bigger the order is, the deeper in the map it should be displayed.) // (order is the inverse of z-index) // For basemap, set a minimal number (arbitrary defined to less than -5000) let zindex; if (isBasemap) { this.basemapLayers[layer.layerUniqueId] = { olayer: olayer, layerWmts: layer }; zindex = -5000 - layer.order; } else { this.wmtsLayers[layer.layerUniqueId] = { olayer: olayer, layerWmts: layer }; zindex = -layer.order; } olayer.setZIndex(zindex); // Add to map if (!this.map.getLayers().getArray().includes(olayer)) { this.map.addLayer(olayer); } } refreshZIndexes() { for (const obj of Object.values(this.wmtsLayers)) { const zindex = -obj.layerWmts.order; obj.olayer.setZIndex(zindex); } } enrichWmtsLayerFromCapabilities(layer, olayer, capabilities) { const layers = (capabilities?.Contents).Layer ?? []; const matchLayer = layers.find((elt) => elt.Identifier == layer.layer); if (matchLayer) { olayer.set('capabilitiesStyles', matchLayer.Style); } else { console.warn('No matching layer name in wmts capabilities.'); } } removeLayer(layer) { console.debug(`Removal asked for WMTS Layer ${layer.name} : UniqueID=${layer.layerUniqueId}`); if (layer.layerUniqueId in this.wmtsAbortsByLayer) { // The Capabilities are curently loading. // We cannot remove the layer yet, it has not been added, but we can abort the loading console.debug(`Cancelling WMTS Layer ${layer.name} : UniqueID=${layer.layerUniqueId}`); const abortController = this.wmtsAbortsByLayer[layer.layerUniqueId]; abortController.abort(); } else if (this.layerExists(layer)) { console.debug(`Removing WMTS Layer ${layer.name} : UniqueID=${layer.layerUniqueId}`); const olayer = this.wmtsLayers[layer.layerUniqueId].olayer; delete this.wmtsLayers[layer.layerUniqueId]; this.map.removeLayer(olayer); } else { throw new Error(`Cannot remove ${layer.name}: it does not exist`); } } layerExists(layer) { return layer.layerUniqueId in this.wmtsLayers || layer.layerUniqueId in this.basemapLayers; } getLayer(layer) { if (this.layerExists(layer)) { return this.wmtsLayers[layer.layerUniqueId].olayer; } return null; } changeOpacity(layer) { this.manageLayerOptions(layer); } manageLayerOptions(layer) { if (!this.layerExists(layer)) { throw new Error('Cannot change opacity for this layer: it does not exist'); } const isBasemapLayer = layer.layerUniqueId in this.basemapLayers; if (layer.hasValidOpacity) { const olayer = isBasemapLayer ? this.basemapLayers[layer.layerUniqueId].olayer : this.wmtsLayers[layer.layerUniqueId].olayer; olayer.setOpacity(layer.opacity); } } createSelectionImageLayer(ogcServer, wmsLayer) { return new ImageLayer({ source: new ImageWMS({ url: ogcServer.url, params: { LAYERS: wmsLayer } }) }); } getOrCreateLayersForOgcServer(ogcServer, wmsLayer, ogcServerToWmsLayers, ogcServerToOlLayer) { let layers = ogcServerToWmsLayers.get(ogcServer); if (!layers) { layers = []; ogcServerToWmsLayers.set(ogcServer, layers); ogcServerToOlLayer.set(ogcServer, this.createSelectionImageLayer(ogcServer, wmsLayer)); } return layers; } createQueryableWmsLayer(wmtsLayer, wmsLayer) { const hasQueryResolutionRestriction = wmtsLayer.minQueryResolution !== undefined || wmtsLayer.maxQueryResolution !== undefined; return new LayerWms(0, wmsLayer, 0, wmtsLayer.ogcServer, { queryLayers: wmsLayer, layers: wmsLayer, queryable: true, minResolution: wmtsLayer.minQueryResolution, maxResolution: wmtsLayer.maxQueryResolution, queryLayersRanges: hasQueryResolutionRestriction ? { [wmsLayer]: { minResolution: wmtsLayer.minQueryResolution, maxResolution: wmtsLayer.maxQueryResolution } } : undefined }); } selectFeatures(extent) { const ogcServerToWmsLayers = new Map(); const ogcServerToOlLayer = new Map(); const allWmtsLayers = [...Object.values(this.basemapLayers), ...Object.values(this.wmtsLayers)]; for (const wmtsItem of allWmtsLayers) { const wmtsLayer = wmtsItem.layerWmts; const queryLayers = wmtsLayer.wmsLayers ?? wmtsLayer.queryLayers; if (!queryLayers || !wmtsLayer.ogcServer) { continue; } const wmsLayers = queryLayers.split(','); for (const wmsLayer of wmsLayers) { const layers = this.getOrCreateLayersForOgcServer(wmtsLayer.ogcServer, wmsLayer, ogcServerToWmsLayers, ogcServerToOlLayer); const alreadyExists = layers.some((l) => l.name === wmsLayer); if (!alreadyExists) { layers.push(this.createQueryableWmsLayer(wmtsLayer, wmsLayer)); } } } const selectionParams = []; for (const [ogcServer, wmsLayers] of ogcServerToWmsLayers) { const oLayer = ogcServerToOlLayer.get(ogcServer); selectionParams.push(new SelectionParam(ogcServer, wmsLayers, this.state.projection, extent, oLayer)); } this.state.selection.selectionParameters.push(...selectionParams); } async getWmtsCapabilities(url) { if (url in this.wmtsPromisesByServer) { // Capabilities are currently loading const promise = this.wmtsPromisesByServer[url]; return promise; } if (url in this.wmtsCapabilitiesByServer) { // Capabilities were already loaded const capabilities = this.wmtsCapabilitiesByServer[url]; return Promise.resolve(capabilities); } this.wmtsPromisesByServer[url] = (async () => { // Capabilities were not loaded yet. const response = await fetch(url); const result = await response.text(); // Create new WMTS Layer from Capabilities const parser = new WMTSCapabilities(); const capabilities = parser.read(result); this.wmtsCapabilitiesByServer[url] = capabilities; return capabilities; })(); return this.wmtsPromisesByServer[url]; } } export default WmtsManager;