@vcmap/core 6.0.7 → 6.1.0-rc.2

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/dist/cesium.d.ts +3 -0
  2. package/dist/index.d.ts +16 -1
  3. package/dist/index.js +16 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/ol.d.ts +8 -1
  6. package/dist/src/featureProvider/featureProviderSymbols.d.ts +5 -0
  7. package/dist/src/featureProvider/featureProviderSymbols.js +5 -1
  8. package/dist/src/featureProvider/featureProviderSymbols.js.map +1 -1
  9. package/dist/src/interaction/featureAtPixelInteraction.js +58 -62
  10. package/dist/src/interaction/featureAtPixelInteraction.js.map +1 -1
  11. package/dist/src/interaction/featureProviderInteraction.js +25 -13
  12. package/dist/src/interaction/featureProviderInteraction.js.map +1 -1
  13. package/dist/src/layer/cesium/sourceVectorContextSync.d.ts +27 -0
  14. package/dist/src/layer/cesium/sourceVectorContextSync.js +94 -0
  15. package/dist/src/layer/cesium/sourceVectorContextSync.js.map +1 -0
  16. package/dist/src/layer/cesium/vectorCesiumImpl.d.ts +4 -27
  17. package/dist/src/layer/cesium/vectorCesiumImpl.js +15 -107
  18. package/dist/src/layer/cesium/vectorCesiumImpl.js.map +1 -1
  19. package/dist/src/layer/cesium/vectorContext.d.ts +12 -1
  20. package/dist/src/layer/cesium/vectorContext.js +6 -0
  21. package/dist/src/layer/cesium/vectorContext.js.map +1 -1
  22. package/dist/src/layer/layerSymbols.js +1 -1
  23. package/dist/src/layer/layerSymbols.js.map +1 -1
  24. package/dist/src/layer/oblique/sourceObliqueSync.d.ts +18 -0
  25. package/dist/src/layer/oblique/sourceObliqueSync.js +319 -0
  26. package/dist/src/layer/oblique/sourceObliqueSync.js.map +1 -0
  27. package/dist/src/layer/oblique/vectorObliqueImpl.d.ts +2 -40
  28. package/dist/src/layer/oblique/vectorObliqueImpl.js +8 -283
  29. package/dist/src/layer/oblique/vectorObliqueImpl.js.map +1 -1
  30. package/dist/src/layer/vectorLayer.d.ts +10 -1
  31. package/dist/src/layer/vectorLayer.js +23 -1
  32. package/dist/src/layer/vectorLayer.js.map +1 -1
  33. package/dist/src/map/baseOLMap.js +8 -1
  34. package/dist/src/map/baseOLMap.js.map +1 -1
  35. package/dist/src/map/cesiumMap.d.ts +2 -0
  36. package/dist/src/map/cesiumMap.js +26 -1
  37. package/dist/src/map/cesiumMap.js.map +1 -1
  38. package/dist/src/map/vcsMap.d.ts +24 -12
  39. package/dist/src/map/vcsMap.js +92 -38
  40. package/dist/src/map/vcsMap.js.map +1 -1
  41. package/dist/src/ol/source/ClusterEnhancedVectorSource.d.ts +6 -4
  42. package/dist/src/ol/source/ClusterEnhancedVectorSource.js +4 -9
  43. package/dist/src/ol/source/ClusterEnhancedVectorSource.js.map +1 -1
  44. package/dist/src/ol/source/VcsCluster.d.ts +10 -10
  45. package/dist/src/ol/source/VcsCluster.js +23 -7
  46. package/dist/src/ol/source/VcsCluster.js.map +1 -1
  47. package/dist/src/util/clipping/clippingPolygonHelper.d.ts +7 -0
  48. package/dist/src/util/clipping/clippingPolygonHelper.js +53 -0
  49. package/dist/src/util/clipping/clippingPolygonHelper.js.map +1 -0
  50. package/dist/src/util/clipping/clippingPolygonObject.d.ts +59 -0
  51. package/dist/src/util/clipping/clippingPolygonObject.js +158 -0
  52. package/dist/src/util/clipping/clippingPolygonObject.js.map +1 -0
  53. package/dist/src/util/clipping/clippingPolygonObjectCollection.d.ts +18 -0
  54. package/dist/src/util/clipping/clippingPolygonObjectCollection.js +167 -0
  55. package/dist/src/util/clipping/clippingPolygonObjectCollection.js.map +1 -0
  56. package/dist/src/util/layerCollection.d.ts +11 -1
  57. package/dist/src/util/layerCollection.js +67 -12
  58. package/dist/src/util/layerCollection.js.map +1 -1
  59. package/dist/src/util/mapCollection.d.ts +16 -1
  60. package/dist/src/util/mapCollection.js +37 -3
  61. package/dist/src/util/mapCollection.js.map +1 -1
  62. package/dist/src/util/renderScreenshot.d.ts +9 -0
  63. package/dist/src/util/renderScreenshot.js +162 -0
  64. package/dist/src/util/renderScreenshot.js.map +1 -0
  65. package/dist/src/util/rotation.d.ts +30 -0
  66. package/dist/src/util/rotation.js +145 -0
  67. package/dist/src/util/rotation.js.map +1 -0
  68. package/dist/src/util/vcsTemplate.d.ts +7 -0
  69. package/dist/src/util/vcsTemplate.js +248 -0
  70. package/dist/src/util/vcsTemplate.js.map +1 -0
  71. package/dist/src/vcsApp.d.ts +7 -0
  72. package/dist/src/vcsApp.js +29 -0
  73. package/dist/src/vcsApp.js.map +1 -1
  74. package/dist/src/vcsModule.d.ts +6 -2
  75. package/dist/src/vcsModule.js.map +1 -1
  76. package/dist/src/vectorCluster/vectorClusterCesiumContext.d.ts +18 -0
  77. package/dist/src/{layer/cesium/clusterContext.js → vectorCluster/vectorClusterCesiumContext.js} +28 -42
  78. package/dist/src/vectorCluster/vectorClusterCesiumContext.js.map +1 -0
  79. package/dist/src/vectorCluster/vectorClusterGroup.d.ts +96 -0
  80. package/dist/src/vectorCluster/vectorClusterGroup.js +320 -0
  81. package/dist/src/vectorCluster/vectorClusterGroup.js.map +1 -0
  82. package/dist/src/vectorCluster/vectorClusterGroupCesiumImpl.d.ts +20 -0
  83. package/dist/src/vectorCluster/vectorClusterGroupCesiumImpl.js +115 -0
  84. package/dist/src/vectorCluster/vectorClusterGroupCesiumImpl.js.map +1 -0
  85. package/dist/src/vectorCluster/vectorClusterGroupCollection.d.ts +19 -0
  86. package/dist/src/vectorCluster/vectorClusterGroupCollection.js +37 -0
  87. package/dist/src/vectorCluster/vectorClusterGroupCollection.js.map +1 -0
  88. package/dist/src/vectorCluster/vectorClusterGroupImpl.d.ts +31 -0
  89. package/dist/src/vectorCluster/vectorClusterGroupImpl.js +76 -0
  90. package/dist/src/vectorCluster/vectorClusterGroupImpl.js.map +1 -0
  91. package/dist/src/vectorCluster/vectorClusterGroupObliqueImpl.d.ts +17 -0
  92. package/dist/src/vectorCluster/vectorClusterGroupObliqueImpl.js +62 -0
  93. package/dist/src/vectorCluster/vectorClusterGroupObliqueImpl.js.map +1 -0
  94. package/dist/src/vectorCluster/vectorClusterGroupOpenlayersImpl.d.ts +17 -0
  95. package/dist/src/vectorCluster/vectorClusterGroupOpenlayersImpl.js +62 -0
  96. package/dist/src/vectorCluster/vectorClusterGroupOpenlayersImpl.js.map +1 -0
  97. package/dist/src/vectorCluster/vectorClusterStyleItem.d.ts +110 -0
  98. package/dist/src/vectorCluster/vectorClusterStyleItem.js +374 -0
  99. package/dist/src/vectorCluster/vectorClusterStyleItem.js.map +1 -0
  100. package/dist/src/vectorCluster/vectorClusterSymbols.d.ts +1 -0
  101. package/dist/src/vectorCluster/vectorClusterSymbols.js +3 -0
  102. package/dist/src/vectorCluster/vectorClusterSymbols.js.map +1 -0
  103. package/index.ts +42 -1
  104. package/package.json +3 -1
  105. package/src/cesium/cesium.d.ts +3 -0
  106. package/src/featureProvider/featureProviderSymbols.ts +6 -1
  107. package/src/interaction/featureAtPixelInteraction.ts +109 -84
  108. package/src/interaction/featureProviderInteraction.ts +42 -28
  109. package/src/layer/cesium/sourceVectorContextSync.ts +134 -0
  110. package/src/layer/cesium/vcsTile/vcsDebugTile.ts +1 -1
  111. package/src/layer/cesium/vcsTile/vcsVectorTile.ts +1 -1
  112. package/src/layer/cesium/vectorCesiumImpl.ts +30 -144
  113. package/src/layer/cesium/vectorContext.ts +17 -1
  114. package/src/layer/layerSymbols.ts +1 -1
  115. package/src/layer/oblique/sourceObliqueSync.ts +436 -0
  116. package/src/layer/oblique/vectorObliqueImpl.ts +11 -397
  117. package/src/layer/vectorLayer.ts +35 -2
  118. package/src/map/baseOLMap.ts +8 -1
  119. package/src/map/cesiumMap.ts +36 -3
  120. package/src/map/vcsMap.ts +121 -47
  121. package/src/ol/ol.d.ts +8 -1
  122. package/src/ol/source/{ClusterEnhancedVectorSource.js → ClusterEnhancedVectorSource.ts} +7 -10
  123. package/src/ol/source/VcsCluster.ts +58 -0
  124. package/src/util/clipping/clippingPolygonHelper.ts +86 -0
  125. package/src/util/clipping/clippingPolygonObject.ts +223 -0
  126. package/src/util/clipping/clippingPolygonObjectCollection.ts +249 -0
  127. package/src/util/layerCollection.ts +90 -12
  128. package/src/util/mapCollection.ts +53 -2
  129. package/src/util/renderScreenshot.ts +193 -0
  130. package/src/util/rotation.ts +215 -0
  131. package/src/util/vcsTemplate.ts +373 -0
  132. package/src/vcsApp.ts +65 -0
  133. package/src/vcsModule.ts +6 -2
  134. package/src/vectorCluster/vectorClusterCesiumContext.ts +123 -0
  135. package/src/vectorCluster/vectorClusterGroup.ts +463 -0
  136. package/src/vectorCluster/vectorClusterGroupCesiumImpl.ts +176 -0
  137. package/src/vectorCluster/vectorClusterGroupCollection.ts +43 -0
  138. package/src/vectorCluster/vectorClusterGroupImpl.ts +107 -0
  139. package/src/vectorCluster/vectorClusterGroupObliqueImpl.ts +84 -0
  140. package/src/vectorCluster/vectorClusterGroupOpenlayersImpl.ts +81 -0
  141. package/src/vectorCluster/vectorClusterStyleItem.ts +490 -0
  142. package/src/vectorCluster/vectorClusterSymbols.ts +2 -0
  143. package/dist/src/layer/cesium/clusterContext.d.ts +0 -20
  144. package/dist/src/layer/cesium/clusterContext.js.map +0 -1
  145. package/src/layer/cesium/clusterContext.ts +0 -140
  146. package/src/ol/source/VcsCluster.js +0 -37
