@vcmap/core 5.0.0-rc.0

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 (146) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +44 -0
  3. package/build/postinstall.js +44 -0
  4. package/index.js +139 -0
  5. package/package.json +92 -0
  6. package/src/cesium/cesium3DTileFeature.js +9 -0
  7. package/src/cesium/cesium3DTilePointFeature.js +9 -0
  8. package/src/cesium/cesiumVcsCameraPrimitive.js +146 -0
  9. package/src/cesium/wallpaperMaterial.js +64 -0
  10. package/src/ol/feature.js +47 -0
  11. package/src/ol/geom/circle.js +24 -0
  12. package/src/ol/geom/geometryCollection.js +33 -0
  13. package/src/ol/render/canvas/canvasTileRenderer.js +179 -0
  14. package/src/ol/source/ClusterEnhancedVectorSource.js +39 -0
  15. package/src/ol/source/VcsCluster.js +37 -0
  16. package/src/vcs/vcm/classRegistry.js +106 -0
  17. package/src/vcs/vcm/event/vcsEvent.js +89 -0
  18. package/src/vcs/vcm/globalCollections.js +11 -0
  19. package/src/vcs/vcm/interaction/abstractInteraction.js +149 -0
  20. package/src/vcs/vcm/interaction/coordinateAtPixel.js +102 -0
  21. package/src/vcs/vcm/interaction/eventHandler.js +425 -0
  22. package/src/vcs/vcm/interaction/featureAtPixelInteraction.js +286 -0
  23. package/src/vcs/vcm/interaction/featureProviderInteraction.js +54 -0
  24. package/src/vcs/vcm/interaction/interactionChain.js +124 -0
  25. package/src/vcs/vcm/interaction/interactionType.js +114 -0
  26. package/src/vcs/vcm/layer/buildings.js +17 -0
  27. package/src/vcs/vcm/layer/cesium/cesiumTilesetCesium.js +359 -0
  28. package/src/vcs/vcm/layer/cesium/clusterContext.js +95 -0
  29. package/src/vcs/vcm/layer/cesium/dataSourceCesium.js +171 -0
  30. package/src/vcs/vcm/layer/cesium/openStreetMapCesium.js +29 -0
  31. package/src/vcs/vcm/layer/cesium/pointCloudCesium.js +58 -0
  32. package/src/vcs/vcm/layer/cesium/rasterLayerCesium.js +110 -0
  33. package/src/vcs/vcm/layer/cesium/singleImageCesium.js +49 -0
  34. package/src/vcs/vcm/layer/cesium/terrainCesium.js +80 -0
  35. package/src/vcs/vcm/layer/cesium/tmsCesium.js +54 -0
  36. package/src/vcs/vcm/layer/cesium/vectorCesium.js +255 -0
  37. package/src/vcs/vcm/layer/cesium/vectorContext.js +167 -0
  38. package/src/vcs/vcm/layer/cesium/vectorRasterTileCesium.js +116 -0
  39. package/src/vcs/vcm/layer/cesium/vectorTileImageryProvider.js +246 -0
  40. package/src/vcs/vcm/layer/cesium/wmsCesium.js +71 -0
  41. package/src/vcs/vcm/layer/cesium/wmtsCesium.js +101 -0
  42. package/src/vcs/vcm/layer/cesium/x3dmHelper.js +22 -0
  43. package/src/vcs/vcm/layer/cesiumTileset.js +376 -0
  44. package/src/vcs/vcm/layer/czml.js +141 -0
  45. package/src/vcs/vcm/layer/dataSource.js +259 -0
  46. package/src/vcs/vcm/layer/featureLayer.js +261 -0
  47. package/src/vcs/vcm/layer/featureStore.js +647 -0
  48. package/src/vcs/vcm/layer/featureStoreChanges.js +360 -0
  49. package/src/vcs/vcm/layer/featureStoreState.js +19 -0
  50. package/src/vcs/vcm/layer/featureVisibility.js +435 -0
  51. package/src/vcs/vcm/layer/geojson.js +185 -0
  52. package/src/vcs/vcm/layer/geojsonHelpers.js +450 -0
  53. package/src/vcs/vcm/layer/globalHider.js +157 -0
  54. package/src/vcs/vcm/layer/layer.js +752 -0
  55. package/src/vcs/vcm/layer/layerImplementation.js +102 -0
  56. package/src/vcs/vcm/layer/layerState.js +17 -0
  57. package/src/vcs/vcm/layer/layerSymbols.js +6 -0
  58. package/src/vcs/vcm/layer/oblique/layerOblique.js +76 -0
  59. package/src/vcs/vcm/layer/oblique/obliqueHelpers.js +175 -0
  60. package/src/vcs/vcm/layer/oblique/vectorOblique.js +469 -0
  61. package/src/vcs/vcm/layer/openStreetMap.js +194 -0
  62. package/src/vcs/vcm/layer/openlayers/layerOpenlayers.js +79 -0
  63. package/src/vcs/vcm/layer/openlayers/openStreetMapOpenlayers.js +27 -0
  64. package/src/vcs/vcm/layer/openlayers/rasterLayerOpenlayers.js +121 -0
  65. package/src/vcs/vcm/layer/openlayers/singleImageOpenlayers.js +49 -0
  66. package/src/vcs/vcm/layer/openlayers/tileDebugOpenlayers.js +39 -0
  67. package/src/vcs/vcm/layer/openlayers/tmsOpenlayers.js +62 -0
  68. package/src/vcs/vcm/layer/openlayers/vectorOpenlayers.js +118 -0
  69. package/src/vcs/vcm/layer/openlayers/vectorTileOpenlayers.js +177 -0
  70. package/src/vcs/vcm/layer/openlayers/wmsOpenlayers.js +55 -0
  71. package/src/vcs/vcm/layer/openlayers/wmtsOpenlayers.js +141 -0
  72. package/src/vcs/vcm/layer/pointCloud.js +162 -0
  73. package/src/vcs/vcm/layer/rasterLayer.js +294 -0
  74. package/src/vcs/vcm/layer/singleImage.js +119 -0
  75. package/src/vcs/vcm/layer/terrain.js +122 -0
  76. package/src/vcs/vcm/layer/terrainHelpers.js +123 -0
  77. package/src/vcs/vcm/layer/tileLoadedHelper.js +72 -0
  78. package/src/vcs/vcm/layer/tileProvider/mvtTileProvider.js +104 -0
  79. package/src/vcs/vcm/layer/tileProvider/staticGeojsonTileProvider.js +67 -0
  80. package/src/vcs/vcm/layer/tileProvider/tileProvider.js +584 -0
  81. package/src/vcs/vcm/layer/tileProvider/tileProviderFactory.js +28 -0
  82. package/src/vcs/vcm/layer/tileProvider/urlTemplateTileProvider.js +106 -0
  83. package/src/vcs/vcm/layer/tms.js +121 -0
  84. package/src/vcs/vcm/layer/vector.js +632 -0
  85. package/src/vcs/vcm/layer/vectorHelpers.js +206 -0
  86. package/src/vcs/vcm/layer/vectorProperties.js +1391 -0
  87. package/src/vcs/vcm/layer/vectorSymbols.js +40 -0
  88. package/src/vcs/vcm/layer/vectorTile.js +480 -0
  89. package/src/vcs/vcm/layer/wfs.js +165 -0
  90. package/src/vcs/vcm/layer/wms.js +270 -0
  91. package/src/vcs/vcm/layer/wmsHelpers.js +65 -0
  92. package/src/vcs/vcm/layer/wmts.js +235 -0
  93. package/src/vcs/vcm/maps/baseOLMap.js +257 -0
  94. package/src/vcs/vcm/maps/cameraLimiter.js +219 -0
  95. package/src/vcs/vcm/maps/cesium.js +1192 -0
  96. package/src/vcs/vcm/maps/map.js +511 -0
  97. package/src/vcs/vcm/maps/mapState.js +17 -0
  98. package/src/vcs/vcm/maps/oblique.js +536 -0
  99. package/src/vcs/vcm/maps/openlayers.js +205 -0
  100. package/src/vcs/vcm/object.js +92 -0
  101. package/src/vcs/vcm/oblique/ObliqueCollection.js +572 -0
  102. package/src/vcs/vcm/oblique/ObliqueDataSet.js +357 -0
  103. package/src/vcs/vcm/oblique/ObliqueImage.js +247 -0
  104. package/src/vcs/vcm/oblique/ObliqueImageMeta.js +126 -0
  105. package/src/vcs/vcm/oblique/ObliqueProvider.js +433 -0
  106. package/src/vcs/vcm/oblique/ObliqueView.js +130 -0
  107. package/src/vcs/vcm/oblique/ObliqueViewDirection.js +40 -0
  108. package/src/vcs/vcm/oblique/helpers.js +483 -0
  109. package/src/vcs/vcm/oblique/parseImageJson.js +248 -0
  110. package/src/vcs/vcm/util/clipping/clippingObject.js +386 -0
  111. package/src/vcs/vcm/util/clipping/clippingObjectManager.js +312 -0
  112. package/src/vcs/vcm/util/clipping/clippingPlaneHelper.js +413 -0
  113. package/src/vcs/vcm/util/collection.js +193 -0
  114. package/src/vcs/vcm/util/dateTime.js +60 -0
  115. package/src/vcs/vcm/util/exclusiveManager.js +135 -0
  116. package/src/vcs/vcm/util/extent.js +124 -0
  117. package/src/vcs/vcm/util/featureProvider/abstractFeatureProvider.js +196 -0
  118. package/src/vcs/vcm/util/featureProvider/featureProviderHelpers.js +51 -0
  119. package/src/vcs/vcm/util/featureProvider/featureProviderSymbols.js +11 -0
  120. package/src/vcs/vcm/util/featureProvider/tileProviderFeatureProvider.js +62 -0
  121. package/src/vcs/vcm/util/featureProvider/wmsFeatureProvider.js +280 -0
  122. package/src/vcs/vcm/util/featureconverter/circleToCesium.js +215 -0
  123. package/src/vcs/vcm/util/featureconverter/convert.js +83 -0
  124. package/src/vcs/vcm/util/featureconverter/extent3d.js +154 -0
  125. package/src/vcs/vcm/util/featureconverter/featureconverterHelper.js +591 -0
  126. package/src/vcs/vcm/util/featureconverter/lineStringToCesium.js +171 -0
  127. package/src/vcs/vcm/util/featureconverter/pointToCesium.js +359 -0
  128. package/src/vcs/vcm/util/featureconverter/polygonToCesium.js +229 -0
  129. package/src/vcs/vcm/util/geometryHelpers.js +172 -0
  130. package/src/vcs/vcm/util/indexedCollection.js +158 -0
  131. package/src/vcs/vcm/util/isMobile.js +12 -0
  132. package/src/vcs/vcm/util/layerCollection.js +216 -0
  133. package/src/vcs/vcm/util/locale.js +53 -0
  134. package/src/vcs/vcm/util/mapCollection.js +363 -0
  135. package/src/vcs/vcm/util/math.js +71 -0
  136. package/src/vcs/vcm/util/projection.js +348 -0
  137. package/src/vcs/vcm/util/splitScreen.js +233 -0
  138. package/src/vcs/vcm/util/style/declarativeStyleItem.js +631 -0
  139. package/src/vcs/vcm/util/style/shapesCategory.js +67 -0
  140. package/src/vcs/vcm/util/style/styleFactory.js +48 -0
  141. package/src/vcs/vcm/util/style/styleHelpers.js +555 -0
  142. package/src/vcs/vcm/util/style/styleItem.js +226 -0
  143. package/src/vcs/vcm/util/style/vectorStyleItem.js +927 -0
  144. package/src/vcs/vcm/util/style/writeStyle.js +48 -0
  145. package/src/vcs/vcm/util/urlHelpers.js +16 -0
  146. package/src/vcs/vcm/util/viewpoint.js +333 -0
