@vcmap/core 6.2.0-rc.1 → 6.2.0-rc.3

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 (74) hide show
  1. package/dist/cesium.d.ts +5 -0
  2. package/dist/index.d.ts +6 -1
  3. package/dist/index.js +5 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/src/interaction/coordinateAtPixel.js +2 -2
  6. package/dist/src/interaction/coordinateAtPixel.js.map +1 -1
  7. package/dist/src/layer/cesium/cogCesiumImpl.d.ts +14 -0
  8. package/dist/src/layer/cesium/cogCesiumImpl.js +28 -0
  9. package/dist/src/layer/cesium/cogCesiumImpl.js.map +1 -0
  10. package/dist/src/layer/cesium/cogImageryProvider.d.ts +31 -0
  11. package/dist/src/layer/cesium/cogImageryProvider.js +258 -0
  12. package/dist/src/layer/cesium/cogImageryProvider.js.map +1 -0
  13. package/dist/src/layer/cogLayer.d.ts +37 -0
  14. package/dist/src/layer/cogLayer.js +119 -0
  15. package/dist/src/layer/cogLayer.js.map +1 -0
  16. package/dist/src/layer/openlayers/cogOpenlayersImpl.d.ts +23 -0
  17. package/dist/src/layer/openlayers/cogOpenlayersImpl.js +54 -0
  18. package/dist/src/layer/openlayers/cogOpenlayersImpl.js.map +1 -0
  19. package/dist/src/layer/openlayers/layerOpenlayersImpl.d.ts +3 -1
  20. package/dist/src/layer/openlayers/layerOpenlayersImpl.js +5 -3
  21. package/dist/src/layer/openlayers/layerOpenlayersImpl.js.map +1 -1
  22. package/dist/src/layer/panoramaDatasetLayer.d.ts +0 -1
  23. package/dist/src/layer/panoramaDatasetLayer.js +1 -10
  24. package/dist/src/layer/panoramaDatasetLayer.js.map +1 -1
  25. package/dist/src/map/panoramaMap.js +7 -2
  26. package/dist/src/map/panoramaMap.js.map +1 -1
  27. package/dist/src/panorama/panoramaImage.d.ts +7 -0
  28. package/dist/src/panorama/panoramaImage.js +5 -0
  29. package/dist/src/panorama/panoramaImage.js.map +1 -1
  30. package/dist/src/panorama/panoramaImageView.js +29 -36
  31. package/dist/src/panorama/panoramaImageView.js.map +1 -1
  32. package/dist/src/panorama/panoramaTile.d.ts +8 -4
  33. package/dist/src/panorama/panoramaTile.js +53 -9
  34. package/dist/src/panorama/panoramaTile.js.map +1 -1
  35. package/dist/src/panorama/panoramaTileMaterial.d.ts +0 -6
  36. package/dist/src/panorama/panoramaTileMaterial.js +0 -12
  37. package/dist/src/panorama/panoramaTileMaterial.js.map +1 -1
  38. package/dist/src/panorama/panoramaTileProvider.js +7 -8
  39. package/dist/src/panorama/panoramaTileProvider.js.map +1 -1
  40. package/dist/src/util/flight/flightRecorder.d.ts +10 -0
  41. package/dist/src/util/flight/flightRecorder.js +175 -0
  42. package/dist/src/util/flight/flightRecorder.js.map +1 -0
  43. package/dist/src/util/mapCollection.d.ts +2 -0
  44. package/dist/src/util/mapCollection.js +3 -0
  45. package/dist/src/util/mapCollection.js.map +1 -1
  46. package/dist/src/util/math.d.ts +4 -1
  47. package/dist/src/util/math.js +20 -1
  48. package/dist/src/util/math.js.map +1 -1
  49. package/dist/tests/data/cog/test_grey_world.tif +0 -0
  50. package/dist/tests/data/cog/test_rgb.tif +0 -0
  51. package/dist/tests/data/cog/test_rgb_world.tif +0 -0
  52. package/index.ts +10 -1
  53. package/package.json +1 -1
  54. package/src/cesium/cesium.d.ts +5 -0
  55. package/src/interaction/coordinateAtPixel.ts +2 -2
  56. package/src/layer/cesium/cogCesiumImpl.ts +36 -0
  57. package/src/layer/cesium/cogImageryProvider.ts +389 -0
  58. package/src/layer/cogLayer.ts +162 -0
  59. package/src/layer/openlayers/cogOpenlayersImpl.ts +75 -0
  60. package/src/layer/openlayers/layerOpenlayersImpl.ts +7 -4
  61. package/src/layer/panoramaDatasetLayer.ts +1 -12
  62. package/src/map/panoramaMap.ts +7 -2
  63. package/src/panorama/panoramaImage.ts +14 -0
  64. package/src/panorama/panoramaImageView.ts +32 -40
  65. package/src/panorama/panoramaTile.ts +81 -16
  66. package/src/panorama/panoramaTileMaterial.ts +0 -14
  67. package/src/panorama/panoramaTileProvider.ts +7 -10
  68. package/src/util/flight/flightRecorder.ts +237 -0
  69. package/src/util/mapCollection.ts +4 -0
  70. package/src/util/math.ts +34 -0
  71. package/dist/src/panorama/panoramaImageCache.d.ts +0 -8
  72. package/dist/src/panorama/panoramaImageCache.js +0 -18
  73. package/dist/src/panorama/panoramaImageCache.js.map +0 -1
  74. package/src/panorama/panoramaImageCache.ts +0 -19