@@ -0,0 +1,463 @@
1
+ import { unByKey } from 'ol/Observable.js';
2
+ import Feature from 'ol/Feature.js';
3
+ import { Point } from 'ol/geom.js';
4
+ import { StyleFunction, StyleLike } from 'ol/style/Style.js';
5
+ import { parseInteger } from '@vcsuite/parsers';
6
+ import { check, maybe } from '@vcsuite/check';
7
+ import VcsObject, { VcsObjectOptions } from '../vcsObject.js';
8
+ import VectorLayer from '../layer/vectorLayer.js';
9
+ import ClusterEnhancedVectorSource from '../ol/source/ClusterEnhancedVectorSource.js';
10
+ import FeatureVisibility, {
11
+ synchronizeFeatureVisibility,
12
+ } from '../layer/featureVisibility.js';
13
+ import LayerState from '../layer/layerState.js';
14
+ import VectorClusterStyleItem, {
15
+ VectorClusterStyleItemOptions,
16
+ getDefaultClusterStyleItem,
17
+ } from './vectorClusterStyleItem.js';
18
+ import type VcsMap from '../map/vcsMap.js';
19
+ import GlobalHider from '../layer/globalHider.js';
20
+ import VectorProperties, {
21
+ VectorPropertiesOptions,
22
+ } from '../layer/vectorProperties.js';
23
+ import CesiumMap from '../map/cesiumMap.js';
24
+ import VectorClusterGroupCesiumImpl from './vectorClusterGroupCesiumImpl.js';
25
+ import VectorClusterGroupOpenlayersImpl from './vectorClusterGroupOpenlayersImpl.js';
26
+ import OpenlayersMap from '../map/openlayersMap.js';
27
+ import type VectorClusterGroupImpl from './vectorClusterGroupImpl.js';
28
+ import ObliqueMap from '../map/obliqueMap.js';
29
+ import VectorClusterGroupObliqueImpl from './vectorClusterGroupObliqueImpl.js';
30
+
31
+ export type VectorClusterGroupOptions = VcsObjectOptions & {
32
+ style?: VectorClusterStyleItemOptions | VectorClusterStyleItem;
33
+ highlightStyle?: VectorClusterStyleItemOptions | VectorClusterStyleItem;
34
+ clusterDistance?: number;
35
+ vectorProperties?: VectorPropertiesOptions;
36
+ };
37
+
38
+ export type VectorClusterGroupImplementationOptions = {
39
+ name: string;
40
+ source: ClusterEnhancedVectorSource;
41
+ maxResolution?: number;
42
+ minResolution?: number;
43
+ globalHider?: GlobalHider;
44
+ featureVisibility: FeatureVisibility;
45
+ vectorProperties: VectorProperties;
46
+ style: StyleFunction;
47
+ clusterDistance: number;
48
+ getLayerByName(this: void, layerName: string): VectorLayer | undefined;
49
+ };
50
+
51
+ function featureIsClusterable(
52
+ feature: Feature | undefined,
53
+ vectorProperties: VectorProperties,
54
+ ): feature is Feature<Point> {
55
+ if (feature) {
56
+ const geometry = feature?.getGeometry();
57
+ return (
58
+ !!geometry &&
59
+ geometry.getType() === 'Point' &&
60
+ vectorProperties.renderAs(feature) === 'geometry'
61
+ );
62
+ }
63
+ return false;
64
+ }
65
+
66
+ function getStyleOrDefaultStyle(
67
+ styleOptions:
68
+ | VectorClusterStyleItemOptions
69
+ | VectorClusterStyleItem
70
+ | undefined,
71
+ defaultValue: VectorClusterStyleItem,
72
+ ): VectorClusterStyleItem {
73
+ if (styleOptions) {
74
+ if (styleOptions instanceof VectorClusterStyleItem) {
75
+ return styleOptions;
76
+ } else {
77
+ return new VectorClusterStyleItem(styleOptions);
78
+ }
79
+ }
80
+ return defaultValue;
81
+ }
82
+
83
+ export default class VectorClusterGroup extends VcsObject {
84
+ static get className(): string {
85
+ return 'VectorClusterGroup';
86
+ }
87
+
88
+ static getDefaultOptions(): VectorClusterGroupOptions {
89
+ return {
90
+ type: 'VectorClusterGroup',
91
+ name: '',
92
+ style: undefined,
93
+ highlightStyle: undefined,
94
+ clusterDistance: 40,
95
+ vectorProperties: {
96
+ ...VectorProperties.getDefaultOptions(),
97
+ eyeOffset: [0, 0, -100],
98
+ heightAboveGround: 60,
99
+ altitudeMode: 'clampToTerrain',
100
+ },
101
+ };
102
+ }
103
+
104
+ private _layerListeners = new Map<VectorLayer, (() => void)[]>();
105
+
106
+ private _activeSourceListeners = new Map<VectorLayer, () => void>();
107
+
108
+ private _source = new ClusterEnhancedVectorSource({});
109
+
110
+ private _featureVisibility = new FeatureVisibility();
111
+
112
+ private _style: VectorClusterStyleItem;
113
+
114
+ highlightStyle: VectorClusterStyleItem | undefined;
115
+
116
+ private _styleFunction: StyleFunction;
117
+
118
+ private _activeMaps = new Set<VcsMap>();
119
+
120
+ private _implementations = new Map<
121
+ VcsMap,
122
+ VectorClusterGroupImpl<VcsMap> | undefined
123
+ >();
124
+
125
+ private _globalHider: GlobalHider | undefined;
126
+
127
+ vectorProperties: VectorProperties;
128
+
129
+ clusterDistance: number;
130
+
131
+ constructor(options: VectorClusterGroupOptions) {
132
+ super(options);
133
+ const defaultOptions = VectorClusterGroup.getDefaultOptions();
134
+
135
+ this._style = getStyleOrDefaultStyle(
136
+ options.style,
137
+ getDefaultClusterStyleItem(),
138
+ );
139
+
140
+ if (options.highlightStyle) {
141
+ this.highlightStyle =
142
+ options.highlightStyle instanceof VectorClusterStyleItem
143
+ ? options.highlightStyle
144
+ : new VectorClusterStyleItem(options.highlightStyle);
145
+ }
146
+
147
+ this.clusterDistance = parseInteger(
148
+ options.clusterDistance,
149
+ defaultOptions.clusterDistance,
150
+ );
151
+
152
+ this.vectorProperties = new VectorProperties(
153
+ options.vectorProperties ?? defaultOptions.vectorProperties!,
154
+ );
155
+
156
+ this._styleFunction = this._style.createStyleFunction((layerName) =>
157
+ [...this._layerListeners.keys()].find((l) => l.name === layerName),
158
+ );
159
+ }
160
+
161
+ get featureVisibility(): FeatureVisibility {
162
+ return this._featureVisibility;
163
+ }
164
+
165
+ get style(): VectorClusterStyleItem {
166
+ return this._style;
167
+ }
168
+
169
+ get globalHider(): GlobalHider | undefined {
170
+ return this._globalHider;
171
+ }
172
+
173
+ setStyle(
174
+ style: VectorClusterStyleItem | VectorClusterStyleItemOptions,
175
+ ): void {
176
+ this._style = getStyleOrDefaultStyle(style, this._style);
177
+ this._styleFunction = this._style.createStyleFunction((layerName) =>
178
+ [...this._layerListeners.keys()].find((l) => l.name === layerName),
179
+ );
180
+
181
+ this._implementations.forEach((impl) => {
182
+ if (impl) {
183
+ impl.style = this._styleFunction;
184
+ }
185
+ });
186
+ this._source
187
+ .getFeatures()
188
+ .filter((f) => !f.getStyle())
189
+ .forEach((f) => f.changed());
190
+ }
191
+
192
+ getHighlightStyleForFeature(feature: Feature): StyleLike | void {
193
+ if (this.highlightStyle) {
194
+ return this.highlightStyle.createStyleFunction((layerName) =>
195
+ [...this._layerListeners.keys()].find((l) => l.name === layerName),
196
+ )(feature, 1);
197
+ }
198
+ return undefined;
199
+ }
200
+
201
+ addLayer(layer: VectorLayer): void {
202
+ check(layer, VectorLayer);
203
+
204
+ this._layerListeners.set(layer, [
205
+ layer.stateChanged.addEventListener((state) => {
206
+ if (state === LayerState.ACTIVE) {
207
+ this._handleActivation(layer);
208
+ } else if (state === LayerState.INACTIVE) {
209
+ this._handleDeactivation(layer);
210
+ }
211
+ }),
212
+ synchronizeFeatureVisibility(
213
+ layer.featureVisibility,
214
+ this._featureVisibility,
215
+ ),
216
+ ]);
217
+
218
+ if (layer.active) {
219
+ this._handleActivation(layer);
220
+ }
221
+ }
222
+
223
+ removeLayer(layer: VectorLayer): void {
224
+ this._handleDeactivation(layer);
225
+ this._layerListeners.get(layer)?.forEach((cb) => cb());
226
+ this._layerListeners.delete(layer);
227
+ }
228
+
229
+ getFeatures(): Feature[] {
230
+ return this._source.getFeatures();
231
+ }
232
+
233
+ /**
234
+ * destroys all current implementations and recreates the ones which have an active map.
235
+ * called for instance when the URL for a layer changes
236
+ */
237
+ async forceRedraw(): Promise<void> {
238
+ const maps = [...this._implementations.keys()];
239
+
240
+ const promises = maps.map((map) => {
241
+ this.removedFromMap(map);
242
+ if (map.active) {
243
+ return this.mapActivated(map);
244
+ }
245
+ return Promise.resolve();
246
+ });
247
+ await Promise.all(promises);
248
+ }
249
+
250
+ setGlobalHider(globalHider?: GlobalHider): void {
251
+ check(globalHider, maybe(GlobalHider));
252
+ this._globalHider = globalHider;
253
+ this.forceRedraw().catch((_e) => {
254
+ this.getLogger().error('Failed to redraw after setting global hider');
255
+ });
256
+ }
257
+
258
+ private _handleActivation(layer: VectorLayer): void {
259
+ if (!this._activeSourceListeners.has(layer)) {
260
+ const source = layer.getSource();
261
+
262
+ if (source.getState() === 'ready') {
263
+ const features = source
264
+ .getFeatures()
265
+ .filter((f) => featureIsClusterable(f, layer.vectorProperties));
266
+
267
+ this._source.addFeatures(features);
268
+ const listeners = [
269
+ source.on('addfeature', ({ feature }) => {
270
+ if (featureIsClusterable(feature, layer.vectorProperties)) {
271
+ this._source.addFeature(feature);
272
+ }
273
+ }),
274
+ source.on('removefeature', ({ feature }) => {
275
+ this._source.removeFeature(feature!);
276
+ }),
277
+ ];
278
+ this._activeSourceListeners.set(layer, () => unByKey(listeners));
279
+ } else {
280
+ source.once('change', this._handleActivation.bind(this, layer));
281
+ }
282
+ }
283
+ }
284
+
285
+ private _handleDeactivation(layer: VectorLayer): void {
286
+ if (this._activeSourceListeners.has(layer)) {
287
+ const source = layer.getSource();
288
+ source
289
+ .getFeatures()
290
+ .filter((f) => featureIsClusterable(f, layer.vectorProperties))
291
+ .forEach((feat) => {
292
+ this._source.removeFeature(feat, true);
293
+ });
294
+ this._source.changed();
295
+ this._activeSourceListeners.get(layer)!();
296
+ this._activeSourceListeners.delete(layer);
297
+ // previous result item & last feature clicked handling
298
+ }
299
+ }
300
+
301
+ // eslint-disable-next-line class-methods-use-this
302
+ isSupported(map: VcsMap): boolean {
303
+ return (
304
+ map.className === 'OpenlayersMap' ||
305
+ map.className === 'CesiumMap' ||
306
+ map.className === 'ObliqueMap'
307
+ );
308
+ }
309
+
310
+ getImplementationOptions(): VectorClusterGroupImplementationOptions {
311
+ return {
312
+ clusterDistance: this.clusterDistance,
313
+ featureVisibility: this._featureVisibility,
314
+ globalHider: this._globalHider,
315
+ maxResolution: 0,
316
+ minResolution: 0,
317
+ name: this.name,
318
+ source: this._source,
319
+ style: this._styleFunction,
320
+ vectorProperties: this.vectorProperties,
321
+ getLayerByName: (layerName: string) =>
322
+ [...this._layerListeners.keys()].find((l) => l.name === layerName),
323
+ };
324
+ }
325
+
326
+ private _createImplementationForMap(
327
+ map: VcsMap,
328
+ ):
329
+ | VectorClusterGroupCesiumImpl
330
+ | VectorClusterGroupOpenlayersImpl
331
+ | VectorClusterGroupObliqueImpl
332
+ | undefined {
333
+ if (map instanceof CesiumMap) {
334
+ return new VectorClusterGroupCesiumImpl(
335
+ map,
336
+ this.getImplementationOptions(),
337
+ );
338
+ } else if (map instanceof OpenlayersMap) {
339
+ return new VectorClusterGroupOpenlayersImpl(
340
+ map,
341
+ this.getImplementationOptions(),
342
+ );
343
+ } else if (map instanceof ObliqueMap) {
344
+ return new VectorClusterGroupObliqueImpl(
345
+ map,
346
+ this.getImplementationOptions(),
347
+ );
348
+ }
349
+ return undefined;
350
+ }
351
+
352
+ /**
353
+ * creates or returns a cached array of cluster group implementations for the given map.
354
+ * @param map initialized Map
355
+ * @returns return the specific implementation
356
+ */
357
+ getImplementationForMap<T extends VcsMap>(
358
+ map: T,
359
+ ): VectorClusterGroupImpl<T> | undefined {
360
+ if (!this._implementations.has(map)) {
361
+ this._implementations.set(map, this._createImplementationForMap(map));
362
+ }
363
+ return this._implementations.get(map) as
364
+ | VectorClusterGroupImpl<T>
365
+ | undefined;
366
+ }
367
+
368
+ /**
369
+ * Returns all implementation of this vector cluster group for all maps
370
+ */
371
+ getImplementations(): VectorClusterGroupImpl<any>[] {
372
+ return [...this._implementations.values()].flat().filter((i) => !!i);
373
+ }
374
+
375
+ /**
376
+ * is called from the map when the map is activated and this vector cluster group belongs to the maps layer collections vector cluster group collection.
377
+ * Will create an implementation if it does not exists and will forward the activation call to the implementation.
378
+ * @param map
379
+ */
380
+ async mapActivated(map: VcsMap): Promise<void> {
381
+ this._activeMaps.add(map);
382
+ await this._activateImplsForMap(map);
383
+ }
384
+
385
+ /**
386
+ * is called from the map when the map is deactivated, and this vector cluster group belongs to the maps layer collections vector cluster group collection.
387
+ * will forward deactivation call to the map specific implementation
388
+ * @param map
389
+ */
390
+ mapDeactivated(map: VcsMap): void {
391
+ this._activeMaps.delete(map);
392
+ const impl = this.getImplementationForMap(map);
393
+ impl?.deactivate();
394
+ }
395
+
396
+ /**
397
+ * is called when a vector cluster group is removed from a maps layer collections vector cluster group collection or said map is destroyed.
398
+ * destroys the associated implementation.
399
+ * @param map
400
+ */
401
+ removedFromMap(map: VcsMap): void {
402
+ this._activeMaps.delete(map);
403
+ const impl = this.getImplementationForMap(map);
404
+ impl?.destroy();
405
+ this._implementations.delete(map);
406
+ }
407
+
408
+ private async _activateImplsForMap(map: VcsMap): Promise<void> {
409
+ const impl = this.getImplementationForMap(map);
410
+ if (impl) {
411
+ try {
412
+ await impl.activate();
413
+ } catch (err) {
414
+ this.getLogger().error(
415
+ `Layer ${this.name} could not activate impl for map ${map.name}`,
416
+ );
417
+ this.getLogger().error(String(err));
418
+ this._implementations.set(map, undefined);
419
+ impl.destroy();
420
+ }
421
+ }
422
+ }
423
+
424
+ toJSON(): VectorClusterGroupOptions {
425
+ const config: Partial<VectorClusterGroupOptions> = super.toJSON();
426
+ const defaultOptions = VectorClusterGroup.getDefaultOptions();
427
+
428
+ if (!this.style.equals(new VectorClusterStyleItem({}))) {
429
+ config.style = this.style.toJSON();
430
+ delete config.style.name;
431
+ delete config.style.type;
432
+ }
433
+
434
+ if (this.highlightStyle) {
435
+ config.highlightStyle = this.highlightStyle.toJSON();
436
+ delete config.highlightStyle.name;
437
+ delete config.highlightStyle.type;
438
+ }
439
+
440
+ if (this.clusterDistance !== defaultOptions.clusterDistance) {
441
+ config.clusterDistance = this.clusterDistance;
442
+ }
443
+
444
+ const vectorProperties = this.vectorProperties.getVcsMeta(
445
+ defaultOptions.vectorProperties,
446
+ );
447
+
448
+ if (Object.keys(vectorProperties).length > 0) {
449
+ config.vectorProperties = vectorProperties;
450
+ }
451
+
452
+ return config;
453
+ }
454
+
455
+ destroy(): void {
456
+ this._featureVisibility.destroy();
457
+ [...this._layerListeners.values()].forEach((arr) =>
458
+ arr.forEach((cb) => cb()),
459
+ );
460
+ this._layerListeners.clear();
461
+ super.destroy();
462
+ }
463
+ }
@@ -0,0 +1,176 @@
1
+ import {
2
+ Billboard,
3
+ Cartographic,
4
+ CustomDataSource,
5
+ Entity,
6
+ HeightReference,
7
+ Label,
8
+ Math as CesiumMath,
9
+ PointPrimitive,
10
+ VerticalOrigin,
11
+ } from '@vcmap-cesium/engine';
12
+ import Feature from 'ol/Feature.js';
13
+ import Point from 'ol/geom/Point.js';
14
+ import { getStylesArray } from '../util/featureconverter/convert.js';
15
+ import { getBillboardOptions } from '../util/featureconverter/pointToCesium.js';
16
+ import Projection from '../util/projection.js';
17
+ import CesiumMap from '../map/cesiumMap.js';
18
+ import { VectorClusterGroupImplementationOptions } from './vectorClusterGroup.js';
19
+ import VectorClusterCesiumContext from './vectorClusterCesiumContext.js';
20
+ import {
21
+ createSourceVectorContextSync,
22
+ SourceVectorContextSync,
23
+ } from '../layer/cesium/sourceVectorContextSync.js';
24
+ import VectorClusterGroupImpl from './vectorClusterGroupImpl.js';
25
+ import { vectorClusterGroupName } from './vectorClusterSymbols.js';
26
+ import type VectorLayer from '../layer/vectorLayer.js';
27
+ import { vcsLayerName } from '../layer/layerSymbols.js';
28
+
29
+ let scratchCartographic = new Cartographic();
30
+
31
+ /**
32
+ * Clusters for vector layers containing point features only
33
+ */
34
+ export default class VectorClusterGroupCesiumImpl extends VectorClusterGroupImpl<CesiumMap> {
35
+ static get className(): string {
36
+ return 'VectorClusterGroupCesiumImpl';
37
+ }
38
+
39
+ private _rootCollection: CustomDataSource;
40
+
41
+ private _removeClusterEventListener: (() => void) | undefined;
42
+
43
+ private _context: VectorClusterCesiumContext | undefined;
44
+
45
+ private _sourceVectorContextSync: SourceVectorContextSync | undefined;
46
+
47
+ private _getLayerByName: (name: string) => VectorLayer | undefined;
48
+
49
+ constructor(
50
+ map: CesiumMap,
51
+ options: VectorClusterGroupImplementationOptions,
52
+ ) {
53
+ super(map, options);
54
+ this._rootCollection = new CustomDataSource(this.name);
55
+ this._rootCollection.clustering.clusterLabels = true;
56
+ this._rootCollection.clustering.clusterPoints = false;
57
+ this._rootCollection.clustering.enabled = true;
58
+ this._rootCollection.clustering.minimumClusterSize = 2;
59
+ this._rootCollection.clustering.pixelRange = options.clusterDistance;
60
+ this._getLayerByName = options.getLayerByName;
61
+ }
62
+
63
+ private _onCluster(
64
+ entities: Entity[],
65
+ cluster: { billboard: Billboard; label: Label; point: PointPrimitive },
66
+ ): void {
67
+ const size = entities.length;
68
+ if (size < 2) {
69
+ return;
70
+ }
71
+
72
+ const features = entities.map((e) => e.olFeature);
73
+ const feature = new Feature({ features });
74
+ feature[vectorClusterGroupName] = this.name;
75
+ const style = getStylesArray(this.style, feature, 0)[0];
76
+
77
+ scratchCartographic = Cartographic.fromCartesian(
78
+ cluster.billboard.position,
79
+ undefined,
80
+ scratchCartographic,
81
+ );
82
+ const position = Projection.wgs84ToMercator(
83
+ [
84
+ CesiumMath.toDegrees(scratchCartographic.longitude),
85
+ CesiumMath.toDegrees(scratchCartographic.latitude),
86
+ scratchCartographic.height,
87
+ ],
88
+ true,
89
+ );
90
+ feature.setGeometry(new Point(position));
91
+
92
+ const bbOptions = getBillboardOptions(
93
+ feature,
94
+ style,
95
+ this.vectorProperties.getAltitudeMode(feature),
96
+ this.vectorProperties,
97
+ );
98
+
99
+ if (bbOptions) {
100
+ cluster.billboard.image = bbOptions.image as string;
101
+ cluster.billboard.scale = bbOptions.scale as number;
102
+ cluster.billboard.heightReference =
103
+ bbOptions.heightReference as HeightReference;
104
+ cluster.billboard.verticalOrigin =
105
+ bbOptions.verticalOrigin as VerticalOrigin;
106
+ if (bbOptions.eyeOffset) {
107
+ cluster.billboard.eyeOffset = bbOptions.eyeOffset;
108
+ }
109
+
110
+ if (bbOptions.scaleByDistance) {
111
+ cluster.billboard.scaleByDistance = bbOptions.scaleByDistance;
112
+ }
113
+
114
+ cluster.billboard.olFeature = feature;
115
+
116
+ cluster.label.show = false;
117
+ cluster.billboard.show = true;
118
+ } else {
119
+ cluster.label.show = false;
120
+ cluster.billboard.show = false;
121
+ }
122
+ }
123
+
124
+ async initialize(): Promise<void> {
125
+ if (!this.initialized) {
126
+ this._context = new VectorClusterCesiumContext(this._rootCollection);
127
+ this._rootCollection[vectorClusterGroupName] = this.name;
128
+ await this.map.addClusterDataSource(this._rootCollection);
129
+ this._sourceVectorContextSync = createSourceVectorContextSync(
130
+ this._source,
131
+ this._context,
132
+ this.map.getScene()!,
133
+ this.style,
134
+ (f) =>
135
+ this._getLayerByName(f[vcsLayerName]!)?.vectorProperties ??
136
+ this.vectorProperties,
137
+ );
138
+ this._removeClusterEventListener =
139
+ this._rootCollection.clustering.clusterEvent.addEventListener(
140
+ (entities, cluster) => {
141
+ this._onCluster(entities, cluster);
142
+ },
143
+ );
144
+ }
145
+ await super.initialize();
146
+ }
147
+
148
+ async activate(): Promise<void> {
149
+ if (!this.active) {
150
+ await super.activate();
151
+ if (this.active) {
152
+ this._sourceVectorContextSync?.activate();
153
+ this._rootCollection.show = true;
154
+ }
155
+ }
156
+ }
157
+
158
+ deactivate(): void {
159
+ this._rootCollection.show = false;
160
+ this._sourceVectorContextSync?.deactivate();
161
+ super.deactivate();
162
+ }
163
+
164
+ destroy(): void {
165
+ if (this._removeClusterEventListener) {
166
+ this._removeClusterEventListener();
167
+ }
168
+ if (this.initialized) {
169
+ this._sourceVectorContextSync?.destroy();
170
+ this._context?.destroy();
171
+ this.map.removeClusterDataSource(this._rootCollection);
172
+ }
173
+ this._context = undefined;
174
+ super.destroy();
175
+ }
176
+ }
@@ -0,0 +1,43 @@
1
+ import { check } from '@vcsuite/check';
2
+ import VectorClusterGroup from './vectorClusterGroup.js';
3
+ import Collection from '../util/collection.js';
4
+ import GlobalHider from '../layer/globalHider.js';
5
+
6
+ export default class VectorClusterGroupCollection extends Collection<VectorClusterGroup> {
7
+ /**
8
+ * The global hider for this collection.
9
+ */
10
+ private _globalHider: GlobalHider;
11
+
12
+ constructor(globalHider: GlobalHider) {
13
+ super();
14
+ this._globalHider = globalHider;
15
+
16
+ this.added.addEventListener((g) => {
17
+ g.setGlobalHider(this._globalHider);
18
+ });
19
+ this.removed.addEventListener((g) => {
20
+ g.setGlobalHider();
21
+ });
22
+ }
23
+
24
+ /**
25
+ * The current global hider of these layers
26
+ */
27
+ get globalHider(): GlobalHider {
28
+ return this._globalHider;
29
+ }
30
+
31
+ /**
32
+ * The current global hider of these layers
33
+ * @param globalHider
34
+ */
35
+ set globalHider(globalHider: GlobalHider) {
36
+ check(globalHider, GlobalHider);
37
+
38
+ this._globalHider = globalHider;
39
+ this._array.forEach((vectorClusterGroup) => {
40
+ vectorClusterGroup.setGlobalHider(this._globalHider);
41
+ });
42
+ }
43
+ }