@loaders.gl/tiles 5.0.0-alpha.7 → 5.0.0-alpha.8
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/README.md +26 -0
- package/dist/dist.dev.js +6381 -6802
- package/dist/dist.min.js +3 -6
- package/dist/index.cjs +209 -65
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/spatial/spatial-coordinate-transformer.d.ts.map +1 -1
- package/dist/spatial/spatial-coordinate-transformer.js +13 -6
- package/dist/spatial/spatial-coordinate-transformer.js.map +1 -1
- package/dist/spatial/spatial-resource-registry.js +3 -3
- package/dist/spatial/spatial-resource-registry.js.map +1 -1
- package/dist/tileset-3d/common/tile-3d.d.ts +4 -0
- package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
- package/dist/tileset-3d/common/tile-3d.js +35 -14
- package/dist/tileset-3d/common/tile-3d.js.map +1 -1
- package/dist/tileset-3d/common/tileset-3d.d.ts +1 -25
- package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-3d.js +8 -41
- package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
- package/dist/tileset-3d/common/tileset-content-traversal.d.ts +37 -0
- package/dist/tileset-3d/common/tileset-content-traversal.d.ts.map +1 -0
- package/dist/tileset-3d/common/tileset-content-traversal.js +80 -0
- package/dist/tileset-3d/common/tileset-content-traversal.js.map +1 -0
- package/dist/tileset-3d/common/tileset-source.d.ts +8 -1
- package/dist/tileset-3d/common/tileset-source.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-source.js.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +2 -0
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js +13 -0
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-source.d.ts +4 -2
- package/dist/tileset-3d/format-i3s/i3s-source.d.ts.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-source.js +60 -9
- package/dist/tileset-3d/format-i3s/i3s-source.js.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.d.ts.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js +1 -1
- package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js.map +1 -1
- package/dist/tileset-3d/helpers/frame-state.d.ts +1 -0
- package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/frame-state.js +5 -4
- package/dist/tileset-3d/helpers/frame-state.js.map +1 -1
- package/package.json +11 -13
- package/src/index.ts +5 -0
- package/src/spatial/spatial-coordinate-transformer.ts +18 -9
- package/src/spatial/spatial-resource-registry.ts +3 -3
- package/src/tileset-3d/common/tile-3d.ts +39 -17
- package/src/tileset-3d/common/tileset-3d.ts +8 -60
- package/src/tileset-3d/common/tileset-content-traversal.ts +114 -0
- package/src/tileset-3d/common/tileset-source.ts +16 -1
- package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +18 -0
- package/src/tileset-3d/format-i3s/i3s-source.ts +84 -13
- package/src/tileset-3d/format-i3s/i3s-tileset-traverser.ts +2 -1
- package/src/tileset-3d/helpers/frame-state.ts +9 -6
|
@@ -58,17 +58,6 @@ export type Tileset3DProps = {
|
|
|
58
58
|
* @default 536870912
|
|
59
59
|
*/
|
|
60
60
|
maximumCacheOverflowBytes?: number;
|
|
61
|
-
/**
|
|
62
|
-
* Soft cache target in mebibytes.
|
|
63
|
-
* @deprecated Use {@link Tileset3DProps.cacheBytes}; when both are supplied, `cacheBytes` wins.
|
|
64
|
-
*/
|
|
65
|
-
maximumMemoryUsage?: number;
|
|
66
|
-
/**
|
|
67
|
-
* Cache overflow headroom in mebibytes.
|
|
68
|
-
* @deprecated Use {@link Tileset3DProps.maximumCacheOverflowBytes}; when both are supplied, the
|
|
69
|
-
* byte-native option wins.
|
|
70
|
-
*/
|
|
71
|
-
memoryCacheOverflow?: number;
|
|
72
61
|
maximumTilesSelected?: number;
|
|
73
62
|
debounceTime?: number;
|
|
74
63
|
|
|
@@ -158,10 +147,6 @@ type Props = {
|
|
|
158
147
|
cacheBytes: number;
|
|
159
148
|
/** Current-frame headroom, in bytes, before memory-adjusted SSE rises. */
|
|
160
149
|
maximumCacheOverflowBytes: number;
|
|
161
|
-
/** @deprecated Byte-native code should use `cacheBytes`. */
|
|
162
|
-
maximumMemoryUsage: number;
|
|
163
|
-
/** @deprecated Byte-native code should use `maximumCacheOverflowBytes`. */
|
|
164
|
-
memoryCacheOverflow: number;
|
|
165
150
|
maximumTilesSelected: number;
|
|
166
151
|
debounceTime: number;
|
|
167
152
|
onTileLoad: (tile: Tile3D) => void;
|
|
@@ -241,31 +226,21 @@ function validateCacheByteLength(value: number, optionName: string): number {
|
|
|
241
226
|
}
|
|
242
227
|
|
|
243
228
|
/**
|
|
244
|
-
* Resolves the base cache target from byte-native or
|
|
229
|
+
* Resolves the base cache target from the byte-native option or its fallback.
|
|
245
230
|
*
|
|
246
|
-
*
|
|
247
|
-
* The fallback is already expressed in bytes and is used for both construction and runtime updates.
|
|
248
|
-
*
|
|
249
|
-
* @param options - Options that may contain byte-native or deprecated cache values.
|
|
231
|
+
* @param options - Options that may contain a byte-native cache value.
|
|
250
232
|
* @param fallbackCacheBytes - Existing or default cache target in bytes.
|
|
251
233
|
* @returns The resolved and validated cache target in bytes.
|
|
252
234
|
*/
|
|
253
235
|
function resolveCacheBytes(options: Tileset3DProps, fallbackCacheBytes: number): number {
|
|
254
|
-
const cacheBytes =
|
|
255
|
-
options.cacheBytes ??
|
|
256
|
-
(options.maximumMemoryUsage !== undefined
|
|
257
|
-
? options.maximumMemoryUsage * BYTES_PER_MEBIBYTE
|
|
258
|
-
: fallbackCacheBytes);
|
|
236
|
+
const cacheBytes = options.cacheBytes ?? fallbackCacheBytes;
|
|
259
237
|
return validateCacheByteLength(cacheBytes, 'cacheBytes');
|
|
260
238
|
}
|
|
261
239
|
|
|
262
240
|
/**
|
|
263
|
-
* Resolves cache overflow headroom from byte-native or
|
|
264
|
-
*
|
|
265
|
-
* The byte-native option takes precedence so callers can migrate the two cache controls
|
|
266
|
-
* independently without unit ambiguity.
|
|
241
|
+
* Resolves cache overflow headroom from the byte-native option or its fallback.
|
|
267
242
|
*
|
|
268
|
-
* @param options - Options that may contain byte-native
|
|
243
|
+
* @param options - Options that may contain a byte-native overflow value.
|
|
269
244
|
* @param fallbackOverflowBytes - Existing or default overflow headroom in bytes.
|
|
270
245
|
* @returns The resolved and validated overflow headroom in bytes.
|
|
271
246
|
*/
|
|
@@ -273,11 +248,7 @@ function resolveMaximumCacheOverflowBytes(
|
|
|
273
248
|
options: Tileset3DProps,
|
|
274
249
|
fallbackOverflowBytes: number
|
|
275
250
|
): number {
|
|
276
|
-
const maximumCacheOverflowBytes =
|
|
277
|
-
options.maximumCacheOverflowBytes ??
|
|
278
|
-
(options.memoryCacheOverflow !== undefined
|
|
279
|
-
? options.memoryCacheOverflow * BYTES_PER_MEBIBYTE
|
|
280
|
-
: fallbackOverflowBytes);
|
|
251
|
+
const maximumCacheOverflowBytes = options.maximumCacheOverflowBytes ?? fallbackOverflowBytes;
|
|
281
252
|
return validateCacheByteLength(maximumCacheOverflowBytes, 'maximumCacheOverflowBytes');
|
|
282
253
|
}
|
|
283
254
|
|
|
@@ -289,8 +260,6 @@ const DEFAULT_PROPS: Props = {
|
|
|
289
260
|
maxRequests: 64,
|
|
290
261
|
cacheBytes: DEFAULT_CACHE_BYTES,
|
|
291
262
|
maximumCacheOverflowBytes: DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES,
|
|
292
|
-
maximumMemoryUsage: DEFAULT_CACHE_BYTES / BYTES_PER_MEBIBYTE,
|
|
293
|
-
memoryCacheOverflow: DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES / BYTES_PER_MEBIBYTE,
|
|
294
263
|
maximumTilesSelected: 0,
|
|
295
264
|
debounceTime: 0,
|
|
296
265
|
onTileLoad: () => {},
|
|
@@ -456,8 +425,6 @@ export class Tileset3D {
|
|
|
456
425
|
...suppliedOptions,
|
|
457
426
|
cacheBytes,
|
|
458
427
|
maximumCacheOverflowBytes,
|
|
459
|
-
maximumMemoryUsage: cacheBytes / BYTES_PER_MEBIBYTE,
|
|
460
|
-
memoryCacheOverflow: maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE,
|
|
461
428
|
memoryAdjustedScreenSpaceError:
|
|
462
429
|
suppliedOptions.memoryAdjustedScreenSpaceError ?? usesTiles3DCacheDefaults
|
|
463
430
|
};
|
|
@@ -552,7 +519,6 @@ export class Tileset3D {
|
|
|
552
519
|
set cacheBytes(value: number) {
|
|
553
520
|
this._cacheBytes = validateCacheByteLength(value, 'cacheBytes');
|
|
554
521
|
this.options.cacheBytes = this._cacheBytes;
|
|
555
|
-
this.options.maximumMemoryUsage = this._cacheBytes / BYTES_PER_MEBIBYTE;
|
|
556
522
|
}
|
|
557
523
|
|
|
558
524
|
/**
|
|
@@ -569,19 +535,6 @@ export class Tileset3D {
|
|
|
569
535
|
set maximumCacheOverflowBytes(value: number) {
|
|
570
536
|
this._maximumCacheOverflowBytes = validateCacheByteLength(value, 'maximumCacheOverflowBytes');
|
|
571
537
|
this.options.maximumCacheOverflowBytes = this._maximumCacheOverflowBytes;
|
|
572
|
-
this.options.memoryCacheOverflow = this._maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
/**
|
|
576
|
-
* Gets or sets the soft cache target in mebibytes.
|
|
577
|
-
* @deprecated Use {@link Tileset3D.cacheBytes}. Assignments remain synchronized for compatibility.
|
|
578
|
-
*/
|
|
579
|
-
get maximumMemoryUsage(): number {
|
|
580
|
-
return this.cacheBytes / BYTES_PER_MEBIBYTE;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
set maximumMemoryUsage(value: number) {
|
|
584
|
-
this.cacheBytes = validateCacheByteLength(value * BYTES_PER_MEBIBYTE, 'maximumMemoryUsage');
|
|
585
538
|
}
|
|
586
539
|
|
|
587
540
|
get queryParams(): string {
|
|
@@ -591,10 +544,7 @@ export class Tileset3D {
|
|
|
591
544
|
}
|
|
592
545
|
|
|
593
546
|
/**
|
|
594
|
-
* Updates traversal and runtime options
|
|
595
|
-
*
|
|
596
|
-
* For each budget, the byte-native option takes precedence when both unit forms are supplied.
|
|
597
|
-
* Unspecified budgets retain their current byte values, avoiding repeated MiB conversion.
|
|
547
|
+
* Updates traversal and runtime options while retaining unspecified byte budgets.
|
|
598
548
|
*
|
|
599
549
|
* @param props - Partial runtime options to apply.
|
|
600
550
|
*/
|
|
@@ -608,9 +558,7 @@ export class Tileset3D {
|
|
|
608
558
|
...this.options,
|
|
609
559
|
...props,
|
|
610
560
|
cacheBytes,
|
|
611
|
-
maximumCacheOverflowBytes
|
|
612
|
-
maximumMemoryUsage: cacheBytes / BYTES_PER_MEBIBYTE,
|
|
613
|
-
memoryCacheOverflow: maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE
|
|
561
|
+
maximumCacheOverflowBytes
|
|
614
562
|
};
|
|
615
563
|
this._cacheBytes = cacheBytes;
|
|
616
564
|
this._maximumCacheOverflowBytes = maximumCacheOverflowBytes;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Tile3D} from './tile-3d';
|
|
6
|
+
import type {Tileset3D} from './tileset-3d';
|
|
7
|
+
import type {TileContentLoadResult} from './tileset-source';
|
|
8
|
+
import type {Tile3DContent} from './tile-3d-contracts';
|
|
9
|
+
import {TILESET_TYPE} from '../../constants';
|
|
10
|
+
|
|
11
|
+
/** Options for complete camera-independent tileset content traversal. */
|
|
12
|
+
export interface TraverseTilesetContentsOptions {
|
|
13
|
+
/** Cancel traversal and stop issuing further source requests. */
|
|
14
|
+
readonly signal?: AbortSignal;
|
|
15
|
+
/**
|
|
16
|
+
* Unload content loaded by this traversal when iteration advances or closes, including on error.
|
|
17
|
+
* Defaults to false. Preloaded content is retained. Use a runtime dedicated to this traversal;
|
|
18
|
+
* consume payloads before advancing and do not retain them or update the runtime concurrently.
|
|
19
|
+
*/
|
|
20
|
+
readonly unloadContent?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** One tile placement and its ordered content entries from a completed source read. */
|
|
24
|
+
export interface TilesetContentTraversalItem {
|
|
25
|
+
/** Runtime tile placement. Shared content resources are yielded once per placement. */
|
|
26
|
+
readonly tile: Tile3D;
|
|
27
|
+
/** Ordered content entries, including multiple contents declared by a single tile. */
|
|
28
|
+
readonly contents: readonly Tile3DContent[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Walks every declared tile placement and its loaded content without a camera or visibility test.
|
|
33
|
+
*
|
|
34
|
+
* Source implementations remain responsible for lazy subtree and node-page resolution. Children
|
|
35
|
+
* are visited in declaration order, nested tilesets are installed by the source before descent,
|
|
36
|
+
* and repeated resource URLs are not deduplicated because each placement can have a distinct
|
|
37
|
+
* transform. Content stays attached unless `unloadContent` is enabled. In that mode, newly loaded
|
|
38
|
+
* payloads are valid until iteration advances or closes. Tile placements and children remain
|
|
39
|
+
* available; callers still own the tileset, source, and archive lifetime.
|
|
40
|
+
*
|
|
41
|
+
* @param tileset - Initialized or initializing source-backed tileset runtime.
|
|
42
|
+
* @param options - Cancellation and decoded-content lifetime options.
|
|
43
|
+
* @returns One item for each visited tile placement in deterministic depth-first order.
|
|
44
|
+
*/
|
|
45
|
+
export async function* traverseTilesetContents(
|
|
46
|
+
tileset: Tileset3D,
|
|
47
|
+
options: TraverseTilesetContentsOptions = {}
|
|
48
|
+
): AsyncGenerator<TilesetContentTraversalItem> {
|
|
49
|
+
await tileset.tilesetInitializationPromise;
|
|
50
|
+
throwIfAborted(options.signal);
|
|
51
|
+
if (!tileset.root) {
|
|
52
|
+
throw new Error('Tileset initialization completed without a root tile');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const stack: Tile3D[] = [tileset.root];
|
|
56
|
+
const visitedTiles = new Set<Tile3D>();
|
|
57
|
+
while (stack.length > 0) {
|
|
58
|
+
throwIfAborted(options.signal);
|
|
59
|
+
const tile = stack.pop() as Tile3D;
|
|
60
|
+
if (visitedTiles.has(tile)) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
visitedTiles.add(tile);
|
|
64
|
+
|
|
65
|
+
if (tile.header.implicitSubtree && tile.childrenState !== 'ready') {
|
|
66
|
+
const loadChildren = tileset.source.loadTileChildrenForTraversal;
|
|
67
|
+
if (!loadChildren) {
|
|
68
|
+
throw new Error('Tileset source does not support camera-independent subtree loading');
|
|
69
|
+
}
|
|
70
|
+
const result = await loadChildren.call(tileset.source, tile, options.signal);
|
|
71
|
+
if (!result.loaded) {
|
|
72
|
+
throw new Error(`Unable to load implicit subtree for tile ${tile.id}`);
|
|
73
|
+
}
|
|
74
|
+
tile.childrenState = 'ready';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (tileset.type === TILESET_TYPE.I3S && tileset.source.loadTileChildrenForTraversal) {
|
|
78
|
+
const result = await tileset.source.loadTileChildrenForTraversal(tile, options.signal);
|
|
79
|
+
if (!result.loaded) {
|
|
80
|
+
throw new Error(`Unable to load I3S child nodes for tile ${tile.id}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
throwIfAborted(options.signal);
|
|
85
|
+
const loadsContent = tile.contentUrls.length > 0 && !tile.content;
|
|
86
|
+
try {
|
|
87
|
+
if (loadsContent) {
|
|
88
|
+
const loadResult: TileContentLoadResult = await tile.loadContentForTraversal();
|
|
89
|
+
throwIfAborted(options.signal);
|
|
90
|
+
if (!loadResult.loaded) {
|
|
91
|
+
throw new Error(`Unable to load tile content for tile ${tile.id}`);
|
|
92
|
+
}
|
|
93
|
+
tileset.source.onTileLoaded?.(tileset, tile, loadResult);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
yield {tile, contents: tile.contentEntries};
|
|
97
|
+
} finally {
|
|
98
|
+
if (options.unloadContent && loadsContent) {
|
|
99
|
+
tile.unloadContent();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
for (let childIndex = tile.children.length - 1; childIndex >= 0; childIndex--) {
|
|
104
|
+
stack.push(tile.children[childIndex]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Throws the signal reason when traversal is cancelled. */
|
|
110
|
+
function throwIfAborted(signal?: AbortSignal): void {
|
|
111
|
+
if (signal?.aborted) {
|
|
112
|
+
throw signal.reason ?? new DOMException('The operation was aborted', 'AbortError');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -221,6 +221,17 @@ export interface Tileset3DSource {
|
|
|
221
221
|
*/
|
|
222
222
|
loadTileChildren?(tile: Tile3D, frameState: FrameState): Promise<TileChildrenLoadResult>;
|
|
223
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Loads all source-managed child headers without requiring camera or visibility state.
|
|
226
|
+
*
|
|
227
|
+
* This hook is used by complete dataset operations such as conversion. Implementations retain
|
|
228
|
+
* ownership of implicit subtrees, node pages, archive resolution, and query inheritance.
|
|
229
|
+
*/
|
|
230
|
+
loadTileChildrenForTraversal?(
|
|
231
|
+
tile: Tile3D,
|
|
232
|
+
signal?: AbortSignal
|
|
233
|
+
): Promise<TileChildrenLoadResult>;
|
|
234
|
+
|
|
224
235
|
/**
|
|
225
236
|
* Resolves a tile-relative path to the final request URL.
|
|
226
237
|
*/
|
|
@@ -239,7 +250,11 @@ export interface Tileset3DSource {
|
|
|
239
250
|
/**
|
|
240
251
|
* Loads child tile metadata on demand for formats that do not expose the full tree up front.
|
|
241
252
|
*/
|
|
242
|
-
loadChildTileHeader?(
|
|
253
|
+
loadChildTileHeader?(
|
|
254
|
+
parentTile: Tile3D,
|
|
255
|
+
childId: string,
|
|
256
|
+
frameState: FrameState | null
|
|
257
|
+
): Promise<any>;
|
|
243
258
|
|
|
244
259
|
/**
|
|
245
260
|
* Updates source-managed bookkeeping after a tile finishes loading.
|
|
@@ -521,6 +521,17 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
521
521
|
};
|
|
522
522
|
}
|
|
523
523
|
|
|
524
|
+
/** Loads an implicit subtree for dataset traversal without camera state. */
|
|
525
|
+
async loadTileChildrenForTraversal(
|
|
526
|
+
tile: Tile3D,
|
|
527
|
+
signal?: AbortSignal
|
|
528
|
+
): Promise<TileChildrenLoadResult> {
|
|
529
|
+
throwIfTraversalAborted(signal);
|
|
530
|
+
const result = await this.loadTileChildren(tile, {} as FrameState);
|
|
531
|
+
throwIfTraversalAborted(signal);
|
|
532
|
+
return result;
|
|
533
|
+
}
|
|
534
|
+
|
|
524
535
|
/**
|
|
525
536
|
* Returns a snapshot of implicit-subtree request, cache, and materialization counters.
|
|
526
537
|
*
|
|
@@ -939,6 +950,13 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
939
950
|
}
|
|
940
951
|
}
|
|
941
952
|
|
|
953
|
+
/** Throws a cancellation reason before or after source-managed traversal work. */
|
|
954
|
+
function throwIfTraversalAborted(signal?: AbortSignal): void {
|
|
955
|
+
if (signal?.aborted) {
|
|
956
|
+
throw signal.reason ?? new DOMException('The operation was aborted', 'AbortError');
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
942
960
|
function isTilesetRequest(input: TilesetSourceInput): input is TilesetSourceRequest {
|
|
943
961
|
return Boolean(
|
|
944
962
|
input && typeof input === 'object' && 'url' in input && 'loader' in input && !('type' in input)
|
|
@@ -8,10 +8,12 @@ import {Vector3} from '@math.gl/core';
|
|
|
8
8
|
import type {CoreAPI, Loader, LoaderOptions} from '@loaders.gl/loader-utils';
|
|
9
9
|
import type {Tile3D} from '../common/tile-3d';
|
|
10
10
|
import {Tile3D as Tile3DNode} from '../common/tile-3d';
|
|
11
|
+
import type {Tile3DContent} from '../common/tile-3d-contracts';
|
|
11
12
|
import {I3STilesetTraverser} from './i3s-tileset-traverser';
|
|
12
13
|
import type {Tileset3D} from '../common/tileset-3d';
|
|
13
14
|
import type {FrameState} from '../helpers/frame-state';
|
|
14
15
|
import type {
|
|
16
|
+
TileChildrenLoadResult,
|
|
15
17
|
TileContentLoadResult,
|
|
16
18
|
TilesetContentFormats,
|
|
17
19
|
TilesetJSON,
|
|
@@ -219,7 +221,17 @@ export class I3SSource implements Tileset3DSource {
|
|
|
219
221
|
};
|
|
220
222
|
|
|
221
223
|
tile.content = await this.loadResourceData(contentUrl, options, this.contentLoader);
|
|
222
|
-
|
|
224
|
+
const contentEntry: Tile3DContent = {
|
|
225
|
+
index: 0,
|
|
226
|
+
uri: tile.contentUrl,
|
|
227
|
+
type: tile.header.layerType,
|
|
228
|
+
payload: tile.content,
|
|
229
|
+
metadata: tile.header.metadata || null,
|
|
230
|
+
boundingVolume: tile.boundingVolume,
|
|
231
|
+
featureIds: tile.featureIdSets,
|
|
232
|
+
renderable: Boolean(tile.content)
|
|
233
|
+
};
|
|
234
|
+
return {loaded: true, contents: [tile.content], contentEntries: [contentEntry]};
|
|
223
235
|
}
|
|
224
236
|
|
|
225
237
|
/**
|
|
@@ -228,7 +240,7 @@ export class I3SSource implements Tileset3DSource {
|
|
|
228
240
|
async loadChildTileHeader(
|
|
229
241
|
_parentTile: Tile3D,
|
|
230
242
|
childId: string,
|
|
231
|
-
_frameState: FrameState
|
|
243
|
+
_frameState: FrameState | null
|
|
232
244
|
): Promise<any> {
|
|
233
245
|
const metadata = this.getMetadata();
|
|
234
246
|
if (metadata.tileset.nodePages || metadata.tileset.pointNodePages) {
|
|
@@ -253,6 +265,50 @@ export class I3SSource implements Tileset3DSource {
|
|
|
253
265
|
return await this.transformTileHeader(header);
|
|
254
266
|
}
|
|
255
267
|
|
|
268
|
+
/** Loads all declared I3S child headers without camera or visibility state. */
|
|
269
|
+
async loadTileChildrenForTraversal(
|
|
270
|
+
parentTile: Tile3D,
|
|
271
|
+
signal?: AbortSignal
|
|
272
|
+
): Promise<TileChildrenLoadResult> {
|
|
273
|
+
const childHeaders = parentTile.header.children || [];
|
|
274
|
+
let loadedChildren = 0;
|
|
275
|
+
const orderedChildren: Tile3D[] = [];
|
|
276
|
+
|
|
277
|
+
for (const childHeader of childHeaders) {
|
|
278
|
+
throwIfTraversalAborted(signal);
|
|
279
|
+
const childId = childHeader.id;
|
|
280
|
+
if (typeof childId !== 'string' && typeof childId !== 'number') {
|
|
281
|
+
throw new Error('I3S child header is missing its node ID');
|
|
282
|
+
}
|
|
283
|
+
const existingChild = parentTile.children.find(
|
|
284
|
+
tile => tile.id === String(childId) || tile.header?.id === childId
|
|
285
|
+
);
|
|
286
|
+
if (existingChild) {
|
|
287
|
+
orderedChildren.push(existingChild);
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const childTileHeader = await this.loadChildTileHeader(parentTile, String(childId), null);
|
|
292
|
+
throwIfTraversalAborted(signal);
|
|
293
|
+
const childTile = new Tile3DNode(
|
|
294
|
+
parentTile.tileset,
|
|
295
|
+
childTileHeader,
|
|
296
|
+
parentTile,
|
|
297
|
+
String(childId)
|
|
298
|
+
);
|
|
299
|
+
orderedChildren.push(childTile);
|
|
300
|
+
loadedChildren++;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const orderedChildSet = new Set(orderedChildren);
|
|
304
|
+
parentTile.children = [
|
|
305
|
+
...orderedChildren,
|
|
306
|
+
...parentTile.children.filter(childTile => !orderedChildSet.has(childTile))
|
|
307
|
+
];
|
|
308
|
+
|
|
309
|
+
return {loaded: true, tileCount: loadedChildren, childSubtreeCount: 0};
|
|
310
|
+
}
|
|
311
|
+
|
|
256
312
|
/**
|
|
257
313
|
* Resolves an I3S request URL with source-managed query parameters.
|
|
258
314
|
*/
|
|
@@ -265,24 +321,28 @@ export class I3SSource implements Tileset3DSource {
|
|
|
265
321
|
return tilePath;
|
|
266
322
|
}
|
|
267
323
|
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
324
|
+
const hashIndex = tilePath.indexOf('#');
|
|
325
|
+
const hash = hashIndex >= 0 ? tilePath.slice(hashIndex) : '';
|
|
326
|
+
const tilePathWithoutHash = hashIndex >= 0 ? tilePath.slice(0, hashIndex) : tilePath;
|
|
327
|
+
const queryDelimiterIndex = tilePathWithoutHash.indexOf('?');
|
|
328
|
+
const path =
|
|
329
|
+
queryDelimiterIndex === -1
|
|
330
|
+
? tilePathWithoutHash
|
|
331
|
+
: tilePathWithoutHash.slice(0, queryDelimiterIndex);
|
|
332
|
+
const existingQuery =
|
|
333
|
+
queryDelimiterIndex === -1 ? '' : tilePathWithoutHash.slice(queryDelimiterIndex + 1);
|
|
334
|
+
const existingQueryParams = new URLSearchParams(existingQuery);
|
|
277
335
|
const queryParams = new URLSearchParams();
|
|
278
336
|
for (const [key, value] of Object.entries(this.queryParams)) {
|
|
279
|
-
if (!
|
|
337
|
+
if (!existingQueryParams.has(key)) {
|
|
280
338
|
queryParams.set(key, value);
|
|
281
339
|
}
|
|
282
340
|
}
|
|
283
341
|
|
|
284
342
|
const queryString = queryParams.toString();
|
|
285
|
-
return
|
|
343
|
+
return `${path}${existingQuery ? `?${existingQuery}` : ''}${
|
|
344
|
+
queryString ? `${existingQuery ? '&' : '?'}${queryString}` : ''
|
|
345
|
+
}${hash}`;
|
|
286
346
|
}
|
|
287
347
|
|
|
288
348
|
/**
|
|
@@ -403,6 +463,10 @@ export class I3SSource implements Tileset3DSource {
|
|
|
403
463
|
* @param loadOptions Loader options that may contain an ArcGIS token.
|
|
404
464
|
*/
|
|
405
465
|
private initializeQueryParams(loadOptions: LoaderOptions): void {
|
|
466
|
+
for (const [key, value] of Object.entries(loadOptions.searchParams || {})) {
|
|
467
|
+
this.queryParams[key] = String(value);
|
|
468
|
+
}
|
|
469
|
+
|
|
406
470
|
const i3sOptions = loadOptions.i3s;
|
|
407
471
|
if (i3sOptions && typeof i3sOptions === 'object' && 'token' in i3sOptions) {
|
|
408
472
|
const token = (i3sOptions as Record<string, unknown>).token;
|
|
@@ -449,6 +513,13 @@ export class I3SSource implements Tileset3DSource {
|
|
|
449
513
|
}
|
|
450
514
|
}
|
|
451
515
|
|
|
516
|
+
/** Throws a cancellation reason before or after source-managed traversal work. */
|
|
517
|
+
function throwIfTraversalAborted(signal?: AbortSignal): void {
|
|
518
|
+
if (signal?.aborted) {
|
|
519
|
+
throw signal.reason ?? new DOMException('The operation was aborted', 'AbortError');
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
452
523
|
/** Returns the format-provided binary content loader, falling back for custom legacy loaders. */
|
|
453
524
|
function getI3SContentLoader(loader: Loader): Loader {
|
|
454
525
|
return (loader as Loader & {contentLoader?: Loader}).contentLoader || loader;
|
|
@@ -38,7 +38,8 @@ export class I3STilesetTraverser extends TilesetTraverser {
|
|
|
38
38
|
for (const child of children) {
|
|
39
39
|
const extendedId = `${child.id}-${frameState.viewport.id}`;
|
|
40
40
|
// if child tile is not fetched
|
|
41
|
-
const childTile =
|
|
41
|
+
const childTile =
|
|
42
|
+
childTiles && childTiles.find(t => t.id === extendedId || t.header?.id === child.id);
|
|
42
43
|
if (!childTile) {
|
|
43
44
|
const request = () => this._loadTile(tile, child.id, frameState);
|
|
44
45
|
const cachedRequest = this._tileManager.find(extendedId);
|
|
@@ -30,7 +30,8 @@ export type FrameState = {
|
|
|
30
30
|
/** Optional world-space clipping planes applied to render-content culling. */
|
|
31
31
|
clippingPlanes?: Plane[];
|
|
32
32
|
frameNumber: number; // TODO: This can be the same between updates, what number is unique for between updates?
|
|
33
|
-
|
|
33
|
+
/** Perspective projection factor: twice the tangent of half the vertical field of view. */
|
|
34
|
+
sseDenominator: number;
|
|
34
35
|
/** View-dependent density used by perspective dynamic screen-space error for this traversal. */
|
|
35
36
|
dynamicScreenSpaceErrorDensity: number;
|
|
36
37
|
};
|
|
@@ -151,6 +152,11 @@ export function getFrameState(
|
|
|
151
152
|
pitch: 0
|
|
152
153
|
});
|
|
153
154
|
|
|
155
|
+
const verticalFieldOfView =
|
|
156
|
+
Number.isFinite(viewport.fovy) && Number(viewport.fovy) > 0 && Number(viewport.fovy) < 180
|
|
157
|
+
? (Number(viewport.fovy) * Math.PI) / 180
|
|
158
|
+
: Math.PI / 3;
|
|
159
|
+
|
|
154
160
|
// TODO: make a file/class for frameState and document what needs to be attached to this so that traversal can function
|
|
155
161
|
return {
|
|
156
162
|
camera: {
|
|
@@ -158,10 +164,7 @@ export function getFrameState(
|
|
|
158
164
|
direction: cameraDirectionCartesian,
|
|
159
165
|
up: cameraUpCartesian,
|
|
160
166
|
cartographicPosition: cameraPositionCartographic,
|
|
161
|
-
verticalFieldOfView
|
|
162
|
-
Number.isFinite(viewport.fovy) && Number(viewport.fovy) > 0
|
|
163
|
-
? (Number(viewport.fovy) * Math.PI) / 180
|
|
164
|
-
: Math.PI / 3,
|
|
167
|
+
verticalFieldOfView,
|
|
165
168
|
timeSinceMovement: options.timeSinceCameraMovement ?? Number.POSITIVE_INFINITY
|
|
166
169
|
},
|
|
167
170
|
viewport,
|
|
@@ -170,7 +173,7 @@ export function getFrameState(
|
|
|
170
173
|
cullingVolume,
|
|
171
174
|
clippingPlanes: viewport.clippingPlanes,
|
|
172
175
|
frameNumber, // TODO: This can be the same between updates, what number is unique for between updates?
|
|
173
|
-
sseDenominator:
|
|
176
|
+
sseDenominator: 2 * Math.tan(verticalFieldOfView / 2),
|
|
174
177
|
// Tileset3D fills this immediately before traversal because it depends on the root volume.
|
|
175
178
|
dynamicScreenSpaceErrorDensity: 0
|
|
176
179
|
};
|