@geogirafe/lib-geoportal 1.2.0-dev.2800201562 → 1.2.0-dev.2816732111

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.
Files changed (70) hide show
  1. package/api/apicontext.d.ts +4 -0
  2. package/api/apicontext.js +8 -0
  3. package/api/apigeogirafeapp.js +7 -5
  4. package/api/style.css +18 -1
  5. package/components/drawing/component.d.ts +9 -2
  6. package/components/drawing/component.js +27 -10
  7. package/components/drawing-toolbar/component.js +2 -2
  8. package/components/extlayers/component.d.ts +0 -1
  9. package/components/extlayers/component.js +1 -6
  10. package/components/main.d.ts +3 -1
  11. package/components/main.js +3 -1
  12. package/components/{map → map-2d}/component.d.ts +9 -24
  13. package/components/{map → map-2d}/component.js +28 -260
  14. package/components/map-2d/deprecatedmapcomponent.d.ts +15 -0
  15. package/components/map-2d/deprecatedmapcomponent.js +24 -0
  16. package/components/{map → map-2d}/style.css +0 -30
  17. package/components/{map → map-2d}/tools/swipemanager.d.ts +1 -1
  18. package/components/map-3d/component.d.ts +50 -0
  19. package/components/map-3d/component.js +139 -0
  20. package/components/map-3d/style.css +34 -0
  21. package/components/map-3d/tools/globe.d.ts +42 -0
  22. package/components/map-3d/tools/globe.js +198 -0
  23. package/components/selectiontool/component.d.ts +2 -2
  24. package/components/selectiontool/component.js +2 -2
  25. package/package.json +1 -1
  26. package/styles/iframe.css +18 -1
  27. package/styles/index.css +18 -1
  28. package/styles/mobile.css +18 -1
  29. package/templates/iframe.html +4 -1
  30. package/templates/index.html +4 -1
  31. package/templates/mobile.html +4 -1
  32. package/templates/public/about.json +1 -1
  33. package/templates/vite.config.js +43 -25
  34. package/tools/app/geogirafeapp-mobile.js +7 -2
  35. package/tools/app/geogirafeapp.js +7 -2
  36. package/tools/context/context.d.ts +4 -0
  37. package/tools/context/context.js +8 -0
  38. package/tools/context/icontext.d.ts +5 -0
  39. package/tools/drawing/cesiumDrawing.d.ts +9 -5
  40. package/tools/drawing/cesiumDrawing.js +8 -8
  41. package/tools/drawing/olDrawing.d.ts +2 -2
  42. package/tools/drawing/olDrawing.js +1 -1
  43. package/tools/main.d.ts +2 -0
  44. package/tools/main.js +2 -0
  45. package/tools/selection/selectionmanager.d.ts +19 -0
  46. package/tools/selection/selectionmanager.js +80 -0
  47. package/tools/state/componentManager.d.ts +1 -1
  48. package/tools/tests/mockcontext.d.ts +4 -0
  49. package/tools/tests/mockcontext.js +8 -0
  50. package/{components/map/tools → tools/wmts}/wmtsmanager.d.ts +4 -7
  51. package/{components/map/tools → tools/wmts}/wmtsmanager.js +7 -10
  52. /package/components/{map → map-2d}/tools/cogmanager.d.ts +0 -0
  53. /package/components/{map → map-2d}/tools/cogmanager.js +0 -0
  54. /package/components/{map → map-2d}/tools/contextmenu.d.ts +0 -0
  55. /package/components/{map → map-2d}/tools/contextmenu.js +0 -0
  56. /package/components/{map → map-2d}/tools/drawingmanager.d.ts +0 -0
  57. /package/components/{map → map-2d}/tools/drawingmanager.js +0 -0
  58. /package/components/{map → map-2d}/tools/focusfeature.d.ts +0 -0
  59. /package/components/{map → map-2d}/tools/focusfeature.js +0 -0
  60. /package/components/{map → map-2d}/tools/osmmanager.d.ts +0 -0
  61. /package/components/{map → map-2d}/tools/osmmanager.js +0 -0
  62. /package/components/{map → map-2d}/tools/swipemanager.js +0 -0
  63. /package/components/{map → map-2d}/tools/vectortilesmanager.d.ts +0 -0
  64. /package/components/{map → map-2d}/tools/vectortilesmanager.js +0 -0
  65. /package/components/{map → map-2d}/tools/viewmanager.d.ts +0 -0
  66. /package/components/{map → map-2d}/tools/viewmanager.js +0 -0
  67. /package/components/{map → map-2d}/tools/xyzmanager.d.ts +0 -0
  68. /package/components/{map → map-2d}/tools/xyzmanager.js +0 -0
  69. /package/components/{map → map-3d}/tools/wmsmanager3d.d.ts +0 -0
  70. /package/components/{map → map-3d}/tools/wmsmanager3d.js +0 -0
