@geogirafe/lib-geoportal 1.2.0-dev.2813242211 → 1.2.0-dev.2829121016
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/apicontext.d.ts +4 -0
- package/api/apicontext.js +8 -0
- package/api/apigeogirafeapp.js +7 -5
- package/api/style.css +18 -1
- package/assets/i18n/de.json +1 -0
- package/assets/i18n/en.json +1 -0
- package/assets/i18n/fr.json +1 -0
- package/assets/i18n/it.json +1 -0
- package/assets/icons/theme-checked.svg +1 -0
- package/components/drawing/component.d.ts +9 -2
- package/components/drawing/component.js +27 -10
- package/components/drawing-toolbar/component.js +2 -2
- package/components/extlayers/component.d.ts +0 -1
- package/components/extlayers/component.js +1 -6
- package/components/main.d.ts +3 -1
- package/components/main.js +3 -1
- package/components/{map → map-2d}/component.d.ts +9 -24
- package/components/{map → map-2d}/component.js +28 -260
- package/components/map-2d/deprecatedmapcomponent.d.ts +15 -0
- package/components/map-2d/deprecatedmapcomponent.js +24 -0
- package/components/{map → map-2d}/style.css +0 -30
- package/components/{map → map-2d}/tools/swipemanager.d.ts +1 -1
- package/components/map-3d/component.d.ts +50 -0
- package/components/map-3d/component.js +139 -0
- package/components/map-3d/style.css +34 -0
- package/components/map-3d/tools/globe.d.ts +42 -0
- package/components/map-3d/tools/globe.js +198 -0
- package/components/search/component.d.ts +5 -0
- package/components/search/component.js +71 -19
- package/components/search/style.css +36 -9
- package/components/selectiontool/component.d.ts +2 -2
- package/components/selectiontool/component.js +2 -2
- package/components/treeview/style.css +4 -0
- package/components/treeview/treeviewgroup/component.js +1 -1
- package/components/treeview/treeviewitem/component.js +1 -1
- package/components/treeview/treeviewtheme/component.d.ts +2 -0
- package/components/treeview/treeviewtheme/component.js +18 -3
- package/package.json +1 -1
- package/styles/iframe.css +18 -1
- package/styles/index.css +18 -1
- package/styles/mobile.css +18 -1
- package/templates/iframe.html +4 -1
- package/templates/index.html +4 -1
- package/templates/mobile.html +4 -1
- package/templates/public/about.json +1 -1
- package/templates/vite.config.js +43 -25
- package/tools/app/geogirafeapp-mobile.js +7 -2
- package/tools/app/geogirafeapp.js +7 -2
- package/tools/context/context.d.ts +4 -0
- package/tools/context/context.js +8 -0
- package/tools/context/icontext.d.ts +4 -0
- package/tools/drawing/cesiumDrawing.d.ts +9 -5
- package/tools/drawing/cesiumDrawing.js +8 -8
- package/tools/drawing/olDrawing.d.ts +2 -2
- package/tools/drawing/olDrawing.js +1 -1
- package/tools/main.d.ts +2 -0
- package/tools/main.js +2 -0
- package/tools/selection/selectionmanager.d.ts +19 -0
- package/tools/selection/selectionmanager.js +80 -0
- package/tools/state/componentManager.d.ts +1 -1
- package/tools/tests/mockcontext.d.ts +4 -0
- package/tools/tests/mockcontext.js +8 -0
- package/tools/themes/themeshelper.d.ts +44 -1
- package/tools/themes/themeshelper.js +149 -16
- package/{components/map/tools → tools/wmts}/wmtsmanager.d.ts +4 -7
- package/{components/map/tools → tools/wmts}/wmtsmanager.js +7 -10
- /package/components/{map → map-2d}/tools/cogmanager.d.ts +0 -0
- /package/components/{map → map-2d}/tools/cogmanager.js +0 -0
- /package/components/{map → map-2d}/tools/contextmenu.d.ts +0 -0
- /package/components/{map → map-2d}/tools/contextmenu.js +0 -0
- /package/components/{map → map-2d}/tools/drawingmanager.d.ts +0 -0
- /package/components/{map → map-2d}/tools/drawingmanager.js +0 -0
- /package/components/{map → map-2d}/tools/focusfeature.d.ts +0 -0
- /package/components/{map → map-2d}/tools/focusfeature.js +0 -0
- /package/components/{map → map-2d}/tools/osmmanager.d.ts +0 -0
- /package/components/{map → map-2d}/tools/osmmanager.js +0 -0
- /package/components/{map → map-2d}/tools/swipemanager.js +0 -0
- /package/components/{map → map-2d}/tools/vectortilesmanager.d.ts +0 -0
- /package/components/{map → map-2d}/tools/vectortilesmanager.js +0 -0
- /package/components/{map → map-2d}/tools/viewmanager.d.ts +0 -0
- /package/components/{map → map-2d}/tools/viewmanager.js +0 -0
- /package/components/{map → map-2d}/tools/xyzmanager.d.ts +0 -0
- /package/components/{map → map-2d}/tools/xyzmanager.js +0 -0
- /package/components/{map → map-3d}/tools/wmsmanager3d.d.ts +0 -0
- /package/components/{map → map-3d}/tools/wmsmanager3d.js +0 -0
|
@@ -26,17 +26,17 @@ export default class CesiumDrawing {
|
|
|
26
26
|
get config() {
|
|
27
27
|
return this.context.configManager.Config;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* The 3D Map has to be loaded before creating a CesiumDrawing.
|
|
31
|
+
* This class is loaded on demand, so that Cesium stays out of the initial bundle.
|
|
32
|
+
*/
|
|
29
33
|
constructor(map, stateLocation, context) {
|
|
30
34
|
this.stateLocation = stateLocation;
|
|
31
35
|
this.context = context;
|
|
32
|
-
map.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.entities = map.map3d.getDataSourceDisplay().defaultDataSource.entities;
|
|
37
|
-
map.subscribe(`extendedState.${this.stateLocation}.activeTool`, (_oldTool, newTool) => newTool === null ? this.deactivateTool() : this.activateTool(newTool));
|
|
38
|
-
}
|
|
39
|
-
});
|
|
36
|
+
this.scene = map.map3d.getCesiumScene();
|
|
37
|
+
this.handler = new Cesium.ScreenSpaceEventHandler(this.scene.canvas);
|
|
38
|
+
this.entities = map.map3d.getDataSourceDisplay().defaultDataSource.entities;
|
|
39
|
+
map.subscribe(`extendedState.${this.stateLocation}.activeTool`, (_oldTool, newTool) => newTool === null ? this.deactivateTool() : this.activateTool(newTool));
|
|
40
40
|
}
|
|
41
41
|
setFixedLineLength(length) {
|
|
42
42
|
this.fixedLineLength = Number.isNaN(length) ? 0 : length;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import DrawingFeature from './drawingFeature.js';
|
|
2
|
-
import
|
|
2
|
+
import Map2dComponent from '../../components/map-2d/component.js';
|
|
3
3
|
import { Feature } from 'ol';
|
|
4
4
|
import { Geometry } from 'ol/geom.js';
|
|
5
5
|
import VectorSource from 'ol/source/Vector.js';
|
|
@@ -36,7 +36,7 @@ export default class OlDrawing {
|
|
|
36
36
|
private get state();
|
|
37
37
|
private get drawingState();
|
|
38
38
|
private get config();
|
|
39
|
-
constructor(mapComponent:
|
|
39
|
+
constructor(mapComponent: Map2dComponent, stateLocation: string, context: IGirafeContext, deleteHandler: (featureId: string) => void);
|
|
40
40
|
private addModifyInteraction;
|
|
41
41
|
private addSnapInteraction;
|
|
42
42
|
private addTranslateInteraction;
|
|
@@ -11,7 +11,7 @@ import { getPointResolution } from 'ol/proj.js';
|
|
|
11
11
|
import { always, never, platformModifierKeyOnly, primaryAction } from 'ol/events/condition.js';
|
|
12
12
|
import { unByKey } from 'ol/Observable.js';
|
|
13
13
|
import { ensurePolygonIsProperlyClosed, getAreaOfPolygon, getDistance, getHalfPoint, getLabelStyle, getLengthAsMetricText, getRadiusDataForCircle, unByKeyAll } from '../utils/olutils.js';
|
|
14
|
-
import { ContextMenu, EntryInteractionType } from '../../components/map/tools/contextmenu.js';
|
|
14
|
+
import { ContextMenu, EntryInteractionType } from '../../components/map-2d/tools/contextmenu.js';
|
|
15
15
|
import { formatCoordinates } from '../geometrytools.js';
|
|
16
16
|
import { v4 as uuidv4 } from 'uuid';
|
|
17
17
|
import { isAlternateMouseClick, isPrimaryPointerAction } from '../state/userinteractionevent.js';
|
package/tools/main.d.ts
CHANGED
|
@@ -65,6 +65,7 @@ export type { GmfSearchConfig } from './search/gmfsearchclient.js';
|
|
|
65
65
|
export { default as GmfSearchClient } from './search/gmfsearchclient.js';
|
|
66
66
|
export type { SearchProviderConfig } from './search/searchmanager.js';
|
|
67
67
|
export { default as SearchManager } from './search/searchmanager.js';
|
|
68
|
+
export { default as SelectionManager } from './selection/selectionmanager.js';
|
|
68
69
|
export type { default as ISessionManager } from './share/isessionmanager.js';
|
|
69
70
|
export type { UrlShortenerResponse, IUrlShortener } from './share/iurlshortener.js';
|
|
70
71
|
export { default as ActiveBasemapsSerializer } from './share/serializers/activebasemapsserializer.js';
|
|
@@ -148,3 +149,4 @@ export { default as WfsParser } from './wfs/wfsparser.js';
|
|
|
148
149
|
export { default as WmsClient, WmsClientQgis, WmsClientMapServer, WmsClientDefault, WmsClientGeoServer } from './wms/wmsclient.js';
|
|
149
150
|
export { default as WmsLegendHelper } from './wms/wmslegendhelper.js';
|
|
150
151
|
export { default as WmsManager } from './wms/wmsmanager.js';
|
|
152
|
+
export { default as WmtsManager } from './wmts/wmtsmanager.js';
|
package/tools/main.js
CHANGED
|
@@ -52,6 +52,7 @@ export { default as AbstractSearchClient, SEARCH_PROVIDER_DEFAULT, SEARCH_GROUP_
|
|
|
52
52
|
export { default as GeoAdminChSearchClient } from './search/geoadminchsearchclient.js';
|
|
53
53
|
export { default as GmfSearchClient } from './search/gmfsearchclient.js';
|
|
54
54
|
export { default as SearchManager } from './search/searchmanager.js';
|
|
55
|
+
export { default as SelectionManager } from './selection/selectionmanager.js';
|
|
55
56
|
export { default as ActiveBasemapsSerializer } from './share/serializers/activebasemapsserializer.js';
|
|
56
57
|
export { default as CustomLayersSerializer } from './share/serializers/customlayersserializer.js';
|
|
57
58
|
export { default as GlobeSerializer } from './share/serializers/globeserializer.js';
|
|
@@ -113,3 +114,4 @@ export { default as WfsParser } from './wfs/wfsparser.js';
|
|
|
113
114
|
export { default as WmsClient, WmsClientQgis, WmsClientMapServer, WmsClientDefault, WmsClientGeoServer } from './wms/wmsclient.js';
|
|
114
115
|
export { default as WmsLegendHelper } from './wms/wmslegendhelper.js';
|
|
115
116
|
export { default as WmsManager } from './wms/wmsmanager.js';
|
|
117
|
+
export { default as WmtsManager } from './wmts/wmtsmanager.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import GirafeSingleton from '../../base/GirafeSingleton.js';
|
|
2
|
+
/**
|
|
3
|
+
* Handles the feature selection, independently from the map (2D or 3D) the selection was triggered from.
|
|
4
|
+
* The selection is done in two steps:
|
|
5
|
+
* 1. `select()` asks every layer manager to add its own <code>SelectionParam</code> to the state.
|
|
6
|
+
* 2. The change of <code>selection.selectionParameters</code> triggers the effective query
|
|
7
|
+
* (WMS GetFeatureInfo and WFS GetFeature) and writes the resulting features into the state.
|
|
8
|
+
*/
|
|
9
|
+
export default class SelectionManager extends GirafeSingleton {
|
|
10
|
+
private get state();
|
|
11
|
+
initializeSingleton(): void;
|
|
12
|
+
/**
|
|
13
|
+
* Selects all the features of the queryable layers that are inside the given extent.
|
|
14
|
+
* Layers selectable today are WMS, WMTS (with wms layer) and Local files.
|
|
15
|
+
*/
|
|
16
|
+
select(extent: number[]): void;
|
|
17
|
+
selectNone(): void;
|
|
18
|
+
private onSelectFeatures;
|
|
19
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import GirafeSingleton from '../../base/GirafeSingleton.js';
|
|
3
|
+
import { SelectionMode } from '../../models/selection.js';
|
|
4
|
+
import { applyFeaturesToSelection } from '../utils/utils.js';
|
|
5
|
+
/**
|
|
6
|
+
* Handles the feature selection, independently from the map (2D or 3D) the selection was triggered from.
|
|
7
|
+
* The selection is done in two steps:
|
|
8
|
+
* 1. `select()` asks every layer manager to add its own <code>SelectionParam</code> to the state.
|
|
9
|
+
* 2. The change of <code>selection.selectionParameters</code> triggers the effective query
|
|
10
|
+
* (WMS GetFeatureInfo and WFS GetFeature) and writes the resulting features into the state.
|
|
11
|
+
*/
|
|
12
|
+
export default class SelectionManager extends GirafeSingleton {
|
|
13
|
+
get state() {
|
|
14
|
+
return this.context.stateManager.state;
|
|
15
|
+
}
|
|
16
|
+
initializeSingleton() {
|
|
17
|
+
this.context.stateManager.subscribe('selection.selectionParameters', (_, newParams) => {
|
|
18
|
+
// The querying is asynchronous and nobody awaits it here,
|
|
19
|
+
// so a failing request must not end up in an unhandled rejection.
|
|
20
|
+
this.onSelectFeatures(newParams).catch((error) => console.error('Could not select the features.', error));
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Selects all the features of the queryable layers that are inside the given extent.
|
|
25
|
+
* Layers selectable today are WMS, WMTS (with wms layer) and Local files.
|
|
26
|
+
*/
|
|
27
|
+
select(extent) {
|
|
28
|
+
// Reset current selection if SelectionMode is Replace
|
|
29
|
+
if (this.state.selection.selectionMode === SelectionMode.Replace) {
|
|
30
|
+
this.selectNone();
|
|
31
|
+
}
|
|
32
|
+
else if (this.state.selection.selectionMode === SelectionMode.Add) {
|
|
33
|
+
this.state.selection.selectionParameters = [];
|
|
34
|
+
}
|
|
35
|
+
// Use batch for changes to prevent multiple selection of objects
|
|
36
|
+
this.context.stateManager.batchChanges(() => {
|
|
37
|
+
this.context.wmsManager.selectFeatures(extent);
|
|
38
|
+
this.context.wmtsManager.selectFeatures(extent);
|
|
39
|
+
this.context.localFileManager.selectFeatures(extent);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
selectNone() {
|
|
43
|
+
this.state.selection.selectedFeatures = [];
|
|
44
|
+
this.state.selection.selectionParameters = [];
|
|
45
|
+
this.state.selection.highlightedFeatures = [];
|
|
46
|
+
}
|
|
47
|
+
async onSelectFeatures(selectionParams) {
|
|
48
|
+
if (selectionParams.length === 0) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this.state.loading = true;
|
|
52
|
+
try {
|
|
53
|
+
// WMS GetFeatureInfo
|
|
54
|
+
const wmsPromises = selectionParams.map((param) => {
|
|
55
|
+
const wmsGetFeatureInfoSelectionParam = param.clone((l) => l.queryable && l.wmsQueryableOnly);
|
|
56
|
+
const client = this.context.wmsManager.getClient(wmsGetFeatureInfoSelectionParam.ogcServer);
|
|
57
|
+
return client.getFeatureInfo(wmsGetFeatureInfoSelectionParam);
|
|
58
|
+
});
|
|
59
|
+
// WFS GetFeature
|
|
60
|
+
const wfsPromises = selectionParams.map((param) => {
|
|
61
|
+
const wfsGetFeatureInfoSelectionParam = param.clone((l) => l.wfsQueryable);
|
|
62
|
+
const client = this.context.wfsManager.getClient(wfsGetFeatureInfoSelectionParam.ogcServer);
|
|
63
|
+
const features = client.getFeature(wfsGetFeatureInfoSelectionParam);
|
|
64
|
+
return features;
|
|
65
|
+
});
|
|
66
|
+
const wmsGmlFeatures = (await Promise.all(wmsPromises)).flat();
|
|
67
|
+
const wfsGmlFeatures = (await Promise.all(wfsPromises)).flat();
|
|
68
|
+
const gmlFeatures = [...wmsGmlFeatures, ...wfsGmlFeatures];
|
|
69
|
+
if (gmlFeatures.length === 0 && this.state.selection.selectedFeatures.length === 0) {
|
|
70
|
+
this.state.interface.selectionComponentVisible = false;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
applyFeaturesToSelection(gmlFeatures, this.state);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
this.state.loading = false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
|
|
2
2
|
import GirafeSingleton from '../../base/GirafeSingleton.js';
|
|
3
3
|
type Callback = (elem: GirafeHTMLElement) => unknown;
|
|
4
|
-
type Constructor<T> = new (...args:
|
|
4
|
+
type Constructor<T> = new (...args: never[]) => T;
|
|
5
5
|
export default class ComponentManager extends GirafeSingleton {
|
|
6
6
|
private readonly components;
|
|
7
7
|
private readonly registeredCallbacks;
|
|
@@ -29,6 +29,8 @@ import UrlManager from '../url/urlmanager';
|
|
|
29
29
|
import UserDataManager from '../userdata/userdatamanager';
|
|
30
30
|
import WfsManager from '../wfs/wfsmanager';
|
|
31
31
|
import WmsManager from '../wms/wmsmanager';
|
|
32
|
+
import WmtsManager from '../wmts/wmtsmanager';
|
|
33
|
+
import SelectionManager from '../selection/selectionmanager';
|
|
32
34
|
import OnBoardingManager from '../onboarding/onboardingmanager';
|
|
33
35
|
import ThemeFavoritesManager from '../themes/themefavoritesmanager';
|
|
34
36
|
import SearchManager from '../search/searchmanager';
|
|
@@ -62,6 +64,8 @@ export default class MockGirafeContext implements IGirafeContext {
|
|
|
62
64
|
readonly orderingManager: OrderingManager;
|
|
63
65
|
readonly userLayerManager: UserLayerManager;
|
|
64
66
|
readonly wmsManager: WmsManager;
|
|
67
|
+
readonly wmtsManager: WmtsManager;
|
|
68
|
+
readonly selectionManager: SelectionManager;
|
|
65
69
|
readonly ogcApiFeaturesManager: OgcApiFeaturesManager;
|
|
66
70
|
readonly localFileManager: LocalFileManager;
|
|
67
71
|
readonly onBoardingManager: OnBoardingManager;
|
|
@@ -30,6 +30,8 @@ import UrlManager from '../url/urlmanager';
|
|
|
30
30
|
import UserDataManager from '../userdata/userdatamanager';
|
|
31
31
|
import WfsManager from '../wfs/wfsmanager';
|
|
32
32
|
import WmsManager from '../wms/wmsmanager';
|
|
33
|
+
import WmtsManager from '../wmts/wmtsmanager';
|
|
34
|
+
import SelectionManager from '../selection/selectionmanager';
|
|
33
35
|
import { MockConfig } from './mockconfig';
|
|
34
36
|
import OnBoardingManager from '../onboarding/onboardingmanager';
|
|
35
37
|
import ThemeFavoritesManager from '../themes/themefavoritesmanager';
|
|
@@ -64,6 +66,8 @@ export default class MockGirafeContext {
|
|
|
64
66
|
orderingManager;
|
|
65
67
|
userLayerManager;
|
|
66
68
|
wmsManager;
|
|
69
|
+
wmtsManager;
|
|
70
|
+
selectionManager;
|
|
67
71
|
ogcApiFeaturesManager;
|
|
68
72
|
localFileManager;
|
|
69
73
|
onBoardingManager;
|
|
@@ -99,6 +103,8 @@ export default class MockGirafeContext {
|
|
|
99
103
|
this.customThemesManager = new CustomThemesManager(this);
|
|
100
104
|
this.themesManager = new ThemesManager(this);
|
|
101
105
|
this.wmsManager = new WmsManager(this);
|
|
106
|
+
this.wmtsManager = new WmtsManager(this);
|
|
107
|
+
this.selectionManager = new SelectionManager(this);
|
|
102
108
|
this.localFileManager = new LocalFileManager(this);
|
|
103
109
|
this.ogcApiFeaturesManager = new OgcApiFeaturesManager(this);
|
|
104
110
|
this.onBoardingManager = new OnBoardingManager(this);
|
|
@@ -143,7 +149,9 @@ export default class MockGirafeContext {
|
|
|
143
149
|
this.customThemesManager.initializeSingleton();
|
|
144
150
|
this.themesManager.initializeSingleton();
|
|
145
151
|
this.wmsManager.initializeSingleton();
|
|
152
|
+
this.wmtsManager.initializeSingleton();
|
|
146
153
|
this.localFileManager.initializeSingleton();
|
|
154
|
+
this.selectionManager.initializeSingleton();
|
|
147
155
|
this.ogcApiFeaturesManager.initializeSingleton();
|
|
148
156
|
this.onBoardingManager.initializeSingleton();
|
|
149
157
|
this.themeFavoritesManager.initializeSingleton();
|
|
@@ -11,13 +11,48 @@ export default class ThemesHelper extends GirafeSingleton {
|
|
|
11
11
|
initializeSingleton(): void;
|
|
12
12
|
private get state();
|
|
13
13
|
findBaseLayerById(layerId: number): BaseLayer;
|
|
14
|
-
|
|
14
|
+
findBaseLayerRecursiveById(layers: BaseLayer[], layerId: number): BaseLayer | null;
|
|
15
15
|
findThemeByName(themename: string): ThemeLayer | null;
|
|
16
16
|
findGroupByName(groupname: string): GroupLayer | null;
|
|
17
17
|
findLayerByName(layername: string): Layer | null;
|
|
18
18
|
private findBaseLayerByName;
|
|
19
19
|
private findLayerRecursive;
|
|
20
|
+
/**
|
|
21
|
+
* @param layer any layer or layer group (or theme) to find the related theme.
|
|
22
|
+
* @returns The original theme configuration.
|
|
23
|
+
*/
|
|
24
|
+
getOriginalTheme(layer: BaseLayer): ThemeLayer | null;
|
|
25
|
+
/**
|
|
26
|
+
* Count the layer in both the given theme and the matching original theme.
|
|
27
|
+
* @param theme the theme to check.
|
|
28
|
+
* @returns true if the count is the same. False otherwise.
|
|
29
|
+
*/
|
|
30
|
+
isThemeComplete(theme: ThemeLayer): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* @return the number of all sublayers (recursively) of the given layer.
|
|
33
|
+
* @private
|
|
34
|
+
*/
|
|
35
|
+
private countLayer;
|
|
36
|
+
/**
|
|
37
|
+
* Add the related theme if it's not already (even partially) in the layertree. If it's
|
|
38
|
+
* already in it, don't add it, warn the user, and highlight it.
|
|
39
|
+
* @returns true if the related theme is added to the layertree, false otherwise.
|
|
40
|
+
*/
|
|
41
|
+
addThemeIfNotPresent(theme: ThemeLayer): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Add the missing layers of an in-use theme, from an original theme description.
|
|
44
|
+
* The added layers are deactivated.
|
|
45
|
+
* @param originalTheme a Theme object, or model, as initially defined.
|
|
46
|
+
*/
|
|
47
|
+
completeTheme(originalTheme: ThemeLayer): void;
|
|
48
|
+
/**
|
|
49
|
+
* Deactivate all layers and sublayer recursively.
|
|
50
|
+
* Meaning state="off" and isDefaultChecked=false.
|
|
51
|
+
* @private
|
|
52
|
+
*/
|
|
53
|
+
private deactivateAllLayers;
|
|
20
54
|
private onSelectedThemeChanged;
|
|
55
|
+
private getThemeInLayersList;
|
|
21
56
|
private onThemeChanged;
|
|
22
57
|
getMinimalClonedThemeForLayer(layer: BaseLayer): ThemeLayer;
|
|
23
58
|
private removeOthers;
|
|
@@ -50,6 +85,14 @@ export default class ThemesHelper extends GirafeSingleton {
|
|
|
50
85
|
private addLayerToLayerTree;
|
|
51
86
|
removeLayersFromLayerTree(layersToRemove: BaseLayer[]): void;
|
|
52
87
|
private removeLayersFromExistingLayerTree;
|
|
88
|
+
/**
|
|
89
|
+
* Find the initial position of the layer (relative to its sibling layers) by looking
|
|
90
|
+
* at the original theme object and insert the layer following this original order.
|
|
91
|
+
* (The "layer.order" value is incremented with the user operations, that's why we can
|
|
92
|
+
* rely on it for new layers and must adapt it to the existing sibling layers.)
|
|
93
|
+
* @private
|
|
94
|
+
*/
|
|
95
|
+
private insertLayerToLayerTreeAtInitialPosition;
|
|
53
96
|
/**
|
|
54
97
|
* Removes all layers from the layer tree except themes that are pinned.
|
|
55
98
|
*/
|
|
@@ -93,6 +93,97 @@ export default class ThemesHelper extends GirafeSingleton {
|
|
|
93
93
|
}
|
|
94
94
|
return null;
|
|
95
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* @param layer any layer or layer group (or theme) to find the related theme.
|
|
98
|
+
* @returns The original theme configuration.
|
|
99
|
+
*/
|
|
100
|
+
getOriginalTheme(layer) {
|
|
101
|
+
const hierarchy = this.getHierarchyFromLayer(layer);
|
|
102
|
+
const theme = hierarchy?.length ? hierarchy[0] : undefined;
|
|
103
|
+
return theme ? this.findThemeByName(theme.name) : null;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Count the layer in both the given theme and the matching original theme.
|
|
107
|
+
* @param theme the theme to check.
|
|
108
|
+
* @returns true if the count is the same. False otherwise.
|
|
109
|
+
*/
|
|
110
|
+
isThemeComplete(theme) {
|
|
111
|
+
const originalTheme = this.getOriginalTheme(theme);
|
|
112
|
+
if (!originalTheme) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
const themeCount = this.countLayer(theme);
|
|
116
|
+
const originalThemeCount = this.countLayer(originalTheme);
|
|
117
|
+
return themeCount === originalThemeCount;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* @return the number of all sublayers (recursively) of the given layer.
|
|
121
|
+
* @private
|
|
122
|
+
*/
|
|
123
|
+
countLayer(layer) {
|
|
124
|
+
let count = 1;
|
|
125
|
+
const layerAsUnknownGroup = layer;
|
|
126
|
+
if (layerAsUnknownGroup.children) {
|
|
127
|
+
layerAsUnknownGroup.children.forEach((child) => {
|
|
128
|
+
count += this.countLayer(child);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return count;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Add the related theme if it's not already (even partially) in the layertree. If it's
|
|
135
|
+
* already in it, don't add it, warn the user, and highlight it.
|
|
136
|
+
* @returns true if the related theme is added to the layertree, false otherwise.
|
|
137
|
+
*/
|
|
138
|
+
addThemeIfNotPresent(theme) {
|
|
139
|
+
const themeInLayersList = this.getThemeInLayersList(theme);
|
|
140
|
+
// The theme is not yet added, add it!
|
|
141
|
+
if (themeInLayersList === undefined) {
|
|
142
|
+
if (this.state.themes.lastSelectedTheme?.id === theme.id) {
|
|
143
|
+
this.state.themes.lastSelectedTheme = null;
|
|
144
|
+
}
|
|
145
|
+
this.state.themes.lastSelectedTheme = theme;
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
// The theme is in the layer tree, then warn the user.
|
|
149
|
+
themeInLayersList.isHighlighted = true;
|
|
150
|
+
const layerName = this.context.i18nManager.getTranslation(theme.name);
|
|
151
|
+
const msg = this.context.i18nManager
|
|
152
|
+
.getTranslation('The theme {name} is already present in the treeview.')
|
|
153
|
+
.replace('{name}', layerName);
|
|
154
|
+
this.state.infobox.elements.push({
|
|
155
|
+
id: theme.treeItemId,
|
|
156
|
+
text: msg,
|
|
157
|
+
type: 'info',
|
|
158
|
+
duration: 5000
|
|
159
|
+
});
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Add the missing layers of an in-use theme, from an original theme description.
|
|
164
|
+
* The added layers are deactivated.
|
|
165
|
+
* @param originalTheme a Theme object, or model, as initially defined.
|
|
166
|
+
*/
|
|
167
|
+
completeTheme(originalTheme) {
|
|
168
|
+
const clonedTheme = originalTheme.clone();
|
|
169
|
+
this.deactivateAllLayers(clonedTheme);
|
|
170
|
+
this.mergeLayerWithExistingLayerTree(clonedTheme, this.state.layers.layersList, false);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Deactivate all layers and sublayer recursively.
|
|
174
|
+
* Meaning state="off" and isDefaultChecked=false.
|
|
175
|
+
* @private
|
|
176
|
+
*/
|
|
177
|
+
deactivateAllLayers(layer) {
|
|
178
|
+
layer.activeState = 'off';
|
|
179
|
+
layer.isDefaultChecked = false;
|
|
180
|
+
const layerAsUnknownGroup = layer;
|
|
181
|
+
if (layerAsUnknownGroup.children) {
|
|
182
|
+
layerAsUnknownGroup.children.forEach((child) => {
|
|
183
|
+
this.deactivateAllLayers(child);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
96
187
|
onSelectedThemeChanged(theme) {
|
|
97
188
|
if (!theme) {
|
|
98
189
|
// Theme is null, nothing to do here
|
|
@@ -108,7 +199,11 @@ export default class ThemesHelper extends GirafeSingleton {
|
|
|
108
199
|
this.onThemeChanged(theme);
|
|
109
200
|
}
|
|
110
201
|
}
|
|
202
|
+
getThemeInLayersList(theme) {
|
|
203
|
+
return this.state.layers.layersList.find((l) => l.id == theme.id);
|
|
204
|
+
}
|
|
111
205
|
onThemeChanged(theme) {
|
|
206
|
+
const themeInLayersList = this.getThemeInLayersList(theme);
|
|
112
207
|
// Create a clone of the theme object to use it in the treeview.
|
|
113
208
|
// This is essential, otherwise all changes are done in the layers
|
|
114
209
|
// (For example, when expanding legend, expanding a group, or activating the layer).
|
|
@@ -116,20 +211,10 @@ export default class ThemesHelper extends GirafeSingleton {
|
|
|
116
211
|
// And when a theme is selected aging from the themes-selector
|
|
117
212
|
// The default configuration will have been overwritten.
|
|
118
213
|
const clonedTheme = theme.clone();
|
|
119
|
-
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const msg = this.context.i18nManager
|
|
124
|
-
.getTranslation('The theme {name} is already present in the treeview.')
|
|
125
|
-
.replace('{name}', layername);
|
|
126
|
-
this.state.infobox.elements.push({
|
|
127
|
-
id: theme.treeItemId,
|
|
128
|
-
text: msg,
|
|
129
|
-
type: 'info',
|
|
130
|
-
duration: 5000
|
|
131
|
-
});
|
|
132
|
-
return;
|
|
214
|
+
// Delete (replace) this theme if it already exists.
|
|
215
|
+
if (themeInLayersList !== undefined) {
|
|
216
|
+
this.context.stateManager.batchChanges(() => this.removeLayersFromLayerTree([themeInLayersList]));
|
|
217
|
+
clonedTheme.order = themeInLayersList.order;
|
|
133
218
|
}
|
|
134
219
|
if (this.context.configManager.Config.themes.selectionMode === 'replace') {
|
|
135
220
|
// Mode is <replace>
|
|
@@ -138,7 +223,6 @@ export default class ThemesHelper extends GirafeSingleton {
|
|
|
138
223
|
}
|
|
139
224
|
else {
|
|
140
225
|
// Mode is <add>
|
|
141
|
-
clonedTheme.order = this.getInitialOrderForNewTheme();
|
|
142
226
|
this.state.layers.layersList.push(clonedTheme);
|
|
143
227
|
}
|
|
144
228
|
const themeFunctionalities = this.state.themes._allFunctionalities[theme.id];
|
|
@@ -384,7 +468,7 @@ export default class ThemesHelper extends GirafeSingleton {
|
|
|
384
468
|
if (parent) {
|
|
385
469
|
newLayer.parent = parent;
|
|
386
470
|
}
|
|
387
|
-
|
|
471
|
+
this.insertLayerToLayerTreeAtInitialPosition(newLayer, existingList);
|
|
388
472
|
if (activate) {
|
|
389
473
|
this.context.layerManager.toggle(newLayer, 'on');
|
|
390
474
|
}
|
|
@@ -411,6 +495,55 @@ export default class ThemesHelper extends GirafeSingleton {
|
|
|
411
495
|
}
|
|
412
496
|
}
|
|
413
497
|
}
|
|
498
|
+
/**
|
|
499
|
+
* Find the initial position of the layer (relative to its sibling layers) by looking
|
|
500
|
+
* at the original theme object and insert the layer following this original order.
|
|
501
|
+
* (The "layer.order" value is incremented with the user operations, that's why we can
|
|
502
|
+
* rely on it for new layers and must adapt it to the existing sibling layers.)
|
|
503
|
+
* @private
|
|
504
|
+
*/
|
|
505
|
+
insertLayerToLayerTreeAtInitialPosition(newLayer, existingList) {
|
|
506
|
+
const originalLayer = this.findBaseLayerById(newLayer.id);
|
|
507
|
+
const originalParent = originalLayer.parent;
|
|
508
|
+
if (!originalParent || existingList.length === 0) {
|
|
509
|
+
existingList.push(newLayer);
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
// Following the original order, find the nearest previous or next sibling layer in the existingList.
|
|
513
|
+
const originalSiblings = originalParent.children;
|
|
514
|
+
const existingListIds = new Set(existingList.map((layer) => layer.id));
|
|
515
|
+
let previousOriginalSibling;
|
|
516
|
+
let nextOriginalSibling;
|
|
517
|
+
let originalLayerEncountered = false;
|
|
518
|
+
originalSiblings.some((oLayer) => {
|
|
519
|
+
if (originalLayerEncountered && (previousOriginalSibling || nextOriginalSibling)) {
|
|
520
|
+
return true;
|
|
521
|
+
}
|
|
522
|
+
if (oLayer.id === newLayer.id) {
|
|
523
|
+
originalLayerEncountered = true;
|
|
524
|
+
}
|
|
525
|
+
else if (existingListIds.has(oLayer.id)) {
|
|
526
|
+
if (originalLayerEncountered) {
|
|
527
|
+
nextOriginalSibling = oLayer;
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
previousOriginalSibling = oLayer;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
// Find back the matching sibling layer in the ExistingList and update the "newLayer.order"
|
|
535
|
+
// to be just before or just after the sibling layer.
|
|
536
|
+
if (previousOriginalSibling) {
|
|
537
|
+
const previousSibling = existingList.find((layer) => layer.id === previousOriginalSibling?.id);
|
|
538
|
+
newLayer.order = previousSibling?.order ? previousSibling.order : newLayer.order;
|
|
539
|
+
}
|
|
540
|
+
else if (nextOriginalSibling) {
|
|
541
|
+
const nextSibling = existingList.find((layer) => layer.id === nextOriginalSibling?.id);
|
|
542
|
+
newLayer.order = nextSibling?.order ? nextSibling.order - 1 : newLayer.order;
|
|
543
|
+
}
|
|
544
|
+
// Push the layer in the list. The orderManager will move the layer regarding the order values.
|
|
545
|
+
existingList.push(newLayer);
|
|
546
|
+
}
|
|
414
547
|
/**
|
|
415
548
|
* Removes all layers from the layer tree except themes that are pinned.
|
|
416
549
|
*/
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import WMTS from 'ol/source/WMTS.js';
|
|
2
2
|
import TileLayer from 'ol/layer/Tile.js';
|
|
3
|
-
import type { Map as olMap } from 'ol';
|
|
4
3
|
import type { Layer as OLayer } from 'ol/layer.js';
|
|
5
|
-
import type LayerWmts from '
|
|
6
|
-
import
|
|
7
|
-
declare class WmtsManager {
|
|
8
|
-
map: olMap;
|
|
9
|
-
private readonly stateManager;
|
|
4
|
+
import type LayerWmts from '../../models/layers/layerwmts.js';
|
|
5
|
+
import GirafeSingleton from '../../base/GirafeSingleton.js';
|
|
6
|
+
declare class WmtsManager extends GirafeSingleton {
|
|
10
7
|
wmtsCapabilitiesByServer: Record<string, Record<string, unknown>>;
|
|
11
8
|
wmtsPromisesByServer: Record<string, Promise<Record<string, unknown>>>;
|
|
12
9
|
wmtsAbortsByLayer: Record<string, AbortController>;
|
|
@@ -19,7 +16,7 @@ declare class WmtsManager {
|
|
|
19
16
|
layerWmts: LayerWmts;
|
|
20
17
|
}>;
|
|
21
18
|
private get state();
|
|
22
|
-
|
|
19
|
+
private get map();
|
|
23
20
|
removeAllBasemapLayers(): void;
|
|
24
21
|
addLayer(layer: LayerWmts): Promise<void>;
|
|
25
22
|
addBasemapLayer(basemap: LayerWmts): void;
|
|
@@ -2,25 +2,22 @@
|
|
|
2
2
|
import WMTS, { optionsFromCapabilities } from 'ol/source/WMTS.js';
|
|
3
3
|
import WMTSCapabilities from 'ol/format/WMTSCapabilities.js';
|
|
4
4
|
import TileLayer from 'ol/layer/Tile.js';
|
|
5
|
-
import SelectionParam from '
|
|
6
|
-
import LayerWms from '
|
|
5
|
+
import SelectionParam from '../../models/selectionparam.js';
|
|
6
|
+
import LayerWms from '../../models/layers/layerwms.js';
|
|
7
7
|
import ImageWMS from 'ol/source/ImageWMS.js';
|
|
8
8
|
import ImageLayer from 'ol/layer/Image.js';
|
|
9
|
-
|
|
10
|
-
class WmtsManager {
|
|
11
|
-
map;
|
|
12
|
-
stateManager;
|
|
9
|
+
import GirafeSingleton from '../../base/GirafeSingleton.js';
|
|
10
|
+
class WmtsManager extends GirafeSingleton {
|
|
13
11
|
wmtsCapabilitiesByServer = {};
|
|
14
12
|
wmtsPromisesByServer = {};
|
|
15
13
|
wmtsAbortsByLayer = {};
|
|
16
14
|
wmtsLayers = {};
|
|
17
15
|
basemapLayers = {};
|
|
18
16
|
get state() {
|
|
19
|
-
return this.stateManager.state;
|
|
17
|
+
return this.context.stateManager.state;
|
|
20
18
|
}
|
|
21
|
-
|
|
22
|
-
this.
|
|
23
|
-
this.stateManager = stateManager;
|
|
19
|
+
get map() {
|
|
20
|
+
return this.context.mapManager.getMap();
|
|
24
21
|
}
|
|
25
22
|
removeAllBasemapLayers() {
|
|
26
23
|
Object.values(this.basemapLayers).forEach((basemap) => {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|