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