@@ -0,0 +1,139 @@
1
+ import { html as uHtml } from 'uhtml';
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
4
+ import LayerWms from '../../models/layers/layerwms.js';
5
+ /**
6
+ * The 3D Map.
7
+ *
8
+ * The Component itself stays lightweight: Cesium and OLCesium are only imported when the 3D Map
9
+ * is displayed for the first time. Everything that needs those libraries lives in ./tools/globe.
10
+ * The synchronization with the 2D Map is done by OLCesium, through the OpenLayers map of the
11
+ * MapManager. There is therefore no direct dependency to the map-2d Component.
12
+ */
13
+ export default class Map3dComponent extends GirafeHTMLElement {
14
+ templateUrl = null;
15
+ styleUrls = null;
16
+ template = () => {
17
+ return uHtml `<style>
18
+ .hidden{display:none!important}.gg-rotate90{transform:rotate(90deg)}.gg-rotate180{transform:rotate(180deg)}.gg-rotate270{transform:rotate(270deg)}img{filter:var(--svg-filter)}img.legend-image{filter:var(--svg-map-filter);background:var(--svg-legend-bkg)}div{scrollbar-width:thin}a,a:visited{color:var(--link-color)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes spin-wait{0%{transform:rotate(0)}7%{transform:rotate(360deg)}to{transform:rotate(360deg)}}.gg-spin{animation-name:spin;animation-duration:2s;animation-timing-function:linear;animation-iteration-count:infinite}.gg-spin-wait{animation-name:spin-wait;animation-duration:10s;animation-timing-function:linear;animation-iteration-count:infinite}::-webkit-scrollbar{width:5px}::-webkit-scrollbar-thumb{background:#999}button,input,select,textarea{font:inherit}.gg-button,.gg-select,.gg-input,.gg-textarea{background-color:var(--bkg-color);color:var(--text-color);border:var(--app-standard-border);box-sizing:border-box;cursor:pointer;border-radius:3px;outline:0;margin:0;padding:0 0 0 .5rem;display:inline-block}.gg-label{background-color:var(--bkg-color);color:var(--text-color);border:none;align-items:center;margin:0;padding:0;display:flex}.gg-button,.gg-select,.gg-input,.gg-label{min-height:calc(var(--app-standard-height) / 1.5)}.gg-textarea{max-height:initial;resize:vertical;height:6rem;padding:.5rem;line-height:1.3rem}.gg-input{cursor:text}.gg-checkbox{accent-color:var(--text-color);width:1.2rem}.gg-range{accent-color:var(--text-color)}.gg-button{padding:0 .5rem}.gg-button.active{border:solid 1px var(--text-color-grad1);background-color:var(--bkg-color-grad2)}.gg-button:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed;border:none}.gg-button:disabled img{filter:opacity(.6)}.gg-input:disabled,.gg-select:disabled,.gg-textarea:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed}.gg-button>img{vertical-align:middle}.gg-icon-button{color:var(--text-color);cursor:pointer;background-color:#0000;border:none;flex-direction:column;justify-content:center;align-items:center;padding:0;display:flex}.gg-icon{justify-content:center;align-items:center;display:flex}.gg-big,.gg-big-withtext{min-width:var(--app-standard-height);min-height:var(--app-standard-height);max-height:var(--app-standard-height)}.gg-big img,.gg-big-withtext img{width:calc(var(--app-standard-height) - 1.5rem);margin:0}.gg-big-withtext span{font-variant:small-caps;padding:0 1rem;font-size:.9rem}.gg-medium,.gg-medium-withtext{min-width:calc(var(--app-standard-height) / 1.2);min-height:calc(var(--app-standard-height) / 1.2);max-height:calc(var(--app-standard-height) / 1.2);flex-direction:row}.gg-medium img{width:calc(var(--app-standard-height) / 2.4);margin:0}.gg-medium-withtext img{width:calc(var(--app-standard-height) / 2.4);margin-left:.5rem}.gg-medium-withtext span{padding:0 1rem 0 .5rem;font-size:.9rem}.gg-small,.gg-small-withtext{min-width:calc(var(--app-standard-height) / 2);min-height:calc(var(--app-standard-height) / 2);max-height:calc(var(--app-standard-height) / 2);flex-direction:row}.gg-small img{width:calc(var(--app-standard-height) / 3);margin:0}.gg-small-withtext img{width:calc(var(--app-standard-height) / 3);margin-left:.5rem}.gg-small-withtext span{padding:0 .5rem 0 .3rem;font-size:.9rem}.gg-button:hover:not(:disabled),.gg-select:hover:not(:disabled),.gg-input:hover:not(:disabled),.gg-textarea:hover:not(:disabled),.gg-icon-button:hover:not(:disabled){background-color:var(--bkg-color-grad1)}.gg-opacity{opacity:.5}.gg-opacity:hover{opacity:1;background-color:#0000}.gg-tabs{cursor:pointer;grid-auto-flow:column;padding-bottom:1rem;font-size:1rem;display:grid}.gg-tab{border:none;border-bottom:var(--app-standard-border);cursor:pointer;color:var(--text-color);background:0 0;padding:.5rem}.gg-tab.active{border-bottom:solid 1px var(--text-color)}.girafe-button-big,.girafe-button-large,.girafe-button-small,.girafe-button-tiny{color:var(--text-color);background-color:#0000;border:none;flex-direction:column;display:flex}.girafe-button-big:hover,.girafe-button-large:hover,.girafe-button-small:hover,.girafe-button-tiny:hover{background-color:var(--bkg-color-grad1);cursor:pointer}.girafe-button-big.dark,.girafe-button-large.dark,.girafe-button-small.dark,.girafe-button-tiny.dark{background-color:var(--bkg-color);filter:invert()}.girafe-button-big{width:var(--app-standard-height);height:var(--app-standard-height);align-items:center;padding:1rem}.girafe-button-big img{overflow:hidden}.girafe-button-large{flex-direction:row}.girafe-button-large img{height:2rem;margin:.3rem}.girafe-button-large span{height:2rem;margin:.3rem;line-height:2rem}.girafe-button-small{min-width:calc(var(--app-standard-height) / 2);height:calc(var(--app-standard-height) / 2);align-items:center;padding:.5rem}.girafe-button-small img{overflow:hidden}.girafe-button-small span{text-align:left;text-overflow:ellipsis;width:100%;overflow:hidden}.girafe-button-tiny{align-items:center;width:1rem;height:1rem;padding:0}.girafe-button-tiny img{overflow:hidden}.girafe-onboarding-theme{background-color:var(--bkg-color)!important;color:var(--text-color)!important}.girafe-onboarding-theme button{background-color:var(--bkg-color)!important;color:var(--text-color)!important;text-shadow:none!important}.girafe-onboarding-theme button.driver-popover-close-btn{z-index:10000}.tippy-box{background-color:var(--bkg-color)!important;border:var(--app-standard-border)!important}.tippy-content{color:var(--text-color)!important}.tippy-arrow{color:var(--bkg-color)!important}.tippy-box[data-theme~=error]{background-color:var(--error-color);white-space:pre-line}div.tippy-box[data-theme=image-popup]{border:var(--app-standard-border)!important;& div.tippy-content{background-color:var(--bkg-color)!important;& img{max-width:20rem;max-height:20rem}}& div.tippy-arrow{color:var(--bkg-color)!important}}
19
+ </style><style>
20
+ #cs-map{background-color:var(--bkg-color);width:100%;height:100%;position:absolute;top:0;overflow:hidden}.hidden{display:none}.center{text-align:center;margin:10px;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.loading span{color:var(--text-color);margin-top:1rem;font-weight:600;display:block}.loading span.quote{font-style:italic;font-weight:300}
21
+ </style>
22
+ <style>${this.customStyle}</style>
23
+ <div id="cs-map"><div class="${this.loading ? 'loading center' : 'loading hidden'}"><img alt="loading-icon" src="icons/loading.svg" class="gg-spin"> <span i18n="Loading cesium...">Loading cesium...</span> <span class="quote" i18n="cesium-loading-quote">Please be patient, like a giraffe reaching for the tastiest leaves.</span></div></div>
24
+ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
25
+ };
26
+ globe = null;
27
+ map3dTarget;
28
+ loading = false;
29
+ pixelTolerance = 10;
30
+ /** Promise of the currently running globe creation, to prevent creating it twice. */
31
+ globeCreation = null;
32
+ /** The OLCesium instance, once the globe has been created. */
33
+ get map3d() {
34
+ return this.globe?.olCesium;
35
+ }
36
+ get config() {
37
+ return this.context.configManager.Config;
38
+ }
39
+ constructor() {
40
+ super('map-3d');
41
+ }
42
+ subscribe(path, callback) {
43
+ // @ts-expect-error The call would have succeeded against this implementation,
44
+ // but implementation signatures of overloads are not externally visible.
45
+ return super.subscribe(path, callback);
46
+ }
47
+ /**
48
+ * The Map needs to be displayed in Fullscreen Mode.
49
+ */
50
+ get isFullscreenComponent() {
51
+ return true;
52
+ }
53
+ connectedCallback() {
54
+ super.connectedCallback();
55
+ this.render();
56
+ this.registerEvents();
57
+ this.onGlobeToggled();
58
+ }
59
+ render() {
60
+ super.render();
61
+ this.map3dTarget = this.shadow.getElementById('cs-map');
62
+ }
63
+ registerEvents() {
64
+ this.subscribe('globe.display', async () => {
65
+ await this.onGlobeToggled();
66
+ this.globe?.setCamera(this.state.globe.camera);
67
+ });
68
+ this.subscribe('globe.shadows', (_oldShadows, newShadows) => this.globe?.setShadows(newShadows));
69
+ this.subscribe('globe.camera', async (_oldCamera, newCamera) => {
70
+ await this.onGlobeToggled();
71
+ this.globe?.setCamera(newCamera);
72
+ });
73
+ this.subscribe('activeBasemaps', (_, newBasemaps) => this.globe?.onChangeBasemaps(newBasemaps));
74
+ this.subscribe(/activeBasemaps\.\d\.opacity/, (_oldOpacity, _newOpacity, basemap) => this.globe?.changeBasemapOpacity(basemap));
75
+ this.subscribe(/layers\.layersList\..*\.activeState/, (_oldActive, _newActive, layer) => {
76
+ if (layer instanceof LayerWms) {
77
+ this.globe?.onLayerToggled(layer);
78
+ }
79
+ });
80
+ this.subscribe(/layers\.layersList\..*\.opacity/, (_oldOpacity, _newOpacity, layer) => this.globe?.changeOpacity(layer));
81
+ this.subscribe(/layers\.layersList\..*\.filter/, (_oldFilter, _newFilter, layer) => this.globe?.changeFilter(layer));
82
+ this.subscribe('application.isReady', (_, isLoaded) => {
83
+ if (isLoaded) {
84
+ // The globe may not be ready when the rest of the application is initialized.
85
+ // The camera of a shared state can only be applied once everything is loaded.
86
+ this.globe?.setCamera(this.state.globe.camera);
87
+ }
88
+ });
89
+ }
90
+ /**
91
+ * Lazily loads Cesium/OLCesium and creates the globe. Does nothing if no 3D configuration
92
+ * is available or if the globe has already been created.
93
+ */
94
+ async createGlobe() {
95
+ if (this.globe || !this.config.map3d) {
96
+ return;
97
+ }
98
+ this.globeCreation ??= (async () => {
99
+ this.loading = true;
100
+ this.refreshRender();
101
+ const { default: Globe } = await import('./tools/globe');
102
+ const globe = new Globe(this.context, this.name, this.pixelTolerance);
103
+ await globe.initialize(this.map3dTarget);
104
+ this.globe = globe;
105
+ this.loading = false;
106
+ this.refreshRender();
107
+ this.state.globe.loaded = true;
108
+ })();
109
+ return this.globeCreation;
110
+ }
111
+ /**
112
+ * Shows or hides the 3D Map and sizes it depending on the current display mode.
113
+ * The 2D Map takes care of its own sizing.
114
+ */
115
+ async onGlobeToggled() {
116
+ if (this.state.globe.display === '3D') {
117
+ // Full screen globe has been enabled
118
+ this.style.display = 'block';
119
+ this.style.left = '0';
120
+ this.style.width = '100%';
121
+ await this.createGlobe();
122
+ this.globe?.setEnabled(true);
123
+ }
124
+ else if (this.state.globe.display === '2D/3D') {
125
+ // Side by side has been enabled
126
+ this.style.display = 'block';
127
+ this.style.left = '45%';
128
+ this.style.width = '55%';
129
+ await this.createGlobe();
130
+ this.globe?.setEnabled(true);
131
+ }
132
+ else {
133
+ // 3d map is not visible
134
+ this.globe?.setEnabled(false);
135
+ this.style.display = 'none';
136
+ this.unregisterInteractionListeners('globe.select');
137
+ }
138
+ }
139
+ }
@@ -0,0 +1,34 @@
1
+ /* SPDX-License-Identifier: Apache-2.0 */
2
+ #cs-map {
3
+ background-color: var(--bkg-color);
4
+ position: absolute;
5
+ top: 0;
6
+ height: 100%;
7
+ width: 100%;
8
+ overflow: hidden;
9
+ }
10
+
11
+ .hidden {
12
+ display: none;
13
+ }
14
+
15
+ .center {
16
+ position: absolute;
17
+ top: 50%;
18
+ left: 50%;
19
+ transform: translate(-50%, -50%);
20
+ margin: 10px;
21
+ text-align: center;
22
+ }
23
+
24
+ .loading span {
25
+ color: var(--text-color);
26
+ display: block;
27
+ margin-top: 1rem;
28
+ font-weight: 600;
29
+ }
30
+
31
+ .loading span.quote {
32
+ font-style: italic;
33
+ font-weight: 300;
34
+ }
@@ -0,0 +1,42 @@
1
+ import IGirafeContext from '../../../tools/context/icontext.js';
2
+ import BaseLayer from '../../../models/layers/baselayer.js';
3
+ import LayerWms from '../../../models/layers/layerwms.js';
4
+ import Basemap from '../../../models/basemaps/basemap.js';
5
+ import { CameraConfig } from '../../../tools/state/globe.js';
6
+ declare global {
7
+ interface Window {
8
+ Cesium: unknown;
9
+ }
10
+ }
11
+ /**
12
+ * The 3D Globe, based on Cesium and synchronized with the OpenLayers map by OLCesium.
13
+ *
14
+ * This module is the only place where Cesium and OLCesium are used. It is loaded on demand
15
+ * by the map-3d Component, so that those (heavy) libraries stay out of the initial bundle
16
+ * as long as the 3D Map is not displayed.
17
+ */
18
+ export default class Globe {
19
+ private readonly context;
20
+ private readonly toolName;
21
+ private readonly pixelTolerance;
22
+ olCesium: any;
23
+ private scene;
24
+ private wmsManager3d;
25
+ private get state();
26
+ private get config();
27
+ constructor(context: IGirafeContext, toolName: string, pixelTolerance: number);
28
+ /**
29
+ * Loads Cesium and OLCesium and initializes the globe on the given target element.
30
+ */
31
+ initialize(target: HTMLDivElement): Promise<void>;
32
+ private registerSelection;
33
+ setEnabled(enabled: boolean): void;
34
+ setShadows(shadows: boolean): void;
35
+ setCamera(camera: CameraConfig | null): void;
36
+ private addAllActiveLayers;
37
+ onLayerToggled(layer: LayerWms): void;
38
+ changeOpacity(layer: BaseLayer): void;
39
+ changeBasemapOpacity(basemap: Basemap): void;
40
+ changeFilter(layer: LayerWms): void;
41
+ onChangeBasemaps(basemaps: Basemap[]): void;
42
+ }
@@ -0,0 +1,198 @@
1
+ import * as Cesium from 'cesium';
2
+ import proj4 from 'proj4';
3
+ import WmsManager3d from './wmsmanager3d.js';
4
+ import GroupLayer from '../../../models/layers/grouplayer.js';
5
+ import LayerWms from '../../../models/layers/layerwms.js';
6
+ import { applyOpacityToLayers } from '../../../tools/utils/utils.js';
7
+ /**
8
+ * The 3D Globe, based on Cesium and synchronized with the OpenLayers map by OLCesium.
9
+ *
10
+ * This module is the only place where Cesium and OLCesium are used. It is loaded on demand
11
+ * by the map-3d Component, so that those (heavy) libraries stay out of the initial bundle
12
+ * as long as the 3D Map is not displayed.
13
+ */
14
+ export default class Globe {
15
+ context;
16
+ toolName;
17
+ pixelTolerance;
18
+ // TODO REG : Howto use the right type here without importing the whole library (it needs to be imported only on demand) ?
19
+ // This works but needs the library: type OLCesiumType = typeof OLCesium;
20
+ olCesium;
21
+ scene;
22
+ wmsManager3d;
23
+ get state() {
24
+ return this.context.stateManager.state;
25
+ }
26
+ get config() {
27
+ return this.context.configManager.Config.map3d;
28
+ }
29
+ constructor(context, toolName, pixelTolerance) {
30
+ this.context = context;
31
+ this.toolName = toolName;
32
+ this.pixelTolerance = pixelTolerance;
33
+ }
34
+ /**
35
+ * Loads Cesium and OLCesium and initializes the globe on the given target element.
36
+ */
37
+ async initialize(target) {
38
+ window.Cesium = Cesium;
39
+ const olcs = await import('olcs');
40
+ this.olCesium = new olcs.default({
41
+ map: this.context.mapManager.getMap(),
42
+ target: target,
43
+ time: () => {
44
+ const date = new Date(this.state.globe.shadowsTimestamp);
45
+ return Number.isNaN(date.getTime()) ? Cesium.JulianDate.now() : Cesium.JulianDate.fromDate(date);
46
+ }
47
+ });
48
+ const scene = this.olCesium.getCesiumScene();
49
+ this.scene = scene;
50
+ const config = this.config;
51
+ scene.screenSpaceCameraController.maximumZoomDistance = config.maximumZoomDistance ?? 30000;
52
+ // Add terrain
53
+ if (config.terrainUrl) {
54
+ scene.terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl(config.terrainUrl);
55
+ }
56
+ // Add terrain imagery
57
+ let coverage = Cesium.Rectangle.MAX_VALUE;
58
+ if (config.terrainImagery) {
59
+ if (config.terrainImagery.coverageArea) {
60
+ coverage = Cesium.Rectangle.fromDegrees(...config.terrainImagery.coverageArea);
61
+ }
62
+ scene.imageryLayers.addImageryProvider(new Cesium.UrlTemplateImageryProvider({
63
+ url: config.terrainImagery.url,
64
+ minimumLevel: config.terrainImagery.minLoD ?? 0,
65
+ maximumLevel: config.terrainImagery.maxLoD,
66
+ tilingScheme: config.terrainImagery.srid === 3857
67
+ ? new Cesium.WebMercatorTilingScheme()
68
+ : new Cesium.GeographicTilingScheme(),
69
+ rectangle: coverage
70
+ }));
71
+ }
72
+ // Add 3D-Tiles layers
73
+ const tilesetOptions = {
74
+ // If the error of the model is higher than this, we increase the resolution
75
+ maximumScreenSpaceError: 0.5,
76
+ // Enable different level of details based on the distance from the camera
77
+ dynamicScreenSpaceError: true,
78
+ // Model error at the max distance from the camera (higher = distant models are of lower quality)
79
+ dynamicScreenSpaceErrorFactor: config.tilesetsMaxError ?? 7
80
+ };
81
+ config.tilesetsUrls.forEach((tilesetUrl) => {
82
+ Cesium.Cesium3DTileset.fromUrl(tilesetUrl, tilesetOptions).then((t) => scene.primitives.add(t));
83
+ });
84
+ // REG: ambientOcclusion was temporary deactivated because of performance impact and horizontal lines artefacts
85
+ // const ambientOcclusion = scene.postProcessStages.ambientOcclusion;
86
+ // ambientOcclusion.enabled = true;
87
+ // ambientOcclusion.uniforms.bias = 0.5;
88
+ // ambientOcclusion.uniforms.stepSize = 1;
89
+ // ambientOcclusion.uniforms.blurStepSize = 1;
90
+ // REG: Adding the following line solves the problem, but it remains a log less performatn with ambiant occlusion.
91
+ // So I don't know what we want to do with it.
92
+ // See https://github.com/CesiumGS/cesium/issues/13039#issuecomment-3583233494
93
+ // viewer.camera.frustum.near = 1.0;
94
+ this.registerSelection();
95
+ this.wmsManager3d = new WmsManager3d(scene, this.context);
96
+ this.state.layers.layersList.forEach((l) => this.addAllActiveLayers(l));
97
+ const camera = scene.camera;
98
+ camera.changed.addEventListener(() => {
99
+ console.debug('Cesium camera moved');
100
+ this.state.globe.camera = {
101
+ heading: camera.heading,
102
+ pitch: camera.pitch,
103
+ roll: camera.roll
104
+ };
105
+ }, 1);
106
+ }
107
+ registerSelection() {
108
+ const scene = this.scene;
109
+ const pickOnGlobe = (position) => {
110
+ const ray = scene.camera.getPickRay(position);
111
+ return ray == undefined ? undefined : scene.globe.pick(ray, scene);
112
+ };
113
+ const cesiumScreenToLocalCoord = (position) => {
114
+ const cart = Cesium.Cartographic.fromCartesian(pickOnGlobe(position));
115
+ const longLat = [Cesium.Math.toDegrees(cart.longitude), Cesium.Math.toDegrees(cart.latitude)];
116
+ return proj4('EPSG:4326', this.context.configManager.Config.map.srid, longLat);
117
+ };
118
+ this.context.userInteractionManager.registerListener('globe.select', true, this.toolName);
119
+ const eventHandler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
120
+ eventHandler.setInputAction((event) => {
121
+ // If the click is on the map and selection is allowed
122
+ if (Cesium.defined(event.position) &&
123
+ this.context.userInteractionManager.canListenerExecute('globe.select', this.toolName)) {
124
+ const topLeftScreen = event.position.clone();
125
+ topLeftScreen.x -= this.pixelTolerance;
126
+ topLeftScreen.y -= this.pixelTolerance;
127
+ const bottomRightScreen = event.position.clone();
128
+ bottomRightScreen.x += this.pixelTolerance;
129
+ bottomRightScreen.y += this.pixelTolerance;
130
+ const topLeft = cesiumScreenToLocalCoord(topLeftScreen);
131
+ const bottomRight = cesiumScreenToLocalCoord(bottomRightScreen);
132
+ this.context.selectionManager.select([topLeft[0], topLeft[1], bottomRight[0], bottomRight[1]]);
133
+ }
134
+ }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
135
+ }
136
+ setEnabled(enabled) {
137
+ this.olCesium.setEnabled(enabled);
138
+ }
139
+ setShadows(shadows) {
140
+ this.scene.shadowMap.enabled = this.scene.globe.enableLighting = shadows;
141
+ }
142
+ setCamera(camera) {
143
+ if (!camera) {
144
+ return;
145
+ }
146
+ this.scene.camera.setView({
147
+ destination: this.scene.camera.position, // Keep current position
148
+ orientation: {
149
+ heading: camera.heading,
150
+ pitch: camera.pitch,
151
+ roll: camera.roll
152
+ }
153
+ });
154
+ }
155
+ addAllActiveLayers(layer) {
156
+ if (layer instanceof LayerWms) {
157
+ if (layer.active) {
158
+ this.wmsManager3d.addLayer(layer);
159
+ }
160
+ }
161
+ else if (layer instanceof GroupLayer) {
162
+ layer.children.forEach((l) => this.addAllActiveLayers(l));
163
+ }
164
+ }
165
+ onLayerToggled(layer) {
166
+ if (layer.active) {
167
+ this.wmsManager3d.addLayer(layer);
168
+ }
169
+ else {
170
+ this.wmsManager3d.removeLayer(layer);
171
+ }
172
+ }
173
+ changeOpacity(layer) {
174
+ if (layer instanceof LayerWms) {
175
+ this.wmsManager3d.changeOpacity(layer);
176
+ }
177
+ }
178
+ changeBasemapOpacity(basemap) {
179
+ applyOpacityToLayers(basemap.opacity, basemap.layersList, (layer) => this.changeOpacity(layer));
180
+ }
181
+ changeFilter(layer) {
182
+ this.wmsManager3d.changeFilter(layer);
183
+ }
184
+ onChangeBasemaps(basemaps) {
185
+ this.wmsManager3d.removeAllBasemapLayers();
186
+ for (const layer of basemaps.flatMap((basemap) => basemap.layersList)) {
187
+ if (layer instanceof LayerWms) {
188
+ this.wmsManager3d.addBasemapLayer(layer);
189
+ }
190
+ }
191
+ // Apply default opacity
192
+ for (const basemap of basemaps) {
193
+ if (!basemap.opacityDisabled) {
194
+ this.changeBasemapOpacity(basemap);
195
+ }
196
+ }
197
+ }
198
+ }
@@ -1,7 +1,7 @@
1
1
  import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
2
2
  import { SelectionMode, SelectionTool } from '../../models/selection.js';
3
3
  import { Draw, Interaction } from 'ol/interaction.js';
4
- import MapComponent from '../map/component.js';
4
+ import Map2dComponent from '../map-2d/component.js';
5
5
  import { Style } from 'ol/style.js';
6
6
  import { FeatureLike } from 'ol/Feature.js';
7
7
  import IGirafePanel from '../../tools/state/igirafepanel.js';
@@ -31,7 +31,7 @@ export default class SelectionToolComponent extends GirafeHTMLElement implements
31
31
  defaultVertexStrokeWidth: number;
32
32
  };
33
33
  get map(): import("ol").Map;
34
- get mapComponent(): MapComponent;
34
+ get mapComponent(): Map2dComponent;
35
35
  private defaultStyle;
36
36
  private labelStyle;
37
37
  styleFunction: (featureLike: FeatureLike) => Style[];
@@ -3,7 +3,7 @@ import { html as uHtml } from 'uhtml';
3
3
  import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
4
4
  import { SelectionMode, SelectionTool } from '../../models/selection.js';
5
5
  import { Draw, Interaction } from 'ol/interaction.js';
6
- import MapComponent from '../map/component.js';
6
+ import Map2dComponent from '../map-2d/component.js';
7
7
  import { createBox } from 'ol/interaction/Draw.js';
8
8
  import { Circle, Fill, RegularShape, Stroke, Style, Text } from 'ol/style.js';
9
9
  import { LineString, Point } from 'ol/geom.js';
@@ -49,7 +49,7 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
49
49
  return this.context.mapManager.getMap();
50
50
  }
51
51
  get mapComponent() {
52
- return this.context.componentManager.getComponents(MapComponent)[0];
52
+ return this.context.componentManager.getComponents(Map2dComponent)[0];
53
53
  }
54
54
  defaultStyle;
55
55
  labelStyle;
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "GeoGirafe PSC",
6
6
  "url": "https://doc.geogirafe.org"
7
7
  },
8
- "version": "1.2.0-dev.2800201562",
8
+ "version": "1.2.0-dev.2816732111",
9
9
  "type": "module",
10
10
  "engines": {
11
11
  "node": ">=20.19.0"
package/styles/iframe.css CHANGED
@@ -39,10 +39,27 @@ body {
39
39
  overflow-y: hidden;
40
40
  }
41
41
 
42
- girafe-map {
42
+ #maps {
43
43
  position: relative;
44
44
  width: 100%;
45
45
  height: 100%;
46
+ overflow: hidden;
47
+ }
48
+
49
+ girafe-map-2d {
50
+ display: block;
51
+ position: relative;
52
+ width: 100%;
53
+ height: 100%;
54
+ }
55
+
56
+ girafe-map-3d {
57
+ display: none;
58
+ position: absolute;
59
+ top: 0;
60
+ height: 100%;
61
+ overflow: hidden;
62
+ border-left: solid 3px #444;
46
63
  }
47
64
 
48
65
  girafe-basemap {
package/styles/index.css CHANGED
@@ -187,10 +187,27 @@ girafe-lr-panel > * {
187
187
  height: 100%;
188
188
  }
189
189
 
190
- girafe-map {
190
+ #maps {
191
191
  position: relative;
192
192
  width: 100%;
193
193
  flex-grow: 1;
194
+ overflow: hidden;
195
+ }
196
+
197
+ girafe-map-2d {
198
+ display: block;
199
+ position: relative;
200
+ width: 100%;
201
+ height: 100%;
202
+ }
203
+
204
+ girafe-map-3d {
205
+ display: none;
206
+ position: absolute;
207
+ top: 0;
208
+ height: 100%;
209
+ overflow: hidden;
210
+ border-left: solid 3px #444;
194
211
  }
195
212
 
196
213
  .info-area {
package/styles/mobile.css CHANGED
@@ -91,10 +91,27 @@ girafe-search {
91
91
  right: 0;
92
92
  }
93
93
 
94
- girafe-map {
94
+ #maps {
95
95
  position: relative;
96
96
  width: 100%;
97
97
  height: 100%;
98
+ overflow: hidden;
99
+ }
100
+
101
+ girafe-map-2d {
102
+ display: block;
103
+ position: relative;
104
+ width: 100%;
105
+ height: 100%;
106
+ }
107
+
108
+ girafe-map-3d {
109
+ display: none;
110
+ position: absolute;
111
+ top: 0;
112
+ height: 100%;
113
+ overflow: hidden;
114
+ border-left: solid 3px #444;
98
115
  }
99
116
 
100
117
  girafe-infobox {
@@ -111,7 +111,10 @@
111
111
 
112
112
  <div id="content">
113
113
  <div id="main">
114
- <girafe-map></girafe-map>
114
+ <div id="maps">
115
+ <girafe-map-2d></girafe-map-2d>
116
+ <girafe-map-3d></girafe-map-3d>
117
+ </div>
115
118
  <girafe-basemap></girafe-basemap>
116
119
  <girafe-search id="search"></girafe-search>
117
120
  </div>
@@ -134,7 +134,10 @@
134
134
  <girafe-tree-view slot="main"></girafe-tree-view>
135
135
  </girafe-lr-panel>
136
136
  <div id="content-map">
137
- <girafe-map></girafe-map>
137
+ <div id="maps">
138
+ <girafe-map-2d></girafe-map-2d>
139
+ <girafe-map-3d></girafe-map-3d>
140
+ </div>
138
141
  <div class="info-area">
139
142
  <girafe-basemap data-tour="basemap-selection"></girafe-basemap>
140
143
  <girafe-infobox></girafe-infobox>
@@ -92,7 +92,10 @@
92
92
  <girafe-search></girafe-search>
93
93
  <girafe-modals></girafe-modals>
94
94
  <div id="content">
95
- <girafe-map></girafe-map>
95
+ <div id="maps">
96
+ <girafe-map-2d></girafe-map-2d>
97
+ <girafe-map-3d></girafe-map-3d>
98
+ </div>
96
99
  <girafe-infobox></girafe-infobox>
97
100
  </div>
98
101
  <girafe-swipe-up-panel>
@@ -1 +1 @@
1
- {"version":"1.2.0-dev.2800201562", "build":"2800201562", "date":"28/08/2026"}
1
+ {"version":"1.2.0-dev.2816732111", "build":"2816732111", "date":"03/09/2026"}