@loaders.gl/tiles 5.0.0-alpha.2 → 5.0.0-alpha.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/LICENSE +1 -1
- package/dist/dist.dev.js +17325 -5650
- package/dist/dist.min.js +4 -1
- package/dist/index.cjs +1924 -178
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/point-cloud/point-cloud-tileset.d.ts +4 -0
- package/dist/point-cloud/point-cloud-tileset.d.ts.map +1 -1
- package/dist/point-cloud/point-cloud-tileset.js +51 -12
- package/dist/point-cloud/point-cloud-tileset.js.map +1 -1
- package/dist/point-cloud/types.d.ts +17 -0
- package/dist/point-cloud/types.d.ts.map +1 -1
- package/dist/raster-set.d.ts +10 -8
- package/dist/raster-set.d.ts.map +1 -1
- package/dist/raster-set.js +18 -1
- package/dist/raster-set.js.map +1 -1
- package/dist/spatial/format-spatial-reference.d.ts +67 -0
- package/dist/spatial/format-spatial-reference.d.ts.map +1 -0
- package/dist/spatial/format-spatial-reference.js +233 -0
- package/dist/spatial/format-spatial-reference.js.map +1 -0
- package/dist/spatial/get-spatial-coordinate-frame.d.ts +9 -0
- package/dist/spatial/get-spatial-coordinate-frame.d.ts.map +1 -0
- package/dist/spatial/get-spatial-coordinate-frame.js +58 -0
- package/dist/spatial/get-spatial-coordinate-frame.js.map +1 -0
- package/dist/spatial/i3s-elevation.d.ts +20 -0
- package/dist/spatial/i3s-elevation.d.ts.map +1 -0
- package/dist/spatial/i3s-elevation.js +90 -0
- package/dist/spatial/i3s-elevation.js.map +1 -0
- package/dist/spatial/i3s-spatial-transformer.d.ts +189 -0
- package/dist/spatial/i3s-spatial-transformer.d.ts.map +1 -0
- package/dist/spatial/i3s-spatial-transformer.js +686 -0
- package/dist/spatial/i3s-spatial-transformer.js.map +1 -0
- package/dist/spatial/spatial-coordinate-transformer.d.ts +48 -0
- package/dist/spatial/spatial-coordinate-transformer.d.ts.map +1 -0
- package/dist/spatial/spatial-coordinate-transformer.js +211 -0
- package/dist/spatial/spatial-coordinate-transformer.js.map +1 -0
- package/dist/spatial/spatial-resource-registry.d.ts +44 -0
- package/dist/spatial/spatial-resource-registry.d.ts.map +1 -0
- package/dist/spatial/spatial-resource-registry.js +67 -0
- package/dist/spatial/spatial-resource-registry.js.map +1 -0
- package/dist/spatial/spatial-types.d.ts +190 -0
- package/dist/spatial/spatial-types.d.ts.map +1 -0
- package/dist/spatial/spatial-types.js +192 -0
- package/dist/spatial/spatial-types.js.map +1 -0
- package/dist/tileset-3d/common/tile-3d.d.ts +49 -1
- package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
- package/dist/tileset-3d/common/tile-3d.js +132 -64
- package/dist/tileset-3d/common/tile-3d.js.map +1 -1
- package/dist/tileset-3d/common/tileset-3d.d.ts +25 -0
- package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-3d.js +49 -7
- package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
- package/dist/tileset-3d/common/tileset-source.d.ts +14 -0
- 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/common/tileset-traverser.d.ts +6 -1
- package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-traverser.js +11 -6
- package/dist/tileset-3d/common/tileset-traverser.js.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts +26 -1
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js +62 -18
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +1 -7
- 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 +32 -55
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-source.d.ts +14 -1
- package/dist/tileset-3d/format-i3s/i3s-source.d.ts.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-source.js +107 -17
- package/dist/tileset-3d/format-i3s/i3s-source.js.map +1 -1
- package/dist/tileset-3d/helpers/bounding-volume.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/bounding-volume.js +3 -33
- package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -1
- package/dist/tileset-3d/helpers/frame-state.d.ts +3 -1
- package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/frame-state.js +1 -0
- package/dist/tileset-3d/helpers/frame-state.js.map +1 -1
- package/dist/tileset-3d/helpers/i3s-lod.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/i3s-lod.js +20 -8
- package/dist/tileset-3d/helpers/i3s-lod.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +12 -9
- package/src/index.ts +42 -1
- package/src/point-cloud/point-cloud-tileset.ts +66 -12
- package/src/point-cloud/types.ts +17 -0
- package/src/raster-set.ts +46 -11
- package/src/spatial/format-spatial-reference.ts +311 -0
- package/src/spatial/get-spatial-coordinate-frame.ts +74 -0
- package/src/spatial/i3s-elevation.ts +96 -0
- package/src/spatial/i3s-spatial-transformer.ts +957 -0
- package/src/spatial/spatial-coordinate-transformer.ts +276 -0
- package/src/spatial/spatial-resource-registry.ts +79 -0
- package/src/spatial/spatial-types.ts +422 -0
- package/src/tileset-3d/common/tile-3d.ts +156 -69
- package/src/tileset-3d/common/tileset-3d.ts +77 -6
- package/src/tileset-3d/common/tileset-source.ts +15 -0
- package/src/tileset-3d/common/tileset-traverser.ts +11 -6
- package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +100 -22
- package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +36 -56
- package/src/tileset-3d/format-i3s/i3s-source.ts +119 -18
- package/src/tileset-3d/helpers/bounding-volume.ts +3 -55
- package/src/tileset-3d/helpers/frame-state.ts +3 -0
- package/src/tileset-3d/helpers/i3s-lod.ts +22 -8
- package/src/types.ts +3 -0
|
@@ -23,8 +23,12 @@ export type ImplicitTileCoordinates = {
|
|
|
23
23
|
export type ImplicitTilingDescriptor = {
|
|
24
24
|
/** Absolute template URL for render content, or an empty string for a contentless hierarchy. */
|
|
25
25
|
contentUrlTemplate: string;
|
|
26
|
+
/** Absolute templates for every implicit content stream, in source order. */
|
|
27
|
+
contentUrlTemplates?: string[];
|
|
26
28
|
/** Non-URI content metadata inherited by each available implicit content resource. */
|
|
27
29
|
contentHeader?: Record<string, any>;
|
|
30
|
+
/** Metadata inherited by every implicit content stream, in source order. */
|
|
31
|
+
contentHeaders?: Array<Record<string, any>>;
|
|
28
32
|
/** Absolute template URL for subtree availability files. */
|
|
29
33
|
subtreesUrlTemplate: string;
|
|
30
34
|
/** Spatial subdivision used by the hierarchy. */
|
|
@@ -66,12 +70,20 @@ export type ParsedImplicitSubtree = {
|
|
|
66
70
|
/** Availability of tiles inside this subtree. */
|
|
67
71
|
tileAvailability: ImplicitAvailability;
|
|
68
72
|
/**
|
|
69
|
-
* Availability of tile content
|
|
73
|
+
* Availability of tile content. Array entries correspond to content templates in source order.
|
|
70
74
|
* Omitted availability denotes a metadata-only subtree with no render content.
|
|
71
75
|
*/
|
|
72
76
|
contentAvailability?: ImplicitAvailability | ImplicitAvailability[];
|
|
73
77
|
/** Availability of subtree roots immediately below this subtree. */
|
|
74
78
|
childSubtreeAvailability: ImplicitAvailability;
|
|
79
|
+
/** Property-table payloads referenced by subtree metadata declarations. */
|
|
80
|
+
propertyTables?: unknown;
|
|
81
|
+
/** Property-table reference for metadata attached to each available tile. */
|
|
82
|
+
tileMetadata?: unknown;
|
|
83
|
+
/** Property-table references for metadata attached to each content stream. */
|
|
84
|
+
contentMetadata?: unknown;
|
|
85
|
+
/** Metadata entity attached to the subtree itself. */
|
|
86
|
+
subtreeMetadata?: unknown;
|
|
75
87
|
};
|
|
76
88
|
|
|
77
89
|
/** Runtime header produced for an available implicit tile. */
|
|
@@ -82,6 +94,20 @@ export type ImplicitTileHeader = Record<string, any> & {
|
|
|
82
94
|
children: ImplicitTileHeader[];
|
|
83
95
|
/** Optional lazy reference when this tile begins a child subtree. */
|
|
84
96
|
implicitSubtree?: ImplicitSubtreeReference;
|
|
97
|
+
/** Raw subtree metadata references preserved for application-level interpretation. */
|
|
98
|
+
implicitMetadata?: ImplicitSubtreeMetadata;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/** Metadata references inherited by generated implicit tile headers. */
|
|
102
|
+
export type ImplicitSubtreeMetadata = {
|
|
103
|
+
/** Property tables declared by the subtree. */
|
|
104
|
+
propertyTables?: unknown;
|
|
105
|
+
/** Tile-level metadata property-table reference. */
|
|
106
|
+
tileMetadata?: unknown;
|
|
107
|
+
/** Content-level metadata property-table references in content order. */
|
|
108
|
+
contentMetadata?: unknown;
|
|
109
|
+
/** Subtree-level metadata entity. */
|
|
110
|
+
subtreeMetadata?: unknown;
|
|
85
111
|
};
|
|
86
112
|
|
|
87
113
|
/** Result of materializing exactly one subtree resource. */
|
|
@@ -198,8 +224,8 @@ function materializeAvailableTile(
|
|
|
198
224
|
}
|
|
199
225
|
|
|
200
226
|
counters.tileCount++;
|
|
201
|
-
const
|
|
202
|
-
|
|
227
|
+
const contentAvailability = getContentAvailability(
|
|
228
|
+
subtree.contentAvailability,
|
|
203
229
|
availabilityIndex
|
|
204
230
|
);
|
|
205
231
|
const children: ImplicitTileHeader[] = [];
|
|
@@ -235,7 +261,7 @@ function materializeAvailableTile(
|
|
|
235
261
|
childIndex,
|
|
236
262
|
descriptor.subdivisionScheme
|
|
237
263
|
);
|
|
238
|
-
children.push(createLazyImplicitTileHeader(descriptor, childCoordinates));
|
|
264
|
+
children.push(createLazyImplicitTileHeader(subtree, descriptor, childCoordinates));
|
|
239
265
|
counters.childSubtreeCount++;
|
|
240
266
|
}
|
|
241
267
|
}
|
|
@@ -243,10 +269,11 @@ function materializeAvailableTile(
|
|
|
243
269
|
}
|
|
244
270
|
|
|
245
271
|
return formatImplicitTileHeader(
|
|
272
|
+
subtree,
|
|
246
273
|
descriptor,
|
|
247
274
|
reference,
|
|
248
275
|
globalCoordinates,
|
|
249
|
-
|
|
276
|
+
contentAvailability,
|
|
250
277
|
children
|
|
251
278
|
);
|
|
252
279
|
}
|
|
@@ -259,6 +286,7 @@ function materializeAvailableTile(
|
|
|
259
286
|
* @returns Header that participates in culling and SSE before its subtree is requested.
|
|
260
287
|
*/
|
|
261
288
|
function createLazyImplicitTileHeader(
|
|
289
|
+
subtree: ParsedImplicitSubtree,
|
|
262
290
|
descriptor: ImplicitTilingDescriptor,
|
|
263
291
|
coordinates: ImplicitTileCoordinates
|
|
264
292
|
): ImplicitTileHeader {
|
|
@@ -276,7 +304,8 @@ function createLazyImplicitTileHeader(
|
|
|
276
304
|
lodMetricType: descriptor.lodMetricType,
|
|
277
305
|
lodMetricValue: descriptor.rootLodMetricValue / 2 ** coordinates.level,
|
|
278
306
|
refine: descriptor.refine,
|
|
279
|
-
type: TILE_TYPE.EMPTY
|
|
307
|
+
type: TILE_TYPE.EMPTY,
|
|
308
|
+
implicitMetadata: getImplicitSubtreeMetadata(subtree)
|
|
280
309
|
};
|
|
281
310
|
}
|
|
282
311
|
|
|
@@ -286,28 +315,48 @@ function createLazyImplicitTileHeader(
|
|
|
286
315
|
* @param descriptor - Shared implicit hierarchy description.
|
|
287
316
|
* @param reference - Current subtree reference used to build stable IDs.
|
|
288
317
|
* @param coordinates - Global tile coordinates.
|
|
289
|
-
* @param
|
|
318
|
+
* @param contentAvailability - Availability for each content stream in source order.
|
|
290
319
|
* @param children - Materialized or lazy child headers.
|
|
291
320
|
* @returns Runtime tile header.
|
|
292
321
|
*/
|
|
293
322
|
function formatImplicitTileHeader(
|
|
323
|
+
subtree: ParsedImplicitSubtree,
|
|
294
324
|
descriptor: ImplicitTilingDescriptor,
|
|
295
325
|
reference: ImplicitSubtreeReference,
|
|
296
326
|
coordinates: ImplicitTileCoordinates,
|
|
297
|
-
|
|
327
|
+
contentAvailability: boolean[],
|
|
298
328
|
children: ImplicitTileHeader[]
|
|
299
329
|
): ImplicitTileHeader {
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
330
|
+
const contentUrlTemplates = descriptor.contentUrlTemplates?.length
|
|
331
|
+
? descriptor.contentUrlTemplates
|
|
332
|
+
: [descriptor.contentUrlTemplate];
|
|
333
|
+
const contentEntries = contentUrlTemplates
|
|
334
|
+
.map((templateUrl, contentIndex) => {
|
|
335
|
+
if (!contentAvailability[contentIndex] || !templateUrl) {
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
contentIndex,
|
|
340
|
+
contentUrl: replaceImplicitUrlTemplate(templateUrl, coordinates)
|
|
341
|
+
};
|
|
342
|
+
})
|
|
343
|
+
.filter((entry): entry is {contentIndex: number; contentUrl: string} => Boolean(entry));
|
|
344
|
+
const availableContentUrls = contentEntries.map(entry => entry.contentUrl);
|
|
345
|
+
const content = contentEntries.map(({contentIndex, contentUrl}) => ({
|
|
346
|
+
...(descriptor.contentHeaders?.[contentIndex] ||
|
|
347
|
+
(contentIndex === 0 ? descriptor.contentHeader : {}) ||
|
|
348
|
+
{}),
|
|
349
|
+
uri: contentUrl
|
|
350
|
+
}));
|
|
351
|
+
const contentUrl = availableContentUrls[0];
|
|
304
352
|
const lodMetricValue = descriptor.rootLodMetricValue / 2 ** coordinates.level;
|
|
305
353
|
|
|
306
354
|
return {
|
|
307
355
|
id: getImplicitTileId(reference, coordinates),
|
|
308
356
|
children,
|
|
309
357
|
contentUrl,
|
|
310
|
-
content:
|
|
358
|
+
content: content.length > 1 ? content : content[0],
|
|
359
|
+
contentUrls: availableContentUrls,
|
|
311
360
|
refine: descriptor.refine,
|
|
312
361
|
type: getImplicitTileType(contentUrl),
|
|
313
362
|
lodMetricType: descriptor.lodMetricType,
|
|
@@ -317,7 +366,36 @@ function formatImplicitTileHeader(
|
|
|
317
366
|
descriptor.rootBoundingVolume,
|
|
318
367
|
coordinates,
|
|
319
368
|
descriptor.subdivisionScheme
|
|
320
|
-
)
|
|
369
|
+
),
|
|
370
|
+
implicitMetadata: getImplicitSubtreeMetadata(subtree)
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Copies subtree metadata references without interpreting property-table schemas.
|
|
376
|
+
*
|
|
377
|
+
* Keeping the references on generated headers makes metadata available to applications while
|
|
378
|
+
* avoiding a false promise that the runtime has already decoded classes, enums, or values.
|
|
379
|
+
*
|
|
380
|
+
* @param subtree - Parsed subtree containing optional metadata declarations.
|
|
381
|
+
* @returns Metadata references, or `undefined` when the subtree declares none.
|
|
382
|
+
*/
|
|
383
|
+
function getImplicitSubtreeMetadata(
|
|
384
|
+
subtree: ParsedImplicitSubtree
|
|
385
|
+
): ImplicitSubtreeMetadata | undefined {
|
|
386
|
+
if (
|
|
387
|
+
!subtree.propertyTables &&
|
|
388
|
+
subtree.tileMetadata === undefined &&
|
|
389
|
+
!subtree.contentMetadata &&
|
|
390
|
+
subtree.subtreeMetadata === undefined
|
|
391
|
+
) {
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
394
|
+
return {
|
|
395
|
+
propertyTables: subtree.propertyTables,
|
|
396
|
+
tileMetadata: subtree.tileMetadata,
|
|
397
|
+
contentMetadata: subtree.contentMetadata,
|
|
398
|
+
subtreeMetadata: subtree.subtreeMetadata
|
|
321
399
|
};
|
|
322
400
|
}
|
|
323
401
|
|
|
@@ -356,17 +434,17 @@ function getAvailabilityValue(availability: ImplicitAvailability, index: number)
|
|
|
356
434
|
}
|
|
357
435
|
|
|
358
436
|
/**
|
|
359
|
-
*
|
|
437
|
+
* Reads every content availability stream for one tile in descriptor/source order.
|
|
360
438
|
*
|
|
361
439
|
* @param availability - Single or multiple-content availability declaration.
|
|
362
|
-
* @returns
|
|
440
|
+
* @returns Availability flags aligned with the content URL templates.
|
|
363
441
|
*/
|
|
364
|
-
function
|
|
365
|
-
availability: ImplicitAvailability | ImplicitAvailability[] | undefined
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
442
|
+
function getContentAvailability(
|
|
443
|
+
availability: ImplicitAvailability | ImplicitAvailability[] | undefined,
|
|
444
|
+
index: number
|
|
445
|
+
): boolean[] {
|
|
446
|
+
const streams = Array.isArray(availability) ? availability : [availability];
|
|
447
|
+
return streams.map(stream => getAvailabilityValue(stream || {constant: 0}, index));
|
|
370
448
|
}
|
|
371
449
|
|
|
372
450
|
/**
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {path} from '@loaders.gl/loader-utils';
|
|
5
|
+
import {path, RequestCache} from '@loaders.gl/loader-utils';
|
|
6
6
|
import {Ellipsoid} from '@math.gl/geospatial';
|
|
7
7
|
import {Vector3} from '@math.gl/core';
|
|
8
8
|
import type {CoreAPI, LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils';
|
|
@@ -26,6 +26,7 @@ import {getZoomFromBoundingVolume} from '../helpers/zoom';
|
|
|
26
26
|
import {TILESET_TYPE} from '../../constants';
|
|
27
27
|
import type {TilesetTraverser, TilesetTraverserProps} from '../common/tileset-traverser';
|
|
28
28
|
import type {FrameState} from '../helpers/frame-state';
|
|
29
|
+
import {get3DTilesSpatialReference} from '../../spatial/format-spatial-reference';
|
|
29
30
|
import {
|
|
30
31
|
materializeImplicitSubtree,
|
|
31
32
|
type ImplicitSubtreeReference,
|
|
@@ -92,12 +93,8 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
92
93
|
private readonly queryParams: Record<string, string> = {};
|
|
93
94
|
/** Final request URLs cached by unmodified tile content URL. */
|
|
94
95
|
private readonly tileUrlCache: Map<string, string> = new Map();
|
|
95
|
-
/** Parsed subtree
|
|
96
|
-
private readonly implicitSubtreeCache:
|
|
97
|
-
/** URLs whose subtree resource requests have not settled. */
|
|
98
|
-
private readonly pendingImplicitSubtreeUrls: Set<string> = new Set();
|
|
99
|
-
/** Maximum number of settled parsed subtrees retained by this source. */
|
|
100
|
-
private readonly maximumCachedSubtrees: number;
|
|
96
|
+
/** Parsed subtree requests keyed by final source URL for deduplication and LRU reuse. */
|
|
97
|
+
private readonly implicitSubtreeCache: RequestCache<ParsedImplicitSubtree>;
|
|
101
98
|
/** Mutable counters exposed as a defensive snapshot through {@link getImplicitTilingStats}. */
|
|
102
99
|
private readonly implicitTilingStats: Omit<
|
|
103
100
|
ImplicitTilingStats,
|
|
@@ -132,9 +129,10 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
132
129
|
const maximumCachedSubtrees = Number(
|
|
133
130
|
(loadOptions['3d-tiles'] as Record<string, unknown> | undefined)?.maximumCachedSubtrees
|
|
134
131
|
);
|
|
135
|
-
|
|
132
|
+
const maximumCacheEntries = Number.isFinite(maximumCachedSubtrees)
|
|
136
133
|
? Math.max(0, Math.floor(maximumCachedSubtrees))
|
|
137
134
|
: DEFAULT_MAXIMUM_CACHED_SUBTREES;
|
|
135
|
+
this.implicitSubtreeCache = new RequestCache({maxEntries: maximumCacheEntries});
|
|
138
136
|
}
|
|
139
137
|
|
|
140
138
|
/**
|
|
@@ -147,7 +145,6 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
147
145
|
this.destroyed = true;
|
|
148
146
|
this.tileUrlCache.clear();
|
|
149
147
|
this.implicitSubtreeCache.clear();
|
|
150
|
-
this.pendingImplicitSubtreeUrls.clear();
|
|
151
148
|
}
|
|
152
149
|
|
|
153
150
|
/**
|
|
@@ -205,7 +202,8 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
205
202
|
tileset: this.rootTileset,
|
|
206
203
|
lodMetricType: this.rootTileset.lodMetricType,
|
|
207
204
|
lodMetricValue: this.rootTileset.lodMetricValue,
|
|
208
|
-
refine: this.rootTileset.root?.refine
|
|
205
|
+
refine: this.rootTileset.root?.refine,
|
|
206
|
+
spatialReference: get3DTilesSpatialReference(this.rootTileset)
|
|
209
207
|
};
|
|
210
208
|
}
|
|
211
209
|
|
|
@@ -275,7 +273,7 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
275
273
|
* Loads binary content or nested tileset JSON for a runtime tile.
|
|
276
274
|
*/
|
|
277
275
|
async loadTileContent(tile: Tile3D): Promise<TileContentLoadResult> {
|
|
278
|
-
const
|
|
276
|
+
const contentUrls = (tile.contentUrls || [tile.contentUrl]).filter(Boolean);
|
|
279
277
|
const tilesetLoaderOptions =
|
|
280
278
|
(this.loadOptions[this.loader.id] as Record<string, unknown>) || {};
|
|
281
279
|
const options = {
|
|
@@ -289,12 +287,18 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
289
287
|
}
|
|
290
288
|
};
|
|
291
289
|
|
|
292
|
-
const
|
|
293
|
-
|
|
290
|
+
const contents = await Promise.all(
|
|
291
|
+
contentUrls.map(contentUrl => this.loadResourceData(this.getTileUrl(contentUrl), options))
|
|
292
|
+
);
|
|
293
|
+
tile.contents = contents;
|
|
294
|
+
tile.content = contents[0] || null;
|
|
295
|
+
const nestedTilesets = contents.filter(content => content?.shape === 'tileset3d');
|
|
294
296
|
|
|
295
297
|
return {
|
|
296
298
|
loaded: true,
|
|
297
|
-
|
|
299
|
+
contents,
|
|
300
|
+
nestedTileset: nestedTilesets[0],
|
|
301
|
+
nestedTilesets
|
|
298
302
|
};
|
|
299
303
|
}
|
|
300
304
|
|
|
@@ -350,8 +354,8 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
350
354
|
getImplicitTilingStats(): ImplicitTilingStats {
|
|
351
355
|
return {
|
|
352
356
|
...this.implicitTilingStats,
|
|
353
|
-
cachedSubtrees: this.implicitSubtreeCache.size - this.
|
|
354
|
-
pendingSubtrees: this.
|
|
357
|
+
cachedSubtrees: this.implicitSubtreeCache.size - this.implicitSubtreeCache.pendingSize,
|
|
358
|
+
pendingSubtrees: this.implicitSubtreeCache.pendingSize
|
|
355
359
|
};
|
|
356
360
|
}
|
|
357
361
|
|
|
@@ -458,7 +462,8 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
458
462
|
* Updates content-format flags and installs nested tileset subtrees.
|
|
459
463
|
*/
|
|
460
464
|
onTileLoaded(tileset: Tileset3D, tile: Tile3D, loadResult: TileContentLoadResult): void {
|
|
461
|
-
const
|
|
465
|
+
const contents = tile.contents.length ? tile.contents : [tile.content];
|
|
466
|
+
const extensionsRemoved = contents.flatMap(content => content?.gltf?.extensionsRemoved || []);
|
|
462
467
|
if (extensionsRemoved.includes('KHR_draco_mesh_compression')) {
|
|
463
468
|
this.contentFormats.draco = true;
|
|
464
469
|
}
|
|
@@ -469,8 +474,10 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
469
474
|
this.contentFormats.ktx2 = true;
|
|
470
475
|
}
|
|
471
476
|
|
|
472
|
-
|
|
473
|
-
|
|
477
|
+
const nestedTilesets =
|
|
478
|
+
loadResult.nestedTilesets || (loadResult.nestedTileset ? [loadResult.nestedTileset] : []);
|
|
479
|
+
for (const nestedTileset of nestedTilesets) {
|
|
480
|
+
tileset._initializeTileHeaders(nestedTileset, tile);
|
|
474
481
|
}
|
|
475
482
|
}
|
|
476
483
|
|
|
@@ -525,33 +532,21 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
525
532
|
const cachedSubtree = this.implicitSubtreeCache.get(subtreeUrl);
|
|
526
533
|
if (cachedSubtree) {
|
|
527
534
|
this.implicitTilingStats.cacheHits++;
|
|
528
|
-
this.implicitSubtreeCache.delete(subtreeUrl);
|
|
529
|
-
this.implicitSubtreeCache.set(subtreeUrl, cachedSubtree);
|
|
530
535
|
return await cachedSubtree;
|
|
531
536
|
}
|
|
532
537
|
|
|
533
538
|
this.implicitTilingStats.requestedSubtrees++;
|
|
534
|
-
this.pendingImplicitSubtreeUrls.add(subtreeUrl);
|
|
535
539
|
const loaderOptions = (this.loadOptions[this.loader.id] as Record<string, unknown>) || {};
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
try {
|
|
547
|
-
return await subtreePromise;
|
|
548
|
-
} catch (error) {
|
|
549
|
-
this.implicitSubtreeCache.delete(subtreeUrl);
|
|
550
|
-
throw error;
|
|
551
|
-
} finally {
|
|
552
|
-
this.pendingImplicitSubtreeUrls.delete(subtreeUrl);
|
|
553
|
-
this.trimImplicitSubtreeCache();
|
|
554
|
-
}
|
|
540
|
+
return await this.implicitSubtreeCache.getOrLoad(subtreeUrl, async () => {
|
|
541
|
+
return (await this.loadResourceData(subtreeUrl, {
|
|
542
|
+
...this.loadOptions,
|
|
543
|
+
[this.loader.id]: {
|
|
544
|
+
...loaderOptions,
|
|
545
|
+
isTileset: false,
|
|
546
|
+
isSubtree: true
|
|
547
|
+
}
|
|
548
|
+
})) as ParsedImplicitSubtree;
|
|
549
|
+
});
|
|
555
550
|
}
|
|
556
551
|
|
|
557
552
|
/**
|
|
@@ -586,21 +581,6 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
586
581
|
}
|
|
587
582
|
return materializedTileCount;
|
|
588
583
|
}
|
|
589
|
-
|
|
590
|
-
/** Evicts least-recently-used settled subtree entries until the configured bound is met. */
|
|
591
|
-
private trimImplicitSubtreeCache(): void {
|
|
592
|
-
if (this.implicitSubtreeCache.size <= this.maximumCachedSubtrees) {
|
|
593
|
-
return;
|
|
594
|
-
}
|
|
595
|
-
for (const subtreeUrl of this.implicitSubtreeCache.keys()) {
|
|
596
|
-
if (!this.pendingImplicitSubtreeUrls.has(subtreeUrl)) {
|
|
597
|
-
this.implicitSubtreeCache.delete(subtreeUrl);
|
|
598
|
-
}
|
|
599
|
-
if (this.implicitSubtreeCache.size <= this.maximumCachedSubtrees) {
|
|
600
|
-
break;
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
584
|
}
|
|
605
585
|
|
|
606
586
|
function isTilesetRequest(input: TilesetSourceInput): input is TilesetSourceRequest {
|
|
@@ -25,6 +25,8 @@ import type {
|
|
|
25
25
|
import {getZoomFromExtent, getZoomFromFullExtent} from '../helpers/zoom';
|
|
26
26
|
import {TILESET_TYPE} from '../../constants';
|
|
27
27
|
import type {TilesetTraverser, TilesetTraverserProps} from '../common/tileset-traverser';
|
|
28
|
+
import {getI3SSpatialReference} from '../../spatial/format-spatial-reference';
|
|
29
|
+
import {I3SSpatialTransformer} from '../../spatial/i3s-spatial-transformer';
|
|
28
30
|
|
|
29
31
|
const EMPTY_CONTENT_FORMATS: TilesetContentFormats = {
|
|
30
32
|
draco: false,
|
|
@@ -63,6 +65,10 @@ export class I3SSource implements Tileset3DSource {
|
|
|
63
65
|
private readonly queryParams: Record<string, string> = {};
|
|
64
66
|
private readonly resolver?: TilesetSourceResolver;
|
|
65
67
|
private rootTileset: TilesetJSON;
|
|
68
|
+
/** Spatial adapter created lazily after Tileset3D applies target options. */
|
|
69
|
+
private spatialTransformer?: I3SSpatialTransformer;
|
|
70
|
+
/** Root header prepared after asynchronous elevation sampling. */
|
|
71
|
+
private preparedRootHeader?: any;
|
|
66
72
|
|
|
67
73
|
/**
|
|
68
74
|
* Creates an I3S source.
|
|
@@ -79,6 +85,7 @@ export class I3SSource implements Tileset3DSource {
|
|
|
79
85
|
this.resolver = request.resolver;
|
|
80
86
|
this.coreApi = request.coreApi;
|
|
81
87
|
this.loadOptions = loadOptions;
|
|
88
|
+
this.initializeQueryParams(loadOptions);
|
|
82
89
|
}
|
|
83
90
|
|
|
84
91
|
/**
|
|
@@ -86,7 +93,7 @@ export class I3SSource implements Tileset3DSource {
|
|
|
86
93
|
*/
|
|
87
94
|
async initialize(): Promise<void> {
|
|
88
95
|
if (!this.rootTileset) {
|
|
89
|
-
this.rootTileset = await this.loadRootData(this.url, this.loadOptions);
|
|
96
|
+
this.rootTileset = await this.loadRootData(this.getTileUrl(this.url), this.loadOptions);
|
|
90
97
|
}
|
|
91
98
|
this.tileset = this.rootTileset;
|
|
92
99
|
|
|
@@ -94,14 +101,6 @@ export class I3SSource implements Tileset3DSource {
|
|
|
94
101
|
this.rootTileset.root = await this.rootTileset.root;
|
|
95
102
|
}
|
|
96
103
|
|
|
97
|
-
const i3sOptions = this.loadOptions.i3s;
|
|
98
|
-
if (i3sOptions && typeof i3sOptions === 'object' && 'token' in i3sOptions) {
|
|
99
|
-
const token = (i3sOptions as Record<string, unknown>).token;
|
|
100
|
-
if (typeof token === 'string') {
|
|
101
|
-
this.queryParams.token = token;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
104
|
this.metadata = {
|
|
106
105
|
type: this.type,
|
|
107
106
|
loader: this.loader,
|
|
@@ -110,7 +109,8 @@ export class I3SSource implements Tileset3DSource {
|
|
|
110
109
|
tileset: this.rootTileset,
|
|
111
110
|
lodMetricType: this.rootTileset.lodMetricType,
|
|
112
111
|
lodMetricValue: this.rootTileset.lodMetricValue,
|
|
113
|
-
refine: this.rootTileset.root?.refine
|
|
112
|
+
refine: this.rootTileset.root?.refine,
|
|
113
|
+
spatialReference: getI3SSpatialReference(this.rootTileset)
|
|
114
114
|
};
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -131,6 +131,26 @@ export class I3SSource implements Tileset3DSource {
|
|
|
131
131
|
return this.getMetadata().tileset;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
/** Prepare option-dependent spatial state and the asynchronously placed root bound. */
|
|
135
|
+
async prepareTileset(tileset: Tileset3D): Promise<void> {
|
|
136
|
+
const spatialReference = tileset.spatialReference;
|
|
137
|
+
if (spatialReference.status === 'unresolved') {
|
|
138
|
+
throw new Error(
|
|
139
|
+
spatialReference.warnings[0] ||
|
|
140
|
+
'I3S spatial operations cannot be resolved from the supplied metadata and options'
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
if (spatialReference.status !== 'transformable' && spatialReference.status !== 'transformed') {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
this.spatialTransformer ||= new I3SSpatialTransformer(
|
|
147
|
+
spatialReference,
|
|
148
|
+
tileset.options.spatial
|
|
149
|
+
);
|
|
150
|
+
tileset.spatialReference = this.spatialTransformer.spatialReference;
|
|
151
|
+
this.preparedRootHeader = await this.transformTileHeader(this.getMetadata().tileset.root);
|
|
152
|
+
}
|
|
153
|
+
|
|
134
154
|
/**
|
|
135
155
|
* Creates the runtime root tile for an I3S subtree.
|
|
136
156
|
*/
|
|
@@ -139,7 +159,11 @@ export class I3SSource implements Tileset3DSource {
|
|
|
139
159
|
tilesetJson: TilesetJSON,
|
|
140
160
|
parentTile?: Tile3D | null
|
|
141
161
|
): Tile3D {
|
|
142
|
-
const
|
|
162
|
+
const rootHeader = parentTile ? tilesetJson.root : this.preparedRootHeader || tilesetJson.root;
|
|
163
|
+
if (this.spatialTransformer && !this.preparedRootHeader) {
|
|
164
|
+
throw new Error('I3S spatial root header was not prepared before tile initialization');
|
|
165
|
+
}
|
|
166
|
+
const rootTile = new Tile3DNode(tileset, rootHeader, parentTile || undefined);
|
|
143
167
|
if (parentTile) {
|
|
144
168
|
parentTile.children.push(rootTile);
|
|
145
169
|
rootTile.depth = parentTile.depth + 1;
|
|
@@ -170,16 +194,22 @@ export class I3SSource implements Tileset3DSource {
|
|
|
170
194
|
_tileOptions: {
|
|
171
195
|
attributeUrls: tile.header.attributeUrls,
|
|
172
196
|
textureUrl: tile.header.textureUrl,
|
|
197
|
+
textureUrls: tile.header.textureUrls,
|
|
173
198
|
textureFormat: tile.header.textureFormat,
|
|
174
199
|
textureLoaderOptions: tile.header.textureLoaderOptions,
|
|
175
200
|
materialDefinition: tile.header.materialDefinition,
|
|
201
|
+
layerType: tile.header.layerType,
|
|
202
|
+
pointRenderer: tile.header.pointRenderer,
|
|
203
|
+
pointSymbol: tile.header.pointSymbol,
|
|
176
204
|
isDracoGeometry: tile.header.isDracoGeometry,
|
|
177
205
|
mbs: tile.header.mbs
|
|
178
206
|
},
|
|
179
207
|
_tilesetOptions: {
|
|
180
208
|
store: metadata.tileset.store,
|
|
181
209
|
attributeStorageInfo: metadata.tileset.attributeStorageInfo,
|
|
182
|
-
fields: metadata.tileset.fields
|
|
210
|
+
fields: metadata.tileset.fields,
|
|
211
|
+
spatialReference: tile.tileset.spatialReference,
|
|
212
|
+
spatialOptions: tile.tileset.options.spatial
|
|
183
213
|
},
|
|
184
214
|
isTileHeader: false
|
|
185
215
|
}
|
|
@@ -198,8 +228,9 @@ export class I3SSource implements Tileset3DSource {
|
|
|
198
228
|
_frameState: FrameState
|
|
199
229
|
): Promise<any> {
|
|
200
230
|
const metadata = this.getMetadata();
|
|
201
|
-
if (metadata.tileset.nodePages) {
|
|
202
|
-
|
|
231
|
+
if (metadata.tileset.nodePages || metadata.tileset.pointNodePages) {
|
|
232
|
+
const header = await metadata.tileset.nodePagesTile.formTileFromNodePages(childId);
|
|
233
|
+
return await this.transformTileHeader(header);
|
|
203
234
|
}
|
|
204
235
|
|
|
205
236
|
const nodeUrl = this.getTileUrl(`${this.url}/nodes/${childId}`);
|
|
@@ -215,7 +246,8 @@ export class I3SSource implements Tileset3DSource {
|
|
|
215
246
|
}
|
|
216
247
|
};
|
|
217
248
|
|
|
218
|
-
|
|
249
|
+
const header = await this.loadResourceData(nodeUrl, options);
|
|
250
|
+
return await this.transformTileHeader(header);
|
|
219
251
|
}
|
|
220
252
|
|
|
221
253
|
/**
|
|
@@ -230,14 +262,48 @@ export class I3SSource implements Tileset3DSource {
|
|
|
230
262
|
return tilePath;
|
|
231
263
|
}
|
|
232
264
|
|
|
233
|
-
const
|
|
234
|
-
|
|
265
|
+
const queryDelimiterIndex = tilePath.indexOf('?');
|
|
266
|
+
if (queryDelimiterIndex === -1) {
|
|
267
|
+
return `${tilePath}?${new URLSearchParams(this.queryParams).toString()}`;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const existingQuery = tilePath.slice(queryDelimiterIndex + 1);
|
|
271
|
+
const existingQueryKeys = new Set(
|
|
272
|
+
existingQuery.split('&').map(parameter => parameter.split('=', 1)[0])
|
|
273
|
+
);
|
|
274
|
+
const queryParams = new URLSearchParams();
|
|
275
|
+
for (const [key, value] of Object.entries(this.queryParams)) {
|
|
276
|
+
if (!existingQueryKeys.has(key)) {
|
|
277
|
+
queryParams.set(key, value);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const queryString = queryParams.toString();
|
|
282
|
+
return queryString ? `${tilePath}${existingQuery ? '&' : ''}${queryString}` : tilePath;
|
|
235
283
|
}
|
|
236
284
|
|
|
237
285
|
/**
|
|
238
286
|
* Derives the default view state from full extent or store extent metadata.
|
|
239
287
|
*/
|
|
240
|
-
getViewState(
|
|
288
|
+
getViewState(rootTile: Tile3D | null): TilesetSourceViewState {
|
|
289
|
+
if (this.spatialTransformer && rootTile) {
|
|
290
|
+
const center = new Vector3(rootTile.boundingVolume.center);
|
|
291
|
+
const cartographicCenter = new Vector3(rootTile.header.i3sLodMbs.slice(0, 3));
|
|
292
|
+
if (this.spatialTransformer.targetCoordinateFrame === 'geographic') {
|
|
293
|
+
return {
|
|
294
|
+
boundingVolume: rootTile.boundingVolume,
|
|
295
|
+
cartographicCenter,
|
|
296
|
+
cartesianCenter: center,
|
|
297
|
+
zoom: 1
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
boundingVolume: rootTile.boundingVolume,
|
|
302
|
+
cartographicCenter,
|
|
303
|
+
cartesianCenter: center,
|
|
304
|
+
zoom: 1
|
|
305
|
+
};
|
|
306
|
+
}
|
|
241
307
|
const metadata = this.getMetadata();
|
|
242
308
|
const fullExtent = metadata.tileset.fullExtent;
|
|
243
309
|
if (fullExtent) {
|
|
@@ -325,6 +391,20 @@ export class I3SSource implements Tileset3DSource {
|
|
|
325
391
|
return await this.loadWithCoreApi(url, options);
|
|
326
392
|
}
|
|
327
393
|
|
|
394
|
+
/**
|
|
395
|
+
* Initializes query parameters before any URL-backed resource is requested.
|
|
396
|
+
* @param loadOptions Loader options that may contain an ArcGIS token.
|
|
397
|
+
*/
|
|
398
|
+
private initializeQueryParams(loadOptions: LoaderOptions): void {
|
|
399
|
+
const i3sOptions = loadOptions.i3s;
|
|
400
|
+
if (i3sOptions && typeof i3sOptions === 'object' && 'token' in i3sOptions) {
|
|
401
|
+
const token = (i3sOptions as Record<string, unknown>).token;
|
|
402
|
+
if (typeof token === 'string') {
|
|
403
|
+
this.queryParams.token = token;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
328
408
|
/**
|
|
329
409
|
* Loads tile metadata or content through an injected resolver when present, otherwise through the injected core API.
|
|
330
410
|
*/
|
|
@@ -335,6 +415,27 @@ export class I3SSource implements Tileset3DSource {
|
|
|
335
415
|
|
|
336
416
|
return await this.loadWithCoreApi(url, options);
|
|
337
417
|
}
|
|
418
|
+
|
|
419
|
+
/** Transform one I3S header bound after target options and elevation providers are available. */
|
|
420
|
+
private async transformTileHeader(header: any): Promise<any> {
|
|
421
|
+
if (!this.spatialTransformer) {
|
|
422
|
+
return header;
|
|
423
|
+
}
|
|
424
|
+
const sourceBounds = {
|
|
425
|
+
mbs: header.mbs,
|
|
426
|
+
obb: header.obb,
|
|
427
|
+
normalReferenceFrame: this.getMetadata().tileset.store?.normalReferenceFrame
|
|
428
|
+
};
|
|
429
|
+
const transformedBounds = await this.spatialTransformer.transformBoundsAsync(sourceBounds);
|
|
430
|
+
return {
|
|
431
|
+
...header,
|
|
432
|
+
// Tileset3D traversal and culling operate in WGS84 ECEF independently of content output.
|
|
433
|
+
boundingVolume: transformedBounds.boundingVolume,
|
|
434
|
+
// Preserve the renderer/output-frame bound for applications and content coordination.
|
|
435
|
+
spatialBoundingVolume: transformedBounds.spatialBoundingVolume,
|
|
436
|
+
i3sLodMbs: transformedBounds.i3sLodMbs
|
|
437
|
+
};
|
|
438
|
+
}
|
|
338
439
|
}
|
|
339
440
|
|
|
340
441
|
function isTilesetRequest(input: TilesetSourceInput): input is TilesetSourceRequest {
|