@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.
- package/dist/cesium.d.ts +5 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/src/interaction/coordinateAtPixel.js +2 -2
- package/dist/src/interaction/coordinateAtPixel.js.map +1 -1
- package/dist/src/layer/cesium/cogCesiumImpl.d.ts +14 -0
- package/dist/src/layer/cesium/cogCesiumImpl.js +28 -0
- package/dist/src/layer/cesium/cogCesiumImpl.js.map +1 -0
- package/dist/src/layer/cesium/cogImageryProvider.d.ts +31 -0
- package/dist/src/layer/cesium/cogImageryProvider.js +258 -0
- package/dist/src/layer/cesium/cogImageryProvider.js.map +1 -0
- package/dist/src/layer/cogLayer.d.ts +37 -0
- package/dist/src/layer/cogLayer.js +119 -0
- package/dist/src/layer/cogLayer.js.map +1 -0
- package/dist/src/layer/openlayers/cogOpenlayersImpl.d.ts +23 -0
- package/dist/src/layer/openlayers/cogOpenlayersImpl.js +54 -0
- package/dist/src/layer/openlayers/cogOpenlayersImpl.js.map +1 -0
- package/dist/src/layer/openlayers/layerOpenlayersImpl.d.ts +3 -1
- package/dist/src/layer/openlayers/layerOpenlayersImpl.js +5 -3
- package/dist/src/layer/openlayers/layerOpenlayersImpl.js.map +1 -1
- package/dist/src/layer/panoramaDatasetLayer.d.ts +0 -1
- package/dist/src/layer/panoramaDatasetLayer.js +1 -10
- package/dist/src/layer/panoramaDatasetLayer.js.map +1 -1
- package/dist/src/map/panoramaMap.js +7 -2
- package/dist/src/map/panoramaMap.js.map +1 -1
- package/dist/src/panorama/panoramaImage.d.ts +7 -0
- package/dist/src/panorama/panoramaImage.js +5 -0
- package/dist/src/panorama/panoramaImage.js.map +1 -1
- package/dist/src/panorama/panoramaImageView.js +29 -36
- package/dist/src/panorama/panoramaImageView.js.map +1 -1
- package/dist/src/panorama/panoramaTile.d.ts +8 -4
- package/dist/src/panorama/panoramaTile.js +53 -9
- package/dist/src/panorama/panoramaTile.js.map +1 -1
- package/dist/src/panorama/panoramaTileMaterial.d.ts +0 -6
- package/dist/src/panorama/panoramaTileMaterial.js +0 -12
- package/dist/src/panorama/panoramaTileMaterial.js.map +1 -1
- package/dist/src/panorama/panoramaTileProvider.js +7 -8
- package/dist/src/panorama/panoramaTileProvider.js.map +1 -1
- package/dist/src/util/flight/flightRecorder.d.ts +10 -0
- package/dist/src/util/flight/flightRecorder.js +175 -0
- package/dist/src/util/flight/flightRecorder.js.map +1 -0
- package/dist/src/util/mapCollection.d.ts +2 -0
- package/dist/src/util/mapCollection.js +3 -0
- package/dist/src/util/mapCollection.js.map +1 -1
- package/dist/src/util/math.d.ts +4 -1
- package/dist/src/util/math.js +20 -1
- package/dist/src/util/math.js.map +1 -1
- package/dist/tests/data/cog/test_grey_world.tif +0 -0
- package/dist/tests/data/cog/test_rgb.tif +0 -0
- package/dist/tests/data/cog/test_rgb_world.tif +0 -0
- package/index.ts +10 -1
- package/package.json +1 -1
- package/src/cesium/cesium.d.ts +5 -0
- package/src/interaction/coordinateAtPixel.ts +2 -2
- package/src/layer/cesium/cogCesiumImpl.ts +36 -0
- package/src/layer/cesium/cogImageryProvider.ts +389 -0
- package/src/layer/cogLayer.ts +162 -0
- package/src/layer/openlayers/cogOpenlayersImpl.ts +75 -0
- package/src/layer/openlayers/layerOpenlayersImpl.ts +7 -4
- package/src/layer/panoramaDatasetLayer.ts +1 -12
- package/src/map/panoramaMap.ts +7 -2
- package/src/panorama/panoramaImage.ts +14 -0
- package/src/panorama/panoramaImageView.ts +32 -40
- package/src/panorama/panoramaTile.ts +81 -16
- package/src/panorama/panoramaTileMaterial.ts +0 -14
- package/src/panorama/panoramaTileProvider.ts +7 -10
- package/src/util/flight/flightRecorder.ts +237 -0
- package/src/util/mapCollection.ts +4 -0
- package/src/util/math.ts +34 -0
- package/dist/src/panorama/panoramaImageCache.d.ts +0 -8
- package/dist/src/panorama/panoramaImageCache.js +0 -18
- package/dist/src/panorama/panoramaImageCache.js.map +0 -1
- package/src/panorama/panoramaImageCache.ts +0 -19
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import GeoTIFF, { type Options as GeoTIFFOptions } from 'ol/source/GeoTIFF.js';
|
|
2
|
+
import type { EventsKey } from 'ol/events.js';
|
|
3
|
+
import { unByKey } from 'ol/Observable.js';
|
|
4
|
+
import RasterLayer, {
|
|
5
|
+
type RasterLayerImplementationOptions,
|
|
6
|
+
type RasterLayerOptions,
|
|
7
|
+
TilingScheme,
|
|
8
|
+
} from './rasterLayer.js';
|
|
9
|
+
import OpenlayersMap from '../map/openlayersMap.js';
|
|
10
|
+
import CesiumMap from '../map/cesiumMap.js';
|
|
11
|
+
import { layerClassRegistry } from '../classRegistry.js';
|
|
12
|
+
import type VcsMap from '../map/vcsMap.js';
|
|
13
|
+
import COGOpenlayersImpl from './openlayers/cogOpenlayersImpl.js';
|
|
14
|
+
import COGCesiumImpl from './cesium/cogCesiumImpl.js';
|
|
15
|
+
import Extent from '../util/extent.js';
|
|
16
|
+
import { mercatorProjection } from '../util/projection.js';
|
|
17
|
+
|
|
18
|
+
export type COGLayerOptions = Omit<RasterLayerOptions, 'tilingSchema'> & {
|
|
19
|
+
/**
|
|
20
|
+
* Passed directly to the GeoTIFF source.
|
|
21
|
+
*/
|
|
22
|
+
convertToRGB?: boolean | 'auto';
|
|
23
|
+
/**
|
|
24
|
+
* Passed directly to the GeoTIFF source.
|
|
25
|
+
*/
|
|
26
|
+
normalize?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Passed directly to the GeoTIFF source.
|
|
29
|
+
*/
|
|
30
|
+
interpolate?: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type COGLayerImplementationOptions = RasterLayerImplementationOptions & {
|
|
34
|
+
source: GeoTIFF;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function getTilingSchemaFromSource(source: GeoTIFF): Promise<TilingScheme> {
|
|
38
|
+
let key: EventsKey | undefined;
|
|
39
|
+
return new Promise<TilingScheme>((resolve, reject) => {
|
|
40
|
+
const handleChange = (): void => {
|
|
41
|
+
if (source.getState() === 'ready') {
|
|
42
|
+
if (key) {
|
|
43
|
+
unByKey(key);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const projection = source.getProjection();
|
|
47
|
+
if (!source.getTileGrid()) {
|
|
48
|
+
reject(new Error(`Missing tilegrid in GeoTIFF source not found.`));
|
|
49
|
+
} else if (projection?.getCode() === 'EPSG:4326') {
|
|
50
|
+
resolve(TilingScheme.GEOGRAPHIC);
|
|
51
|
+
} else if (projection?.getCode() === 'EPSG:3857') {
|
|
52
|
+
resolve(TilingScheme.MERCATOR);
|
|
53
|
+
} else {
|
|
54
|
+
reject(new Error(`Unexpected code projection`));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
key = source.on('change', handleChange);
|
|
60
|
+
handleChange();
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
class COGLayer extends RasterLayer<COGOpenlayersImpl | COGCesiumImpl> {
|
|
65
|
+
static get className(): string {
|
|
66
|
+
return 'COGLayer';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static getDefaultOptions(): COGLayerOptions {
|
|
70
|
+
return {
|
|
71
|
+
...RasterLayer.getDefaultOptions(),
|
|
72
|
+
convertToRGB: 'auto',
|
|
73
|
+
normalize: undefined,
|
|
74
|
+
interpolate: undefined,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private _sourceOptions: Partial<GeoTIFFOptions>;
|
|
79
|
+
private _source: GeoTIFF | undefined;
|
|
80
|
+
|
|
81
|
+
constructor(options: COGLayerOptions) {
|
|
82
|
+
super(options);
|
|
83
|
+
const defaultOptions = COGLayer.getDefaultOptions();
|
|
84
|
+
this._sourceOptions = {
|
|
85
|
+
convertToRGB: options.convertToRGB ?? defaultOptions.convertToRGB,
|
|
86
|
+
normalize: options.normalize,
|
|
87
|
+
interpolate: options.interpolate,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
this._supportedMaps = [OpenlayersMap.className, CesiumMap.className];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async initialize(): Promise<void> {
|
|
94
|
+
if (!this.initialized) {
|
|
95
|
+
this._source = new GeoTIFF({
|
|
96
|
+
...this._sourceOptions,
|
|
97
|
+
sources: [{ url: this.url }],
|
|
98
|
+
});
|
|
99
|
+
this.tilingSchema = await getTilingSchemaFromSource(this._source);
|
|
100
|
+
}
|
|
101
|
+
return super.initialize();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
getImplementationOptions(): COGLayerImplementationOptions {
|
|
105
|
+
return {
|
|
106
|
+
...super.getImplementationOptions(),
|
|
107
|
+
source: this._source!,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
createImplementationsForMap(
|
|
112
|
+
map: VcsMap,
|
|
113
|
+
): (COGOpenlayersImpl | COGCesiumImpl)[] {
|
|
114
|
+
if (map instanceof OpenlayersMap) {
|
|
115
|
+
return [new COGOpenlayersImpl(map, this.getImplementationOptions())];
|
|
116
|
+
} else if (map instanceof CesiumMap) {
|
|
117
|
+
return [new COGCesiumImpl(map, this.getImplementationOptions())];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
toJSON(): COGLayerOptions {
|
|
124
|
+
const config: Partial<COGLayerOptions> & RasterLayerOptions =
|
|
125
|
+
super.toJSON();
|
|
126
|
+
const defaultOptions = COGLayer.getDefaultOptions();
|
|
127
|
+
|
|
128
|
+
delete config.tilingSchema;
|
|
129
|
+
if (this._sourceOptions.convertToRGB !== defaultOptions.convertToRGB) {
|
|
130
|
+
config.convertToRGB = this._sourceOptions.convertToRGB;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (this._sourceOptions.normalize != null) {
|
|
134
|
+
config.normalize = this._sourceOptions.normalize;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (this._sourceOptions.interpolate != null) {
|
|
138
|
+
config.interpolate = this._sourceOptions.interpolate;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return config;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
destroy(): void {
|
|
145
|
+
this._source?.dispose();
|
|
146
|
+
super.destroy();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
getZoomToExtent(): Extent | null {
|
|
150
|
+
const extent = this._source?.getTileGrid()?.getExtent();
|
|
151
|
+
if (extent) {
|
|
152
|
+
return new Extent({
|
|
153
|
+
coordinates: extent,
|
|
154
|
+
projection: mercatorProjection.toJSON(),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return super.getZoomToExtent();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
layerClassRegistry.registerClass(COGLayer.className, COGLayer);
|
|
162
|
+
export default COGLayer;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type GeoTIFFSource from 'ol/source/GeoTIFF.js';
|
|
2
|
+
import WebGLTile from 'ol/layer/WebGLTile.js';
|
|
3
|
+
import { getRenderPixel } from 'ol/render.js';
|
|
4
|
+
import type RenderEvent from 'ol/render/Event.js';
|
|
5
|
+
import { SplitDirection } from '@vcmap-cesium/engine';
|
|
6
|
+
import RasterLayerOpenlayersImpl from './rasterLayerOpenlayersImpl.js';
|
|
7
|
+
import type { COGLayerImplementationOptions } from '../cogLayer.js';
|
|
8
|
+
import type OpenlayersMap from '../../map/openlayersMap.js';
|
|
9
|
+
|
|
10
|
+
const vcsCleared = Symbol('vcsCleared');
|
|
11
|
+
|
|
12
|
+
declare global {
|
|
13
|
+
interface WebGL2RenderingContext {
|
|
14
|
+
[vcsCleared]: number;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* COGLayer implementation for {@link OpenlayersMap}.
|
|
20
|
+
*/
|
|
21
|
+
class COGOpenlayersImpl extends RasterLayerOpenlayersImpl {
|
|
22
|
+
static get className(): string {
|
|
23
|
+
return 'COGOpenlayersImpl';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private _source: GeoTIFFSource;
|
|
27
|
+
|
|
28
|
+
constructor(map: OpenlayersMap, options: COGLayerImplementationOptions) {
|
|
29
|
+
super(map, options);
|
|
30
|
+
this._source = options.source;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getOLLayer(): WebGLTile {
|
|
34
|
+
return new WebGLTile({
|
|
35
|
+
source: this._source,
|
|
36
|
+
opacity: this.opacity,
|
|
37
|
+
minZoom: this.minRenderingLevel,
|
|
38
|
+
maxZoom: this.maxRenderingLevel,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
protected override _splitPreRender(event: RenderEvent): void {
|
|
43
|
+
const gl = event.context as WebGL2RenderingContext;
|
|
44
|
+
if (gl[vcsCleared] !== event.frameState?.time) {
|
|
45
|
+
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
46
|
+
}
|
|
47
|
+
gl.enable(gl.SCISSOR_TEST);
|
|
48
|
+
|
|
49
|
+
const mapSize = this.map.olMap?.getSize();
|
|
50
|
+
if (!mapSize) {
|
|
51
|
+
throw new Error('Map size is not available for scissor test');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const bottomLeft = getRenderPixel(event, [0, mapSize[1]]);
|
|
55
|
+
const topRight = getRenderPixel(event, [mapSize[0], 0]);
|
|
56
|
+
|
|
57
|
+
const width = Math.round(
|
|
58
|
+
(topRight[0] - bottomLeft[0]) * this.map.splitPosition,
|
|
59
|
+
);
|
|
60
|
+
const height = topRight[1] - bottomLeft[1];
|
|
61
|
+
if (this.splitDirection === SplitDirection.LEFT) {
|
|
62
|
+
gl.scissor(bottomLeft[0], bottomLeft[1], width, height);
|
|
63
|
+
} else {
|
|
64
|
+
gl.scissor(bottomLeft[0] + width, bottomLeft[1], topRight[0], height);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// eslint-disable-next-line class-methods-use-this
|
|
69
|
+
protected override _splitPostReder(event: RenderEvent): void {
|
|
70
|
+
const gl = event.context as WebGL2RenderingContext;
|
|
71
|
+
gl.disable(gl.SCISSOR_TEST);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default COGOpenlayersImpl;
|
|
@@ -88,16 +88,14 @@ class LayerOpenlayersImpl extends LayerImplementation<OpenlayersMap> {
|
|
|
88
88
|
this.olLayer!.on('prerender', this._splitPreRender.bind(this)),
|
|
89
89
|
);
|
|
90
90
|
this._splitDirectionRenderListeners.push(
|
|
91
|
-
this.olLayer!.on('postrender', (
|
|
92
|
-
(event.context as CanvasRenderingContext2D).restore();
|
|
93
|
-
}),
|
|
91
|
+
this.olLayer!.on('postrender', this._splitPostReder.bind(this)),
|
|
94
92
|
);
|
|
95
93
|
this.olLayer!.changed();
|
|
96
94
|
}
|
|
97
95
|
}
|
|
98
96
|
}
|
|
99
97
|
|
|
100
|
-
|
|
98
|
+
protected _splitPreRender(event: RenderEvent): void {
|
|
101
99
|
const context = event.context as CanvasRenderingContext2D;
|
|
102
100
|
const width = context.canvas.width * this.map.splitPosition;
|
|
103
101
|
context.save();
|
|
@@ -116,6 +114,11 @@ class LayerOpenlayersImpl extends LayerImplementation<OpenlayersMap> {
|
|
|
116
114
|
context.clip();
|
|
117
115
|
}
|
|
118
116
|
|
|
117
|
+
// eslint-disable-next-line class-methods-use-this
|
|
118
|
+
protected _splitPostReder(event: RenderEvent): void {
|
|
119
|
+
(event.context as CanvasRenderingContext2D).restore();
|
|
120
|
+
}
|
|
121
|
+
|
|
119
122
|
destroy(): void {
|
|
120
123
|
if (this.olLayer) {
|
|
121
124
|
this.map.removeOLLayer(this.olLayer);
|
|
@@ -15,7 +15,6 @@ import { layerClassRegistry } from '../classRegistry.js';
|
|
|
15
15
|
import FlatGeobufTileProvider from './tileProvider/flatGeobufTileProvider.js';
|
|
16
16
|
import { mercatorProjection, wgs84Projection } from '../util/projection.js';
|
|
17
17
|
import { panoramaFeature } from './vectorSymbols.js';
|
|
18
|
-
import { PanoramaImageCache } from '../panorama/panoramaImageCache.js';
|
|
19
18
|
import {
|
|
20
19
|
createPanoramaImageFromURL,
|
|
21
20
|
type PanoramaImage,
|
|
@@ -63,8 +62,6 @@ export default class PanoramaDatasetLayer extends VectorTileLayer<PanoramaDatase
|
|
|
63
62
|
|
|
64
63
|
private _panoramaVectorProperties = new VectorProperties({});
|
|
65
64
|
|
|
66
|
-
private _cache = new PanoramaImageCache();
|
|
67
|
-
|
|
68
65
|
private _dataExtent?: Extent;
|
|
69
66
|
|
|
70
67
|
readonly baseUrl: string;
|
|
@@ -178,15 +175,7 @@ export default class PanoramaDatasetLayer extends VectorTileLayer<PanoramaDatase
|
|
|
178
175
|
* @returns
|
|
179
176
|
*/
|
|
180
177
|
createPanoramaImage(name: string): Promise<PanoramaImage> {
|
|
181
|
-
|
|
182
|
-
if (this._cache.containsKey(imageUrl)) {
|
|
183
|
-
return this._cache.get(imageUrl);
|
|
184
|
-
}
|
|
185
|
-
const imagePromise = createPanoramaImageFromURL(imageUrl, this);
|
|
186
|
-
this._cache.set(imageUrl, imagePromise);
|
|
187
|
-
this._cache.expireCache();
|
|
188
|
-
|
|
189
|
-
return imagePromise;
|
|
178
|
+
return createPanoramaImageFromURL(`${this.baseUrl}/${name}_rgb.tif`, this);
|
|
190
179
|
}
|
|
191
180
|
|
|
192
181
|
override getZoomToExtent(): Extent | null {
|
package/src/map/panoramaMap.ts
CHANGED
|
@@ -236,9 +236,12 @@ export default class PanoramaMap extends VcsMap {
|
|
|
236
236
|
|
|
237
237
|
if (closestImage) {
|
|
238
238
|
if (viewpoint.heading != null) {
|
|
239
|
-
this._cesiumWidget
|
|
239
|
+
const { camera } = this._cesiumWidget;
|
|
240
|
+
camera.setView({
|
|
240
241
|
orientation: {
|
|
241
242
|
heading: CesiumMath.toRadians(viewpoint.heading),
|
|
243
|
+
pitch: camera.pitch,
|
|
244
|
+
roll: camera.roll,
|
|
242
245
|
},
|
|
243
246
|
});
|
|
244
247
|
}
|
|
@@ -317,8 +320,10 @@ export default class PanoramaMap extends VcsMap {
|
|
|
317
320
|
* @param image
|
|
318
321
|
*/
|
|
319
322
|
setCurrentImage(image?: PanoramaImage): void {
|
|
320
|
-
if (this._currentImage
|
|
323
|
+
if (!this._currentImage?.equals(image)) {
|
|
324
|
+
const currentImage = this._currentImage;
|
|
321
325
|
this._currentImage = image;
|
|
326
|
+
currentImage?.destroy();
|
|
322
327
|
this.currentImageChanged.raiseEvent(image);
|
|
323
328
|
}
|
|
324
329
|
}
|
|
@@ -104,6 +104,13 @@ export type PanoramaImage = Readonly<
|
|
|
104
104
|
coordinate: [number, number],
|
|
105
105
|
result?: Cartesian3,
|
|
106
106
|
): Promise<Cartesian3 | undefined>;
|
|
107
|
+
/**
|
|
108
|
+
* Checks if this panorama image is equal to another panorama image
|
|
109
|
+
* by comparing name and baseUrl of dataset.
|
|
110
|
+
* @param other - the other image to compare with
|
|
111
|
+
* @returns true if the images represent the same panorama data
|
|
112
|
+
*/
|
|
113
|
+
equals(other?: PanoramaImage): boolean;
|
|
107
114
|
destroy(): void;
|
|
108
115
|
};
|
|
109
116
|
|
|
@@ -510,6 +517,13 @@ export async function createPanoramaImage(
|
|
|
510
517
|
): Promise<Cartesian3 | undefined> {
|
|
511
518
|
return positionAtDepth(imageCoordinate, true, result);
|
|
512
519
|
},
|
|
520
|
+
equals(other?: PanoramaImage): boolean {
|
|
521
|
+
return (
|
|
522
|
+
other != null &&
|
|
523
|
+
nameOrId === other.name &&
|
|
524
|
+
dataset?.baseUrl === other.dataset?.baseUrl
|
|
525
|
+
);
|
|
526
|
+
},
|
|
513
527
|
destroy(): void {
|
|
514
528
|
tileProvider.destroy();
|
|
515
529
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Camera } from '@vcmap-cesium/engine';
|
|
1
|
+
import type { Camera, Primitive } from '@vcmap-cesium/engine';
|
|
2
2
|
import { Cartesian2, Matrix4, Cartesian3 } from '@vcmap-cesium/engine';
|
|
3
3
|
import { getWidth } from 'ol/extent.js';
|
|
4
4
|
import type { Size } from 'ol/size.js';
|
|
@@ -164,16 +164,13 @@ function setupDepthHandling(
|
|
|
164
164
|
|
|
165
165
|
/**
|
|
166
166
|
* The image wrapper is responsible for managing the panorama image tiles and rendering them.
|
|
167
|
-
* @param image
|
|
168
|
-
* @param primitiveCollection
|
|
169
|
-
* @param camera
|
|
170
|
-
* @param canvas
|
|
171
167
|
*/
|
|
172
168
|
function createImageWrapper(
|
|
173
169
|
image: PanoramaImage,
|
|
174
170
|
primitiveCollection: PanoramaTilePrimitiveCollection,
|
|
175
171
|
camera: Camera,
|
|
176
172
|
canvas: HTMLCanvasElement,
|
|
173
|
+
map: PanoramaMap,
|
|
177
174
|
): ImageWrapper {
|
|
178
175
|
const { tileSize, maxLevel, minLevel, hasDepth } = image;
|
|
179
176
|
const baseTileCoordinates = createMinLevelTiles(minLevel);
|
|
@@ -181,7 +178,7 @@ function createImageWrapper(
|
|
|
181
178
|
? setupDepthHandling(image, primitiveCollection, camera, canvas)
|
|
182
179
|
: (): void => {};
|
|
183
180
|
|
|
184
|
-
const currentTiles = new Map<string,
|
|
181
|
+
const currentTiles = new Map<string, Primitive>();
|
|
185
182
|
let currentLevel = minLevel;
|
|
186
183
|
let currentTileCoordinates: PanoramaTileCoordinate[] = [
|
|
187
184
|
...baseTileCoordinates,
|
|
@@ -193,14 +190,15 @@ function createImageWrapper(
|
|
|
193
190
|
.createVisibleTiles(currentTileCoordinates)
|
|
194
191
|
.forEach((tile) => {
|
|
195
192
|
if (!currentTiles.has(tile.tileCoordinate.key)) {
|
|
193
|
+
const primitive = tile.getPrimitive(map);
|
|
196
194
|
if (
|
|
197
195
|
tile.tileCoordinate.level === minLevel &&
|
|
198
196
|
!(tile as PanoramaTile & { [baseLevelScaled]?: boolean })[
|
|
199
197
|
baseLevelScaled
|
|
200
198
|
]
|
|
201
199
|
) {
|
|
202
|
-
|
|
203
|
-
|
|
200
|
+
primitive.modelMatrix = Matrix4.multiplyByScale(
|
|
201
|
+
primitive.modelMatrix,
|
|
204
202
|
new Cartesian3(1.01, 1.01, 1.01),
|
|
205
203
|
new Matrix4(),
|
|
206
204
|
);
|
|
@@ -209,8 +207,8 @@ function createImageWrapper(
|
|
|
209
207
|
] = true;
|
|
210
208
|
}
|
|
211
209
|
|
|
212
|
-
currentTiles.set(tile.tileCoordinate.key,
|
|
213
|
-
primitiveCollection.add(
|
|
210
|
+
currentTiles.set(tile.tileCoordinate.key, primitive);
|
|
211
|
+
primitiveCollection.add(primitive);
|
|
214
212
|
}
|
|
215
213
|
});
|
|
216
214
|
};
|
|
@@ -290,12 +288,10 @@ function createImageWrapper(
|
|
|
290
288
|
|
|
291
289
|
// push base tile coordinates onto the back of the array, so they are always loaded first.
|
|
292
290
|
currentTileCoordinates.push(...baseTileCoordinates);
|
|
293
|
-
currentTiles.forEach((
|
|
294
|
-
if (
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
primitiveCollection.remove(tile.primitive);
|
|
298
|
-
currentTiles.delete(tile.tileCoordinate.key);
|
|
291
|
+
currentTiles.forEach((primitive, key) => {
|
|
292
|
+
if (!currentTileCoordinates.find((c) => c.key === key)) {
|
|
293
|
+
primitiveCollection.remove(primitive);
|
|
294
|
+
currentTiles.delete(key);
|
|
299
295
|
}
|
|
300
296
|
});
|
|
301
297
|
|
|
@@ -313,8 +309,8 @@ function createImageWrapper(
|
|
|
313
309
|
},
|
|
314
310
|
render,
|
|
315
311
|
destroy(): void {
|
|
316
|
-
currentTiles.forEach((
|
|
317
|
-
primitiveCollection.remove(
|
|
312
|
+
currentTiles.forEach((primitive) => {
|
|
313
|
+
primitiveCollection.remove(primitive);
|
|
318
314
|
});
|
|
319
315
|
currentTiles.clear();
|
|
320
316
|
showIntensityListener();
|
|
@@ -323,31 +319,26 @@ function createImageWrapper(
|
|
|
323
319
|
};
|
|
324
320
|
}
|
|
325
321
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
overlay.innerText = 'No Image';
|
|
343
|
-
|
|
344
|
-
emptyImageOverlay = overlay;
|
|
345
|
-
}
|
|
346
|
-
return emptyImageOverlay;
|
|
322
|
+
function createEmptyImageBitmap(): HTMLDivElement {
|
|
323
|
+
const overlay = document.createElement('div');
|
|
324
|
+
overlay.style.position = 'absolute';
|
|
325
|
+
overlay.style.display = 'block';
|
|
326
|
+
overlay.style.top = '0px';
|
|
327
|
+
overlay.style.left = '0px';
|
|
328
|
+
overlay.style.bottom = '0px';
|
|
329
|
+
overlay.style.right = '0px';
|
|
330
|
+
overlay.style.backgroundColor = '#409D76';
|
|
331
|
+
overlay.style.padding = '8px';
|
|
332
|
+
overlay.style.font = 'bold 64px Monospace, Courier New';
|
|
333
|
+
overlay.style.textAlign = 'center';
|
|
334
|
+
overlay.style.alignContent = 'center';
|
|
335
|
+
overlay.style.color = '#424242';
|
|
336
|
+
overlay.innerText = 'No Image';
|
|
337
|
+
return overlay;
|
|
347
338
|
}
|
|
348
339
|
|
|
349
340
|
function setupEmptyImageOverlay(container: HTMLElement): () => void {
|
|
350
|
-
const overlay =
|
|
341
|
+
const overlay = createEmptyImageBitmap();
|
|
351
342
|
container.appendChild(overlay);
|
|
352
343
|
|
|
353
344
|
let remover: (() => void) | undefined = () => {
|
|
@@ -400,6 +391,7 @@ export function createPanoramaImageView(map: PanoramaMap): PanoramaImageView {
|
|
|
400
391
|
primitiveCollection,
|
|
401
392
|
scene.camera,
|
|
402
393
|
scene.canvas,
|
|
394
|
+
map,
|
|
403
395
|
);
|
|
404
396
|
currentView.suspendTileLoading = suspendTileLoading;
|
|
405
397
|
} else {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Matrix4 } from '@vcmap-cesium/engine';
|
|
2
1
|
import {
|
|
3
2
|
Cartesian3,
|
|
4
3
|
EllipsoidGeometry,
|
|
@@ -6,16 +5,28 @@ import {
|
|
|
6
5
|
MaterialAppearance,
|
|
7
6
|
Primitive,
|
|
8
7
|
VertexFormat,
|
|
8
|
+
type Matrix4,
|
|
9
9
|
} from '@vcmap-cesium/engine';
|
|
10
10
|
import type { Size } from 'ol/size.js';
|
|
11
11
|
import PanoramaTileMaterial from './panoramaTileMaterial.js';
|
|
12
12
|
import type { PanoramaTileCoordinate } from './panoramaTileCoordinate.js';
|
|
13
13
|
import { tileSizeInRadians } from './panoramaTileCoordinate.js';
|
|
14
|
+
import type PanoramaMap from '../map/panoramaMap.js';
|
|
15
|
+
import type {
|
|
16
|
+
PanoramaResourceData,
|
|
17
|
+
PanoramaResourceType,
|
|
18
|
+
} from './panoramaTileProvider.js';
|
|
14
19
|
|
|
15
20
|
export type PanoramaTile = {
|
|
16
|
-
|
|
21
|
+
hasResource(type: PanoramaResourceType): boolean;
|
|
22
|
+
setResource<T extends PanoramaResourceType>(
|
|
23
|
+
type: T,
|
|
24
|
+
resource: PanoramaResourceData<T>,
|
|
25
|
+
): void;
|
|
26
|
+
getDepthAtPixel(x: number, y: number): number | undefined;
|
|
17
27
|
readonly tileCoordinate: PanoramaTileCoordinate;
|
|
18
|
-
|
|
28
|
+
getPrimitive(map: PanoramaMap): Primitive;
|
|
29
|
+
getMaterial(map: PanoramaMap): PanoramaTileMaterial | undefined;
|
|
19
30
|
destroy(): void;
|
|
20
31
|
};
|
|
21
32
|
|
|
@@ -64,26 +75,80 @@ export function createPanoramaTile(
|
|
|
64
75
|
modelMatrix: Matrix4,
|
|
65
76
|
tileSize: Size,
|
|
66
77
|
): PanoramaTile {
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
let destroyed = false;
|
|
79
|
+
const primitives = new Map<
|
|
80
|
+
PanoramaMap,
|
|
81
|
+
{ primitive: Primitive; material: PanoramaTileMaterial }
|
|
82
|
+
>();
|
|
83
|
+
|
|
84
|
+
let resources: { [K in PanoramaResourceType]?: PanoramaResourceData<K> } = {};
|
|
73
85
|
|
|
74
86
|
return {
|
|
75
|
-
get primitive(): Primitive {
|
|
76
|
-
return primitive;
|
|
77
|
-
},
|
|
78
87
|
get tileCoordinate(): PanoramaTileCoordinate {
|
|
79
88
|
return tileCoordinate;
|
|
80
89
|
},
|
|
81
|
-
|
|
82
|
-
return
|
|
90
|
+
hasResource(type: PanoramaResourceType): boolean {
|
|
91
|
+
return resources[type] != null;
|
|
92
|
+
},
|
|
93
|
+
setResource<T extends PanoramaResourceType>(
|
|
94
|
+
type: T,
|
|
95
|
+
resource: PanoramaResourceData<T>,
|
|
96
|
+
): void {
|
|
97
|
+
if (this.hasResource(type)) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Resource of type "${type}" already set for this tile. Cannot overwrite.`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
resources[type] = resource as ImageBitmap & Float32Array;
|
|
103
|
+
primitives.forEach(({ material }) => {
|
|
104
|
+
material.setTexture(type, resource);
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
getPrimitive(map: PanoramaMap): Primitive {
|
|
108
|
+
if (destroyed) {
|
|
109
|
+
throw new Error('Cannot get primitive from destroyed panorama tile.');
|
|
110
|
+
}
|
|
111
|
+
if (primitives.has(map)) {
|
|
112
|
+
return primitives.get(map)!.primitive;
|
|
113
|
+
}
|
|
114
|
+
const material = new PanoramaTileMaterial(tileCoordinate, tileSize);
|
|
115
|
+
Object.entries(resources).forEach(([type, resource]) => {
|
|
116
|
+
material.setTexture(type as PanoramaResourceType, resource);
|
|
117
|
+
});
|
|
118
|
+
const primitive = createPanoramaTilePrimitive(
|
|
119
|
+
tileCoordinate,
|
|
120
|
+
modelMatrix,
|
|
121
|
+
material,
|
|
122
|
+
);
|
|
123
|
+
primitives.set(map, { primitive, material });
|
|
124
|
+
return primitive;
|
|
125
|
+
},
|
|
126
|
+
getMaterial(map: PanoramaMap): PanoramaTileMaterial | undefined {
|
|
127
|
+
if (primitives.has(map)) {
|
|
128
|
+
return primitives.get(map)!.material;
|
|
129
|
+
}
|
|
130
|
+
return undefined;
|
|
131
|
+
},
|
|
132
|
+
/**
|
|
133
|
+
* Returns the normalized depth value [0, 1] at the given pixel coordinates in the panorama tile.
|
|
134
|
+
* @param x
|
|
135
|
+
* @param y
|
|
136
|
+
*/
|
|
137
|
+
getDepthAtPixel(x: number, y: number): number | undefined {
|
|
138
|
+
if (!resources.depth) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const index = y * tileSize[0] + x;
|
|
143
|
+
return resources.depth[index];
|
|
83
144
|
},
|
|
84
145
|
destroy(): void {
|
|
85
|
-
|
|
86
|
-
|
|
146
|
+
destroyed = true;
|
|
147
|
+
resources = {};
|
|
148
|
+
primitives.forEach(({ primitive, material }) => {
|
|
149
|
+
primitive.destroy();
|
|
150
|
+
material.destroy();
|
|
151
|
+
});
|
|
87
152
|
},
|
|
88
153
|
};
|
|
89
154
|
}
|
|
@@ -258,20 +258,6 @@ export default class PanoramaTileMaterial extends Material {
|
|
|
258
258
|
return this.uniforms[uniform] !== Material.DefaultImageId;
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
/**
|
|
262
|
-
* Returns the normalized depth value [0, 1] at the given pixel coordinates in the panorama tile.
|
|
263
|
-
* @param x
|
|
264
|
-
* @param y
|
|
265
|
-
*/
|
|
266
|
-
getDepthAtPixel(x: number, y: number): number | undefined {
|
|
267
|
-
if (!this._depthData) {
|
|
268
|
-
return undefined;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const index = y * this._tileSize[0] + x;
|
|
272
|
-
return this._depthData[index];
|
|
273
|
-
}
|
|
274
|
-
|
|
275
261
|
/**
|
|
276
262
|
* Internal cesium API to update the material.
|
|
277
263
|
* @param context
|