UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

348 lines (347 loc) 14.7 kB
import { v4 as uuidv4 } from 'uuid'; import GirafeSingleton from '../../base/GirafeSingleton'; import Basemap from '../../models/basemap'; import ConfigManager from '../configuration/configmanager'; import StateManager from '../state/statemanager'; import GroupLayer from '../../models/layers/grouplayer'; import LayerOsm from '../../models/layers/layerosm'; import LayerVectorTiles from '../../models/layers/layervectortiles'; import LayerWmts from '../../models/layers/layerwmts'; import LayerWms from '../../models/layers/layerwms'; import LayerManager from '../layers/layermanager'; import ShareManager from '../share/sharemanager'; import LayerConsts from '../../models/layers/layerconsts'; import LayerCog from '../../models/layers/layercog'; import LayerXYZ from '../../models/layers/layerxyz'; import ServerOgc from '../../models/serverogc'; import ThemeLayer from '../../models/layers/themelayer'; import WfsManager from '../wfs/wfsmanager'; import CustomThemesManager from './customthemesmanager'; import ErrorManager from '../error/errormanager'; class ThemesManager extends GirafeSingleton { get state() { return this.stateManager.state; } constructor(type) { super(type); Object.defineProperty(this, "configManager", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "stateManager", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "layerManager", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "shareManager", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "customThemesManager", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "isInitializingForUserInfo", { enumerable: true, configurable: true, writable: true, value: new WeakMap() }); Object.defineProperty(this, "anonymousUserInfo", { enumerable: true, configurable: true, writable: true, value: { u: 'anonymous' } }); this.configManager = ConfigManager.getInstance(); this.stateManager = StateManager.getInstance(); this.layerManager = LayerManager.getInstance(); this.shareManager = ShareManager.getInstance(); this.customThemesManager = CustomThemesManager.getInstance(); this.stateManager.subscribe('oauth.userInfo', () => { this.initialize(); }); } async initialize() { const userInfo = this.state.oauth.userInfo ?? this.anonymousUserInfo; if (this.isInitializingForUserInfo.get(userInfo)) { // Already initializing for this userinfo return; } this.isInitializingForUserInfo.set(userInfo, true); try { await this.configManager.loadConfig(); await this.loadThemes(); console.log('Themes were loaded'); // We shouldn't set the default theme is there is any configured hash if (this.shareManager.hasSharedState()) { this.shareManager.setStateFromUrl(); } else { this.setDefaultTheme(); } } catch (error) { // Themes could not be loaded console.error(error); await window.gAlert('An error occurred while loading the themes. This has nothing to do with GeoGirafe and is very probably a backend-configuration error (wrong content in themes.json, or CORS error). Please check the backend configuration.', 'Backend error'); await window.gAlert('This instance of GeoGirafe cannot be used at the moment.', 'Backend error'); console.error(error); } finally { this.isInitializingForUserInfo.set(userInfo, false); } } /** * Load themes from backend and configures background layers if needed */ async loadThemes() { this.state.themes.isLoaded = false; const response = await fetch(this.configManager.Config.themes.url); const content = await response.json(); this.state.ogcServers = this.prepareOgcServers(content['ogcServers']); if (this.configManager.Config.basemaps.show) { this.state.basemaps = this.prepareBasemaps(content['background_layers']); // Configure default basemap (only if there is no sharedstate) if (!ShareManager.getInstance().hasSharedState()) { for (const basemap of Object.values(this.state.basemaps)) { if (basemap.name === this.configManager.Config.basemaps.defaultBasemap) { this.state.activeBasemap = basemap; break; } } } } this.state.themes._allThemes = this.prepareThemes(content['themes']); this.customThemesManager.loadCustomThemes(); this.state.themes.isLoaded = true; if (this.configManager.Config.themes.showErrorsOnStart) { // Display themes errors only if configured so. // Parse errors if any for (const error of content['errors']) { ErrorManager.getInstance().pushMessage(uuidv4(), error, 'error'); } } } setDefaultTheme() { // Set default theme if any if (!this.isNullOrUndefinedOrBlank(this.configManager.Config.themes.defaultTheme)) { const themes = [ ...Object.values(this.state.themes._allThemes), ...Object.values(this.customThemesManager.customThemes) ]; const defaultTheme = themes.find((t) => t.name === this.configManager.Config.themes.defaultTheme); if (defaultTheme) { this.state.themes.lastSelectedTheme = defaultTheme; } else { // The default theme was not found console.warn(`The default theme ${this.configManager.Config.themes.defaultTheme} could not be found.`); } } } prepareOgcServers(ogcServerJson) { const servers = {}; if (ogcServerJson) { for (const serverName of Object.keys(ogcServerJson)) { const server = new ServerOgc(serverName, ogcServerJson[serverName]); servers[serverName] = server; } // Preload WFS FeatureInfos this.preloadWfsServer(servers); } return servers; } /** * Preload all WFS DescribeFeatureType * In order to limit the network overload, the servers calls are done sequetially * and each call will wait the previous one to be done */ async preloadWfsServer(ogcServers) { for (const server of Object.values(ogcServers)) { if (server.wfsSupport) { await WfsManager.getInstance().getServerWfs(server); } } } prepareBasemaps(basemapJson) { const basemaps = {}; const basemapMetadata = { isLegendExpanded: false, wasLegendExpanded: false, exclusiveGroup: false, isExpanded: false, isChecked: false }; if (this.configManager.Config.basemaps.emptyBasemap) { // Add an empty basemap const emptyBasemap = new Basemap({ id: 0, name: 'Empty', metadata: { ...basemapMetadata, thumbnail: 'images/basemap_empty.png' } }); basemaps[emptyBasemap.id] = emptyBasemap; } if (this.configManager.Config.basemaps.OSM) { // Add default OSM Option const osmBasemap = new Basemap({ id: -1, name: 'OpenStreetMap', metadata: { ...basemapMetadata, thumbnail: 'images/basemap_osm.png' } }); basemaps[osmBasemap.id] = osmBasemap; osmBasemap.layersList.push(new LayerOsm(0)); } if (this.configManager.Config.basemaps.SwissTopoVectorTiles) { // Add default Vector Tiles const vectorBasemap = new Basemap({ id: -2, name: 'Vector-Tiles', metadata: { ...basemapMetadata, thumbnail: 'images/basemap_vectortiles.png' } }); basemaps[vectorBasemap.id] = vectorBasemap; const vectorTilesLayer = new LayerVectorTiles(LayerConsts.LayerSwisstopoVectorTilesId, 'Vector-Tiles', 0, 'https://vectortiles.geo.admin.ch/styles/ch.swisstopo.leichte-basiskarte.vt/style.json', 'leichtebasiskarte_v3.0.1', { projection: 'EPSG:3857' }); vectorBasemap.layersList.push(vectorTilesLayer); } basemapJson.forEach((elem) => { // Create basemap const basemap = new Basemap(elem); basemaps[basemap.id] = basemap; // List all layers in this basemap const order = { value: 0 }; if (elem.children) { // Multiple layers elem.children.forEach((child) => { const layer = this.prepareThemeLayer(child, null, order); if (layer) { basemap.layersList.push(layer); } }); } else { // Only one layer in this basemap const layer = this.prepareThemeLayer(elem, null, order); if (layer) { basemap.layersList.push(layer); } } }); return basemaps; } prepareThemes(themesJson) { const themes = {}; const order = { value: 0 }; themesJson.forEach((themeJson, index) => { if (!themeJson.icon.startsWith('http') && this.configManager.Config.themes.imagesUrlPrefix) { themeJson.icon = this.configManager.Config.themes.imagesUrlPrefix + themeJson.icon; } const theme = new ThemeLayer(themeJson['id'], themeJson['name'], index, themeJson['icon'], themeJson['metadata']); themeJson.children.forEach((layerJson) => { const layer = this.prepareThemeLayer(layerJson, null, order); if (layer) { layer.parent = theme; theme.children.push(layer); } }); themes[index] = theme; }); return themes; } /** * Will create layer and child layers if elem passed is a group of layers * @param elem either a layer or a group of layers * @param parentServer in case children are not mixed layers, the parentServer will apply for all children * @param order the order in the layer list * @returns the created girafe layer */ prepareThemeLayer(elem, parentServer, order) { // If a server is defined on this node, we use it. // Otherwise, we use the server of the parent const ogcServerName = elem.ogcServer ? elem.ogcServer : parentServer; // Create Layer let layer = null; switch (elem.type) { case 'OSM': { layer = new LayerOsm(order.value); break; } case 'VectorTiles': { const options = { projection: elem.projection, isDefaultChecked: elem.metadata?.isChecked, disclaimer: elem.metadata?.disclaimer, opacity: 1 // TODO REG : Set default opacity }; layer = new LayerVectorTiles(elem.id, elem.name, order.value, elem.style, elem.source, options); break; } case 'WMTS': { const ogcServer = elem.metadata?.ogcServer ? this.state.ogcServers[elem.metadata?.ogcServer] : undefined; layer = new LayerWmts(elem.id, elem.name, order.value, elem.url, elem.layer, elem, ogcServer); break; } case 'WMS': { if (ogcServerName) { const ogcServer = this.state.ogcServers[ogcServerName]; layer = new LayerWms(elem.id, elem.name, order.value, ogcServer, elem); } else { // Layer is invalid : it does not have any OGC-Server ErrorManager.getInstance().pushMessage(uuidv4(), `Layer ${elem.name} (id=${elem.id}) is invalid and cannot be created: missing OGC-Server.`, 'error'); } break; } case 'COG': { layer = new LayerCog(elem.id, elem.name, order.value, elem.url, elem); break; } case 'XYZ': { layer = new LayerXYZ(elem.id, elem.name, order.value, elem.url, elem); break; } default: { // Group const options = { isDefaultChecked: elem.metadata?.isChecked, metadataUrl: elem.metadata?.metadataUrl, disclaimer: elem.metadata?.disclaimer, isDefaultExpanded: elem.metadata?.isExpanded, isExclusiveGroup: elem.metadata?.exclusiveGroup, time: elem.time }; const group = new GroupLayer(elem.id, elem.name, order.value, options); // Append children if (elem.children) { elem.children.forEach((child) => { // Append time options to child if (options.time) { child.time = { ...options.time }; } const childLayer = this.prepareThemeLayer(child, ogcServerName, order); if (childLayer) { childLayer.parent = group; group.children.push(childLayer); } }); } layer = group; } } order.value = order.value + 1; return layer; } } export default ThemesManager;