@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,436 @@
1
+ import { getLogger } from '@vcsuite/logger';
2
+ import VectorSource from 'ol/source/Vector.js';
3
+ import { unByKey } from 'ol/Observable.js';
4
+ import { Feature } from 'ol';
5
+ import { Extent as OLExtent } from 'ol/extent.js';
6
+ import type { Geometry } from 'ol/geom.js';
7
+ import type { EventsKey } from 'ol/events.js';
8
+ import ObliqueMap from '../../map/obliqueMap.js';
9
+ import {
10
+ actuallyIsCircle,
11
+ alreadyTransformedToImage,
12
+ doNotTransform,
13
+ obliqueGeometry,
14
+ originalFeatureSymbol,
15
+ } from '../vectorSymbols.js';
16
+ import {
17
+ getPolygonizedGeometry,
18
+ imageGeometryToMercatorGeometry,
19
+ mercatorGeometryToImageGeometry,
20
+ setNewGeometry,
21
+ } from './obliqueHelpers.js';
22
+ import type ObliqueImage from '../../oblique/obliqueImage.js';
23
+ import { mercatorProjection } from '../../util/projection.js';
24
+
25
+ export type SourceObliqueSync = {
26
+ readonly active: boolean;
27
+ readonly obliqueSource: VectorSource;
28
+ activate(): void;
29
+ deactivate(): void;
30
+ destroy(): void;
31
+ };
32
+
33
+ type UpdatingTimeouts = Record<
34
+ string | number,
35
+ number | boolean | null | NodeJS.Timeout
36
+ >;
37
+
38
+ type FeatureListeners = {
39
+ originalFeatureGeometryChanged: EventsKey;
40
+ originalFeatureChanged: EventsKey;
41
+ originalGeometryChanged: EventsKey;
42
+ obliqueGeometryChanged: EventsKey;
43
+ };
44
+
45
+ function featureInExtent(feature: Feature, extent?: OLExtent): boolean {
46
+ if (extent) {
47
+ const geometry = feature.getGeometry();
48
+ if (geometry) {
49
+ return (
50
+ geometry[alreadyTransformedToImage] || geometry.intersectsExtent(extent)
51
+ );
52
+ }
53
+ }
54
+ return false;
55
+ }
56
+
57
+ function createFeatureListener(
58
+ originalFeature: Feature,
59
+ obliqueFeature: Feature,
60
+ updatingMercator: UpdatingTimeouts,
61
+ updateObliqueGeometry: (
62
+ originalFeature: Feature,
63
+ obliqueFeature: Feature,
64
+ ) => void,
65
+ updateMercatorGeometry: (
66
+ originalFeature: Feature,
67
+ obliqueFeature: Feature,
68
+ ) => void,
69
+ ): () => void {
70
+ const featureId = obliqueFeature.getId() as string | number;
71
+
72
+ const originalGeometryChanged = (listeners: FeatureListeners): void => {
73
+ unByKey(listeners.originalGeometryChanged);
74
+ unByKey(listeners.obliqueGeometryChanged);
75
+ setNewGeometry(originalFeature, obliqueFeature);
76
+ updateObliqueGeometry(originalFeature, obliqueFeature);
77
+ listeners.originalGeometryChanged = originalFeature
78
+ .getGeometry()!
79
+ .on('change', () => {
80
+ updateObliqueGeometry(originalFeature, obliqueFeature);
81
+ });
82
+ listeners.obliqueGeometryChanged = obliqueFeature
83
+ .getGeometry()!
84
+ .on('change', () => {
85
+ updateMercatorGeometry(originalFeature, obliqueFeature);
86
+ });
87
+ };
88
+
89
+ const listeners: FeatureListeners = {
90
+ originalFeatureGeometryChanged: originalFeature.on(
91
+ 'change:geometry',
92
+ () => {
93
+ const originalGeometry = originalFeature.getGeometry() as Geometry;
94
+ if (originalGeometry[actuallyIsCircle]) {
95
+ unByKey(listeners.originalGeometryChanged);
96
+ listeners.originalGeometryChanged = originalFeature
97
+ .getGeometry()!
98
+ .on('change', () => {
99
+ if (updatingMercator[featureId]) {
100
+ return;
101
+ }
102
+ delete originalGeometry[actuallyIsCircle];
103
+ originalGeometryChanged(listeners);
104
+ });
105
+ return;
106
+ }
107
+ originalGeometryChanged(listeners);
108
+ },
109
+ ),
110
+ originalFeatureChanged: originalFeature.on('change', () => {
111
+ obliqueFeature.setStyle(originalFeature.getStyle());
112
+ }),
113
+ originalGeometryChanged: originalFeature.getGeometry()!.on('change', () => {
114
+ updateObliqueGeometry(originalFeature, obliqueFeature);
115
+ }),
116
+ obliqueGeometryChanged: obliqueFeature.getGeometry()!.on('change', () => {
117
+ updateMercatorGeometry(originalFeature, obliqueFeature);
118
+ }),
119
+ };
120
+
121
+ return () => {
122
+ unByKey(Object.values(listeners));
123
+ };
124
+ }
125
+
126
+ function clearUpdatingTimeouts(timeouts: UpdatingTimeouts): void {
127
+ Object.values(timeouts).forEach((timeout) => {
128
+ if (typeof timeout !== 'boolean') {
129
+ clearTimeout(timeout as number);
130
+ }
131
+ });
132
+ }
133
+
134
+ /**
135
+ * Creates a SourceObliqueSync. This ensures that the features in the source are synced to the oblique source.
136
+ * Adding, removing and changing features in the source will be reflected in the oblique source. Changing the oblique geometry will
137
+ * be synced back onto the original geometry. Changing the geometry will create a new oblique geometry on the same feature.
138
+ * Feature changes will lead to an update of style.
139
+ * @param source
140
+ * @param map
141
+ */
142
+ export function createSourceObliqueSync(
143
+ source: VectorSource,
144
+ map: ObliqueMap,
145
+ ): SourceObliqueSync {
146
+ const obliqueSource = new VectorSource();
147
+ let active = false;
148
+ let sourceListeners = (): void => {};
149
+ let imageChangedListener: (() => void) | undefined;
150
+ const featureListeners = new Map<string | number, () => void>();
151
+
152
+ let updatingMercator: UpdatingTimeouts = {};
153
+ let updatingOblique: UpdatingTimeouts = {};
154
+
155
+ // the extent of the current image
156
+ let currentExtent: OLExtent | undefined;
157
+
158
+ let currentImageName: string | undefined;
159
+
160
+ const convertToOblique = async (
161
+ originalFeature: Feature,
162
+ obliqueFeature: Feature,
163
+ ): Promise<void> => {
164
+ const id = originalFeature.getId()!;
165
+ const vectorGeometry = originalFeature.getGeometry() as Geometry;
166
+ const imageGeometry = obliqueFeature.getGeometry() as Geometry;
167
+ updatingOblique[id] = true;
168
+ let promise: Promise<unknown>;
169
+ if (!vectorGeometry[alreadyTransformedToImage]) {
170
+ promise = mercatorGeometryToImageGeometry(
171
+ vectorGeometry,
172
+ imageGeometry,
173
+ map.currentImage as ObliqueImage,
174
+ );
175
+ } else {
176
+ obliqueFeature
177
+ .getGeometry()!
178
+ .setCoordinates(vectorGeometry.getCoordinates());
179
+ // we MUST wait for a promise, otherwise this is sync and you can add a feature twice
180
+ promise = Promise.resolve();
181
+ }
182
+ await promise;
183
+ updatingOblique[id] = null;
184
+ };
185
+
186
+ const updateObliqueGeometry = (
187
+ originalFeature: Feature,
188
+ obliqueFeature: Feature,
189
+ ): void => {
190
+ const id = originalFeature.getId()!;
191
+ if (updatingMercator[id]) {
192
+ return;
193
+ }
194
+ if (updatingOblique[id] != null) {
195
+ clearTimeout(updatingOblique[id] as number);
196
+ }
197
+ if (originalFeature.getGeometry()?.[alreadyTransformedToImage]) {
198
+ convertToOblique(originalFeature, obliqueFeature)
199
+ .catch((_e) => {
200
+ getLogger('SourceObliqueSync').warning(
201
+ `Failed to convert feature with id ${id} to oblique`,
202
+ );
203
+ })
204
+ .finally(() => {
205
+ updatingOblique[id] = null;
206
+ });
207
+ } else {
208
+ updatingOblique[id] = setTimeout(() => {
209
+ convertToOblique(originalFeature, obliqueFeature)
210
+ .catch((_e) => {
211
+ getLogger('SourceObliqueSync').warning(
212
+ `Failed to convert feature with id ${id} to oblique`,
213
+ );
214
+ })
215
+ .finally(() => {
216
+ updatingOblique[id] = null;
217
+ });
218
+ }, 200);
219
+ }
220
+ };
221
+
222
+ const updateMercatorGeometry = (
223
+ originalFeature: Feature,
224
+ obliqueFeature: Feature,
225
+ ): void => {
226
+ const id = originalFeature.getId() as string | number;
227
+ if (updatingOblique[id]) {
228
+ return;
229
+ }
230
+ if (updatingMercator[id] != null) {
231
+ clearTimeout(updatingMercator[id] as number);
232
+ }
233
+ const imageName = currentImageName;
234
+ updatingMercator[id] = setTimeout(() => {
235
+ const originalGeometry = getPolygonizedGeometry(originalFeature, false);
236
+ if (originalGeometry[actuallyIsCircle]) {
237
+ originalFeature.setGeometry(originalGeometry);
238
+ }
239
+ const imageGeometry = getPolygonizedGeometry(obliqueFeature, true);
240
+ updatingMercator[id] = true;
241
+ imageGeometryToMercatorGeometry(
242
+ imageGeometry,
243
+ originalGeometry,
244
+ map.collection!.getImageByName(imageName as string) as ObliqueImage,
245
+ )
246
+ .catch((_e) => {
247
+ getLogger('SourceObliqueSync').warning(
248
+ `Failed to update feature with id ${id} mercator geometry`,
249
+ );
250
+ })
251
+ .finally(() => {
252
+ updatingMercator[id] = null;
253
+ });
254
+ }, 200);
255
+ };
256
+
257
+ const addFeature = async (originalFeature: Feature): Promise<void> => {
258
+ if (!active) {
259
+ currentImageName = undefined;
260
+ } else if (currentExtent) {
261
+ const id = originalFeature.getId()!;
262
+ const originalGeometry = originalFeature.getGeometry();
263
+ if (originalFeature[doNotTransform]) {
264
+ if (originalGeometry && !obliqueSource.getFeatureById(id)) {
265
+ obliqueSource.addFeature(originalFeature);
266
+ }
267
+ return;
268
+ }
269
+
270
+ if (obliqueSource.getFeatureById(id) || updatingOblique[id] != null) {
271
+ return;
272
+ }
273
+ const obliqueFeature = new Feature({});
274
+ obliqueFeature.setId(id);
275
+ obliqueFeature[originalFeatureSymbol] = originalFeature;
276
+ setNewGeometry(originalFeature, obliqueFeature);
277
+ obliqueFeature.setStyle(originalFeature.getStyle());
278
+
279
+ const featureListener = createFeatureListener(
280
+ originalFeature,
281
+ obliqueFeature,
282
+ updatingMercator,
283
+ updateObliqueGeometry,
284
+ updateMercatorGeometry,
285
+ );
286
+
287
+ await convertToOblique(originalFeature, obliqueFeature);
288
+ if (source.hasFeature(originalFeature)) {
289
+ // if not in source, feature has been removed in between.
290
+ obliqueSource.addFeature(obliqueFeature);
291
+ featureListeners.set(id, featureListener);
292
+ } else {
293
+ featureListener();
294
+ }
295
+ }
296
+ };
297
+
298
+ const removeFeature = (feature: Feature): void => {
299
+ const id = feature.getId()!;
300
+ const feat = obliqueSource.getFeatureById(id);
301
+ if (updatingOblique[id] != null) {
302
+ clearTimeout(updatingOblique[id] as number);
303
+ updatingOblique[id] = null;
304
+ }
305
+ if (feat) {
306
+ featureListeners.get(id)?.();
307
+ featureListeners.delete(id);
308
+ obliqueSource.removeFeature(feat);
309
+ }
310
+ };
311
+
312
+ const setSourceListeners = (): void => {
313
+ sourceListeners();
314
+ const listeners = [
315
+ source.on('addfeature', (event) => {
316
+ const f = event.feature as Feature;
317
+ if (featureInExtent(f, currentExtent)) {
318
+ addFeature(f).catch((_e) => {
319
+ getLogger('SourceObliqueSync').warning(
320
+ `Failed to add feature with id ${f.getId()!} to oblique source`,
321
+ );
322
+ });
323
+ }
324
+ }),
325
+ source.on('removefeature', (event) => {
326
+ removeFeature(event.feature as Feature);
327
+ }),
328
+ source.on('changefeature', (event) => {
329
+ const f = event.feature as Feature;
330
+ const newFeatureId = f.getId()!;
331
+ if (
332
+ !featureListeners.has(newFeatureId) &&
333
+ featureInExtent(f, currentExtent)
334
+ ) {
335
+ addFeature(f).catch((_e) => {
336
+ getLogger('SourceObliqueSync').warning(
337
+ `Failed to add feature with id ${newFeatureId} to oblique source`,
338
+ );
339
+ });
340
+ }
341
+ }),
342
+ ];
343
+ sourceListeners = (): void => {
344
+ unByKey(listeners);
345
+ };
346
+ };
347
+
348
+ const fetchFeaturesInView = (): void => {
349
+ if (
350
+ active &&
351
+ map.currentImage &&
352
+ currentImageName !== map.currentImage.name
353
+ ) {
354
+ currentExtent = map
355
+ .getExtentOfCurrentImage()
356
+ .getCoordinatesInProjection(mercatorProjection);
357
+ source.forEachFeatureInExtent(currentExtent, (feature) => {
358
+ addFeature(feature).catch((_e) => {
359
+ getLogger('SourceObliqueSync').warning(
360
+ `Failed to add feature with id ${feature.getId()!} to oblique source`,
361
+ );
362
+ });
363
+ });
364
+ source.forEachFeature((feature) => {
365
+ if (feature.getGeometry()?.[alreadyTransformedToImage]) {
366
+ addFeature(feature).catch((_e) => {
367
+ getLogger('SourceObliqueSync').warning(
368
+ `Failed to add feature with id ${feature.getId()!} to oblique source`,
369
+ );
370
+ });
371
+ }
372
+ });
373
+ currentImageName = map.currentImage.name;
374
+ }
375
+ };
376
+
377
+ const clearCurrentImage = (): void => {
378
+ featureListeners.forEach((listener): void => {
379
+ listener();
380
+ });
381
+ featureListeners.clear();
382
+ clearUpdatingTimeouts(updatingOblique);
383
+ clearUpdatingTimeouts(updatingMercator);
384
+ updatingOblique = {};
385
+ updatingMercator = {};
386
+ obliqueSource.getFeatures().forEach((f) => {
387
+ const original = f[originalFeatureSymbol];
388
+ if (original) {
389
+ delete original[obliqueGeometry];
390
+ const originalGeometry = original.getGeometry();
391
+ if (originalGeometry?.[alreadyTransformedToImage]) {
392
+ updateMercatorGeometry(original, f);
393
+ }
394
+ delete originalGeometry?.[alreadyTransformedToImage];
395
+ }
396
+ });
397
+ obliqueSource.clear(true);
398
+ currentImageName = undefined;
399
+ currentExtent = undefined;
400
+ };
401
+
402
+ const activate = (): void => {
403
+ active = true;
404
+ setSourceListeners();
405
+ imageChangedListener = map.imageChanged?.addEventListener(() => {
406
+ clearCurrentImage();
407
+ fetchFeaturesInView();
408
+ });
409
+ fetchFeaturesInView();
410
+ };
411
+
412
+ const deactivate = (): void => {
413
+ active = false;
414
+ imageChangedListener?.();
415
+ sourceListeners();
416
+ clearCurrentImage();
417
+ };
418
+
419
+ const destroy = (): void => {
420
+ obliqueSource.clear(true);
421
+ obliqueSource.dispose();
422
+ clearCurrentImage();
423
+ };
424
+
425
+ return {
426
+ get active(): boolean {
427
+ return active;
428
+ },
429
+ get obliqueSource(): VectorSource {
430
+ return obliqueSource;
431
+ },
432
+ activate,
433
+ deactivate,
434
+ destroy,
435
+ };
436
+ }