@@ -168,7 +168,7 @@ function createPanoramaResourceProvider(
168
168
  ): Promise<void> => {
169
169
  const { tile, resource } = request;
170
170
  const { type } = resource;
171
- if (!tile.material.hasTexture(type)) {
171
+ if (!tile.hasResource(type)) {
172
172
  const { levelImages } = resource;
173
173
  const { tileCoordinate } = tile;
174
174
  const levelImage = levelImages[tileCoordinate.level - minLevel];
@@ -181,7 +181,7 @@ function createPanoramaResourceProvider(
181
181
  poolOrDecoder,
182
182
  );
183
183
 
184
- tile.material.setTexture(
184
+ tile.setResource(
185
185
  type,
186
186
  resourceData.data as unknown as PanoramaResourceData<PanoramaResourceType>,
187
187
  );
@@ -257,17 +257,14 @@ function createPanoramaResourceProvider(
257
257
  const createOrUpdateQueue = (panoramaTile: PanoramaTile[]): void => {
258
258
  const newResources = panoramaTile.flatMap((tile) => {
259
259
  const resourceRequests: TileResourceRequest<PanoramaResourceType>[] = [];
260
- if (!tile.material.hasTexture('rgb') && !loadingTiles.get(tile)?.rgb) {
260
+ if (!tile.hasResource('rgb') && !loadingTiles.get(tile)?.rgb) {
261
261
  resourceRequests.push({
262
262
  tile,
263
263
  resource: resources.rgb,
264
264
  });
265
265
  }
266
266
  if (resources.depth) {
267
- if (
268
- !tile.material.hasTexture('depth') &&
269
- !loadingTiles.get(tile)?.depth
270
- ) {
267
+ if (!tile.hasResource('depth') && !loadingTiles.get(tile)?.depth) {
271
268
  resourceRequests.push({
272
269
  tile,
273
270
  resource: resources.depth,
@@ -276,7 +273,7 @@ function createPanoramaResourceProvider(
276
273
  }
277
274
  if (showIntensity && resources.intensity) {
278
275
  if (
279
- !tile.material.hasTexture('intensity') &&
276
+ !tile.hasResource('intensity') &&
280
277
  !loadingTiles.get(tile)?.intensity
281
278
  ) {
282
279
  resourceRequests.push({
@@ -329,7 +326,7 @@ function createPanoramaResourceProvider(
329
326
  throw new Error(`Resource type ${type} not found`);
330
327
  }
331
328
 
332
- if (tile.material.hasTexture(type)) {
329
+ if (tile.hasResource(type)) {
333
330
  return Promise.resolve();
334
331
  }
335
332
 
@@ -447,7 +444,7 @@ export function createPanoramaTileProvider(
447
444
  const x = tileSize[0] - Math.floor((offsetX / width) * tileSize[0]);
448
445
  const y = Math.floor((offsetY / height) * tileSize[1]);
449
446
 
450
- const depthValue = tile.material.getDepthAtPixel(x, y);
447
+ const depthValue = tile.getDepthAtPixel(x, y);
451
448
  if (depthValue) {
452
449
  return interpolateDepth(
453
450
  depthValue,
@@ -0,0 +1,237 @@
1
+ import type VcsApp from '../../vcsApp.js';
2
+ import type Layer from '../../layer/layer.js';
3
+ import CesiumMap from '../../map/cesiumMap.js';
4
+ import CesiumTilesetLayer from '../../layer/cesiumTilesetLayer.js';
5
+ import { type FlightPlayer } from './flightPlayer.js';
6
+
7
+ export type FlightPathRecorderOptions = {
8
+ fps?: number;
9
+ highDefinition?: boolean;
10
+ };
11
+
12
+ async function getFlightFrames(
13
+ app: VcsApp,
14
+ player: FlightPlayer,
15
+ fps: number,
16
+ cancelToken: { cancelled: boolean },
17
+ maxNumberOfFrames = 100,
18
+ ): Promise<ImageBitmap[]> {
19
+ const scene = (app.maps.activeMap as CesiumMap).getScene()!;
20
+ const { canvas, globe } = scene;
21
+ let recordedTime = player.clock.currentTime - 1;
22
+ let numberOfFrames = Math.ceil(
23
+ (player.clock.endTime - player.clock.currentTime) * fps,
24
+ );
25
+ if (numberOfFrames > maxNumberOfFrames) {
26
+ numberOfFrames = maxNumberOfFrames;
27
+ }
28
+ const images = new Array<Promise<ImageBitmap>>(numberOfFrames);
29
+ const tilesetImpl = [...app.layers]
30
+ .filter(
31
+ (l: Layer): l is CesiumTilesetLayer =>
32
+ l instanceof CesiumTilesetLayer && l.active,
33
+ )
34
+ .flatMap((tileset) =>
35
+ tileset.getImplementationsForMap(app.maps.activeMap!),
36
+ );
37
+
38
+ const layerListeners = [
39
+ app.layers.stateChanged.addEventListener((layer) => {
40
+ if (layer instanceof CesiumTilesetLayer) {
41
+ const implementations = layer.getImplementationsForMap(
42
+ app.maps.activeMap!,
43
+ );
44
+ if (layer.active) {
45
+ tilesetImpl.push(...implementations);
46
+ } else {
47
+ implementations.forEach((impl) => {
48
+ tilesetImpl.splice(
49
+ tilesetImpl.findIndex((i) => i === impl),
50
+ 1,
51
+ );
52
+ });
53
+ }
54
+ }
55
+ }),
56
+ ];
57
+
58
+ let currentFrame = 0;
59
+ await new Promise<void>((resolve) => {
60
+ const handler = scene.postRender.addEventListener(() => {
61
+ if (cancelToken.cancelled) {
62
+ handler();
63
+ return;
64
+ }
65
+
66
+ if (globe.tilesLoaded) {
67
+ if (tilesetImpl.some((impl) => !impl.cesium3DTileset?.tilesLoaded)) {
68
+ return;
69
+ }
70
+ if (player.clock.currentTime === recordedTime) {
71
+ return;
72
+ }
73
+
74
+ recordedTime = player.clock.currentTime;
75
+ images[currentFrame] = createImageBitmap(canvas);
76
+ if (player.clock.currentTime === player.clock.endTime) {
77
+ handler();
78
+ resolve();
79
+ }
80
+
81
+ let nextTick = player.clock.currentTime + 1 / fps;
82
+ if (nextTick >= player.clock.endTime) {
83
+ nextTick = player.clock.endTime;
84
+ }
85
+ player.goToTime(nextTick);
86
+ currentFrame += 1;
87
+ if (currentFrame >= numberOfFrames) {
88
+ handler();
89
+ resolve();
90
+ }
91
+ }
92
+ });
93
+ });
94
+
95
+ layerListeners.forEach((listener) => {
96
+ listener();
97
+ });
98
+
99
+ return Promise.all(images);
100
+ }
101
+
102
+ export function createFlightMovie(
103
+ app: VcsApp,
104
+ player: FlightPlayer,
105
+ options?: FlightPathRecorderOptions,
106
+ ): { start: () => Promise<Blob>; cancel: () => void } {
107
+ if (!(app.maps.activeMap instanceof CesiumMap)) {
108
+ throw new Error('No active Cesium map found');
109
+ }
110
+
111
+ const cancelToken = { cancelled: false };
112
+ const videoBitsPerSecond = options?.highDefinition ? 12000000 : 6000000;
113
+
114
+ const map = app.maps.activeMap;
115
+ const scene = map.getScene()!;
116
+ const { canvas } = scene;
117
+
118
+ const destinationCanvas = document.createElement('canvas');
119
+ destinationCanvas.width = canvas.width;
120
+ destinationCanvas.height = canvas.height;
121
+ const context = destinationCanvas.getContext('2d')!;
122
+
123
+ const stream = destinationCanvas.captureStream(options?.fps ?? 30);
124
+ const recorder = new MediaRecorder(stream, {
125
+ mimeType: 'video/webm',
126
+ videoBitsPerSecond,
127
+ });
128
+
129
+ const resetMapControls = app.maps.requestExclusiveMapControls(
130
+ { apiCalls: true, keyEvents: true, pointerEvents: true },
131
+ () => {},
132
+ );
133
+
134
+ const finished = new Promise<Blob>((resolve, reject) => {
135
+ const chunksBuffer: Blob[] = [];
136
+
137
+ recorder.ondataavailable = (event): void => {
138
+ if (event.data.size > 0) {
139
+ chunksBuffer.push(event.data);
140
+ }
141
+ };
142
+
143
+ recorder.onerror = reject;
144
+ recorder.onstop = (): void => {
145
+ if (cancelToken.cancelled) {
146
+ return;
147
+ }
148
+ resolve(new Blob(chunksBuffer, { type: 'video/webm' }));
149
+ };
150
+ });
151
+
152
+ const renderBuffer = (buffer: ImageBitmap[]): Promise<void> => {
153
+ let interval: ReturnType<typeof setInterval>;
154
+ const promise = new Promise<void>((resolve) => {
155
+ const frameDuration = 1000 / (options?.fps || 30);
156
+ let currentFrame = 0;
157
+ interval = setInterval(() => {
158
+ if (cancelToken.cancelled) {
159
+ resolve();
160
+ }
161
+ const image = buffer[currentFrame];
162
+ if (image) {
163
+ context.drawImage(image, 0, 0);
164
+ currentFrame += 1;
165
+ if (currentFrame >= buffer.length) {
166
+ resolve();
167
+ }
168
+ } else {
169
+ resolve();
170
+ }
171
+ }, frameDuration);
172
+ });
173
+
174
+ return promise
175
+ .then(() => {
176
+ clearInterval(interval);
177
+ })
178
+ .catch((err: unknown) => {
179
+ clearInterval(interval);
180
+ throw err;
181
+ });
182
+ };
183
+
184
+ const requestBuffer = async (): Promise<boolean> => {
185
+ const images = await getFlightFrames(
186
+ app,
187
+ player,
188
+ options?.fps ?? 30,
189
+ cancelToken,
190
+ );
191
+
192
+ map.getCesiumWidget()!.useDefaultRenderLoop = false;
193
+ if (recorder.state === 'paused') {
194
+ recorder.resume();
195
+ } else {
196
+ recorder.start();
197
+ }
198
+
199
+ await renderBuffer(images);
200
+ map.getCesiumWidget()!.useDefaultRenderLoop = true;
201
+ if (
202
+ player.clock.currentTime === player.clock.endTime ||
203
+ cancelToken.cancelled
204
+ ) {
205
+ return false;
206
+ }
207
+ recorder.pause();
208
+ recorder.requestData();
209
+ return true;
210
+ };
211
+
212
+ const reset = (): void => {
213
+ map.getCesiumWidget()!.useDefaultRenderLoop = true;
214
+ resetMapControls?.();
215
+ };
216
+
217
+ async function start(): Promise<Blob> {
218
+ let buffering = true;
219
+ while (buffering) {
220
+ // eslint-disable-next-line no-await-in-loop
221
+ buffering = await requestBuffer();
222
+ }
223
+ stream.getTracks().forEach((track) => {
224
+ track.stop();
225
+ });
226
+ reset();
227
+ return finished;
228
+ }
229
+
230
+ const cancel = (): void => {
231
+ cancelToken.cancelled = true;
232
+ reset();
233
+ recorder.stop();
234
+ };
235
+
236
+ return { start, cancel };
237
+ }
@@ -179,6 +179,10 @@ class MapCollection extends Collection<VcsMap> {
179
179
  return this._activeMap;
180
180
  }
181
181
 
182
+ get panoramaImageSelection(): PanoramaImageSelection {
183
+ return this._panoramaImageSelection;
184
+ }
185
+
182
186
  /**
183
187
  * The currently set HTML element in which to render the maps
184
188
  */
package/src/util/math.ts CHANGED
@@ -2,9 +2,11 @@ import {
2
2
  Math as CesiumMath,
3
3
  Cartesian3,
4
4
  Cartographic,
5
+ Rectangle,
5
6
  } from '@vcmap-cesium/engine';
6
7
  import type { Coordinate } from 'ol/coordinate.js';
7
8
  import { getDistance as haversineDistance } from 'ol/sphere.js';
9
+ import { type Extent, getBottomLeft, getTopRight } from 'ol/extent.js';
8
10
  import Projection from './projection.js';
9
11
 
10
12
  /**
@@ -177,6 +179,38 @@ export function cartesianToMercator(cartesian: Cartesian3): Coordinate {
177
179
  return Projection.wgs84ToMercator(wgs84);
178
180
  }
179
181
 
182
+ export function mercatorExtentToRectangle(
183
+ extent: Extent,
184
+ result?: Rectangle,
185
+ ): Rectangle {
186
+ const bottomLeft = getBottomLeft(extent);
187
+ const topRight = getTopRight(extent);
188
+
189
+ Projection.mercatorToWgs84(bottomLeft, true);
190
+ Projection.mercatorToWgs84(topRight, true);
191
+
192
+ return Rectangle.fromDegrees(
193
+ bottomLeft[0],
194
+ bottomLeft[1],
195
+ topRight[0],
196
+ topRight[1],
197
+ result,
198
+ );
199
+ }
200
+
201
+ export function rectangleToMercatorExtent(rectangle: Rectangle): Extent {
202
+ const bottomLeft = Projection.wgs84ToMercator([
203
+ CesiumMath.toDegrees(rectangle.west),
204
+ CesiumMath.toDegrees(rectangle.south),
205
+ ]);
206
+ const topRight = Projection.wgs84ToMercator([
207
+ CesiumMath.toDegrees(rectangle.east),
208
+ CesiumMath.toDegrees(rectangle.north),
209
+ ]);
210
+
211
+ return [bottomLeft[0], bottomLeft[1], topRight[0], topRight[1]];
212
+ }
213
+
180
214
  export function getMidPoint(p1: Coordinate, p2: Coordinate): Coordinate {
181
215
  const stride = p1.length;
182
216
  const output = new Array<number>(stride);
@@ -1,8 +0,0 @@
1
- import LRUCache from 'ol/structs/LRUCache.js';
2
- import type { PanoramaImage } from './panoramaImage.js';
3
- /**
4
- * A specialized LRU cache
5
- */
6
- export declare class PanoramaImageCache extends LRUCache<Promise<PanoramaImage>> {
7
- deleteOldest(): void;
8
- }
@@ -1,18 +0,0 @@
1
- import LRUCache from 'ol/structs/LRUCache.js';
2
- /**
3
- * A specialized LRU cache
4
- */
5
- export class PanoramaImageCache extends LRUCache {
6
- deleteOldest() {
7
- const entry = this.pop();
8
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
9
- if (entry) {
10
- entry
11
- .then((image) => {
12
- image.destroy();
13
- })
14
- .catch(() => { });
15
- }
16
- }
17
- }
18
- //# sourceMappingURL=panoramaImageCache.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"panoramaImageCache.js","sourceRoot":"","sources":["../../../src/panorama/panoramaImageCache.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAG9C;;GAEG;AACH,MAAM,OAAO,kBAAmB,SAAQ,QAAgC;IACtE,YAAY;QACV,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,kEAAkE;QAClE,IAAI,KAAK,EAAE,CAAC;YACV,KAAK;iBACF,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;gBACd,KAAK,CAAC,OAAO,EAAE,CAAC;YAClB,CAAC,CAAC;iBACD,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;CACF"}
@@ -1,19 +0,0 @@
1
- import LRUCache from 'ol/structs/LRUCache.js';
2
- import type { PanoramaImage } from './panoramaImage.js';
3
-
4
- /**
5
- * A specialized LRU cache
6
- */
7
- export class PanoramaImageCache extends LRUCache<Promise<PanoramaImage>> {
8
- deleteOldest(): void {
9
- const entry = this.pop();
10
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
11
- if (entry) {
12
- entry
13
- .then((image) => {
14
- image.destroy();
15
- })
16
- .catch(() => {});
17
- }
18
- }
19
- }