@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,572 @@
1
+ import RBush from 'rbush';
2
+ import knn from 'rbush-knn';
3
+ import { v4 as uuidv4 } from 'uuid';
4
+ import { getTransform } from 'ol/proj.js';
5
+ import { createXYZ } from 'ol/tilegrid.js';
6
+ import Feature from 'ol/Feature.js';
7
+ import Polygon, { fromExtent } from 'ol/geom/Polygon.js';
8
+ import Vector from 'ol/source/Vector.js';
9
+ import { boundingExtent, buffer, containsCoordinate, getCenter } from 'ol/extent.js';
10
+ import { Event as CesiumEvent } from '@vcmap/cesium';
11
+ import { DataState, getStateFromStatesArray } from './ObliqueDataSet.js';
12
+ import { ObliqueViewDirection } from './ObliqueViewDirection.js';
13
+
14
+ /**
15
+ * @typedef {Object} ObliqueCameraOptions
16
+ * @property {string} name
17
+ * @property {!import("ol/coordinate").Coordinate} "principal-point"
18
+ * @property {import("ol/coordinate").Coordinate|undefined} "pixel-size"
19
+ * @property {Array<number>|undefined} "radial-distorsion-expected-2-found"
20
+ * @property {Array<number>|undefined} "radial-distorsion-found-2-expected"
21
+ * @property {import("ol/size").Size|undefined} size
22
+ * @api
23
+ */
24
+
25
+ /**
26
+ * @typedef {Object} ObliqueGeneralImageInfo
27
+ * @property {number} width
28
+ * @property {number} height
29
+ * @property {Array<number>} "tile-resolution"
30
+ * @property {number} "tile-width"
31
+ * @property {number} "tile-height"
32
+ * @property {Array<ObliqueCameraOptions>|undefined} cameraParameter
33
+ * @property {string} crs
34
+ * @api
35
+ */
36
+
37
+ /**
38
+ * The data within an image.json
39
+ * @typedef {Object} ObliqueImageJson
40
+ * @property {ObliqueGeneralImageInfo} generalImageInfo
41
+ * @property {number|undefined} tileLevel
42
+ * @property {Array<Array<*>>|undefined} images
43
+ * @property {Array<string>|undefined} availableTiles
44
+ * @api
45
+ */
46
+
47
+ /**
48
+ * @typedef {Object} ObliqueVersion
49
+ * @property {number} version
50
+ * @property {number} buildNumber
51
+ * @api
52
+ */
53
+
54
+ /**
55
+ * @typedef {Object} ObliqueCollectionOptions
56
+ * @property {string|undefined} name
57
+ * @property {Array<import("@vcmap/core").ObliqueDataSet>} dataSets
58
+ * @property {number|undefined} maxZoom
59
+ * @property {number|undefined} minZoom
60
+ * @property {number|undefined} [scaleFactor=4]
61
+ * @property {number|undefined} hideLevels
62
+ * @api
63
+ */
64
+
65
+ /**
66
+ * @param {Array<import("@vcmap/core").ObliqueImage>} images
67
+ * @returns {Array<import("ol").Feature<import("ol/geom/Geometry").default>>}
68
+ */
69
+ function getImageFeatures(images) {
70
+ return images.map((image) => {
71
+ const transform = getTransform(image.meta.projection, 'EPSG:3857');
72
+ const feature = new Feature({
73
+ geometry: new Polygon([image.groundCoordinates.map(c => transform(c.slice(0, 2)))]),
74
+ viewDirection: image.viewDirection,
75
+ });
76
+ feature.setId(image.name);
77
+ return feature;
78
+ });
79
+ }
80
+
81
+ /**
82
+ * @param {Object<string, DataState>} tiles
83
+ * @returns {Array<import("ol").Feature<import("ol/geom/Geometry").default>>}
84
+ */
85
+ function getTileFeatures(tiles) {
86
+ const tileGrid = createXYZ();
87
+ return Object.entries(tiles)
88
+ .map(([stringTileCoord, state]) => {
89
+ const tileCoord = stringTileCoord.split('/')
90
+ .map(tc => Number.parseInt(tc, 10));
91
+
92
+ const extent = tileGrid.getTileCoordExtent(tileCoord);
93
+ const feature = new Feature({
94
+ geometry: fromExtent(extent),
95
+ state,
96
+ });
97
+ feature.setId(stringTileCoord);
98
+ return feature;
99
+ });
100
+ }
101
+
102
+ /**
103
+ * @class
104
+ * @export
105
+ */
106
+ class ObliqueCollection {
107
+ /**
108
+ * @param {ObliqueCollectionOptions} options
109
+ */
110
+ constructor(options) {
111
+ /**
112
+ * The unique name of the collection
113
+ * @type {string}
114
+ * @api
115
+ */
116
+ this.name = options.name || uuidv4();
117
+
118
+ /**
119
+ * Maps each direction to an RTree
120
+ * @type {Map<import("@vcmap/core").ObliqueViewDirection, RBush>}
121
+ * @private
122
+ */
123
+ this._directionTrees = new Map();
124
+ /**
125
+ * Maps image name to image
126
+ * @type {Map<string, import("@vcmap/core").ObliqueImage>}
127
+ * @private
128
+ */
129
+ this._images = new Map();
130
+ /**
131
+ * Maps urls to general infos & cameras
132
+ * @type {Array<import("@vcmap/core").ObliqueDataSet>}
133
+ * @private
134
+ */
135
+ this._dataSets = [];
136
+
137
+ /** @type {ObliqueViewOptions} */
138
+ this.viewOptions = {
139
+ maxZoom: options.maxZoom || 0,
140
+ minZoom: options.minZoom || 0,
141
+ scaleFactor: options.scaleFactor || 4,
142
+ hideLevels: options.hideLevels || 0,
143
+ };
144
+
145
+ /** @type {boolean} */
146
+ this._loaded = false;
147
+
148
+ /**
149
+ * Event raised when images are loaded. Is passed an Array of ObliqueImages as its only argument.
150
+ * @type {import("@vcmap/cesium").Event}
151
+ * @api
152
+ */
153
+ this.imagesLoaded = new CesiumEvent();
154
+
155
+ /**
156
+ * @type {import("ol/source").Vector<import("ol/geom/Geometry").default>|null}
157
+ * @private
158
+ */
159
+ this._tileFeatureSource = null;
160
+ /**
161
+ * @type {import("ol/source").Vector<import("ol/geom/Geometry").default>|null}
162
+ * @private
163
+ */
164
+ this._imageFeatureSource = null;
165
+ if (Array.isArray(options.dataSets)) {
166
+ options.dataSets.forEach((dataSet) => {
167
+ this._addDataSet(dataSet);
168
+ });
169
+ }
170
+ }
171
+
172
+ /**
173
+ * @type {Array<import("@vcmap/core").ObliqueDataSet>}
174
+ * @api
175
+ * @readonly
176
+ */
177
+ get dataSets() {
178
+ return this._dataSets.slice();
179
+ }
180
+
181
+ /**
182
+ * Indicates, that this collection has been loaded
183
+ * @type {boolean}
184
+ * @api
185
+ */
186
+ get loaded() {
187
+ return this._loaded;
188
+ }
189
+
190
+ /**
191
+ * A vector source of all tiles available to this collection in mercator. The feature have a "state" property,
192
+ * the id is the string tile coordinate "z/x/y" of the tile
193
+ * @type {import("ol/source/Vector").default<import("ol/geom/Geometry").default>}
194
+ * @api
195
+ * @readonly
196
+ */
197
+ get tileFeatureSource() {
198
+ if (!this._tileFeatureSource) {
199
+ this._tileFeatureSource = this._createTileFeatureSource();
200
+ }
201
+ return this._tileFeatureSource;
202
+ }
203
+
204
+ /**
205
+ * A vector source of all image currently loaded for this collection in mercator.
206
+ * The id is the image name. The feature has a "viewDirection" property.
207
+ * @type {import("ol/source/Vector").default<import("ol/geom/Geometry").default>}
208
+ * @api
209
+ * @readonly
210
+ */
211
+ get imageFeatureSource() {
212
+ if (!this._imageFeatureSource) {
213
+ this._imageFeatureSource = this._createImageFeatureSource();
214
+ }
215
+ return this._imageFeatureSource;
216
+ }
217
+
218
+ /**
219
+ * All currently loaded images
220
+ * @type {Array<import("@vcmap/core").ObliqueImage>}
221
+ * @api
222
+ * @readonly
223
+ */
224
+ get images() {
225
+ return [...this._images.values()];
226
+ }
227
+
228
+ /**
229
+ * @returns {import("ol/source/Vector").default<import("ol/geom/Geometry").default>}
230
+ * @private
231
+ */
232
+ _createTileFeatureSource() {
233
+ const features = getTileFeatures(this.getTiles());
234
+ const source = new Vector();
235
+ source.addFeatures(features);
236
+ return source;
237
+ }
238
+
239
+ /**
240
+ * @returns {import("ol/source/Vector").default<import("ol/geom/Geometry").default>}
241
+ * @private
242
+ */
243
+ _createImageFeatureSource() {
244
+ const features = getImageFeatures([...this._images.values()]);
245
+ const source = new Vector();
246
+ source.addFeatures(features);
247
+ return source;
248
+ }
249
+
250
+ /**
251
+ * @param {import("@vcmap/core").ObliqueDataSet} dataSet
252
+ * @returns {Promise<void>}
253
+ * @private
254
+ */
255
+ async _loadDataSet(dataSet) {
256
+ await dataSet.load();
257
+ if (this._tileFeatureSource) {
258
+ const features = getTileFeatures(dataSet.getTiles());
259
+ this._tileFeatureSource.addFeatures(features);
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Adds an oblique data set to this collection.
265
+ * @param {import("@vcmap/core").ObliqueDataSet} dataSet
266
+ * @private
267
+ */
268
+ _addDataSet(dataSet) {
269
+ dataSet.imagesLoaded.addEventListener((images, tileCoordinate) => {
270
+ this._loadImages(images, tileCoordinate);
271
+ });
272
+ this._loadImages(dataSet.images);
273
+ this._dataSets.push(dataSet);
274
+ }
275
+
276
+ /**
277
+ * Adds an oblique data set to this collection.
278
+ * @param {import("@vcmap/core").ObliqueDataSet} dataSet
279
+ * @returns {Promise<void>}
280
+ * @api
281
+ */
282
+ async addDataSet(dataSet) {
283
+ if (this._loadingPromise) {
284
+ await this._loadingPromise;
285
+ await this._loadDataSet(dataSet);
286
+ }
287
+ this._addDataSet(dataSet);
288
+ }
289
+
290
+ /**
291
+ * Loads all meta data associated with this collection
292
+ * @returns {Promise<void>}
293
+ * @api
294
+ */
295
+ async load() {
296
+ if (!this._loadingPromise) {
297
+ this._loadingPromise = Promise.all(this._dataSets.map(i => this._loadDataSet(i)));
298
+ await this._loadingPromise;
299
+ this._loaded = true;
300
+ }
301
+
302
+ await this._loadingPromise;
303
+ }
304
+
305
+ /**
306
+ * @param {Array<import("@vcmap/core").ObliqueImage>} images
307
+ * @param {string=} tileCoordinate
308
+ * @private
309
+ */
310
+ _loadImages(images, tileCoordinate) {
311
+ if (tileCoordinate && this._tileFeatureSource) {
312
+ const tileFeature = this._tileFeatureSource.getFeatureById(tileCoordinate);
313
+ if (tileFeature) {
314
+ tileFeature.set('state', DataState.READY);
315
+ }
316
+ }
317
+
318
+ const directions = new Map();
319
+ images.forEach((image) => {
320
+ this._images.set(image.name, image);
321
+ if (!directions.has(image.viewDirection)) {
322
+ directions.set(image.viewDirection, []);
323
+ }
324
+
325
+ const transform = getTransform(image.meta.projection, 'EPSG:3857');
326
+ const coord = image.centerPointOnGround.slice(0, 2);
327
+ transform(coord, coord);
328
+ directions.get(image.viewDirection).push({
329
+ minX: coord[0],
330
+ minY: coord[1],
331
+ maxX: coord[0],
332
+ maxY: coord[1],
333
+ name: image.name,
334
+ });
335
+ });
336
+
337
+ directions.forEach((imageItems, direction) => {
338
+ if (!this._directionTrees.get(direction)) {
339
+ this._directionTrees.set(direction, new RBush());
340
+ }
341
+ this._directionTrees.get(direction).load(imageItems);
342
+ });
343
+
344
+ if (this._imageFeatureSource) {
345
+ const features = getImageFeatures(images);
346
+ this._imageFeatureSource.addFeatures(features);
347
+ }
348
+ this.imagesLoaded.raiseEvent(images);
349
+ }
350
+
351
+ /**
352
+ * Gets all available tile coordinates across all data sets, including their loaded state. Return value is
353
+ * an object, where the key is the tile coordinate in z/x/y and the value is the data state
354
+ * @returns {Object<string, DataState>}
355
+ * @api
356
+ */
357
+ getTiles() {
358
+ /** @type {Object<string, DataState>} */
359
+ const tiles = {};
360
+ this._dataSets.forEach((dataSet) => {
361
+ Object.entries(dataSet.getTiles())
362
+ .forEach(([tileCoord, state]) => {
363
+ if (tiles[tileCoord]) {
364
+ tiles[tileCoord] = getStateFromStatesArray([state, tiles[tileCoord]]);
365
+ } else {
366
+ tiles[tileCoord] = state;
367
+ }
368
+ });
369
+ });
370
+
371
+ return tiles;
372
+ }
373
+
374
+ /**
375
+ * Returns an image by its name, if it has been loaded
376
+ * @param {string} name
377
+ * @returns {import("@vcmap/core").ObliqueImage}
378
+ * @api
379
+ */
380
+ getImageByName(name) {
381
+ return this._images.get(name);
382
+ }
383
+
384
+ /**
385
+ * Returns a list of viewDirections which a currently available in this collection
386
+ * @returns {Array<ObliqueViewDirection>}
387
+ * @api
388
+ */
389
+ getAvailableViewDirections() {
390
+ return [...this._directionTrees.keys()];
391
+ }
392
+
393
+ /**
394
+ * Returns the state of the data for a given location an all underlying data sources
395
+ * @param {import("ol/coordinate").Coordinate} mercatorCoordinate - coordinate in web mercator
396
+ * @returns {DataState}
397
+ * @api
398
+ */
399
+ getDataStateForCoordinate(mercatorCoordinate) {
400
+ const states = this._dataSets.map(i => i.getDataStateForCoordinate(mercatorCoordinate));
401
+ return getStateFromStatesArray(states);
402
+ }
403
+
404
+ /**
405
+ * Returns the state of the data for a given location an all underlying data sources
406
+ * @param {import("ol/coordinate").Coordinate} extent - coordinate in web mercator
407
+ * @returns {DataState}
408
+ * @api
409
+ */
410
+ getDataStateForExtent(extent) {
411
+ const states = this._dataSets.map(i => i.getDataStateForExtent(extent));
412
+ return getStateFromStatesArray(states);
413
+ }
414
+
415
+ /**
416
+ * Loads data for a given mercator Coordinate
417
+ * @param {import("ol/coordinate").Coordinate} mercatorCoordinate - coordinate in web mercator
418
+ * @returns {Promise<void>}
419
+ * @api
420
+ */
421
+ async loadDataForCoordinate(mercatorCoordinate) {
422
+ await Promise.all(this._dataSets.map(i => i.loadDataForCoordinate(mercatorCoordinate)));
423
+ }
424
+
425
+ /**
426
+ * Loads all data tiles in the given extent
427
+ * @param {import("ol/extent").Extent} extent
428
+ * @returns {Promise<void>}
429
+ * @api
430
+ */
431
+ async loadDataForExtent(extent) {
432
+ await Promise.all(this._dataSets.map(i => i.loadDataForExtent(extent)));
433
+ }
434
+
435
+ /**
436
+ * @param {import("ol/coordinate").Coordinate} mercatorCoordinate
437
+ * @param {import("@vcmap/core").ObliqueViewDirection} direction
438
+ * @returns {import("@vcmap/core").ObliqueImage|undefined}
439
+ * @private
440
+ */
441
+ _getNextImageForCoordinate(mercatorCoordinate, direction) {
442
+ const tree = this._directionTrees.get(direction);
443
+ if (tree) {
444
+ const candidates = knn(tree, mercatorCoordinate[0], mercatorCoordinate[1], 1);
445
+ if (candidates.length === 1 && candidates[0].name) {
446
+ return this.getImageByName(candidates[0].name);
447
+ }
448
+ }
449
+ return undefined;
450
+ }
451
+
452
+ /**
453
+ * Returns the <i>closest</i> image for the given location and direction (location and image extent must not overlap).
454
+ * Returns undefined, if there are no images for the given direction
455
+ * @param {import("ol/coordinate").Coordinate} mercatorCoordinate - coordinate in web mercator
456
+ * @param {import("@vcmap/core").ObliqueViewDirection} direction - the preferred direction if no image in that direction can be found, other direction will be queried
457
+ * @returns {import("@vcmap/core").ObliqueImage|undefined}
458
+ * @api
459
+ */
460
+ getImageForCoordinate(mercatorCoordinate, direction) {
461
+ const directions = [direction, ...Object.values(ObliqueViewDirection).filter(d => d !== direction)];
462
+ for (let i = 0; i < directions.length; i++) {
463
+ const image = this._getNextImageForCoordinate(mercatorCoordinate, directions[i]);
464
+ if (image) {
465
+ return image;
466
+ }
467
+ }
468
+ return undefined;
469
+ }
470
+
471
+ /**
472
+ * Loads all data for a location and then returns the <i>closest</i> image for the given location and direction (location and image extent must not overlap).
473
+ * Returns undefined, if there are no images for the given direction
474
+ * @param {import("ol/coordinate").Coordinate} mercatorCoordinate - coordinate in web mercator
475
+ * @param {import("@vcmap/core").ObliqueViewDirection} direction
476
+ * @returns {Promise<import("@vcmap/core").ObliqueImage|undefined>}
477
+ * @api
478
+ */
479
+ async loadImageForCoordinate(mercatorCoordinate, direction) {
480
+ await this.loadDataForCoordinate(mercatorCoordinate);
481
+ return this.getImageForCoordinate(mercatorCoordinate, direction);
482
+ }
483
+
484
+ /**
485
+ * Checks, if an image exists for a given coordinated
486
+ * @param {import("ol/coordinate").Coordinate} mercatorCoordinate - coordinate in web mercator
487
+ * @param {import("@vcmap/core").ObliqueViewDirection} direction
488
+ * @returns {Promise<boolean>}
489
+ * @api
490
+ */
491
+ async hasImageAtCoordinate(mercatorCoordinate, direction) {
492
+ const image = await this.loadImageForCoordinate(mercatorCoordinate, direction);
493
+ if (image) {
494
+ const transform = getTransform('EPSG:3857', image.meta.projection);
495
+ const internalCoordinates = mercatorCoordinate.slice(0, 2);
496
+ transform(internalCoordinates, internalCoordinates);
497
+ const extent = boundingExtent(image.groundCoordinates);
498
+ return containsCoordinate(extent, internalCoordinates);
499
+ }
500
+ return false;
501
+ }
502
+
503
+ /**
504
+ * Loads the image adjacent to a given image in a certain direction from the provided image.
505
+ * Returns undefined if there are no images in that direction or there are no images for the direction
506
+ * of the provided image.
507
+ * @param {import("@vcmap/core").ObliqueImage} image
508
+ * @param {number} heading - 0 = east, PI / 2 = north, PI = west and PI * 1.5 = south
509
+ * @param {number=} [deviation=PI/4]
510
+ * @returns {Promise<import("@vcmap/core").ObliqueImage|undefined>}
511
+ * @api
512
+ */
513
+ async loadAdjacentImage(image, heading, deviation = Math.PI / 4) {
514
+ const tree = this._directionTrees.get(image.viewDirection);
515
+ if (tree) {
516
+ const transform = getTransform(image.meta.projection, 'EPSG:3857');
517
+ const coords = image.groundCoordinates.map(c => transform(c.slice(0, 2)));
518
+ const extent = boundingExtent(coords);
519
+ await this.loadDataForExtent(buffer(extent, 200));
520
+ const center = getCenter(extent);
521
+ const neighbors = knn(tree, center[0], center[1], 20);
522
+ const found = neighbors.find((neighbour) => {
523
+ if (neighbour.name !== image.name) {
524
+ let angle = Math.atan2(neighbour.minY - center[1], neighbour.minX - center[0]);
525
+ if (angle <= 0) {
526
+ angle += Math.PI * 2;
527
+ }
528
+ let differenceAngle = angle - heading;
529
+ if (differenceAngle > Math.PI) {
530
+ differenceAngle -= (Math.PI * 2);
531
+ } else if (differenceAngle < -Math.PI) {
532
+ differenceAngle += (Math.PI * 2);
533
+ }
534
+ if (differenceAngle <= deviation && differenceAngle >= -deviation) {
535
+ return neighbour;
536
+ }
537
+ }
538
+ return false;
539
+ });
540
+ if (found) {
541
+ return this.getImageByName(found.name);
542
+ }
543
+ }
544
+ return undefined;
545
+ }
546
+
547
+ /**
548
+ * Destroys all data sets and all images and any image/tile features for this collection
549
+ * @api
550
+ */
551
+ destroy() {
552
+ this._dataSets.forEach((ds) => { ds.destroy(); });
553
+ this._dataSets = [];
554
+
555
+ [...this._directionTrees.values()].forEach((tree) => { tree.clear(); });
556
+ this._directionTrees.clear();
557
+
558
+ this._images.clear();
559
+
560
+ if (this._tileFeatureSource) {
561
+ this._tileFeatureSource.clear(true);
562
+ this._tileFeatureSource = null;
563
+ }
564
+
565
+ if (this._imageFeatureSource) {
566
+ this._imageFeatureSource.clear(true);
567
+ this._imageFeatureSource = null;
568
+ }
569
+ }
570
+ }
571
+
572
+ export default ObliqueCollection;