@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,357 @@
1
+ import { Event as CesiumEvent } from '@vcmap/cesium';
2
+ import axios from 'axios';
3
+ import { createXYZ } from 'ol/tilegrid.js';
4
+ import { destroyCesiumEvent } from './helpers.js';
5
+ import { cartesian2DDistance } from '../util/math.js';
6
+ import { parseImageData, parseImageMeta, parseLegacyImageData, getVersionFromImageJson } from './parseImageJson.js';
7
+
8
+ /**
9
+ * Enumeration of data set states
10
+ * @enum {number}
11
+ * @property {number} PENDING
12
+ * @property {number} LOADING
13
+ * @property {number} READY
14
+ * @export
15
+ * @api
16
+ */
17
+ export const DataState = {
18
+ PENDING: 1,
19
+ LOADING: 2,
20
+ READY: 3,
21
+ };
22
+
23
+ /**
24
+ * @param {Array<DataState>} states
25
+ * @returns {DataState}
26
+ */
27
+ export function getStateFromStatesArray(states) {
28
+ if (states.some(s => s === DataState.PENDING)) {
29
+ return DataState.PENDING;
30
+ }
31
+
32
+ if (states.some(s => s === DataState.LOADING)) {
33
+ return DataState.LOADING;
34
+ }
35
+
36
+ return DataState.READY;
37
+ }
38
+
39
+ /**
40
+ * @class
41
+ * @export
42
+ */
43
+ class ObliqueDataSet {
44
+ /**
45
+ * @param {string} url
46
+ * @param {import("ol/proj/Projection").default} projection
47
+ * @param {import("@vcmap/cesium").CesiumTerrainProvider=} terrainProvider
48
+ */
49
+ constructor(url, projection, terrainProvider) {
50
+ /** @type {string} */
51
+ this.url = url;
52
+ if (!/\.json$/.test(this.url)) {
53
+ this.url = this.url.replace(/\/?$/, '/image.json');
54
+ }
55
+
56
+ /** @type {string} */
57
+ this.baseUrl = this.url.replace(/\/?([^/]+\.json)?$/, '');
58
+ /** @type {import("ol/proj/Projection").default} */
59
+ this.projection = projection;
60
+ /** @type {import("@vcmap/cesium").CesiumTerrainProvider|undefined} */
61
+ this.terrainProvider = terrainProvider;
62
+ /**
63
+ * @type {Array<import("@vcmap/core").ObliqueImageMeta>}
64
+ * @private
65
+ */
66
+ this._imageMetas = [];
67
+ /**
68
+ * Event raised when images are loaded. Is passed an Array of ObliqueImages as the first argument and optionally
69
+ * a string representing the tile coordinate ("z/x/y"), if the images where loaded for a tile.
70
+ * @type {import("@vcmap/cesium").Event}
71
+ * @api
72
+ */
73
+ this.imagesLoaded = new CesiumEvent();
74
+ /** @type {Map<string, DataState>} */
75
+ this._tiles = new Map();
76
+ /** @type {Map<string, Promise<void>>} */
77
+ this._loadingPromises = new Map();
78
+ this._state = DataState.PENDING;
79
+ /**
80
+ * @type {number|null}
81
+ * @private
82
+ */
83
+ this._tileLevel = null;
84
+ /** @type {import("ol/tilegrid/TileGrid").default} */
85
+ this._tileGrid = createXYZ();
86
+
87
+ /**
88
+ * @type {Array<import("@vcmap/core").ObliqueImage>}
89
+ * @private
90
+ */
91
+ this._images = [];
92
+
93
+ /**
94
+ * @type {CopyrightOptions|undefined}
95
+ * @api
96
+ */
97
+ this.copyright = undefined;
98
+ }
99
+
100
+ /**
101
+ * The loaded images of this DataSet
102
+ * @api
103
+ * @readonly
104
+ * @type {Array<import("@vcmap/core").ObliqueImage>}
105
+ */
106
+ get images() {
107
+ return this._images.slice();
108
+ }
109
+
110
+ /**
111
+ * Gets the state of the meta data information. For tiled data sets, this does not
112
+ * reflect the state of loaded tiles.
113
+ * @type {DataState}
114
+ * @api
115
+ * @readonly
116
+ */
117
+ get state() {
118
+ return this._state;
119
+ }
120
+
121
+ /**
122
+ * Loads the data set.
123
+ * @returns {Promise<void>}
124
+ * @api
125
+ */
126
+ load() {
127
+ if (!this._loadingPromise) {
128
+ this._state = DataState.LOADING;
129
+
130
+ this._loadingPromise = axios.get(this.url)
131
+ .then(({ data }) => {
132
+ this._initialize(data);
133
+ });
134
+ }
135
+ return this._loadingPromise;
136
+ }
137
+
138
+ /**
139
+ * Returns all tiles of this data set, including its DataState
140
+ * @returns {Object<string, DataState>}
141
+ * @api
142
+ */
143
+ getTiles() {
144
+ /** @type {Object<string, DataState>} */
145
+ const tiles = {};
146
+ this._tiles.forEach((state, tile) => {
147
+ tiles[tile] = state;
148
+ });
149
+ return tiles;
150
+ }
151
+
152
+ /**
153
+ * initialize the DataSet with an existing Object.
154
+ * DataSets cannot be initialized more then once.
155
+ * @param {ObliqueImageJson} json
156
+ * @api
157
+ */
158
+ initialize(json) {
159
+ if (this._state !== DataState.PENDING) {
160
+ throw new Error('DataSet has already been loaded');
161
+ }
162
+ this._loadingPromise = Promise.resolve();
163
+ this._initialize(json);
164
+ }
165
+
166
+ /**
167
+ * @param {ObliqueImageJson} json
168
+ * @private
169
+ */
170
+ _initialize(json) {
171
+ this._parseMetaData(json);
172
+ this._state = DataState.READY;
173
+ }
174
+
175
+ /**
176
+ * @param {ObliqueImageJson} json
177
+ * @private
178
+ */
179
+ _parseMetaData(json) {
180
+ this._imageMetas = parseImageMeta(json, this.baseUrl, this.projection, this.terrainProvider);
181
+ const { version, buildNumber } = getVersionFromImageJson(json);
182
+
183
+ if (json.tileLevel) {
184
+ this._tileLevel = json.tileLevel;
185
+ json.availableTiles.forEach((tile) => {
186
+ this._tiles.set(tile, DataState.PENDING);
187
+ });
188
+ } else {
189
+ let images = [];
190
+ if (version >= 3.5 || (version === 3.4 && buildNumber > 36)) {
191
+ images = parseImageData(json, this._imageMetas);
192
+ } else if (version >= 3.1 || version === null) {
193
+ images = parseLegacyImageData(json, this._imageMetas);
194
+ }
195
+ if (images.length > 0) {
196
+ this._images = images;
197
+ this.imagesLoaded.raiseEvent(images);
198
+ }
199
+ }
200
+ }
201
+
202
+ /**
203
+ * @param {import("ol/coordinate").Coordinate} mercatorCoordinate
204
+ * @returns {import("ol/tilecoord").TileCoord|null}
205
+ * @private
206
+ */
207
+ _getClosestTileCoordinate(mercatorCoordinate) {
208
+ if (!this._tileLevel) {
209
+ return null;
210
+ }
211
+ const actualTile = this._tileGrid.getTileCoordForCoordAndZ(mercatorCoordinate, this._tileLevel);
212
+ if (this._tiles.has(actualTile.join('/'))) {
213
+ return actualTile;
214
+ }
215
+
216
+ let minDistance = Infinity;
217
+ let minTile = null;
218
+ [...this._tiles.keys()].forEach((tile) => {
219
+ const tileCoord = tile.split('/').map(Number);
220
+ const dist = cartesian2DDistance([actualTile[1], actualTile[2]], [tileCoord[1], tileCoord[2]]);
221
+ if (dist < minDistance) {
222
+ minDistance = dist;
223
+ minTile = tileCoord;
224
+ }
225
+ });
226
+ return minTile || actualTile;
227
+ }
228
+
229
+ /**
230
+ * @param {import("ol/extent").Extent} extent
231
+ * @returns {Array<string>}
232
+ * @private
233
+ */
234
+ _getTileCoordinatesForExtent(extent) {
235
+ const topLeft = this._tileGrid.getTileCoordForCoordAndZ([extent[0], extent[3]], this._tileLevel);
236
+ const bottomRight = this._tileGrid.getTileCoordForCoordAndZ([extent[2], extent[1]], this._tileLevel);
237
+ const tileCoordinates = [];
238
+ for (let x = topLeft[1]; x <= bottomRight[1]; x++) {
239
+ for (let y = topLeft[2]; y <= bottomRight[2]; y++) {
240
+ tileCoordinates.push([this._tileLevel, x, y]);
241
+ }
242
+ }
243
+
244
+ return tileCoordinates
245
+ .map(tc => tc.join('/'))
246
+ .filter((tc) => {
247
+ const state = this._tiles.get(tc);
248
+ return state && state !== DataState.READY;
249
+ });
250
+ }
251
+
252
+ /**
253
+ * Requests the state of data at a certain location. No data is automatically READY.
254
+ * @param {import("ol/coordinate").Coordinate} mercatorCoordinate - coordinate in web mercator
255
+ * @returns {DataState}
256
+ * @api
257
+ */
258
+ getDataStateForCoordinate(mercatorCoordinate) {
259
+ if (this._state !== DataState.READY || this._tiles.size === 0) {
260
+ return this._state;
261
+ }
262
+
263
+ const tileCoordinate = this._getClosestTileCoordinate(mercatorCoordinate).join('/');
264
+ return this._tiles.has(tileCoordinate) ? this._tiles.get(tileCoordinate) : DataState.READY;
265
+ }
266
+
267
+ /**
268
+ * Loads all the tiles for a given extent.
269
+ * @param {import("ol/extent").Extent} extent
270
+ * @returns {DataState}
271
+ * @api
272
+ */
273
+ getDataStateForExtent(extent) {
274
+ if (this._state !== DataState.READY || this._tiles.size === 0) {
275
+ return this._state;
276
+ }
277
+
278
+ const tileCoordinates = this._getTileCoordinatesForExtent(extent);
279
+ const states = tileCoordinates
280
+ .map(tc => this._tiles.get(tc))
281
+ .filter(s => s);
282
+ return getStateFromStatesArray(states);
283
+ }
284
+
285
+ /**
286
+ * @param {string} stringTileCoordinates
287
+ * @returns {Promise<void>}
288
+ * @private
289
+ */
290
+ _loadTile(stringTileCoordinates) {
291
+ if (this._loadingPromises.has(stringTileCoordinates)) {
292
+ return this._loadingPromises.get(stringTileCoordinates);
293
+ }
294
+
295
+ if (this._tiles.get(stringTileCoordinates) !== DataState.PENDING) {
296
+ return Promise.resolve();
297
+ }
298
+
299
+ this._tiles.set(stringTileCoordinates, DataState.LOADING);
300
+ const promise = axios
301
+ .get(`${this.baseUrl}/${stringTileCoordinates}.json`)
302
+ .then(({ data }) => {
303
+ const images = parseImageData(data, this._imageMetas);
304
+ if (images.length > 0) {
305
+ this._images = this._images.concat(images);
306
+ this.imagesLoaded.raiseEvent(images, stringTileCoordinates);
307
+ }
308
+ })
309
+ .catch((err) => {
310
+ // eslint-disable-next-line no-console
311
+ console.error(err);
312
+ })
313
+ .finally(() => {
314
+ this._tiles.set(stringTileCoordinates, DataState.READY);
315
+ this._loadingPromises.delete(stringTileCoordinates);
316
+ });
317
+
318
+ this._loadingPromises.set(stringTileCoordinates, promise);
319
+ return promise;
320
+ }
321
+
322
+ /**
323
+ * Loads the closest data tile for a certain location. Resolves when all data for the location has been loaded.
324
+ * @param {import("ol/coordinate").Coordinate} mercatorCoordinate - coordinate in web mercator
325
+ * @returns {Promise<void>}
326
+ * @api
327
+ */
328
+ async loadDataForCoordinate(mercatorCoordinate) {
329
+ const tileCoordinate = this._getClosestTileCoordinate(mercatorCoordinate);
330
+ if (tileCoordinate) {
331
+ await this._loadTile(tileCoordinate.join('/'));
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Loads all the tiles for a given extent.
337
+ * @param {import("ol/extent").Extent} extent
338
+ * @returns {Promise<void>}
339
+ * @api
340
+ */
341
+ async loadDataForExtent(extent) {
342
+ const tileCoordinates = this._getTileCoordinatesForExtent(extent);
343
+ await Promise.all(tileCoordinates.map(tc => this._loadTile(tc)));
344
+ }
345
+
346
+ destroy() {
347
+ destroyCesiumEvent(this.imagesLoaded);
348
+ this._images = [];
349
+ this._imageMetas = [];
350
+ this._tiles.clear();
351
+ this._loadingPromises.clear();
352
+ this._tileGrid = null;
353
+ this.terrainProvider = null;
354
+ }
355
+ }
356
+
357
+ export default ObliqueDataSet;
@@ -0,0 +1,247 @@
1
+ import { Cartesian3, Cartesian4, Matrix3, Matrix4 } from '@vcmap/cesium';
2
+ import { transformCWIFC } from './helpers.js';
3
+ import { getHeightFromTerrainProvider } from '../layer/terrainHelpers.js';
4
+
5
+ /**
6
+ * @typedef {Object} ObliqueImageOptions
7
+ * @property {!string} name
8
+ * @property {import("@vcmap/core").ObliqueViewDirection} viewDirection
9
+ * @property {number} viewDirectionAngle
10
+ * @property {!Array<import("ol/coordinate").Coordinate>} groundCoordinates
11
+ * @property {!import("ol/coordinate").Coordinate} centerPointOnGround
12
+ * @property {!import("@vcmap/core").ObliqueImageMeta} meta
13
+ * @property {import("@vcmap/cesium").Cartesian3|undefined} projectionCenter
14
+ * @property {import("@vcmap/cesium").Matrix3|undefined} pToRealworld
15
+ * @property {import("@vcmap/cesium").Matrix4|undefined} pToImage
16
+ * @property {import("ol/proj/Projection").default|undefined} [projection]
17
+ * @property {import("@vcmap/cesium").CesiumTerrainProvider|undefined} terrainProvider
18
+ * @api
19
+ */
20
+
21
+ /**
22
+ * @class
23
+ * @export
24
+ */
25
+ class ObliqueImage {
26
+ /**
27
+ * @param {ObliqueImageOptions} options
28
+ */
29
+ constructor(options) {
30
+ /**
31
+ * Name of the image
32
+ * @type {string}
33
+ * @api
34
+ */
35
+ this.name = options.name;
36
+
37
+ /**
38
+ * Meta information shared across multiple images.
39
+ * @type {import("@vcmap/core").ObliqueImageMeta}
40
+ * @api
41
+ */
42
+ this.meta = options.meta;
43
+
44
+ /**
45
+ * viewDirection
46
+ * @type {import("@vcmap/core").ObliqueViewDirection}
47
+ * @api
48
+ */
49
+ this.viewDirection = options.viewDirection;
50
+
51
+ /**
52
+ * viewDirectionAngle in radians, where 0 = east, PI / 2 = north, PI = west and PI * 1.5 = south
53
+ * @type {number|null}
54
+ * @api
55
+ */
56
+ this.viewDirectionAngle = options.viewDirectionAngle;
57
+
58
+ /**
59
+ * The ground coordinates of the image corners (in image world projection).
60
+ * @type {Array<import("ol/coordinate").Coordinate>}
61
+ * @api
62
+ */
63
+ this.groundCoordinates = options.groundCoordinates;
64
+
65
+ /**
66
+ * The center point of the image in world coordinates (in image world projection).
67
+ * @type {import("ol/coordinate").Coordinate}
68
+ * @api
69
+ */
70
+ this.centerPointOnGround = options.centerPointOnGround;
71
+
72
+ /**
73
+ * The transformation matrix image to real world (in image world projection).
74
+ * @type {import("@vcmap/cesium").Matrix3}
75
+ * @api
76
+ */
77
+ this.pToRealworld = options.pToRealworld || null;
78
+
79
+ /**
80
+ * The transformation matrix real to image (in image world projection).
81
+ * @type {import("@vcmap/cesium").Matrix4}
82
+ */
83
+ this.pToImage = options.pToImage || null;
84
+
85
+ /**
86
+ * The projection center in image world projection
87
+ * @type {import("@vcmap/cesium").Cartesian3}
88
+ * @api
89
+ */
90
+ this.projectionCenter = options.projectionCenter || null;
91
+
92
+ /**
93
+ * The calculated average height of an image
94
+ * @type {number|null}
95
+ * @private
96
+ */
97
+ this._averageHeight = null;
98
+ }
99
+
100
+ /**
101
+ * returns the averageHeight of the image or 0 if not defined. Be sure to call calculateAverageHeight before hand.
102
+ * @type {number}
103
+ * @readonly
104
+ * @api
105
+ */
106
+ get averageHeight() {
107
+ return this._averageHeight != null ? this._averageHeight : 0;
108
+ }
109
+
110
+ /**
111
+ * returns whether this image supports exact Coordinate transformation
112
+ * @type {boolean}
113
+ * @readonly
114
+ * @api
115
+ */
116
+ get hasCamera() {
117
+ return !!this.meta.principalPoint;
118
+ }
119
+
120
+ /**
121
+ * @param {import("ol/coordinate").Coordinate} imageCoordinate
122
+ * @param {number=} optAvgHeight
123
+ * @returns {import("ol/coordinate").Coordinate}
124
+ * @api
125
+ */
126
+ transformImage2RealWorld(imageCoordinate, optAvgHeight) {
127
+ let distortedCoordinate = imageCoordinate;
128
+ if (!this.meta.principalPoint) {
129
+ return this._transformNoCamera(distortedCoordinate, true, optAvgHeight);
130
+ } else if (this.meta.hasRadial) {
131
+ distortedCoordinate = this.meta.radialDistortionCoordinate(distortedCoordinate, true);
132
+ }
133
+
134
+ const x = new Cartesian3(distortedCoordinate[0], this.meta.size[1] - distortedCoordinate[1], 1);
135
+ const rayWorld = Matrix3.multiplyByVector(this.pToRealworld, x, new Cartesian3());
136
+ const avgHeight = optAvgHeight || this.averageHeight;
137
+ const centerPointOnGround =
138
+ new Cartesian3(this.centerPointOnGround[0], this.centerPointOnGround[1], avgHeight);
139
+ const w0 = Cartesian3.subtract(this.projectionCenter, centerPointOnGround, new Cartesian3());
140
+ const a = Cartesian3.dot(Cartesian3.UNIT_Z, w0) * (-1);
141
+ const b = Cartesian3.dot(Cartesian3.UNIT_Z, rayWorld);
142
+
143
+ const r = a / b;
144
+
145
+ const intr = Cartesian3.add(
146
+ this.projectionCenter,
147
+ Cartesian3.multiplyByScalar(rayWorld, r, new Cartesian3()),
148
+ new Cartesian3(),
149
+ );
150
+ return [intr.x, intr.y, avgHeight];
151
+ }
152
+
153
+ /**
154
+ * @param {import("ol/coordinate").Coordinate} worldCoordinate
155
+ * @param {number=} optAvgHeight
156
+ * @returns {import("ol/coordinate").Coordinate}
157
+ * @api
158
+ */
159
+ transformRealWorld2Image(worldCoordinate, optAvgHeight) {
160
+ // if we dont have camera parameters
161
+ if (!this.meta.principalPoint) {
162
+ return this._transformNoCamera(worldCoordinate, false, optAvgHeight);
163
+ }
164
+
165
+ // usage of perspective projection
166
+ // the averaged height is used for projection so far
167
+ const avgHeight = optAvgHeight || this.averageHeight;
168
+
169
+ const P = new Cartesian4(worldCoordinate[0], worldCoordinate[1], avgHeight, 1);
170
+
171
+ const camSys = Matrix4.multiplyByVector(this.pToImage, P, new Cartesian4());
172
+ const respectiveImageCoords = [camSys.x / camSys.z, camSys.y / camSys.z];
173
+ // adjust to ol image coordinates
174
+ const imCoords = [respectiveImageCoords[0], this.meta.size[1] - respectiveImageCoords[1]];
175
+
176
+ return this.meta.radialDistortionCoordinate(imCoords, false);
177
+ }
178
+
179
+
180
+ /**
181
+ * @param {import("ol/coordinate").Coordinate} coord
182
+ * @param {boolean} isImage
183
+ * @param {number=} optAvgHeight
184
+ * @private
185
+ * @returns {import("ol/coordinate").Coordinate}
186
+ */
187
+ _transformNoCamera(coord, isImage, optAvgHeight) {
188
+ const imageCoords = [[0, 0], [this.meta.size[0], 0], this.meta.size, [0, this.meta.size[1]]];
189
+ const intrCross = transformCWIFC(
190
+ isImage ? imageCoords : this.groundCoordinates,
191
+ isImage ? this.groundCoordinates : imageCoords,
192
+ isImage,
193
+ coord,
194
+ this.viewDirection,
195
+ );
196
+ const height = optAvgHeight || this.averageHeight;
197
+ // if intersection could not be determined write error and return center
198
+ if (intrCross === null || intrCross.x == null || intrCross.y == null) {
199
+ // eslint-disable-next-line no-console
200
+ console.error('Real world coordinate could not be determined from footprint data, center will be returned');
201
+ const coords = [this.centerPointOnGround[0], this.centerPointOnGround[1]];
202
+ if (isImage) {
203
+ coords.push(height);
204
+ }
205
+ return coords;
206
+ }
207
+ const coords = [intrCross.x, intrCross.y];
208
+ if (isImage) {
209
+ coords.push(height);
210
+ }
211
+ return coords;
212
+ }
213
+
214
+ /**
215
+ * calculates the averageHeight of this image, if a terrainProvider is given the height will be requested
216
+ * @returns {Promise<void>}
217
+ * @api
218
+ */
219
+ calculateImageAverageHeight() {
220
+ if (this._averageHeight === null) {
221
+ const averageHeight = (
222
+ this.groundCoordinates[0][2] +
223
+ this.groundCoordinates[1][2] +
224
+ this.groundCoordinates[2][2] +
225
+ this.groundCoordinates[3][2]) / 4;
226
+ if (averageHeight === 0 && this.meta.terrainProvider) {
227
+ return getHeightFromTerrainProvider(
228
+ this.meta.terrainProvider,
229
+ [this.centerPointOnGround.slice()],
230
+ this.meta.projection,
231
+ )
232
+ .then((coords) => {
233
+ if (coords[0] && coords[0][2] != null) {
234
+ this._averageHeight = coords[0][2];
235
+ }
236
+ })
237
+ .catch(() => {
238
+ this._averageHeight = averageHeight;
239
+ });
240
+ }
241
+ this._averageHeight = averageHeight;
242
+ }
243
+ return Promise.resolve();
244
+ }
245
+ }
246
+
247
+ export default ObliqueImage;