@@ -0,0 +1,216 @@
1
+ import IndexedCollection from './indexedCollection.js';
2
+ import ExclusiveManager from './exclusiveManager.js';
3
+ import LayerState from '../layer/layerState.js';
4
+ import VcsEvent from '../event/vcsEvent.js';
5
+
6
+ /**
7
+ * A collection of layers. Manages rendering order and layer exclusivity. Emits state changes for convenience. Passed to
8
+ * {@link Map} for layers available to said map. Layers must have unique names.
9
+ * @class
10
+ * @export
11
+ * @api
12
+ * @extends {IndexedCollection<import("@vcmap/core").Layer>}}
13
+ */
14
+ // ignored do to static issues, see https://github.com/microsoft/TypeScript/issues/4628
15
+ // @ts-ignore
16
+ class LayerCollection extends IndexedCollection {
17
+ /**
18
+ * Creates a LayerCollection from an iterable of layers, such as an Array.
19
+ * @param {Iterable<import("@vcmap/core").Layer>} iterable
20
+ * @returns {LayerCollection}
21
+ * @override
22
+ * @api
23
+ */
24
+ static from(iterable) {
25
+ const collection = new LayerCollection();
26
+
27
+ if (iterable) {
28
+ // eslint-disable-next-line no-restricted-syntax
29
+ for (const layer of iterable) {
30
+ collection.add(layer);
31
+ }
32
+ }
33
+ return collection;
34
+ }
35
+
36
+ constructor() {
37
+ super();
38
+
39
+ /**
40
+ * Array of layer event listeners
41
+ * @type {Object<string, Array<Function>>}
42
+ * @private
43
+ */
44
+ this._layerEventListeners = {};
45
+
46
+ /**
47
+ * A symbol to describe the local z index of a layer. The local z index must not equal the layers z index, but is
48
+ * always consistent in comparison to the neighbouring layers. If a layer is moved other then by z index, the collection
49
+ * ensures consistency by setting a new local z index if needed.
50
+ * @type {symbol}
51
+ * @private
52
+ */
53
+ this._zIndexSymbol = Symbol('zIndex');
54
+
55
+ /**
56
+ * Event raised, when a layer of this collection changes its state. Passed the layer.
57
+ * @type {VcsEvent<import("@vcmap/core").Layer>}
58
+ * @api
59
+ */
60
+ this.stateChanged = new VcsEvent();
61
+
62
+ /**
63
+ * The exclusive manager for this collection. Layers within this collection are automatically added and tracked.
64
+ * @type {ExclusiveManager}
65
+ * @api
66
+ */
67
+ this.exclusiveManager = new ExclusiveManager();
68
+ }
69
+
70
+ /**
71
+ * @param {import("@vcmap/core").Layer} layer
72
+ * @private
73
+ */
74
+ _listenToLayerEvents(layer) {
75
+ const stateListener = layer.stateChanged.addEventListener((state) => {
76
+ if (state === LayerState.ACTIVE) {
77
+ this.exclusiveManager.handleLayerActivated(layer);
78
+ }
79
+ this.stateChanged.raiseEvent(layer);
80
+ });
81
+
82
+ const zIndexListener = layer.zIndexChanged.addEventListener(() => {
83
+ this._zIndexChanged(layer);
84
+ });
85
+
86
+ const exclusiveGroupsListener = layer.exclusiveGroupsChanged.addEventListener(() => {
87
+ this.exclusiveManager.handleExclusiveGroupsChanged(layer);
88
+ });
89
+
90
+ const listeners = [stateListener, zIndexListener, exclusiveGroupsListener];
91
+ if (/** @type {SplitLayer} */ (layer).splitDirectionChanged) {
92
+ listeners.push(/** @type {SplitLayer} */ (layer).splitDirectionChanged.addEventListener(() => {
93
+ this.exclusiveManager.handleSplitDirectionChanged(layer);
94
+ }));
95
+ }
96
+ this._layerEventListeners[layer.name] = listeners;
97
+ }
98
+
99
+ /**
100
+ * Determines the location in the array before the first entry with a higher local z index or null if there is no such position
101
+ * @param {number} zIndex
102
+ * @returns {number|null}
103
+ * @private
104
+ */
105
+ _findZIndexPosition(zIndex) {
106
+ const usedIndex = this._array.findIndex(l => l[this._zIndexSymbol] > zIndex);
107
+ return usedIndex > -1 ? usedIndex : null;
108
+ }
109
+
110
+ /**
111
+ * This is callback for a layers zIndex changed. It reevaluates the array given the new zIndex
112
+ * an moves the specified layer to its new location determined by findeZIndexPosition or the end of the array failing that.
113
+ * @param {import("@vcmap/core").Layer} layer
114
+ * @private
115
+ */
116
+ _zIndexChanged(layer) {
117
+ const currentIndex = this.indexOf(layer);
118
+ if (currentIndex > -1) {
119
+ const increased = layer[this._zIndexSymbol] < layer.zIndex;
120
+ layer[this._zIndexSymbol] = layer.zIndex;
121
+ let zIndexPosition = this._findZIndexPosition(layer.zIndex);
122
+ if (increased && zIndexPosition > 0) {
123
+ zIndexPosition -= 1; // remove self from count
124
+ }
125
+ zIndexPosition = zIndexPosition != null ? zIndexPosition : this._array.length - 1;
126
+ this._move(layer, currentIndex, zIndexPosition);
127
+ this._ensureLocalZIndex(layer);
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Ensures the local z index is consisten with the neighbours of a given layer.
133
+ * e.g. the layer on elower must have a lower or equal zIndex
134
+ * and the one higher a higher or equal zIndex.
135
+ * @param {import("@vcmap/core").Layer} layer
136
+ * @private
137
+ */
138
+ _ensureLocalZIndex(layer) {
139
+ const currentIndex = this.indexOf(layer);
140
+ if (currentIndex > 0) {
141
+ const below = this._array[currentIndex - 1][this._zIndexSymbol];
142
+ if (below > layer[this._zIndexSymbol]) {
143
+ layer[this._zIndexSymbol] = below;
144
+ }
145
+ }
146
+
147
+ if (currentIndex < this._array.length - 1) {
148
+ const above = this._array[currentIndex + 1][this._zIndexSymbol];
149
+ if (above < layer[this._zIndexSymbol]) {
150
+ layer[this._zIndexSymbol] = above;
151
+ }
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Adds a layer to the collection. Can optionally be passed an index at which to insert the layer.
157
+ * @param {import("@vcmap/core").Layer} layer
158
+ * @param {number=} index
159
+ * @returns {number|null} returns the layer index or null, if the layers name is not unique
160
+ * @api
161
+ */
162
+ add(layer, index) {
163
+ let usedIndex = index;
164
+ if (index == null) {
165
+ usedIndex = this._findZIndexPosition(layer.zIndex);
166
+ }
167
+ const insertedAt = super.add(layer, usedIndex);
168
+ if (insertedAt != null) {
169
+ layer[this._zIndexSymbol] = layer.zIndex;
170
+ this._ensureLocalZIndex(layer);
171
+ this._listenToLayerEvents(layer);
172
+ this.exclusiveManager.registerLayer(layer);
173
+ }
174
+ return insertedAt;
175
+ }
176
+
177
+ /**
178
+ * Removes a layer from the collection.
179
+ * @param {import("@vcmap/core").Layer} layer
180
+ * @api
181
+ */
182
+ remove(layer) {
183
+ if (this._layerEventListeners[layer.name]) {
184
+ this._layerEventListeners[layer.name].forEach((cb) => { cb(); });
185
+ delete this._layerEventListeners[layer.name];
186
+ }
187
+ delete layer[this._zIndexSymbol];
188
+ this.exclusiveManager.unregisterLayer(layer);
189
+ super.remove(layer);
190
+ }
191
+
192
+ clear() {
193
+ Object.values(this._layerEventListeners)
194
+ .flat()
195
+ .forEach((r) => { r(); });
196
+ this._array.forEach((l) => {
197
+ delete l[this._zIndexSymbol];
198
+ });
199
+
200
+ this.exclusiveManager.clear();
201
+ this._layerEventListeners = {};
202
+ super.clear();
203
+ }
204
+
205
+ destroy() {
206
+ Object.values(this._layerEventListeners)
207
+ .flat()
208
+ .forEach((r) => { r(); });
209
+
210
+ this._layerEventListeners = {};
211
+ this.exclusiveManager.destroy();
212
+ super.destroy();
213
+ }
214
+ }
215
+
216
+ export default LayerCollection;
@@ -0,0 +1,53 @@
1
+ import VcsEvent from '../event/vcsEvent.js';
2
+
3
+ /**
4
+ * @type {VcsEvent<string>}
5
+ */
6
+ let localeChangedEvent;
7
+
8
+ /**
9
+ * @returns {VcsEvent<string>}
10
+ */
11
+ export function getLocaleChangedEvent() {
12
+ if (!localeChangedEvent) {
13
+ localeChangedEvent = new VcsEvent();
14
+ }
15
+ return localeChangedEvent;
16
+ }
17
+
18
+ /**
19
+ * returns the default browserLocale, if not possible 'en'
20
+ * @returns {string}
21
+ */
22
+ export function detectBrowserLocale() {
23
+ if (navigator.language) {
24
+ const lang = navigator.language;
25
+ return lang.substring(0, 2);
26
+ }
27
+ return 'en';
28
+ }
29
+
30
+ /**
31
+ * @type {string}
32
+ */
33
+ let currentLocale;
34
+
35
+ /**
36
+ * @returns {string}
37
+ */
38
+ export function getCurrentLocale() {
39
+ if (!currentLocale) {
40
+ currentLocale = detectBrowserLocale();
41
+ }
42
+ return currentLocale;
43
+ }
44
+
45
+ /**
46
+ * @param {string} value
47
+ */
48
+ export function setCurrentLocale(value) {
49
+ if (typeof value === 'string') {
50
+ currentLocale = value;
51
+ getLocaleChangedEvent().raiseEvent(value);
52
+ }
53
+ }
@@ -0,0 +1,363 @@
1
+ import { checkMaybe } from '@vcsuite/check';
2
+ import { getLogger } from '@vcsuite/logger';
3
+ import VcsEvent from '../event/vcsEvent.js';
4
+ import Collection from './collection.js';
5
+ import EventHandler from '../interaction/eventHandler.js';
6
+ import LayerCollection from './layerCollection.js';
7
+ import ClippingObjectManager from './clipping/clippingObjectManager.js';
8
+ import SplitScreen from './splitScreen.js';
9
+
10
+ /**
11
+ * @typedef {Object} MapCollectionInitializationError
12
+ * @property {Error} error
13
+ * @property {import("@vcmap/core").VcsMap} map
14
+ */
15
+
16
+ /**
17
+ * @param {import("@vcmap/core").CesiumMap} cesiumMap
18
+ * @param {import("@vcmap/core").Openlayers} olMap
19
+ * @returns {Promise<void>}
20
+ */
21
+ async function setCesiumToOLViewpoint(cesiumMap, olMap) {
22
+ const viewPoint = cesiumMap.getViewPointSync();
23
+ const northDownVp = viewPoint.clone();
24
+ northDownVp.heading = 0;
25
+ northDownVp.pitch = -90;
26
+ if (viewPoint && !viewPoint.equals(northDownVp)) {
27
+ if (olMap.fixedNorthOrientation) {
28
+ viewPoint.heading = 0;
29
+ }
30
+
31
+ viewPoint.pitch = -90;
32
+ viewPoint.animate = true;
33
+ viewPoint.duration = 1;
34
+
35
+ if (viewPoint.groundPosition) {
36
+ viewPoint.cameraPosition = null;
37
+ }
38
+
39
+ await cesiumMap.gotoViewPoint(viewPoint);
40
+ }
41
+ }
42
+
43
+ /**
44
+ * @class
45
+ * @export
46
+ * @extends {Collection<import("@vcmap/core").VcsMap>}}
47
+ */
48
+ // ignored do to static issues, see https://github.com/microsoft/TypeScript/issues/4628
49
+ // @ts-ignore
50
+ class MapCollection extends Collection {
51
+ /**
52
+ * Creates a LayerCollection from an iterable of layers, such as an Array.
53
+ * @param {Iterable<import("@vcmap/core").VcsMap>} iterable
54
+ * @returns {MapCollection}
55
+ * @override
56
+ * @api
57
+ */
58
+ static from(iterable) {
59
+ const collection = new MapCollection();
60
+
61
+ if (iterable) {
62
+ // eslint-disable-next-line no-restricted-syntax
63
+ for (const map of iterable) {
64
+ collection.add(map);
65
+ }
66
+ }
67
+ return collection;
68
+ }
69
+
70
+ constructor() {
71
+ super();
72
+
73
+ /**
74
+ * @type {import("@vcmap/core").VcsMap}
75
+ * @private
76
+ */
77
+ this._activeMap = null;
78
+
79
+ /**
80
+ *
81
+ * @type {HTMLElement}
82
+ * @private
83
+ */
84
+ this._target = null;
85
+
86
+ /**
87
+ * The map pointer event handler. The EventHandler is shared amongst all maps within the collection.
88
+ * @type {EventHandler}
89
+ * @api
90
+ */
91
+ this.eventHandler = new EventHandler();
92
+
93
+ /**
94
+ * Collection of layers shared amongst the maps within this collection,
95
+ * layers will be rendered if supported on the currently active map.
96
+ * @type {LayerCollection}
97
+ * @api
98
+ */
99
+ this.layerCollection = new LayerCollection();
100
+
101
+ /**
102
+ * Called, if a map fails to initialize. The map causing the error will be removed from the collection.
103
+ * @type {VcsEvent<MapCollectionInitializationError>}
104
+ * @api
105
+ */
106
+ this.initializeError = new VcsEvent();
107
+
108
+ /**
109
+ * Called, when a map (typically an oblique map) cannot show the current viewpoint. Is passed
110
+ * the map which cannot show the current viewpoint.
111
+ * @type {VcsEvent<import("@vcmap/core").VcsMap>}
112
+ * @api
113
+ */
114
+ this.fallbackMapActivated = new VcsEvent();
115
+
116
+ /**
117
+ * Called, when a map is activated. Is passed the activated map.
118
+ * @type {VcsEvent<import("@vcmap/core").VcsMap>}
119
+ * @api
120
+ */
121
+ this.mapActivated = new VcsEvent();
122
+
123
+ /**
124
+ * Manages the clipping object for the maps in this collection.
125
+ * @type {ClippingObjectManager}
126
+ * @api
127
+ */
128
+ this.clippingObjectManager = new ClippingObjectManager(this.layerCollection);
129
+
130
+ /**
131
+ * @type {SplitScreen}
132
+ */
133
+ this.splitScreen = new SplitScreen(this.clippingObjectManager);
134
+
135
+ /**
136
+ * @type {Array<Function>}
137
+ * @private
138
+ */
139
+ this._mapPointerListeners = [];
140
+ }
141
+
142
+ /**
143
+ * The currently active map
144
+ * @type {import("@vcmap/core").VcsMap}
145
+ * @api
146
+ * @readonly
147
+ */
148
+ get activeMap() { return this._activeMap; }
149
+
150
+ /**
151
+ * The currently set HTML element in which to render the maps
152
+ * @type {HTMLElement|null}
153
+ * @api
154
+ * @readonly
155
+ */
156
+ get target() {
157
+ return this._target;
158
+ }
159
+
160
+ /**
161
+ * Adds a map to the collection. This will set the collections target, {@link SplitScreen}
162
+ * and the collections {@link LayerCollection} on the map.
163
+ * It will add map event listeners and pass them to the event handler of this collection.
164
+ * @param {import("@vcmap/core").VcsMap} map
165
+ * @returns {number|null}
166
+ */
167
+ add(map) {
168
+ const added = super.add(map);
169
+ if (added !== null) {
170
+ this._mapPointerListeners
171
+ .push(map.pointerInteractionEvent.addEventListener(this.eventHandler.handleMapEvent.bind(this.eventHandler)));
172
+ map.layerCollection = this.layerCollection;
173
+ map.splitScreen = this.splitScreen;
174
+ map.setTarget(this._target);
175
+ }
176
+ return added;
177
+ }
178
+
179
+ /**
180
+ * Removes the map from the collection. Will also set splitScreen & target to null and an empty layerCollection on the map,
181
+ * if the map is currently part of the collection.
182
+ * @param {import("@vcmap/core").VcsMap} map
183
+ */
184
+ remove(map) {
185
+ if (this.has(map)) {
186
+ map.setTarget(null);
187
+ map.splitScreen = null;
188
+ map.layerCollection = new LayerCollection();
189
+ }
190
+ super.remove(map);
191
+ if (this._activeMap === map) {
192
+ this._activeMap = null;
193
+ }
194
+ }
195
+
196
+ /**
197
+ * @private
198
+ */
199
+ _setActiveMapCSSClass() {
200
+ if (this._target && this._activeMap) {
201
+ const mapClassName = this._activeMap.className.split('.').pop();
202
+ this._target.classList.add(mapClassName);
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Set the target for these maps.
208
+ * @param {(string|HTMLElement)} target
209
+ * @api
210
+ */
211
+ setTarget(target) {
212
+ checkMaybe(target, [String, HTMLElement]);
213
+
214
+ this._target = typeof target === 'string' ? document.getElementById(target) : target;
215
+ this._array.forEach((map) => {
216
+ map.setTarget(this._target);
217
+ });
218
+
219
+ this._setActiveMapCSSClass();
220
+ }
221
+
222
+ /**
223
+ * @param {import("@vcmap/core").VcsMap} map
224
+ * @returns {null|import("@vcmap/core").VcsMap}
225
+ * @private
226
+ */
227
+ _getFallbackMap(map) {
228
+ const { fallbackMap } = map;
229
+ if (fallbackMap) {
230
+ const fMap = this.getByKey(fallbackMap);
231
+ if (fMap && fMap !== map) {
232
+ return fMap;
233
+ } else {
234
+ getLogger().warning(`the fallback map with the name: ${fallbackMap} is missconfigured`);
235
+ }
236
+ }
237
+ return null;
238
+ }
239
+
240
+ /**
241
+ * @param {import("@vcmap/core").VcsMap} map
242
+ * @returns {null|import("@vcmap/core").VcsMap}
243
+ * @private
244
+ */
245
+ _getFallbackMapOrDefault(map) {
246
+ const fallbackMap = this._getFallbackMap(map);
247
+ return fallbackMap ||
248
+ this.getByType('vcs.vcm.maps.Openlayers')[0] ||
249
+ this._array[0];
250
+ }
251
+
252
+ /**
253
+ * Sets the active map. This will 1. get the current viewpoint of an acitve map (if one is set) 2.
254
+ * determine that the map to be activated can show this viewpoint or has no fallback map set and 3.
255
+ * activates the map 4. calls gotoViewpoint with the previous maps viewpoint
256
+ * @param {string} mapName
257
+ * @returns {Promise<void>}
258
+ */
259
+ async setActiveMap(mapName) {
260
+ const map = this.getByKey(mapName);
261
+ if (!map) {
262
+ getLogger('vcs.vcm.util.MapCollection').warning(`could not find map with name ${mapName}`);
263
+ return Promise.resolve();
264
+ }
265
+
266
+ if (
267
+ this._activeMap &&
268
+ this._activeMap.className === 'vcs.vcm.maps.Cesium' &&
269
+ map.className === 'vcs.vcm.maps.Openlayers'
270
+ ) {
271
+ await setCesiumToOLViewpoint(
272
+ /** @type {import("@vcmap/core").CesiumMap} */ (this._activeMap),
273
+ /** @type {import("@vcmap/core").Openlayers} */ (map),
274
+ );
275
+ }
276
+
277
+ try {
278
+ await map.initialize();
279
+ } catch (error) { // typically unsupported webGL and cesium map
280
+ getLogger('vcs.vcm.util.MapCollection').error(error);
281
+ this.remove(map);
282
+ const fallbackMap = this._getFallbackMapOrDefault(map);
283
+ this.initializeError.raiseEvent({
284
+ map,
285
+ error,
286
+ });
287
+ if (fallbackMap) {
288
+ this.fallbackMapActivated.raiseEvent(map);
289
+ return this.setActiveMap(fallbackMap.name);
290
+ }
291
+ throw new Error('cannot activate a single map');
292
+ }
293
+
294
+ let viewpoint;
295
+ if (this._activeMap) {
296
+ if (this._activeMap === map) {
297
+ return map.activate();
298
+ }
299
+
300
+ viewpoint = await this._activeMap.getViewPoint();
301
+ const canShow = await map.canShowViewpoint(viewpoint);
302
+ if (!canShow) {
303
+ const fallbackMap = this._getFallbackMap(map);
304
+ if (fallbackMap) {
305
+ this.fallbackMapActivated.raiseEvent(map);
306
+ return this.setActiveMap(fallbackMap.name);
307
+ }
308
+ }
309
+ this._activeMap.deactivate();
310
+ if (this._target) {
311
+ const mapClassName = this._activeMap.className.split('.').pop();
312
+ this._target.classList.remove(mapClassName);
313
+ }
314
+ }
315
+
316
+ this._activeMap = map;
317
+ await this._activeMap.activate();
318
+ this._setActiveMapCSSClass();
319
+
320
+ if (viewpoint) {
321
+ await this._activeMap.gotoViewPoint(viewpoint);
322
+ }
323
+
324
+ this.clippingObjectManager.mapActivated(map);
325
+ this.splitScreen.mapActivated(map);
326
+ this.mapActivated.raiseEvent(map);
327
+ return Promise.resolve();
328
+ }
329
+
330
+ /**
331
+ * Returns all maps of a specified type
332
+ * @param {string} type
333
+ * @returns {Array<import("@vcmap/core").VcsMap>}
334
+ * @api
335
+ */
336
+ getByType(type) {
337
+ return this._array.filter(m => m.className === type);
338
+ }
339
+
340
+ /**
341
+ * @inheritDoc
342
+ */
343
+ destroy() {
344
+ super.destroy();
345
+ [...this.layerCollection].forEach((l) => { l.destroy(); });
346
+ this.layerCollection.destroy();
347
+ this.eventHandler.destroy();
348
+ this.mapActivated.destroy();
349
+ this.clippingObjectManager.destroy();
350
+ this.clippingObjectManager = null;
351
+ this.splitScreen.destroy();
352
+ this.splitScreen = null;
353
+ this.fallbackMapActivated.destroy();
354
+ this.initializeError.destroy();
355
+
356
+ this._mapPointerListeners.forEach((cb) => { cb(); });
357
+ this._mapPointerListeners = [];
358
+
359
+ this._target = null;
360
+ }
361
+ }
362
+
363
+ export default MapCollection;
@@ -0,0 +1,71 @@
1
+ import { Math as CesiumMath, Cartesian3 } from '@vcmap/cesium';
2
+
3
+ /**
4
+ * returns a new coordinate ([lon, lat] in degrees) from a distance, bearing and starting coordinate
5
+ * @param {Array.<number>} coord [lon, lat] in degrees
6
+ * @param {number} d distance in m to new coordinate
7
+ * @param {number} brng bearing in degrees ( 0 == north, 90° == east)
8
+ * @returns {Array.<number>};
9
+ */
10
+ export function coordinateAtDistance(coord, d, brng) {
11
+ const R = 6371000;
12
+ const brngRadians = CesiumMath.toRadians(brng);
13
+ const lat1 = CesiumMath.toRadians(coord[1]);
14
+ const lon1 = CesiumMath.toRadians(coord[0]);
15
+ const lat2 = Math.asin((Math.sin(lat1) * Math.cos(d / R)) +
16
+ (Math.cos(lat1) * Math.sin(d / R) * Math.cos(brngRadians)));
17
+ const lon2 = lon1 + Math.atan2(
18
+ Math.sin(brngRadians) * Math.sin(d / R) * Math.cos(lat1),
19
+ Math.cos(d / R) - (Math.sin(lat1) * Math.sin(lat2)),
20
+ );
21
+ return [parseFloat(CesiumMath.toDegrees(lon2).toFixed(5)), parseFloat(CesiumMath.toDegrees(lat2).toFixed(5))];
22
+ }
23
+
24
+
25
+ /**
26
+ * returns the initial bearing in degrees (0-360) between two coordinates
27
+ * @param {Array.<number>} coords1 [lon, lat] in degrees
28
+ * @param {Array.<number>} coords2 [lon, lat] in degrees
29
+ * @returns {number};
30
+ */
31
+ export function initialBearingBetweenCoords(coords1, coords2) {
32
+ // long
33
+ const l1 = CesiumMath.toRadians(coords1[0]);
34
+ // lat
35
+ const f1 = CesiumMath.toRadians(coords1[1]);
36
+ // long
37
+ const l2 = CesiumMath.toRadians(coords2[0]);
38
+ // lat
39
+ const f2 = CesiumMath.toRadians(coords2[1]);
40
+
41
+ const y = Math.sin(l2 - l1) * Math.cos(f2);
42
+ const x = (Math.cos(f1) * Math.sin(f2)) - (Math.sin(f1) * Math.cos(f2) * Math.cos(l2 - l1));
43
+ let brng = CesiumMath.toDegrees(Math.atan2(y, x));
44
+ brng = (brng + 360) % 360;
45
+ return brng;
46
+ }
47
+
48
+
49
+ /**
50
+ * returns distance between two coordinates
51
+ * @param {import("ol/coordinate").Coordinate} point0
52
+ * @param {import("ol/coordinate").Coordinate} point1
53
+ * @returns {number}
54
+ */
55
+ export function cartesian2DDistance(point0, point1) {
56
+ const distX = point0[0] - point1[0];
57
+ const distY = point0[1] - point1[1];
58
+ return Math.sqrt((distX ** 2) + (distY ** 2));
59
+ }
60
+
61
+ /**
62
+ * @param {import("ol/coordinate").Coordinate} p1
63
+ * @param {import("ol/coordinate").Coordinate} p2
64
+ * @returns {number}
65
+ */
66
+ export function cartesian3DDistance(p1, p2) {
67
+ const point0 = Cartesian3.fromElements(p1[0], p1[1], p1[2]);
68
+ const point1 = Cartesian3.fromElements(p2[0], p2[1], p2[2]);
69
+ return Cartesian3.distance(point0, point1);
70
+ }
71
+