UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

235 lines (234 loc) 9.43 kB
import WMTS, { optionsFromCapabilities } from 'ol/source/WMTS'; import WMTSCapabilities from 'ol/format/WMTSCapabilities'; import TileLayer from 'ol/layer/Tile'; import SelectionParam from '../../../models/selectionparam'; import StateManager from '../../../tools/state/statemanager'; import LayerWms from '../../../models/layers/layerwms'; import OLayerImage from 'ol/layer/Image'; import OSourceImageWMS from 'ol/source/ImageWMS'; import ServerOgc from '../../../models/serverogc'; class WmtsManager { get state() { return StateManager.getInstance().state; } constructor(map) { Object.defineProperty(this, "map", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "wmtsCapabilitiesByServer", { enumerable: true, configurable: true, writable: true, value: {} }); Object.defineProperty(this, "wmtsPromisesByServer", { enumerable: true, configurable: true, writable: true, value: {} }); Object.defineProperty(this, "wmtsAbortsByLayer", { enumerable: true, configurable: true, writable: true, value: {} }); Object.defineProperty(this, "wmtsLayers", { enumerable: true, configurable: true, writable: true, value: {} }); Object.defineProperty(this, "basemapLayers", { enumerable: true, configurable: true, writable: true, value: {} }); this.map = map; } removeAllBasemapLayers() { Object.values(this.basemapLayers).forEach((basemap) => { this.map.removeLayer(basemap.olayer); }); this.basemapLayers = {}; } addLayer(layer) { this.addLayerInternal(layer, false); } 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; } getLayer(layer) { if (this.layerExists(layer)) { return this.wmtsLayers[layer.layerUniqueId].olayer; } return null; } changeOpacity(layer, opacity) { if (this.layerExists(layer)) { const olayer = this.wmtsLayers[layer.layerUniqueId].olayer; olayer.setOpacity(opacity); } else { throw new Error('Cannot change opacity for this layer: it does not exist'); } } selectFeatures(extent) { const selectionParams = []; const allWmtsLayers = [...Object.values(this.basemapLayers), ...Object.values(this.wmtsLayers)]; // TODO: Pushing 1 selectionParam per WMTS layer: efficient/meaningful? smells bad allWmtsLayers.forEach((wmtsItem) => { const wmtsLayer = wmtsItem.layerWmts; const queryLayers = wmtsLayer.wmsLayers ?? wmtsLayer.queryLayers; if (!queryLayers || !wmtsLayer.ogcServer) { return; } const ogcServer = new ServerOgc(wmtsLayer.ogcServer.name, wmtsLayer.ogcServer); const layers = queryLayers.split(',').map((wmsLayer) => { return new LayerWms(0, wmsLayer, 0, ogcServer, { queryLayers, layers: queryLayers, queryable: true }); }); const oLayer = new OLayerImage({ source: new OSourceImageWMS({ url: ogcServer.url, params: { LAYERS: queryLayers } }) }); selectionParams.push(new SelectionParam(ogcServer, layers, extent, this.state.projection, oLayer)); }); StateManager.getInstance().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;