@loaders.gl/tiles 5.0.0-alpha.5 → 5.0.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/dist/dist.dev.js +5122 -2641
  2. package/dist/dist.min.js +4 -2
  3. package/dist/index.cjs +1670 -403
  4. package/dist/index.cjs.map +3 -3
  5. package/dist/index.d.ts +8 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +3 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/spatial/format-spatial-reference.d.ts +1 -0
  10. package/dist/spatial/format-spatial-reference.d.ts.map +1 -1
  11. package/dist/spatial/format-spatial-reference.js +104 -46
  12. package/dist/spatial/format-spatial-reference.js.map +1 -1
  13. package/dist/spatial/get-spatial-coordinate-frame.d.ts +12 -1
  14. package/dist/spatial/get-spatial-coordinate-frame.d.ts.map +1 -1
  15. package/dist/spatial/get-spatial-coordinate-frame.js +105 -24
  16. package/dist/spatial/get-spatial-coordinate-frame.js.map +1 -1
  17. package/dist/spatial/normalize-crs-identifier.d.ts +15 -0
  18. package/dist/spatial/normalize-crs-identifier.d.ts.map +1 -0
  19. package/dist/spatial/normalize-crs-identifier.js +22 -0
  20. package/dist/spatial/normalize-crs-identifier.js.map +1 -0
  21. package/dist/spatial/spatial-coordinate-transformer.d.ts +4 -0
  22. package/dist/spatial/spatial-coordinate-transformer.d.ts.map +1 -1
  23. package/dist/spatial/spatial-coordinate-transformer.js +89 -12
  24. package/dist/spatial/spatial-coordinate-transformer.js.map +1 -1
  25. package/dist/spatial/tiles-3d-spatial-transformer.d.ts +32 -0
  26. package/dist/spatial/tiles-3d-spatial-transformer.d.ts.map +1 -0
  27. package/dist/spatial/tiles-3d-spatial-transformer.js +175 -0
  28. package/dist/spatial/tiles-3d-spatial-transformer.js.map +1 -0
  29. package/dist/tileset-3d/common/indexed-archive-tileset-source.d.ts +3 -3
  30. package/dist/tileset-3d/common/indexed-archive-tileset-source.d.ts.map +1 -1
  31. package/dist/tileset-3d/common/indexed-archive-tileset-source.js.map +1 -1
  32. package/dist/tileset-3d/common/tile-3d-contracts.d.ts +63 -0
  33. package/dist/tileset-3d/common/tile-3d-contracts.d.ts.map +1 -0
  34. package/dist/tileset-3d/common/tile-3d-contracts.js +51 -0
  35. package/dist/tileset-3d/common/tile-3d-contracts.js.map +1 -0
  36. package/dist/tileset-3d/common/tile-3d.d.ts +29 -5
  37. package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
  38. package/dist/tileset-3d/common/tile-3d.js +151 -20
  39. package/dist/tileset-3d/common/tile-3d.js.map +1 -1
  40. package/dist/tileset-3d/common/tileset-3d.d.ts +44 -2
  41. package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
  42. package/dist/tileset-3d/common/tileset-3d.js +31 -1
  43. package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
  44. package/dist/tileset-3d/common/tileset-observability.d.ts +60 -0
  45. package/dist/tileset-3d/common/tileset-observability.d.ts.map +1 -0
  46. package/dist/tileset-3d/common/tileset-observability.js +39 -0
  47. package/dist/tileset-3d/common/tileset-observability.js.map +1 -0
  48. package/dist/tileset-3d/common/tileset-source.d.ts +11 -8
  49. package/dist/tileset-3d/common/tileset-source.d.ts.map +1 -1
  50. package/dist/tileset-3d/common/tileset-source.js.map +1 -1
  51. package/dist/tileset-3d/common/tileset-traverser.d.ts +14 -0
  52. package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
  53. package/dist/tileset-3d/common/tileset-traverser.js +21 -5
  54. package/dist/tileset-3d/common/tileset-traverser.js.map +1 -1
  55. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts +47 -1
  56. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts.map +1 -1
  57. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js +327 -30
  58. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js.map +1 -1
  59. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +34 -2
  60. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts.map +1 -1
  61. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js +265 -14
  62. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -1
  63. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts +70 -3
  64. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -1
  65. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js +216 -4
  66. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js.map +1 -1
  67. package/dist/tileset-3d/format-i3s/i3s-source.d.ts +5 -3
  68. package/dist/tileset-3d/format-i3s/i3s-source.d.ts.map +1 -1
  69. package/dist/tileset-3d/format-i3s/i3s-source.js +14 -7
  70. package/dist/tileset-3d/format-i3s/i3s-source.js.map +1 -1
  71. package/dist/tileset-3d/helpers/bounding-volume.d.ts +7 -3
  72. package/dist/tileset-3d/helpers/bounding-volume.d.ts.map +1 -1
  73. package/dist/tileset-3d/helpers/bounding-volume.js +19 -61
  74. package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -1
  75. package/dist/tileset-3d/helpers/frame-state.d.ts +4 -0
  76. package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
  77. package/dist/tileset-3d/helpers/frame-state.js +6 -1
  78. package/dist/tileset-3d/helpers/frame-state.js.map +1 -1
  79. package/dist/tileset-3d/helpers/s2-bounding-volume.d.ts +17 -0
  80. package/dist/tileset-3d/helpers/s2-bounding-volume.d.ts.map +1 -0
  81. package/dist/tileset-3d/helpers/s2-bounding-volume.js +20 -0
  82. package/dist/tileset-3d/helpers/s2-bounding-volume.js.map +1 -0
  83. package/dist/types.d.ts +2 -0
  84. package/dist/types.d.ts.map +1 -1
  85. package/package.json +14 -13
  86. package/src/index.ts +13 -1
  87. package/src/spatial/format-spatial-reference.ts +127 -53
  88. package/src/spatial/get-spatial-coordinate-frame.ts +126 -31
  89. package/src/spatial/normalize-crs-identifier.ts +26 -0
  90. package/src/spatial/spatial-coordinate-transformer.ts +106 -15
  91. package/src/spatial/tiles-3d-spatial-transformer.ts +206 -0
  92. package/src/tileset-3d/common/indexed-archive-tileset-source.ts +4 -9
  93. package/src/tileset-3d/common/tile-3d-contracts.ts +115 -0
  94. package/src/tileset-3d/common/tile-3d.ts +178 -20
  95. package/src/tileset-3d/common/tileset-3d.ts +63 -3
  96. package/src/tileset-3d/common/tileset-observability.ts +100 -0
  97. package/src/tileset-3d/common/tileset-source.ts +11 -12
  98. package/src/tileset-3d/common/tileset-traverser.ts +32 -5
  99. package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +481 -34
  100. package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +390 -17
  101. package/src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts +243 -4
  102. package/src/tileset-3d/format-i3s/i3s-source.ts +25 -9
  103. package/src/tileset-3d/helpers/bounding-volume.ts +29 -62
  104. package/src/tileset-3d/helpers/frame-state.ts +6 -1
  105. package/src/tileset-3d/helpers/s2-bounding-volume.ts +35 -0
  106. package/src/types.ts +2 -0
package/dist/index.cjs CHANGED
@@ -39,12 +39,14 @@ __export(index_exports, {
39
39
  TILE_TYPE: () => TILE_TYPE,
40
40
  Tile3D: () => Tile3D,
41
41
  Tiles3DSource: () => Tiles3DSource,
42
+ Tiles3DSpatialTransformer: () => Tiles3DSpatialTransformer,
42
43
  Tileset2D: () => Tileset2D,
43
44
  Tileset3D: () => Tileset3D,
44
45
  TilesetCache: () => TilesetCache,
45
46
  TilesetTraverser: () => TilesetTraverser,
46
47
  applyTilesetSpatialOptions: () => applyTilesetSpatialOptions,
47
48
  calculateTransformProps: () => calculateTransformProps,
49
+ convertS2BoundingVolumeToOBB: () => convertS2BoundingVolumeToOBB,
48
50
  createBoundingVolume: () => createBoundingVolume,
49
51
  createImplicitSubtreeReference: () => createImplicitSubtreeReference,
50
52
  createTilesetSpatialReference: () => createTilesetSpatialReference,
@@ -54,6 +56,7 @@ __export(index_exports, {
54
56
  getI3SSpatialReference: () => getI3SSpatialReference,
55
57
  getLodStatus: () => getLodStatus,
56
58
  getSpatialCoordinateFrame: () => getSpatialCoordinateFrame,
59
+ getTileset3DTraversalSnapshot: () => getTileset3DTraversalSnapshot,
57
60
  isTileset3DSource: () => isTileset3DSource,
58
61
  markTilesetSpatialReferenceTransformed: () => markTilesetSpatialReferenceTransformed,
59
62
  materializeImplicitSubtree: () => materializeImplicitSubtree,
@@ -77,6 +80,13 @@ function assert(condition, message) {
77
80
  }
78
81
  }
79
82
 
83
+ // ../loader-utils/src/lib/binary-utils/array-buffer-utils.ts
84
+ function sliceArrayBuffer(arrayBuffer, byteOffset, byteLength) {
85
+ const subArray = byteLength !== void 0 ? new Uint8Array(arrayBuffer).subarray(byteOffset, byteOffset + byteLength) : new Uint8Array(arrayBuffer).subarray(byteOffset);
86
+ const arrayCopy = new Uint8Array(subArray);
87
+ return arrayCopy.buffer;
88
+ }
89
+
80
90
  // ../loader-utils/src/lib/request-utils/request-scheduler.ts
81
91
  var import_stats = require("@probe.gl/stats");
82
92
  var STAT_QUEUED_REQUESTS = "Queued Requests";
@@ -1900,7 +1910,7 @@ function getFrameState(viewport, frameNumber, options = {}) {
1900
1910
  ).normalize();
1901
1911
  commonSpacePlanesToWGS84(viewport);
1902
1912
  const ViewportClass = viewport.constructor;
1903
- const { longitude, latitude, width, bearing, zoom } = viewport;
1913
+ const { longitude, latitude, width, bearing, zoom, position } = viewport;
1904
1914
  const topDownViewport = new ViewportClass({
1905
1915
  longitude,
1906
1916
  latitude,
@@ -1908,6 +1918,7 @@ function getFrameState(viewport, frameNumber, options = {}) {
1908
1918
  width,
1909
1919
  bearing,
1910
1920
  zoom,
1921
+ position,
1911
1922
  pitch: 0
1912
1923
  });
1913
1924
  return {
@@ -2265,30 +2276,32 @@ function calculateTileRequestPriority(parameters) {
2265
2276
  }
2266
2277
 
2267
2278
  // dist/spatial/spatial-types.js
2268
- var import_crs = require("@math.gl/crs");
2279
+ var import_crs2 = require("@math.gl/crs");
2269
2280
 
2270
2281
  // dist/spatial/get-spatial-coordinate-frame.js
2271
- var GEOGRAPHIC_CRS = "EPSG:4326";
2272
- var GEOCENTRIC_CRS = "EPSG:4978";
2282
+ var import_crs = require("@math.gl/crs");
2283
+
2284
+ // dist/spatial/normalize-crs-identifier.js
2285
+ var WGS84_GEOGRAPHIC_CRS = "EPSG:4326";
2286
+ var WGS84_GEOCENTRIC_CRS = "EPSG:4978";
2273
2287
  function normalizeCrsIdentifier(identifier) {
2274
2288
  const normalizedIdentifier = identifier.trim().toUpperCase();
2275
2289
  const ogcMatch = normalizedIdentifier.match(/(?:\/DEF\/CRS\/|URN:OGC:DEF:CRS:)([A-Z0-9_-]+)(?:\/|::)(?:[^/:]*[/:])?([A-Z0-9_.-]+)$/);
2276
2290
  return ogcMatch ? `${ogcMatch[1]}:${ogcMatch[2]}` : normalizedIdentifier;
2277
2291
  }
2292
+
2293
+ // dist/spatial/get-spatial-coordinate-frame.js
2278
2294
  function getSpatialCoordinateFrame(definition) {
2279
2295
  var _a, _b;
2280
2296
  if (typeof definition === "string") {
2281
- const normalized = normalizeCrsIdentifier(definition);
2282
- if (normalized === GEOCENTRIC_CRS || /^GEOCCS\s*[\[(]/.test(normalized)) {
2283
- return "geocentric";
2297
+ const representation = (0, import_crs.inferCRSRepresentation)(definition);
2298
+ if (representation === "wkt") {
2299
+ return getWktCoordinateFrame(definition);
2284
2300
  }
2285
- if (normalized === GEOGRAPHIC_CRS || normalized === "EPSG:4490" || normalized === "EPSG:4979" || normalized === "OGC:CRS84" || normalized.includes("+PROJ=LONGLAT") || normalized.includes("+PROJ=LATLONG") || /^(?:GEOGCS|GEOGCRS|GEOGRAPHICCRS|GEOGRAPHIC2DCRS|GEOGRAPHIC3DCRS)\s*[\[(]/.test(normalized)) {
2286
- return "geographic";
2287
- }
2288
- if (/^(?:GEODCRS|GEODETICCRS)\s*[\[(]/.test(normalized)) {
2289
- return /CS\s*[\[(]\s*CARTESIAN/.test(normalized) ? "geocentric" : "geographic";
2301
+ if (representation === "proj-string") {
2302
+ return getProjStringCoordinateFrame(definition);
2290
2303
  }
2291
- return "projected";
2304
+ return getKnownCrsIdentifierCoordinateFrame(definition) || "projected";
2292
2305
  }
2293
2306
  const projJsonDefinition = definition;
2294
2307
  if (projJsonDefinition.type === "BoundCRS" && projJsonDefinition.source_crs) {
@@ -2306,7 +2319,84 @@ function getSpatialCoordinateFrame(definition) {
2306
2319
  if (String(projJsonDefinition.type).includes("Geodetic")) {
2307
2320
  return ((_b = projJsonDefinition.coordinate_system) == null ? void 0 : _b.subtype) === "Cartesian" ? "geocentric" : "geographic";
2308
2321
  }
2309
- return "projected";
2322
+ return "unknown";
2323
+ }
2324
+ function getKnownCrsIdentifierCoordinateFrame(definition) {
2325
+ if (typeof definition !== "string") {
2326
+ return void 0;
2327
+ }
2328
+ const normalized = normalizeCrsIdentifier(definition);
2329
+ if (normalized === WGS84_GEOCENTRIC_CRS) {
2330
+ return "geocentric";
2331
+ }
2332
+ if (normalized === WGS84_GEOGRAPHIC_CRS || normalized === "OGC:CRS84") {
2333
+ return "geographic";
2334
+ }
2335
+ const epsgMatch = /^EPSG:(\d+)$/.exec(normalized);
2336
+ if (!epsgMatch) {
2337
+ return void 0;
2338
+ }
2339
+ const identifier = Number(epsgMatch[1]);
2340
+ if (identifier === 7789) {
2341
+ return "geocentric";
2342
+ }
2343
+ if (identifier === 4490 || identifier === 4979) {
2344
+ return "geographic";
2345
+ }
2346
+ if (identifier === 3857 || identifier >= 32601 && identifier <= 32660 || identifier >= 32701 && identifier <= 32760) {
2347
+ return "projected";
2348
+ }
2349
+ return void 0;
2350
+ }
2351
+ function getWktCoordinateFrame(definition) {
2352
+ let root;
2353
+ try {
2354
+ root = (0, import_crs.parseWKTCRS)(definition).root;
2355
+ } catch {
2356
+ return "unknown";
2357
+ }
2358
+ switch (root.keyword.toUpperCase()) {
2359
+ case "GEOCCS":
2360
+ return "geocentric";
2361
+ case "GEOGCS":
2362
+ case "GEOGCRS":
2363
+ case "GEOGRAPHICCRS":
2364
+ case "GEOGRAPHIC2DCRS":
2365
+ case "GEOGRAPHIC3DCRS":
2366
+ return "geographic";
2367
+ case "PROJCS":
2368
+ case "PROJCRS":
2369
+ case "PROJECTEDCRS":
2370
+ return "projected";
2371
+ case "GEODCRS":
2372
+ case "GEODETICCRS": {
2373
+ const coordinateSystem = root.values.find((value) => value.type === "node" && value.keyword.toUpperCase() === "CS");
2374
+ const subtype = coordinateSystem == null ? void 0 : coordinateSystem.values[0];
2375
+ if ((subtype == null ? void 0 : subtype.type) !== "enumeration" && (subtype == null ? void 0 : subtype.type) !== "string") {
2376
+ return "unknown";
2377
+ }
2378
+ return subtype.value.toUpperCase() === "CARTESIAN" ? "geocentric" : subtype.value.toUpperCase() === "ELLIPSOIDAL" ? "geographic" : "unknown";
2379
+ }
2380
+ default:
2381
+ return "unknown";
2382
+ }
2383
+ }
2384
+ function getProjStringCoordinateFrame(definition) {
2385
+ var _a;
2386
+ try {
2387
+ const projection = (_a = (0, import_crs.parsePROJString)(definition).parameters.find((parameter) => parameter.name.toLowerCase() === "proj")) == null ? void 0 : _a.value;
2388
+ switch (projection == null ? void 0 : projection.toLowerCase()) {
2389
+ case "longlat":
2390
+ case "latlong":
2391
+ return "geographic";
2392
+ case "geocent":
2393
+ return "geocentric";
2394
+ default:
2395
+ return projection ? "projected" : "unknown";
2396
+ }
2397
+ } catch {
2398
+ return "unknown";
2399
+ }
2310
2400
  }
2311
2401
 
2312
2402
  // dist/spatial/spatial-types.js
@@ -2331,11 +2421,11 @@ function createTilesetSpatialReference(discovered, options = {}) {
2331
2421
  const provenance = hasSourceCrsOverride ? "caller-override" : discovered.provenance || "unknown";
2332
2422
  const coordinateFrame = hasSourceCrsOverride && sourceCrs ? getSpatialCoordinateFrame(sourceCrs) : discovered.coordinateFrame;
2333
2423
  const sourceCrsState = hasSourceCrsOverride ? "explicit" : discovered.sourceCrsState || (sourceCrs ? provenance === "format-default" ? "default" : "explicit" : "absent");
2334
- const spatialReference = (0, import_crs.createSpatialReference)({
2424
+ const spatialReference = (0, import_crs2.createSpatialReference)({
2335
2425
  crs: sourceCrs ? {
2336
2426
  state: sourceCrsState === "default" ? "default" : "explicit",
2337
2427
  definition: sourceCrs,
2338
- representation: !hasSourceCrsOverride && discovered.sourceCrsRepresentation || (0, import_crs.inferCRSRepresentation)(sourceCrs),
2428
+ representation: !hasSourceCrsOverride && discovered.sourceCrsRepresentation || (0, import_crs2.inferCRSRepresentation)(sourceCrs),
2339
2429
  provenance,
2340
2430
  alternatives: hasSourceCrsOverride ? void 0 : discovered.sourceCrsAlternatives
2341
2431
  } : {
@@ -2345,7 +2435,7 @@ function createTilesetSpatialReference(discovered, options = {}) {
2345
2435
  vertical: discovered.verticalCrs ? {
2346
2436
  state: "explicit",
2347
2437
  definition: discovered.verticalCrs,
2348
- representation: (0, import_crs.inferCRSRepresentation)(discovered.verticalCrs),
2438
+ representation: (0, import_crs2.inferCRSRepresentation)(discovered.verticalCrs),
2349
2439
  provenance: discovered.provenance || "metadata"
2350
2440
  } : void 0,
2351
2441
  coordinateEpoch: options.coordinateEpoch ?? discovered.coordinateEpoch,
@@ -2402,11 +2492,11 @@ function getKnownCRSDefinition(reference) {
2402
2492
  return reference.state === "explicit" || reference.state === "default" ? reference.definition : void 0;
2403
2493
  }
2404
2494
  function cloneReadonlyCRSDefinition(definition) {
2405
- const spatialReference = (0, import_crs.createSpatialReference)({
2495
+ const spatialReference = (0, import_crs2.createSpatialReference)({
2406
2496
  crs: {
2407
2497
  state: "explicit",
2408
2498
  definition,
2409
- representation: (0, import_crs.inferCRSRepresentation)(definition),
2499
+ representation: (0, import_crs2.inferCRSRepresentation)(definition),
2410
2500
  provenance: "caller-override"
2411
2501
  }
2412
2502
  });
@@ -2495,6 +2585,10 @@ var DEFAULT_PROPS3 = {
2495
2585
  foveatedTimeDelay: 0.2,
2496
2586
  maximumScreenSpaceError: 8,
2497
2587
  skipLevelOfDetail: false,
2588
+ baseScreenSpaceError: 1024,
2589
+ skipScreenSpaceErrorFactor: 16,
2590
+ skipLevels: 1,
2591
+ immediatelyLoadDesiredLevelOfDetail: false,
2498
2592
  dynamicScreenSpaceError: true,
2499
2593
  dynamicScreenSpaceErrorDensity: 2e-4,
2500
2594
  dynamicScreenSpaceErrorFactor: 24,
@@ -2657,6 +2751,24 @@ var Tileset3D = class {
2657
2751
  get frameNumber() {
2658
2752
  return this._frameNumber;
2659
2753
  }
2754
+ /**
2755
+ * Returns the tiles requested during the most recent completed traversal.
2756
+ *
2757
+ * A defensive copy makes request ordering deterministic for diagnostics and prevents callers
2758
+ * from mutating the scheduler's working set.
2759
+ */
2760
+ get requestedTiles() {
2761
+ return this._requestedTiles.slice();
2762
+ }
2763
+ /**
2764
+ * Returns empty tiles considered during the most recent completed traversal.
2765
+ *
2766
+ * Empty tiles can still be useful hierarchy placeholders, but their IDs are included in
2767
+ * observability snapshots so applications can distinguish traversal work from render content.
2768
+ */
2769
+ get emptyTiles() {
2770
+ return this._emptyTiles.slice();
2771
+ }
2660
2772
  /**
2661
2773
  * Gets or sets the soft target, in bytes, for cached tile content not needed this frame.
2662
2774
  *
@@ -2784,6 +2896,12 @@ var Tileset3D = class {
2784
2896
  return;
2785
2897
  }
2786
2898
  const preparedViewports = viewports instanceof Array ? viewports : [viewports];
2899
+ const activeViewportIds = new Set(preparedViewports.map((viewport) => viewport.id));
2900
+ for (const frameStateId of Object.keys(this.frameStateData)) {
2901
+ if (!activeViewportIds.has(frameStateId)) {
2902
+ delete this.frameStateData[frameStateId];
2903
+ }
2904
+ }
2787
2905
  this._cache.reset();
2788
2906
  this._frameNumber++;
2789
2907
  this.traverseCounter = preparedViewports.length;
@@ -2972,21 +3090,21 @@ var Tileset3D = class {
2972
3090
  this.stats.get(MAXIMUM_SSE).count = this.memoryAdjustedScreenSpaceError;
2973
3091
  }
2974
3092
  async _initializeTileSet() {
2975
- var _a, _b, _c;
3093
+ var _a, _b, _c, _d;
2976
3094
  try {
2977
3095
  const initializePromise = this.source.initialize();
2978
3096
  const initialMetadata = this._getSourceMetadata();
2979
3097
  if (initialMetadata) {
2980
3098
  this._syncSourceState(initialMetadata);
2981
3099
  }
2982
- if (((_a = initialMetadata == null ? void 0 : initialMetadata.tileset) == null ? void 0 : _a.root) && typeof initialMetadata.tileset.root.then !== "function" && !this.source.prepareTileset) {
3100
+ if (((_a = initialMetadata == null ? void 0 : initialMetadata.tileset) == null ? void 0 : _a.root) && typeof initialMetadata.tileset.root.then !== "function" && (!this.source.prepareTileset || ((_b = this.spatialReference) == null ? void 0 : _b.status) === "native")) {
2983
3101
  this.root = this._initializeTileHeaders(initialMetadata.tileset, null);
2984
3102
  this._applyViewState();
2985
3103
  }
2986
3104
  await initializePromise;
2987
3105
  const metadata = this.source.getMetadata();
2988
3106
  this._syncSourceState(metadata);
2989
- await ((_c = (_b = this.source).prepareTileset) == null ? void 0 : _c.call(_b, this));
3107
+ await ((_d = (_c = this.source).prepareTileset) == null ? void 0 : _d.call(_c, this));
2990
3108
  if (!this.root) {
2991
3109
  this.root = this._initializeTileHeaders(metadata.tileset, null);
2992
3110
  }
@@ -3207,6 +3325,36 @@ function preserveTransformedSpatialReference(current, next) {
3207
3325
  return (current == null ? void 0 : current.status) === "transformed" && current.sourceCrs === next.sourceCrs && current.targetCrs === next.targetCrs && current.targetHeightReference === next.targetHeightReference && current.outputCoordinates === next.outputCoordinates ? current : next;
3208
3326
  }
3209
3327
 
3328
+ // dist/tileset-3d/common/tileset-observability.js
3329
+ function getTileset3DTraversalSnapshot(tileset) {
3330
+ var _a;
3331
+ const selectedTiles = tileset.selectedTiles.slice();
3332
+ const requestedTiles = tileset.requestedTiles.slice();
3333
+ const emptyTiles = tileset.emptyTiles.slice();
3334
+ const getTileIds = (tiles) => tiles.map((tile) => String(tile.id)).sort();
3335
+ const getStatCount = (name) => {
3336
+ const count = tileset.stats.get(name).count;
3337
+ return typeof count === "number" && Number.isFinite(count) ? count : 0;
3338
+ };
3339
+ const implicitTilingSource = tileset.source;
3340
+ const implicitTiling = (_a = implicitTilingSource.getImplicitTilingStats) == null ? void 0 : _a.call(implicitTilingSource);
3341
+ return {
3342
+ frameNumber: tileset.frameNumber,
3343
+ selectedTileIds: getTileIds(selectedTiles),
3344
+ requestedTileIds: getTileIds(requestedTiles),
3345
+ emptyTileIds: getTileIds(emptyTiles),
3346
+ visibleTileCount: selectedTiles.length,
3347
+ renderableTileCount: selectedTiles.filter((tile) => tile.contentAvailable && Boolean(tile.content)).length,
3348
+ loadingTileCount: getStatCount("Tiles Loading"),
3349
+ loadedTileCount: getStatCount("Tiles Loaded"),
3350
+ failedTileCount: getStatCount("Failed Tile Loads"),
3351
+ cachedTileCount: getStatCount("Tiles In Memory"),
3352
+ cacheBytes: tileset.gpuMemoryUsageInBytes,
3353
+ maximumScreenSpaceError: tileset.memoryAdjustedScreenSpaceError,
3354
+ ...implicitTiling ? { implicitTiling } : {}
3355
+ };
3356
+ }
3357
+
3210
3358
  // dist/tileset-3d/common/indexed-archive-tileset-source.js
3211
3359
  var IndexedArchiveTilesetSource = class {
3212
3360
  /** URL exposed to the tileset source that owns this archive source. */
@@ -3323,6 +3471,47 @@ function stripQueryString(url) {
3323
3471
  var import_core8 = require("@math.gl/core");
3324
3472
  var import_culling4 = require("@math.gl/culling");
3325
3473
 
3474
+ // dist/tileset-3d/common/tile-3d-contracts.js
3475
+ function getTile3DFeatureIdSets(payload) {
3476
+ const featureIdSets = [];
3477
+ const visited = /* @__PURE__ */ new Set();
3478
+ const visit = (value) => {
3479
+ var _a, _b;
3480
+ if (!value || typeof value !== "object") {
3481
+ return;
3482
+ }
3483
+ if (visited.has(value)) {
3484
+ return;
3485
+ }
3486
+ visited.add(value);
3487
+ const record = value;
3488
+ const declarations = (_b = (_a = record.extensions) == null ? void 0 : _a.EXT_mesh_features) == null ? void 0 : _b.featureIds;
3489
+ if (Array.isArray(declarations)) {
3490
+ for (const declaration of declarations) {
3491
+ if (!declaration || typeof declaration !== "object") {
3492
+ continue;
3493
+ }
3494
+ const source = declaration.texture ? "texture" : declaration.implicit ? "implicit" : declaration.attribute !== void 0 ? "attribute" : declaration.propertyTable !== void 0 ? "property-table" : "constant";
3495
+ featureIdSets.push({
3496
+ source,
3497
+ attribute: declaration.attribute,
3498
+ propertyTable: declaration.propertyTable,
3499
+ constant: declaration.constant,
3500
+ texture: declaration.texture,
3501
+ implicit: declaration.implicit,
3502
+ values: declaration.values,
3503
+ details: declaration
3504
+ });
3505
+ }
3506
+ }
3507
+ for (const child of Object.values(record)) {
3508
+ visit(child);
3509
+ }
3510
+ };
3511
+ visit(payload);
3512
+ return featureIdSets;
3513
+ }
3514
+
3326
3515
  // dist/tileset-3d/helpers/bounding-volume.js
3327
3516
  var import_core6 = require("@math.gl/core");
3328
3517
  var import_culling3 = require("@math.gl/culling");
@@ -3332,21 +3521,25 @@ function defined(x) {
3332
3521
  }
3333
3522
  var scratchPoint = new import_core6.Vector3();
3334
3523
  var scratchScale = new import_core6.Vector3();
3335
- function createBoundingVolume(boundingVolumeHeader, transform, result) {
3524
+ var identityTransform = new import_core6.Matrix4();
3525
+ function createBoundingVolume(boundingVolumeHeader, transform, result, initialTransform = transform) {
3336
3526
  assert(boundingVolumeHeader, "3D Tile: boundingVolume must be defined");
3337
3527
  if (boundingVolumeHeader.box) {
3338
- return createBox(boundingVolumeHeader.box, transform, result);
3528
+ const boxTransform = boundingVolumeHeader.s2VolumeInfo ? identityTransform : transform;
3529
+ return createBox(boundingVolumeHeader.box, boxTransform, result);
3339
3530
  }
3340
3531
  if (boundingVolumeHeader.region) {
3341
- return (0, import_geospatial4.makeOBBFromRegion)(boundingVolumeHeader.region);
3532
+ const boundingVolume = (0, import_geospatial4.makeOBBFromRegion)(boundingVolumeHeader.region);
3533
+ const regionTransform = transform.equals(initialTransform) ? identityTransform : transform.clone().multiplyRight(initialTransform.clone().invert());
3534
+ return createBox([...boundingVolume.center, ...boundingVolume.halfAxes], regionTransform, result);
3342
3535
  }
3343
3536
  if (boundingVolumeHeader.sphere) {
3344
3537
  return createSphere(boundingVolumeHeader.sphere, transform, result);
3345
3538
  }
3346
3539
  throw new Error("3D Tile: boundingVolume must contain a sphere, region, or box");
3347
3540
  }
3348
- function getCartographicBounds(boundingVolumeHeader, boundingVolume) {
3349
- if (boundingVolumeHeader.box) {
3541
+ function getCartographicBounds(boundingVolumeHeader, boundingVolume, isRegionTransformed = false) {
3542
+ if (boundingVolumeHeader.box || boundingVolumeHeader.region && isRegionTransformed) {
3350
3543
  return orientedBoundingBoxToCartographicBounds(boundingVolume);
3351
3544
  }
3352
3545
  if (boundingVolumeHeader.region) {
@@ -3691,6 +3884,10 @@ var ManagedArray = class {
3691
3884
  var DEFAULT_PROPS4 = {
3692
3885
  loadSiblings: false,
3693
3886
  skipLevelOfDetail: false,
3887
+ baseScreenSpaceError: 1024,
3888
+ skipScreenSpaceErrorFactor: 16,
3889
+ skipLevels: 1,
3890
+ immediatelyLoadDesiredLevelOfDetail: false,
3694
3891
  updateTransforms: true,
3695
3892
  onTraversalEnd: () => {
3696
3893
  },
@@ -3699,6 +3896,8 @@ var DEFAULT_PROPS4 = {
3699
3896
  };
3700
3897
  var TilesetTraverser = class {
3701
3898
  options;
3899
+ /** Whether loaded content has disabled skip-LOD traversal for this tileset. */
3900
+ disableSkipLevelOfDetail = false;
3702
3901
  // fulfill in traverse call
3703
3902
  root = null;
3704
3903
  // tiles should be rendered
@@ -3724,6 +3923,10 @@ var TilesetTraverser = class {
3724
3923
  constructor(options) {
3725
3924
  this.options = { ...DEFAULT_PROPS4, ...options };
3726
3925
  }
3926
+ /** Returns whether skip-LOD traversal is enabled and supported by the loaded content. */
3927
+ isSkipLevelOfDetailEnabled() {
3928
+ return this.options.skipLevelOfDetail && !this.disableSkipLevelOfDetail;
3929
+ }
3727
3930
  // tiles should be visible
3728
3931
  traverse(root, frameState, options) {
3729
3932
  this.root = root;
@@ -3776,13 +3979,17 @@ var TilesetTraverser = class {
3776
3979
  this.selectTile(tile, frameState);
3777
3980
  } else if (tile.refine === TILE_REFINEMENT.REPLACE) {
3778
3981
  this.loadTile(tile, frameState);
3779
- if (stoppedRefining || this.options.skipLevelOfDetail) {
3982
+ if (stoppedRefining || this.isSkipLevelOfDetailEnabled()) {
3780
3983
  this.selectTile(tile, frameState);
3781
3984
  }
3782
3985
  }
3783
3986
  this.touchTile(tile, frameState);
3784
3987
  tile._shouldRefine = shouldRefine && parentRefines;
3785
3988
  }
3989
+ this.completeTraversal(frameState);
3990
+ }
3991
+ /** Notifies the owning tileset when traversal is complete or its debounce interval expires. */
3992
+ completeTraversal(frameState) {
3786
3993
  const newTime = (/* @__PURE__ */ new Date()).getTime();
3787
3994
  if (this.traversalFinished(frameState) || newTime - this.lastUpdate > this.updateDebounceTime) {
3788
3995
  this.lastUpdate = newTime;
@@ -3797,7 +4004,8 @@ var TilesetTraverser = class {
3797
4004
  }
3798
4005
  /* eslint-disable complexity, max-statements */
3799
4006
  updateAndPushChildren(tile, frameState, stack, depth) {
3800
- const { loadSiblings, skipLevelOfDetail } = this.options;
4007
+ const { loadSiblings } = this.options;
4008
+ const skipLevelOfDetail = this.isSkipLevelOfDetailEnabled();
3801
4009
  const children = tile.children;
3802
4010
  children.sort(this.compareDistanceToCamera.bind(this));
3803
4011
  const checkRefines = tile.refine === TILE_REFINEMENT.REPLACE && tile.hasRenderContent && !skipLevelOfDetail;
@@ -3811,7 +4019,7 @@ var TilesetTraverser = class {
3811
4019
  }
3812
4020
  stack.push(child);
3813
4021
  hasVisibleChild = true;
3814
- } else if (checkRefines || loadSiblings) {
4022
+ } else if ((checkRefines || loadSiblings) && child._inRequestVolume) {
3815
4023
  this.loadTile(child, frameState);
3816
4024
  this.touchTile(child, frameState);
3817
4025
  }
@@ -3998,6 +4206,13 @@ function getContentVisibility(contentVolumes, fallbackVolume, cullingVolume2, vi
3998
4206
  }
3999
4207
  return intersectingVolume ? "intersecting" : "inside";
4000
4208
  }
4209
+ function isNestedTilesetPayload(payload) {
4210
+ if (!payload || typeof payload !== "object") {
4211
+ return false;
4212
+ }
4213
+ const record = payload;
4214
+ return record.shape === "tileset3d" || record.type === "tileset3d" || Boolean(record.tileset);
4215
+ }
4001
4216
  function defined2(x) {
4002
4217
  return x !== void 0 && x !== null;
4003
4218
  }
@@ -4025,6 +4240,8 @@ var Tile3D = class {
4025
4240
  * separate ensures transform updates never compound an already-scaled geometric error.
4026
4241
  */
4027
4242
  _unscaledLodMetricValue = 0;
4243
+ /** Whether the source format defines geometric error in transform-local units. */
4244
+ _scaleGeometricError = true;
4028
4245
  /** @todo math.gl is not exporting BoundingVolume base type? */
4029
4246
  boundingVolume = null;
4030
4247
  /**
@@ -4034,6 +4251,14 @@ var Tile3D = class {
4034
4251
  content = null;
4035
4252
  /** Loaded payloads in the same order as {@link contentUrls}; `content` remains the primary payload. */
4036
4253
  contents = [];
4254
+ /** Ordered renderer-neutral descriptors for each source content entry. */
4255
+ contentEntries = [];
4256
+ /** Raw metadata references inherited by this tile and its content. */
4257
+ metadataContext = {};
4258
+ /** Feature-id declarations normalized from supported content extensions. */
4259
+ featureIdSets = [];
4260
+ /** Decoded 3D Tiles vector topology for the primary loaded content, when present. */
4261
+ vectorContent = null;
4037
4262
  /**
4038
4263
  * Metadata entity attached to this tile in the source tileset, if present.
4039
4264
  *
@@ -4100,6 +4325,7 @@ var Tile3D = class {
4100
4325
  get viewerRequestVolume() {
4101
4326
  return this._viewerRequestVolume;
4102
4327
  }
4328
+ /** Accumulated tile transforms from the source JSON, excluding the tileset model matrix. */
4103
4329
  _initialTransform = new import_core8.Matrix4();
4104
4330
  // Used by traverser, cannot be marked private
4105
4331
  _priority = 0;
@@ -4141,15 +4367,14 @@ var Tile3D = class {
4141
4367
  this.type = header.type;
4142
4368
  this.contentUrl = header.contentUrl;
4143
4369
  this.contentUrls = header.contentUrls || (header.contentUrl ? [header.contentUrl] : []);
4144
- this.metadata = header.metadata || null;
4145
- const contentHeaders = Array.isArray(header.content) ? header.content : [header.content];
4146
- this.contentMetadata = contentHeaders.filter(Boolean).map((contentHeader) => contentHeader.metadata || null);
4370
+ this._initializeMetadata(header);
4147
4371
  this.childrenState = header.implicitSubtree ? "unloaded" : "ready";
4148
4372
  this._initializeLodMetric(header);
4149
4373
  this._initializeTransforms(header);
4150
4374
  this._updateLodMetricScale();
4151
4375
  this._initializeBoundingVolumes(header);
4152
4376
  this._initializeContent(header);
4377
+ this._initializeContentEntries(header);
4153
4378
  this._initializeRenderingState(header);
4154
4379
  Object.seal(this);
4155
4380
  }
@@ -4234,12 +4459,12 @@ var Tile3D = class {
4234
4459
  return this._screenSpaceError;
4235
4460
  }
4236
4461
  /**
4237
- * Get bounding box in cartographic coordinates
4462
+ * Get the current world-space bounding volume in cartographic coordinates.
4238
4463
  * @returns [min, max] each in [longitude, latitude, altitude]
4239
4464
  */
4240
4465
  get boundingBox() {
4241
4466
  if (!this._boundingBox) {
4242
- this._boundingBox = getCartographicBounds(this.header.boundingVolume, this.boundingVolume);
4467
+ this._boundingBox = getCartographicBounds(this.header.boundingVolume, this.boundingVolume, !this.computedTransform.equals(this._initialTransform));
4243
4468
  }
4244
4469
  return this._boundingBox;
4245
4470
  }
@@ -4307,7 +4532,7 @@ var Tile3D = class {
4307
4532
  // eslint-disable-next-line complexity
4308
4533
  _calculateRequestPriority(allowUnloadedContent) {
4309
4534
  const traverser = this.tileset._traverser;
4310
- const { skipLevelOfDetail } = traverser.options;
4535
+ const skipLevelOfDetail = traverser.options.skipLevelOfDetail && !traverser.disableSkipLevelOfDetail;
4311
4536
  const maySkipTile = this.refine === TILE_REFINEMENT.ADD || skipLevelOfDetail;
4312
4537
  if (maySkipTile && !this.isVisible && this._visible !== void 0) {
4313
4538
  return -1;
@@ -4339,6 +4564,7 @@ var Tile3D = class {
4339
4564
  */
4340
4565
  // eslint-disable-next-line max-statements, complexity
4341
4566
  async loadContent() {
4567
+ var _a;
4342
4568
  if (this.hasEmptyContent) {
4343
4569
  return { loaded: false };
4344
4570
  }
@@ -4361,11 +4587,13 @@ var Tile3D = class {
4361
4587
  this.contents = loadResult.contents;
4362
4588
  this.content = this.contents[0] || null;
4363
4589
  }
4590
+ this.vectorContent = ((_a = this.contents.find((content) => Boolean(content == null ? void 0 : content.vectorContent))) == null ? void 0 : _a.vectorContent) || null;
4364
4591
  if (this.tileset.options.contentLoader) {
4365
4592
  await this.tileset.options.contentLoader(this);
4366
4593
  }
4367
4594
  this.contentState = TILE_CONTENT_STATE.READY;
4368
4595
  this._onContentLoaded();
4596
+ this._updateContentEntriesAfterLoad(loadResult);
4369
4597
  return loadResult;
4370
4598
  } catch (error) {
4371
4599
  this.contentState = TILE_CONTENT_STATE.FAILED;
@@ -4428,30 +4656,114 @@ var Tile3D = class {
4428
4656
  /**
4429
4657
  * Replaces a contentless implicit placeholder with its materialized subtree-root header.
4430
4658
  *
4431
- * Transform state is intentionally preserved: the placeholder already owns the composed
4432
- * transform, and reapplying the root transform here would double-transform bounds and geometric
4433
- * error. Content and raw geometric error are reinitialized from the newly available header.
4659
+ * The placeholder transform is preserved when the subtree omits a root transform. An explicit
4660
+ * subtree-root transform replaces it and is recomposed against the runtime parent. Content,
4661
+ * metadata, bounds, and raw geometric error are reinitialized from the available header.
4434
4662
  *
4435
4663
  * @param materializedHeader - Available root header produced from one subtree resource.
4436
4664
  */
4437
4665
  applyImplicitSubtreeHeader(materializedHeader) {
4438
4666
  const existingTransform = this.header.transform;
4439
4667
  const existingTransformMatrix = this.header.transformMatrix;
4668
+ const hasMaterializedTransform = materializedHeader.transform !== void 0;
4440
4669
  this.header = {
4441
4670
  ...this.header,
4442
4671
  ...materializedHeader,
4443
- transform: existingTransform,
4444
- transformMatrix: existingTransformMatrix,
4672
+ transform: hasMaterializedTransform ? materializedHeader.transform : existingTransform,
4673
+ transformMatrix: hasMaterializedTransform ? materializedHeader.transformMatrix : existingTransformMatrix,
4445
4674
  implicitSubtree: void 0
4446
4675
  };
4676
+ if (hasMaterializedTransform) {
4677
+ this._initializeTransforms(this.header);
4678
+ }
4447
4679
  this.refine = this._getRefine(materializedHeader.refine);
4448
4680
  this.type = materializedHeader.type;
4449
4681
  this.contentUrl = materializedHeader.contentUrl;
4450
4682
  this.contentUrls = materializedHeader.contentUrls || (materializedHeader.contentUrl ? [materializedHeader.contentUrl] : []);
4683
+ this._initializeMetadata(this.header);
4451
4684
  this._initializeLodMetric(materializedHeader);
4452
4685
  this._updateLodMetricScale();
4453
4686
  this._initializeBoundingVolumes(this.header);
4687
+ this._boundingBox = void 0;
4454
4688
  this._initializeContent(this.header);
4689
+ this._initializeContentEntries(this.header);
4690
+ }
4691
+ /** Refreshes tile and content metadata from a source or materialized tile header. */
4692
+ _initializeMetadata(header) {
4693
+ var _a;
4694
+ this.metadata = header.metadata || null;
4695
+ const contentHeaders = Array.isArray(header.content) ? header.content : [header.content];
4696
+ this.contentMetadata = contentHeaders.filter(Boolean).map((contentHeader) => contentHeader.metadata || null);
4697
+ const firstContentHeader = contentHeaders.find(Boolean) || {};
4698
+ const groupReference = firstContentHeader.group;
4699
+ const group = typeof groupReference === "number" ? ((_a = this.tileset.groups) == null ? void 0 : _a[groupReference]) || null : groupReference || null;
4700
+ const metadataContext = {
4701
+ tileset: this.tileset.metadata || null,
4702
+ group,
4703
+ tile: this.metadata,
4704
+ content: this.contentMetadata[0] || null,
4705
+ subtree: header.implicitMetadata || null
4706
+ };
4707
+ if (this.tileset.groups) {
4708
+ metadataContext.groups = this.tileset.groups;
4709
+ }
4710
+ this.metadataContext = metadataContext;
4711
+ }
4712
+ /** Updates payload and renderability while retaining immutable metadata descriptors. */
4713
+ _updateContentEntriesAfterLoad(loadResult) {
4714
+ if (loadResult.contentEntries) {
4715
+ this.contentEntries = loadResult.contentEntries.map((entry, index) => {
4716
+ var _a;
4717
+ return {
4718
+ ...entry,
4719
+ index,
4720
+ boundingVolume: entry.boundingVolume || this._contentBoundingVolumes[index] || this.boundingVolume,
4721
+ payload: entry.payload ?? this.contents[index] ?? null,
4722
+ featureIds: ((_a = entry.featureIds) == null ? void 0 : _a.length) ? entry.featureIds : getTile3DFeatureIdSets(entry.payload ?? this.contents[index]),
4723
+ renderable: Boolean(entry.renderable !== false && (entry.payload ?? this.contents[index]) && !isNestedTilesetPayload(entry.payload ?? this.contents[index]))
4724
+ };
4725
+ });
4726
+ this.featureIdSets = this.contentEntries.flatMap((entry) => entry.featureIds);
4727
+ return;
4728
+ }
4729
+ this.contentEntries = this.contentEntries.map((entry, index) => ({
4730
+ ...entry,
4731
+ payload: this.contents[index] ?? null,
4732
+ featureIds: getTile3DFeatureIdSets(this.contents[index]),
4733
+ renderable: Boolean(this.contents[index]) && !isNestedTilesetPayload(this.contents[index])
4734
+ }));
4735
+ this.featureIdSets = this.contentEntries.flatMap((entry) => entry.featureIds);
4736
+ }
4737
+ /** Returns one ordered content descriptor, or null when the index is out of range. */
4738
+ getContentEntry(index) {
4739
+ return this.contentEntries[index] || null;
4740
+ }
4741
+ /** Returns whether an ordered content entry currently has renderable payload. */
4742
+ isContentRenderable(index) {
4743
+ var _a;
4744
+ return Boolean((_a = this.contentEntries[index]) == null ? void 0 : _a.renderable);
4745
+ }
4746
+ /** Initializes additive renderer-neutral descriptors from a normalized tile header. */
4747
+ _initializeContentEntries(tileHeader) {
4748
+ const headers = Array.isArray(tileHeader.content) ? tileHeader.content : tileHeader.content ? [tileHeader.content] : [];
4749
+ this.contentEntries = headers.map((contentHeader, index) => {
4750
+ const normalizedContentHeader = contentHeader || {};
4751
+ return {
4752
+ index,
4753
+ uri: this.contentUrls[index] || normalizedContentHeader.uri || normalizedContentHeader.url,
4754
+ group: normalizedContentHeader.group,
4755
+ type: normalizedContentHeader.type,
4756
+ payload: null,
4757
+ metadata: normalizedContentHeader.metadata || null,
4758
+ boundingVolume: this._contentBoundingVolumes[index] || this.boundingVolume,
4759
+ featureIds: this._getFeatureIdSets(normalizedContentHeader),
4760
+ renderable: false
4761
+ };
4762
+ });
4763
+ }
4764
+ /** Extracts renderer-neutral feature-id declarations without decoding property values. */
4765
+ _getFeatureIdSets(contentHeader) {
4766
+ return getTile3DFeatureIdSets(contentHeader);
4455
4767
  }
4456
4768
  // Unloads the tile's content.
4457
4769
  unloadContent() {
@@ -4462,6 +4774,13 @@ var Tile3D = class {
4462
4774
  }
4463
4775
  this.contents = [];
4464
4776
  this.content = null;
4777
+ this.vectorContent = null;
4778
+ this.contentEntries = this.contentEntries.map((entry) => ({
4779
+ ...entry,
4780
+ payload: null,
4781
+ renderable: false
4782
+ }));
4783
+ this.featureIdSets = this.contentEntries.flatMap((entry) => entry.featureIds);
4465
4784
  if (this.header.content && this.header.content.destroy) {
4466
4785
  this.header.content.destroy();
4467
4786
  }
@@ -4523,9 +4842,11 @@ var Tile3D = class {
4523
4842
  return;
4524
4843
  }
4525
4844
  this._foveatedFactor = calculateFoveatedFactor(this.boundingVolume, frameState.camera);
4845
+ const traverser = this.tileset._traverser;
4846
+ const skipLevelOfDetail = traverser.options.skipLevelOfDetail && !traverser.disableSkipLevelOfDetail;
4526
4847
  const deferralEligible = isFoveatedRequestDeferred({
4527
4848
  refinement: this.refine,
4528
- skipLevelOfDetail: this.tileset._traverser.options.skipLevelOfDetail,
4849
+ skipLevelOfDetail,
4529
4850
  foveatedScreenSpaceError: options.foveatedScreenSpaceError,
4530
4851
  foveatedConeSize: options.foveatedConeSize,
4531
4852
  minimumScreenSpaceErrorRelaxation: options.foveatedMinimumScreenSpaceErrorRelaxation,
@@ -4556,8 +4877,9 @@ var Tile3D = class {
4556
4877
  * @param frameState Current camera, culling, and optional clipping-plane state.
4557
4878
  * @returns The content visibility classification.
4558
4879
  */
4559
- contentVisibility(frameState) {
4560
- return getContentVisibility(this._contentBoundingVolumes, this.boundingVolume, frameState.cullingVolume, this._visibilityPlaneMask, frameState.clippingPlanes);
4880
+ contentVisibility(frameState, contentIndex) {
4881
+ const contentVolumes = contentIndex === void 0 ? this._contentBoundingVolumes : this._contentBoundingVolumes[contentIndex] ? [this._contentBoundingVolumes[contentIndex]] : [];
4882
+ return getContentVisibility(contentVolumes, this.boundingVolume, frameState.cullingVolume, this._visibilityPlaneMask, frameState.clippingPlanes);
4561
4883
  }
4562
4884
  /**
4563
4885
  * Computes the (potentially approximate) distance from the closest point of the tile's bounding volume to the camera.
@@ -4604,6 +4926,8 @@ var Tile3D = class {
4604
4926
  // INTERNAL METHODS
4605
4927
  /** Initializes the tile's LOD metric from its header or its nearest available ancestor. */
4606
4928
  _initializeLodMetric(header) {
4929
+ var _a;
4930
+ this._scaleGeometricError = typeof header._scaleGeometricError === "boolean" ? header._scaleGeometricError : ((_a = this.parent) == null ? void 0 : _a._scaleGeometricError) ?? true;
4607
4931
  if ("lodMetricType" in header) {
4608
4932
  this.lodMetricType = header.lodMetricType;
4609
4933
  } else {
@@ -4639,7 +4963,7 @@ var Tile3D = class {
4639
4963
  * I3S `maxScreenThreshold` is already a screen-space metric and must not be transform-scaled.
4640
4964
  */
4641
4965
  _updateLodMetricScale() {
4642
- if (this.lodMetricType !== LOD_METRIC_TYPE.GEOMETRIC_ERROR) {
4966
+ if (this.lodMetricType !== LOD_METRIC_TYPE.GEOMETRIC_ERROR || !this._scaleGeometricError) {
4643
4967
  this.lodMetricValue = this._unscaledLodMetricValue;
4644
4968
  return;
4645
4969
  }
@@ -4719,16 +5043,17 @@ var Tile3D = class {
4719
5043
  }
4720
5044
  }
4721
5045
  _updateBoundingVolume(header) {
4722
- this.boundingVolume = createBoundingVolume(header.boundingVolume, this.computedTransform, this.boundingVolume);
5046
+ this._boundingBox = void 0;
5047
+ this.boundingVolume = createBoundingVolume(header.boundingVolume, this.computedTransform, this.boundingVolume, this._initialTransform);
4723
5048
  if (header.viewerRequestVolume) {
4724
- this._viewerRequestVolume = createBoundingVolume(header.viewerRequestVolume, this.computedTransform, this._viewerRequestVolume);
5049
+ this._viewerRequestVolume = createBoundingVolume(header.viewerRequestVolume, this.computedTransform, this._viewerRequestVolume, this._initialTransform);
4725
5050
  }
4726
5051
  const content = header.content;
4727
5052
  if (content) {
4728
5053
  this._contentBoundingVolumeHeaders = Array.isArray(content) ? content : [content];
4729
5054
  }
4730
5055
  const contentHeaders = content ? Array.isArray(content) ? content : [content] : this._contentBoundingVolumeHeaders;
4731
- this._contentBoundingVolumes = contentHeaders.length ? contentHeaders.map((headerEntry) => (headerEntry == null ? void 0 : headerEntry.boundingVolume) ? createBoundingVolume(headerEntry.boundingVolume, this.computedTransform) : this.boundingVolume) : [this.boundingVolume];
5056
+ this._contentBoundingVolumes = contentHeaders.length ? contentHeaders.map((headerEntry) => (headerEntry == null ? void 0 : headerEntry.boundingVolume) ? createBoundingVolume(headerEntry.boundingVolume, this.computedTransform, void 0, this._initialTransform) : this.boundingVolume) : [this.boundingVolume];
4732
5057
  this._contentBoundingVolume = this._contentBoundingVolumes[0] || null;
4733
5058
  }
4734
5059
  /**
@@ -4748,8 +5073,8 @@ var Tile3D = class {
4748
5073
  };
4749
5074
 
4750
5075
  // dist/tileset-3d/format-3d-tiles/tiles-3d-source.js
4751
- var import_geospatial7 = require("@math.gl/geospatial");
4752
- var import_core10 = require("@math.gl/core");
5076
+ var import_geospatial10 = require("@math.gl/geospatial");
5077
+ var import_core12 = require("@math.gl/core");
4753
5078
 
4754
5079
  // dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js
4755
5080
  var Tileset3DTraverser = class extends TilesetTraverser {
@@ -4770,14 +5095,201 @@ var Tileset3DTraverser = class extends TilesetTraverser {
4770
5095
  }
4771
5096
  return true;
4772
5097
  }
5098
+ /**
5099
+ * Runs normal replacement traversal or the dedicated skip-LOD traversal selected by options.
5100
+ *
5101
+ * @param root - Root of the runtime subtree to traverse.
5102
+ * @param frameState - Current culling and LOD state.
5103
+ */
5104
+ executeTraversal(root, frameState) {
5105
+ if (!this.isSkipLevelOfDetailEnabled()) {
5106
+ super.executeTraversal(root, frameState);
5107
+ return;
5108
+ }
5109
+ this.executeSkipTraversal(root, frameState);
5110
+ }
5111
+ /**
5112
+ * Traverses visible replacement branches without requesting skipped intermediate content.
5113
+ *
5114
+ * Base-traversal tiles establish coarse coverage. Below that base, only final desired tiles and
5115
+ * tiles that cross the configured depth/SSE thresholds are requested. Already available
5116
+ * ancestors remain selectable while those requests stream in.
5117
+ *
5118
+ * @param root - Root of the runtime subtree to traverse.
5119
+ * @param frameState - Current culling and LOD state.
5120
+ */
5121
+ executeSkipTraversal(root, frameState) {
5122
+ const stack = [root];
5123
+ root._selectionDepth = root.parent ? root.parent._selectionDepth : 1;
5124
+ while (stack.length > 0) {
5125
+ const tile = stack.pop();
5126
+ const parentRefines = !tile.parent || tile.parent._shouldRefine;
5127
+ const shouldRefine = this.canTraverse(tile, frameState) && this.updateAndPushSkipChildren(tile, frameState, stack) && parentRefines;
5128
+ const stoppedRefining = !shouldRefine && parentRefines;
5129
+ if (!tile.hasRenderContent) {
5130
+ this.emptyTiles[tile.id] = tile;
5131
+ this.loadTile(tile, frameState);
5132
+ if (stoppedRefining) {
5133
+ this.selectDesiredTile(tile, frameState);
5134
+ }
5135
+ } else if (tile.refine === TILE_REFINEMENT.ADD) {
5136
+ this.loadTile(tile, frameState);
5137
+ this.selectDesiredTile(tile, frameState);
5138
+ } else if (tile.refine === TILE_REFINEMENT.REPLACE) {
5139
+ if (this.isInBaseTraversal(tile, frameState)) {
5140
+ this.loadTile(tile, frameState);
5141
+ if (stoppedRefining) {
5142
+ this.selectDesiredTile(tile, frameState);
5143
+ }
5144
+ } else if (stoppedRefining) {
5145
+ this.loadTile(tile, frameState);
5146
+ this.selectDesiredTile(tile, frameState);
5147
+ } else if (this.hasReachedSkippingThreshold(tile, frameState)) {
5148
+ this.loadTile(tile, frameState);
5149
+ }
5150
+ }
5151
+ this.touchTile(tile, frameState);
5152
+ tile._shouldRefine = shouldRefine;
5153
+ }
5154
+ this.completeTraversal(frameState);
5155
+ }
5156
+ /**
5157
+ * Updates and pushes visible children for skip traversal without requiring sibling readiness.
5158
+ *
5159
+ * @param tile - Parent whose children are considered.
5160
+ * @param frameState - Current culling and LOD state.
5161
+ * @param stack - Depth-first traversal stack.
5162
+ * @returns `true` when at least one visible child continues refinement.
5163
+ */
5164
+ updateAndPushSkipChildren(tile, frameState, stack) {
5165
+ this.updateChildTiles(tile, frameState);
5166
+ tile.children.sort(this.compareDistanceToCamera.bind(this));
5167
+ let hasVisibleChild = false;
5168
+ for (const child of tile.children) {
5169
+ child._selectionDepth = tile.hasRenderContent ? tile._selectionDepth + 1 : tile._selectionDepth;
5170
+ if (child.isVisibleAndInRequestVolume) {
5171
+ stack.push(child);
5172
+ hasVisibleChild = true;
5173
+ } else if (this.options.loadSiblings) {
5174
+ this.loadTile(child, frameState);
5175
+ this.touchTile(child, frameState);
5176
+ }
5177
+ }
5178
+ return hasVisibleChild;
5179
+ }
5180
+ /**
5181
+ * Determines whether a tile belongs to the coarse non-skipping portion of the traversal.
5182
+ *
5183
+ * @param tile - Tile considered for loading.
5184
+ * @param frameState - Current traversal frame.
5185
+ * @returns `true` when the tile should always be requested for fallback coverage.
5186
+ */
5187
+ isInBaseTraversal(tile, frameState) {
5188
+ if (this.options.immediatelyLoadDesiredLevelOfDetail) {
5189
+ return false;
5190
+ }
5191
+ if (!this.findNearestRequestedOrLoadedAncestor(tile, frameState)) {
5192
+ return true;
5193
+ }
5194
+ const configuredBaseScreenSpaceError = Number.isFinite(this.options.baseScreenSpaceError) ? Math.max(this.options.baseScreenSpaceError, 0) : 1024;
5195
+ const baseScreenSpaceError = Math.max(configuredBaseScreenSpaceError, tile.tileset.memoryAdjustedScreenSpaceError);
5196
+ if (tile._screenSpaceError === 0 && tile.parent) {
5197
+ return tile.parent._screenSpaceError > baseScreenSpaceError;
5198
+ }
5199
+ return tile._screenSpaceError > baseScreenSpaceError;
5200
+ }
5201
+ /**
5202
+ * Determines whether a skipped branch has crossed its configured request threshold.
5203
+ *
5204
+ * @param tile - Tile considered for loading.
5205
+ * @param frameState - Current traversal frame.
5206
+ * @returns `true` when the tile must be requested instead of skipped.
5207
+ */
5208
+ hasReachedSkippingThreshold(tile, frameState) {
5209
+ if (this.options.immediatelyLoadDesiredLevelOfDetail) {
5210
+ return false;
5211
+ }
5212
+ const progressiveResolutionLeaf = tile._priorityProgressiveResolution && tile._screenSpaceErrorProgressiveResolution <= tile.tileset.memoryAdjustedScreenSpaceError && Boolean(tile.parent && tile.parent._screenSpaceErrorProgressiveResolution > tile.tileset.memoryAdjustedScreenSpaceError);
5213
+ if (progressiveResolutionLeaf) {
5214
+ return true;
5215
+ }
5216
+ const ancestor = this.findNearestRequestedOrLoadedAncestor(tile, frameState);
5217
+ if (!ancestor) {
5218
+ return false;
5219
+ }
5220
+ const skipScreenSpaceErrorFactor = Number.isFinite(this.options.skipScreenSpaceErrorFactor) && this.options.skipScreenSpaceErrorFactor > 0 ? this.options.skipScreenSpaceErrorFactor : 16;
5221
+ const skipLevels = Number.isFinite(this.options.skipLevels) ? Math.max(Math.floor(this.options.skipLevels), 0) : 1;
5222
+ return tile._screenSpaceError < ancestor._screenSpaceError / skipScreenSpaceErrorFactor && tile.depth > ancestor.depth + skipLevels;
5223
+ }
5224
+ /**
5225
+ * Finds the nearest ancestor whose render content is loaded, loading, or requested this frame.
5226
+ *
5227
+ * @param tile - Descendant whose request threshold needs an anchor.
5228
+ * @param frameState - Current traversal frame.
5229
+ * @returns The nearest request anchor, or `null` when none exists.
5230
+ */
5231
+ findNearestRequestedOrLoadedAncestor(tile, frameState) {
5232
+ let ancestor = tile.parent;
5233
+ while (ancestor) {
5234
+ const contentIsUsableAnchor = ancestor.hasRenderContent && !ancestor.contentFailed && (!ancestor.hasUnloadedContent || ancestor._requestedFrame === frameState.frameNumber);
5235
+ if (contentIsUsableAnchor) {
5236
+ return ancestor;
5237
+ }
5238
+ ancestor = ancestor.parent;
5239
+ }
5240
+ return null;
5241
+ }
5242
+ /**
5243
+ * Selects the desired tile, its nearest ready ancestor, or nearby ready descendants.
5244
+ *
5245
+ * @param tile - Tile that ended the desired refinement branch.
5246
+ * @param frameState - Current culling state.
5247
+ */
5248
+ selectDesiredTile(tile, frameState) {
5249
+ let fallbackTile = tile;
5250
+ while (fallbackTile) {
5251
+ if (this.shouldSelectTile(fallbackTile, frameState)) {
5252
+ this.selectTile(fallbackTile, frameState);
5253
+ return;
5254
+ }
5255
+ fallbackTile = fallbackTile.parent;
5256
+ }
5257
+ this.selectLoadedDescendants(tile, frameState);
5258
+ }
5259
+ /**
5260
+ * Selects ready descendants near an unavailable desired tile to reduce temporary empty regions.
5261
+ *
5262
+ * @param root - Unavailable tile whose descendants are searched.
5263
+ * @param frameState - Current culling state.
5264
+ */
5265
+ selectLoadedDescendants(root, frameState) {
5266
+ const stack = [{ tile: root, depth: 0 }];
5267
+ while (stack.length > 0) {
5268
+ const { tile, depth } = stack.pop();
5269
+ for (const child of tile.children) {
5270
+ this.updateTile(child, frameState);
5271
+ if (!child.isVisibleAndInRequestVolume) {
5272
+ continue;
5273
+ }
5274
+ if (child.contentAvailable) {
5275
+ this.selectTile(child, frameState);
5276
+ this.touchTile(child, frameState);
5277
+ } else if (depth < 1) {
5278
+ stack.push({ tile: child, depth: depth + 1 });
5279
+ }
5280
+ }
5281
+ }
5282
+ }
4773
5283
  compareDistanceToCamera(a, b) {
4774
5284
  return b._distanceToCamera === 0 && a._distanceToCamera === 0 ? b._centerZDepth - a._centerZDepth : b._distanceToCamera - a._distanceToCamera;
4775
5285
  }
4776
5286
  /**
4777
- * Starts an implicit subtree request only after culling, request-volume, and SSE eligibility.
5287
+ * Starts an implicit subtree request after culling and request-volume eligibility.
4778
5288
  *
4779
- * The current parent remains the traversal boundary while metadata is loading, which preserves
4780
- * REPLACE coverage and ADD accumulation. Request scheduling reads the tile's existing
5289
+ * The first subtree of an implicit hierarchy materializes independently of SSE because its
5290
+ * placeholder has no render content. Deeper subtree boundaries remain SSE-gated. The current
5291
+ * parent remains the traversal boundary while metadata is loading, which preserves REPLACE
5292
+ * coverage and ADD accumulation. Request scheduling reads the tile's existing
4781
5293
  * progressive-resolution and foveated metrics, so subtree metadata follows the same priority
4782
5294
  * policy as render content. A completion notification lets the owning tileset publish the newly
4783
5295
  * materialized traversal result on the next update.
@@ -4786,7 +5298,9 @@ var Tileset3DTraverser = class extends TilesetTraverser {
4786
5298
  * @param frameState - Current culling and LOD state.
4787
5299
  */
4788
5300
  updateChildTiles(tile, frameState) {
4789
- if (tile.hasUnloadedChildren && tile.isVisibleAndInRequestVolume && this.shouldRefine(tile, frameState)) {
5301
+ var _a;
5302
+ const isImplicitHierarchyRoot = ((_a = tile.header.implicitSubtree) == null ? void 0 : _a.coordinates.level) === 0;
5303
+ if (tile.hasUnloadedChildren && tile.isVisibleAndInRequestVolume && (isImplicitHierarchyRoot || this.shouldRefine(tile, frameState))) {
4790
5304
  const requestKey = `${frameState.viewport.id}:${tile.id}`;
4791
5305
  if (!this.pendingImplicitSubtrees.has(requestKey)) {
4792
5306
  this.pendingImplicitSubtrees.set(requestKey, frameState.frameNumber);
@@ -4977,7 +5491,7 @@ function getI3SSpatialReference(layer) {
4977
5491
  const verticalUnitScale = getI3SVerticalUnitScale(verticalUnit, layer.ZFactor);
4978
5492
  const heightReference = heightModel === "ellipsoidal" ? "ellipsoidal" : heightModel === "gravity_related_height" ? "orthometric" : "unknown";
4979
5493
  const sourceIdentifier = getCrsIdentifier(spatialReference);
4980
- const coordinateFrame = sourceIdentifier === void 0 ? getWktCoordinateFrame(spatialReference == null ? void 0 : spatialReference.wkt) : getIdentifierCoordinateFrame(sourceIdentifier);
5494
+ const coordinateFrame = sourceIdentifier === void 0 ? sourceCrs ? getSpatialCoordinateFrame(sourceCrs) : "unknown" : getSpatialCoordinateFrame(`EPSG:${sourceIdentifier}`);
4981
5495
  const units = getI3SCoordinateUnits(coordinateFrame, verticalUnit, layer.ZFactor);
4982
5496
  const warnings = [];
4983
5497
  const elevationMode = getI3SElevationMode((_e = layer.elevationInfo) == null ? void 0 : _e.mode);
@@ -5035,6 +5549,10 @@ function getI3SElevationMode(mode) {
5035
5549
  }
5036
5550
  function get3DTilesSpatialReference(tileset) {
5037
5551
  var _a, _b;
5552
+ const draftSpatialReference = getDraft3DTilesSpatialReference(tileset);
5553
+ if (draftSpatialReference) {
5554
+ return draftSpatialReference;
5555
+ }
5038
5556
  const geocentricCrs = getTilesetSemanticValue(tileset, "TILESET_CRS_GEOCENTRIC");
5039
5557
  const coordinateEpoch = getTilesetSemanticValue(tileset, "TILESET_CRS_COORDINATE_EPOCH");
5040
5558
  const warnings = [];
@@ -5072,6 +5590,87 @@ function get3DTilesSpatialReference(tileset) {
5072
5590
  warnings
5073
5591
  });
5074
5592
  }
5593
+ function getDraft3DTilesSpatialReference(tileset) {
5594
+ var _a, _b, _c;
5595
+ const extension = (_a = tileset.extensions) == null ? void 0 : _a.EXT_geospatial_crs;
5596
+ if (!extension) {
5597
+ return void 0;
5598
+ }
5599
+ const warnings = [];
5600
+ let sourceCrs;
5601
+ let verticalCrs;
5602
+ let epoch;
5603
+ let representation;
5604
+ if (extension.format === "wkid") {
5605
+ const wkidExtension = (_b = extension.extensions) == null ? void 0 : _b.EXT_geospatial_crs_wkid;
5606
+ if (typeof (wkidExtension == null ? void 0 : wkidExtension.authority) === "string" && Number.isInteger(wkidExtension.wkid)) {
5607
+ sourceCrs = `${wkidExtension.authority}:${wkidExtension.wkid}`;
5608
+ representation = "identifier";
5609
+ if (Number.isInteger(wkidExtension.vcsWkid)) {
5610
+ verticalCrs = `${wkidExtension.authority}:${wkidExtension.vcsWkid}`;
5611
+ }
5612
+ epoch = parseCoordinateEpoch(wkidExtension.epoch);
5613
+ if (wkidExtension.epoch !== void 0 && epoch === void 0) {
5614
+ warnings.push("EXT_geospatial_crs_wkid epoch is not a finite decimal year");
5615
+ }
5616
+ } else {
5617
+ warnings.push("EXT_geospatial_crs_wkid requires string authority and integer wkid");
5618
+ }
5619
+ } else if (extension.format === "wkt2") {
5620
+ const wktExtension = (_c = extension.extensions) == null ? void 0 : _c.EXT_geospatial_crs_wkt2;
5621
+ if (typeof (wktExtension == null ? void 0 : wktExtension.wkt2) === "string" && wktExtension.wkt2.trim()) {
5622
+ sourceCrs = wktExtension.wkt2;
5623
+ representation = "wkt";
5624
+ } else {
5625
+ warnings.push("EXT_geospatial_crs_wkt2 requires a nonempty wkt2 string");
5626
+ }
5627
+ } else {
5628
+ warnings.push(`Unsupported EXT_geospatial_crs format ${String(extension.format)}`);
5629
+ }
5630
+ const coordinateFrame = representation === "wkt" ? getSpatialCoordinateFrame(sourceCrs) : getKnownCrsIdentifierCoordinateFrame(sourceCrs) || "unknown";
5631
+ const verticalSemantics = getDraftVerticalCrsSemantics(verticalCrs);
5632
+ if (verticalCrs && !verticalSemantics) {
5633
+ warnings.push(`Unsupported vertical CRS ${String(verticalCrs)}; vertical unit and height reference are unknown`);
5634
+ }
5635
+ const verticalUnit = verticalCrs ? verticalSemantics == null ? void 0 : verticalSemantics.unit : "meter";
5636
+ return createTilesetSpatialReference({
5637
+ sourceCrs,
5638
+ sourceCrsState: sourceCrs ? "explicit" : "unknown",
5639
+ sourceCrsRepresentation: representation,
5640
+ verticalCrs,
5641
+ coordinateEpoch: epoch,
5642
+ units: coordinateFrame === "geocentric" ? ["meter", "meter", "meter"] : coordinateFrame === "geographic" && verticalUnit ? ["degree", "degree", verticalUnit] : void 0,
5643
+ verticalUnitScale: verticalCrs ? (verticalSemantics == null ? void 0 : verticalSemantics.verticalUnitScale) ?? Number.NaN : 1,
5644
+ heightReference: verticalCrs ? (verticalSemantics == null ? void 0 : verticalSemantics.heightReference) ?? "unknown" : sourceCrs ? "ellipsoidal" : "unknown",
5645
+ coordinateFrame,
5646
+ axisOrder: sourceCrs ? "xyz" : "unknown",
5647
+ provenance: "metadata",
5648
+ warnings
5649
+ });
5650
+ }
5651
+ function getDraftVerticalCrsSemantics(verticalCrs) {
5652
+ if (typeof verticalCrs !== "string") {
5653
+ return void 0;
5654
+ }
5655
+ const epsgMatch = /^EPSG:(\d+)$/i.exec(verticalCrs);
5656
+ if (!epsgMatch) {
5657
+ return void 0;
5658
+ }
5659
+ switch (Number(epsgMatch[1])) {
5660
+ case 5702:
5661
+ // NGVD29 height (ftUS)
5662
+ case 6360:
5663
+ return {
5664
+ unit: "us-foot",
5665
+ verticalUnitScale: 1200 / 3937,
5666
+ heightReference: "orthometric"
5667
+ };
5668
+ case 5703:
5669
+ return { unit: "meter", verticalUnitScale: 1, heightReference: "orthometric" };
5670
+ default:
5671
+ return void 0;
5672
+ }
5673
+ }
5075
5674
  function parseCoordinateEpoch(value) {
5076
5675
  if (typeof value !== "string" && typeof value !== "number") {
5077
5676
  return void 0;
@@ -5096,71 +5695,493 @@ function getI3SVerticalCrsDefinition(spatialReference) {
5096
5695
  function getCrsIdentifier(spatialReference) {
5097
5696
  return (spatialReference == null ? void 0 : spatialReference.latestWkid) ?? (spatialReference == null ? void 0 : spatialReference.wkid);
5098
5697
  }
5099
- function getIdentifierCoordinateFrame(identifier) {
5100
- if (identifier === 4978) {
5101
- return "geocentric";
5698
+ function getTilesetSemanticValue(tileset, semantic) {
5699
+ var _a, _b, _c, _d;
5700
+ const classIdentifier = (_a = tileset.metadata) == null ? void 0 : _a.class;
5701
+ if (!classIdentifier) {
5702
+ return void 0;
5102
5703
  }
5103
- if (identifier === 4326 || identifier === 4490 || identifier === 4979) {
5104
- return "geographic";
5704
+ const classDefinition = (_c = (_b = tileset.schema) == null ? void 0 : _b.classes) == null ? void 0 : _c[classIdentifier];
5705
+ const propertyDefinitions = classDefinition == null ? void 0 : classDefinition.properties;
5706
+ const propertyValues = (_d = tileset.metadata) == null ? void 0 : _d.properties;
5707
+ for (const [propertyIdentifier, propertyDefinition] of Object.entries(propertyDefinitions || {})) {
5708
+ if (propertyDefinition.semantic === semantic) {
5709
+ return propertyValues == null ? void 0 : propertyValues[propertyIdentifier];
5710
+ }
5105
5711
  }
5106
- return "projected";
5712
+ return void 0;
5107
5713
  }
5108
- function getWktCoordinateFrame(wkt) {
5109
- var _a;
5110
- if (!wkt) {
5111
- return "unknown";
5112
- }
5113
- const normalizedWkt = wkt.trim().toUpperCase();
5114
- const rootKeyword = (_a = normalizedWkt.match(/^([A-Z][A-Z0-9_]*)\s*[\[(]/)) == null ? void 0 : _a[1];
5115
- if (rootKeyword === "GEOCCS") {
5116
- return "geocentric";
5117
- }
5118
- if (rootKeyword === "GEOGCS" || rootKeyword === "GEOGRAPHICCRS" || rootKeyword === "GEOGRAPHIC2DCRS" || rootKeyword === "GEOGRAPHIC3DCRS") {
5119
- return "geographic";
5714
+
5715
+ // dist/spatial/spatial-coordinate-transformer.js
5716
+ var import_core10 = require("@math.gl/core");
5717
+ var import_geospatial7 = require("@math.gl/geospatial");
5718
+ var import_proj42 = require("@math.gl/proj4");
5719
+
5720
+ // dist/spatial/spatial-resource-registry.js
5721
+ var import_geoid = require("@math.gl/geoid");
5722
+ var import_proj4 = require("@math.gl/proj4");
5723
+ var geoidModels = /* @__PURE__ */ new Map();
5724
+ function registerSpatialCrs(name, definition) {
5725
+ validateResourceName(name, "CRS");
5726
+ import_proj4.Proj4Projection.defineProjectionAliases({ [name]: definition });
5727
+ }
5728
+ function registerSpatialDatumGrid(name, data) {
5729
+ validateResourceName(name, "datum grid");
5730
+ import_proj4.Proj4Projection.registerDatumGrid(name, data);
5731
+ }
5732
+ function registerGeoidModel(name, geoid) {
5733
+ validateResourceName(name, "geoid model");
5734
+ geoidModels.set(name, geoid);
5735
+ }
5736
+ function registerGeoidModelFromPgm(name, data, options = {}) {
5737
+ const geoid = (0, import_geoid.parsePGM)(data, options);
5738
+ registerGeoidModel(name, geoid);
5739
+ return geoid;
5740
+ }
5741
+ function getGeoidModel(name) {
5742
+ return geoidModels.get(name);
5743
+ }
5744
+ function validateResourceName(name, resourceType) {
5745
+ if (!name.trim()) {
5746
+ throw new Error(`${resourceType} name must not be empty`);
5120
5747
  }
5121
- if (rootKeyword === "PROJCS" || rootKeyword === "PROJCRS" || rootKeyword === "PROJECTEDCRS") {
5122
- return "projected";
5748
+ }
5749
+
5750
+ // dist/spatial/spatial-coordinate-transformer.js
5751
+ var SpatialCoordinateTransformer = class {
5752
+ /** Normalized spatial metadata describing this transformation. */
5753
+ spatialReference;
5754
+ /** Projection from the source CRS directly to the requested horizontal target CRS. */
5755
+ horizontalProjection;
5756
+ /** Projection from the source CRS to geographic longitude, latitude, and ellipsoidal height. */
5757
+ geographicProjection;
5758
+ /** Projection from adjusted geographic coordinates to the requested output CRS. */
5759
+ heightOutputProjection;
5760
+ /** Whether source coordinates use the WGS84 geocentric frame handled by math.gl. */
5761
+ sourceIsGeocentric;
5762
+ /** Whether output coordinates use the WGS84 geocentric frame handled by math.gl. */
5763
+ outputIsGeocentric;
5764
+ /** Whether source coordinates already use conventional WGS84 longitude/latitude order. */
5765
+ sourceIsGeographic;
5766
+ /** Whether output coordinates use conventional WGS84 longitude/latitude order. */
5767
+ outputIsGeographic;
5768
+ /** Geoid model used to convert between ellipsoidal and orthometric heights. */
5769
+ geoid;
5770
+ /**
5771
+ * Creates a coordinate transformer.
5772
+ *
5773
+ * @param spatialReference - Format-discovered and option-normalized spatial metadata.
5774
+ * @param options - Application options containing an optional geoid model.
5775
+ */
5776
+ constructor(spatialReference, options = {}) {
5777
+ this.spatialReference = spatialReference;
5778
+ validateTransformRequest(spatialReference);
5779
+ const outputCrs = spatialReference.targetCrs || spatialReference.sourceCrs;
5780
+ this.sourceIsGeocentric = isWgs84Geocentric(spatialReference.sourceCrs);
5781
+ this.outputIsGeocentric = isWgs84Geocentric(outputCrs);
5782
+ this.sourceIsGeographic = isWgs84Geographic(spatialReference.sourceCrs);
5783
+ this.outputIsGeographic = isWgs84Geographic(outputCrs);
5784
+ if (spatialReference.sourceCrs && spatialReference.targetCrs && !this.sourceIsGeocentric && !this.outputIsGeocentric) {
5785
+ this.horizontalProjection = new import_proj42.Proj4Projection({
5786
+ from: getHorizontalProj4Definition(spatialReference.sourceCrs),
5787
+ to: getHorizontalProj4Definition(spatialReference.targetCrs),
5788
+ enforceAxis: false
5789
+ });
5790
+ }
5791
+ if (requiresHeightTransformation(spatialReference) || this.sourceIsGeocentric || this.outputIsGeocentric) {
5792
+ if (!this.sourceIsGeographic && !this.sourceIsGeocentric) {
5793
+ this.geographicProjection = new import_proj42.Proj4Projection({
5794
+ from: getHorizontalProj4Definition(spatialReference.sourceCrs),
5795
+ to: WGS84_GEOGRAPHIC_CRS,
5796
+ enforceAxis: false
5797
+ });
5798
+ }
5799
+ if (!this.outputIsGeographic && !this.outputIsGeocentric) {
5800
+ this.heightOutputProjection = new import_proj42.Proj4Projection({
5801
+ from: WGS84_GEOGRAPHIC_CRS,
5802
+ to: getHorizontalProj4Definition(outputCrs),
5803
+ enforceAxis: false
5804
+ });
5805
+ }
5806
+ }
5807
+ if (requiresHeightTransformation(spatialReference)) {
5808
+ this.geoid = resolveGeoid(options.geoidModel);
5809
+ if (!this.geoid) {
5810
+ const modelName = typeof options.geoidModel === "string" ? ` "${options.geoidModel}"` : "";
5811
+ throw new Error(`Height conversion requires a registered geoid model${modelName}; call registerGeoidModel() or pass a parsed Geoid instance`);
5812
+ }
5813
+ }
5123
5814
  }
5124
- if (rootKeyword === "GEODCRS" || rootKeyword === "GEODETICCRS") {
5125
- if (/\bCS\s*[\[(]\s*CARTESIAN\s*,\s*3\b/.test(normalizedWkt)) {
5126
- return "geocentric";
5815
+ /**
5816
+ * Transforms one `[x, y, z?]` coordinate while retaining additional components.
5817
+ *
5818
+ * @param coordinate - Source coordinate in the format adapter's normalized `x/y/z` order.
5819
+ * @returns A new transformed coordinate array.
5820
+ */
5821
+ transformPosition(coordinate) {
5822
+ if (coordinate.length < 2) {
5823
+ throw new Error("A spatial coordinate must contain at least x and y components");
5127
5824
  }
5128
- if (/\bCS\s*[\[(]\s*ELLIPSOIDAL\s*,\s*[23]\b/.test(normalizedWkt)) {
5129
- return "geographic";
5825
+ const result = [...coordinate];
5826
+ if (requiresHeightTransformation(this.spatialReference) || this.sourceIsGeocentric || this.outputIsGeocentric) {
5827
+ if (result.length < 3 || !Number.isFinite(result[2])) {
5828
+ throw new Error("Three-dimensional CRS conversion requires a finite z coordinate");
5829
+ }
5830
+ const geographic = this.toGeographic(result);
5831
+ if (requiresHeightTransformation(this.spatialReference)) {
5832
+ const geoidUndulation = this.geoid.getHeight(geographic[1], geographic[0]);
5833
+ geographic[2] = transformHeight(geographic[2], geoidUndulation, this.spatialReference.heightReference, this.spatialReference.targetHeightReference);
5834
+ }
5835
+ const projected = this.fromGeographic(geographic);
5836
+ result.splice(0, projected.length, ...projected);
5837
+ return result;
5130
5838
  }
5839
+ if (this.horizontalProjection) {
5840
+ const projected = this.horizontalProjection.project(result.slice(0, 3));
5841
+ result.splice(0, projected.length, ...projected);
5842
+ }
5843
+ return result;
5131
5844
  }
5132
- return "unknown";
5845
+ /** Transform a packed sequence of xyz positions, preserving the input array type only when safe. */
5846
+ transformPositions(positions) {
5847
+ if (positions.length % 3 !== 0) {
5848
+ throw new Error("Packed spatial positions must contain a multiple of three components");
5849
+ }
5850
+ const transformedPositions = new Float64Array(positions.length);
5851
+ for (let index = 0; index < positions.length; index += 3) {
5852
+ const transformed = this.transformPosition([
5853
+ Number(positions[index]),
5854
+ Number(positions[index + 1]),
5855
+ Number(positions[index + 2])
5856
+ ]);
5857
+ transformedPositions[index] = transformed[0];
5858
+ transformedPositions[index + 1] = transformed[1];
5859
+ transformedPositions[index + 2] = transformed[2];
5860
+ }
5861
+ return transformedPositions;
5862
+ }
5863
+ /** Transform packed xyz normals using a local finite-difference tangent approximation. */
5864
+ transformNormals(normals, positions) {
5865
+ if (normals.length !== positions.length || normals.length % 3 !== 0) {
5866
+ throw new Error("Spatial normals and positions must have matching xyz component counts");
5867
+ }
5868
+ const transformedNormals = new Float32Array(normals.length);
5869
+ const epsilon = 1e-5;
5870
+ for (let index = 0; index < normals.length; index += 3) {
5871
+ const position = [
5872
+ Number(positions[index]),
5873
+ Number(positions[index + 1]),
5874
+ Number(positions[index + 2])
5875
+ ];
5876
+ const normal = [
5877
+ Number(normals[index]),
5878
+ Number(normals[index + 1]),
5879
+ Number(normals[index + 2])
5880
+ ];
5881
+ const jacobian = getFiniteDifferenceJacobian(this, position, epsilon);
5882
+ const transformedNormal = inverseTransposeMultiply(jacobian, normal);
5883
+ const length = Math.hypot(...transformedNormal);
5884
+ if (length > 0 && Number.isFinite(length)) {
5885
+ transformedNormals[index] = transformedNormal[0] / length;
5886
+ transformedNormals[index + 1] = transformedNormal[1] / length;
5887
+ transformedNormals[index + 2] = transformedNormal[2] / length;
5888
+ } else {
5889
+ transformedNormals[index] = normal[0];
5890
+ transformedNormals[index + 1] = normal[1];
5891
+ transformedNormals[index + 2] = normal[2];
5892
+ }
5893
+ }
5894
+ return transformedNormals;
5895
+ }
5896
+ /** Convert one source coordinate to conventional WGS84 longitude, latitude, and height. */
5897
+ toGeographic(coordinate) {
5898
+ if (this.sourceIsGeocentric) {
5899
+ return Array.from(import_geospatial7.Ellipsoid.WGS84.cartesianToCartographic(new import_core10.Vector3(coordinate)));
5900
+ }
5901
+ if (this.sourceIsGeographic) {
5902
+ return coordinate.slice(0, 3);
5903
+ }
5904
+ return this.geographicProjection.project(coordinate.slice(0, 3));
5905
+ }
5906
+ /** Convert one conventional WGS84 geographic coordinate to the selected output CRS. */
5907
+ fromGeographic(coordinate) {
5908
+ if (this.outputIsGeocentric) {
5909
+ return Array.from(import_geospatial7.Ellipsoid.WGS84.cartographicToCartesian(new import_core10.Vector3(coordinate)));
5910
+ }
5911
+ if (this.outputIsGeographic) {
5912
+ return coordinate.slice(0, 3);
5913
+ }
5914
+ return this.heightOutputProjection.project(coordinate.slice(0, 3));
5915
+ }
5916
+ };
5917
+ function getFiniteDifferenceJacobian(transformer, position, epsilon) {
5918
+ const columns = [];
5919
+ for (let axis = 0; axis < 3; axis++) {
5920
+ const plus = position.slice();
5921
+ const minus = position.slice();
5922
+ plus[axis] += epsilon;
5923
+ minus[axis] -= epsilon;
5924
+ const plusPosition = transformer.transformPosition(plus);
5925
+ const minusPosition = transformer.transformPosition(minus);
5926
+ columns.push([
5927
+ (plusPosition[0] - minusPosition[0]) / (2 * epsilon),
5928
+ (plusPosition[1] - minusPosition[1]) / (2 * epsilon),
5929
+ (plusPosition[2] - minusPosition[2]) / (2 * epsilon)
5930
+ ]);
5931
+ }
5932
+ return columns;
5133
5933
  }
5134
- function getTilesetSemanticValue(tileset, semantic) {
5135
- var _a, _b, _c, _d;
5136
- const classIdentifier = (_a = tileset.metadata) == null ? void 0 : _a.class;
5137
- if (!classIdentifier) {
5138
- return void 0;
5934
+ function inverseTransposeMultiply(jacobianColumns, vector) {
5935
+ const [a, b, c] = jacobianColumns;
5936
+ const determinant = a[0] * (b[1] * c[2] - b[2] * c[1]) - b[0] * (a[1] * c[2] - a[2] * c[1]) + c[0] * (a[1] * b[2] - a[2] * b[1]);
5937
+ if (!Number.isFinite(determinant) || Math.abs(determinant) < 1e-15) {
5938
+ return vector;
5139
5939
  }
5140
- const classDefinition = (_c = (_b = tileset.schema) == null ? void 0 : _b.classes) == null ? void 0 : _c[classIdentifier];
5141
- const propertyDefinitions = classDefinition == null ? void 0 : classDefinition.properties;
5142
- const propertyValues = (_d = tileset.metadata) == null ? void 0 : _d.properties;
5143
- for (const [propertyIdentifier, propertyDefinition] of Object.entries(propertyDefinitions || {})) {
5144
- if (propertyDefinition.semantic === semantic) {
5145
- return propertyValues == null ? void 0 : propertyValues[propertyIdentifier];
5940
+ const inverseTranspose = [
5941
+ [b[1] * c[2] - b[2] * c[1], a[2] * c[1] - a[1] * c[2], a[1] * b[2] - a[2] * b[1]],
5942
+ [b[2] * c[0] - b[0] * c[2], a[0] * c[2] - a[2] * c[0], a[2] * b[0] - a[0] * b[2]],
5943
+ [b[0] * c[1] - b[1] * c[0], a[1] * c[0] - a[0] * c[1], a[0] * b[1] - a[1] * b[0]]
5944
+ ];
5945
+ return inverseTranspose.map((row) => (row[0] * vector[0] + row[1] * vector[1] + row[2] * vector[2]) / determinant);
5946
+ }
5947
+ function getHorizontalProj4Definition(definition) {
5948
+ if (!definition) {
5949
+ throw new Error("Cannot construct a projection because the CRS is unknown");
5950
+ }
5951
+ return (0, import_proj42.toProj4CRSDefinition)(definition, { mode: "horizontal" });
5952
+ }
5953
+ function validateTransformRequest(spatialReference) {
5954
+ if (spatialReference.outputCoordinates === "local-enu") {
5955
+ throw new Error("local-enu output requires a dataset-derived local origin and must be created by a tileset source");
5956
+ }
5957
+ if (spatialReference.outputCoordinates === "target-crs" && !spatialReference.targetCrs) {
5958
+ throw new Error("target-crs output requires targetCrs");
5959
+ }
5960
+ if (!spatialReference.sourceCrs && spatialReference.status === "unresolved") {
5961
+ throw new Error("Cannot transform coordinates because the source CRS is unknown");
5962
+ }
5963
+ if (requiresHeightTransformation(spatialReference) && spatialReference.heightReference === "unknown") {
5964
+ throw new Error("Cannot convert heights because the source height reference is unknown");
5965
+ }
5966
+ if (spatialReference.coordinateFrame === "geocentric" && !isWgs84Geocentric(spatialReference.sourceCrs)) {
5967
+ throw new Error("Only EPSG:4978 geocentric coordinates are supported by the current spatial transformer");
5968
+ }
5969
+ if (spatialReference.status === "unresolved") {
5970
+ throw new Error("The requested spatial output cannot be resolved from the available metadata");
5971
+ }
5972
+ }
5973
+ function isWgs84Geocentric(definition) {
5974
+ return typeof definition === "string" && normalizeCrsIdentifier(definition) === WGS84_GEOCENTRIC_CRS;
5975
+ }
5976
+ function isWgs84Geographic(definition) {
5977
+ if (typeof definition !== "string") {
5978
+ return false;
5979
+ }
5980
+ const identifier = normalizeCrsIdentifier(definition);
5981
+ return identifier === WGS84_GEOGRAPHIC_CRS || identifier === "EPSG:4979" || identifier === "OGC:CRS84";
5982
+ }
5983
+ function requiresHeightTransformation(spatialReference) {
5984
+ const target = spatialReference.targetHeightReference;
5985
+ return target !== "native" && target !== spatialReference.heightReference;
5986
+ }
5987
+ function resolveGeoid(model) {
5988
+ return typeof model === "string" ? getGeoidModel(model) : model;
5989
+ }
5990
+ function transformHeight(height, geoidUndulation, source, target) {
5991
+ if (source === target || target === "native") {
5992
+ return height;
5993
+ }
5994
+ if (source === "orthometric" && target === "ellipsoidal") {
5995
+ return height + geoidUndulation;
5996
+ }
5997
+ if (source === "ellipsoidal" && target === "orthometric") {
5998
+ return height - geoidUndulation;
5999
+ }
6000
+ throw new Error(`Unsupported height conversion from ${source} to ${target}`);
6001
+ }
6002
+
6003
+ // dist/spatial/tiles-3d-spatial-transformer.js
6004
+ var import_core11 = require("@math.gl/core");
6005
+ var import_geospatial8 = require("@math.gl/geospatial");
6006
+ var Tiles3DSpatialTransformer = class {
6007
+ /** Normalized reference describing the completed operation. */
6008
+ spatialReference;
6009
+ coordinateTransformer;
6010
+ geographicCoordinateTransformer;
6011
+ /** Creates a 3D Tiles transformer for a requested target CRS. */
6012
+ constructor(spatialReference, options = {}) {
6013
+ this.coordinateTransformer = new SpatialCoordinateTransformer(spatialReference, options);
6014
+ const geographicSpatialReference = {
6015
+ ...spatialReference,
6016
+ sourceCrs: "EPSG:4326",
6017
+ targetCrs: spatialReference.targetCrs || spatialReference.sourceCrs,
6018
+ coordinateFrame: "geographic",
6019
+ axisOrder: "xy"
6020
+ };
6021
+ this.geographicCoordinateTransformer = new SpatialCoordinateTransformer(geographicSpatialReference, options);
6022
+ this.spatialReference = spatialReference;
6023
+ }
6024
+ /** Transform packed POSITION values into the target CRS. */
6025
+ transformPositions(positions) {
6026
+ return this.coordinateTransformer.transformPositions(positions);
6027
+ }
6028
+ /** Transform packed NORMAL values using the corresponding source positions. */
6029
+ transformNormals(normals, positions) {
6030
+ return this.coordinateTransformer.transformNormals(normals, positions);
6031
+ }
6032
+ /** Rebuild a conservative target-frame volume from a source volume. */
6033
+ transformBoundingVolume(volume, sourceTransform) {
6034
+ const transform = sourceTransform ? new import_core11.Matrix4(Array.from(sourceTransform)) : void 0;
6035
+ const samples = volume.region ? getRegionSamples(volume.region) : getVolumeSamples(volume).map((sample) => transform ? Array.from(transform.transformAsPoint(sample)) : sample);
6036
+ if (!samples.length) {
6037
+ return volume;
6038
+ }
6039
+ const transformed = volume.region ? samples.map((sample) => this.transformRegionSample(sample)) : samples.map((sample) => this.coordinateTransformer.transformPosition(sample));
6040
+ return { box: createAxisAlignedBox(transformed) };
6041
+ }
6042
+ /** Transforms one WGS84 region sample into the selected output frame. */
6043
+ transformRegionSample(sample) {
6044
+ const [longitudeDegrees, latitudeDegrees, height] = sample;
6045
+ const targetCrs = this.spatialReference.targetCrs || this.spatialReference.sourceCrs;
6046
+ if (targetCrs && getSpatialCoordinateFrame(targetCrs) === "geocentric") {
6047
+ const cartesian = import_geospatial8.Ellipsoid.WGS84.cartographicToCartesian([
6048
+ longitudeDegrees,
6049
+ latitudeDegrees,
6050
+ height
6051
+ ]);
6052
+ return Array.from(cartesian);
5146
6053
  }
6054
+ return this.geographicCoordinateTransformer.transformPosition(sample);
5147
6055
  }
5148
- return void 0;
6056
+ };
6057
+ function getRegionSamples(region) {
6058
+ if (region.length < 6 || region.some((value) => !Number.isFinite(value))) {
6059
+ return [];
6060
+ }
6061
+ const [west, south, east, north, minimumHeight, maximumHeight] = region;
6062
+ const longitudeEnd = east < west ? east + Math.PI * 2 : east;
6063
+ const longitudes = getIntervalSamples(west, longitudeEnd);
6064
+ const latitudes = getIntervalSamples(south, north);
6065
+ const samples = [];
6066
+ for (const longitude of longitudes) {
6067
+ for (const latitude of latitudes) {
6068
+ for (const height of [minimumHeight, maximumHeight]) {
6069
+ samples.push([longitude * 180 / Math.PI, latitude * 180 / Math.PI, height]);
6070
+ }
6071
+ }
6072
+ }
6073
+ return samples;
6074
+ }
6075
+ function getIntervalSamples(start, end) {
6076
+ const samples = [start, (start + end) / 2, end];
6077
+ const angularStep = Math.PI / 2;
6078
+ const firstCriticalIndex = Math.ceil(start / angularStep);
6079
+ const lastCriticalIndex = Math.floor(end / angularStep);
6080
+ for (let criticalIndex = firstCriticalIndex; criticalIndex <= lastCriticalIndex; criticalIndex++) {
6081
+ samples.push(criticalIndex * angularStep);
6082
+ }
6083
+ return samples;
6084
+ }
6085
+ function getVolumeSamples(volume) {
6086
+ if (volume.box && volume.box.length >= 12) {
6087
+ const center = volume.box.slice(0, 3);
6088
+ const axes = [volume.box.slice(3, 6), volume.box.slice(6, 9), volume.box.slice(9, 12)];
6089
+ const samples = [];
6090
+ for (let firstIndex = 0; firstIndex <= 4; firstIndex++) {
6091
+ for (let secondIndex = 0; secondIndex <= 4; secondIndex++) {
6092
+ for (let thirdIndex = 0; thirdIndex <= 4; thirdIndex++) {
6093
+ const first = firstIndex / 2 - 1;
6094
+ const second = secondIndex / 2 - 1;
6095
+ const third = thirdIndex / 2 - 1;
6096
+ samples.push([
6097
+ center[0] + first * axes[0][0] + second * axes[1][0] + third * axes[2][0],
6098
+ center[1] + first * axes[0][1] + second * axes[1][1] + third * axes[2][1],
6099
+ center[2] + first * axes[0][2] + second * axes[1][2] + third * axes[2][2]
6100
+ ]);
6101
+ }
6102
+ }
6103
+ }
6104
+ return samples;
6105
+ }
6106
+ if (volume.sphere && volume.sphere.length >= 4) {
6107
+ const [x, y, z, radius] = volume.sphere;
6108
+ const samples = [
6109
+ [x - radius, y, z],
6110
+ [x + radius, y, z],
6111
+ [x, y - radius, z],
6112
+ [x, y + radius, z],
6113
+ [x, y, z - radius],
6114
+ [x, y, z + radius]
6115
+ ];
6116
+ for (let longitudeIndex = 0; longitudeIndex < 16; longitudeIndex++) {
6117
+ const longitude = longitudeIndex * Math.PI * 2 / 16;
6118
+ samples.push([x + radius * Math.cos(longitude), y + radius * Math.sin(longitude), z]);
6119
+ }
6120
+ return samples;
6121
+ }
6122
+ return [];
6123
+ }
6124
+ function createAxisAlignedBox(samples) {
6125
+ const minimum = [Infinity, Infinity, Infinity];
6126
+ const maximum = [-Infinity, -Infinity, -Infinity];
6127
+ for (const sample of samples) {
6128
+ for (let axis = 0; axis < 3; axis++) {
6129
+ minimum[axis] = Math.min(minimum[axis], sample[axis]);
6130
+ maximum[axis] = Math.max(maximum[axis], sample[axis]);
6131
+ }
6132
+ }
6133
+ const padding = [
6134
+ Math.max((maximum[0] - minimum[0]) * 0.01, 1e-9),
6135
+ Math.max((maximum[1] - minimum[1]) * 0.01, 1e-9),
6136
+ Math.max((maximum[2] - minimum[2]) * 0.01, 1e-9)
6137
+ ];
6138
+ const center = [
6139
+ (minimum[0] + maximum[0]) / 2,
6140
+ (minimum[1] + maximum[1]) / 2,
6141
+ (minimum[2] + maximum[2]) / 2
6142
+ ];
6143
+ return [
6144
+ ...center,
6145
+ (maximum[0] - minimum[0]) / 2 + padding[0],
6146
+ 0,
6147
+ 0,
6148
+ 0,
6149
+ (maximum[1] - minimum[1]) / 2 + padding[1],
6150
+ 0,
6151
+ 0,
6152
+ 0,
6153
+ (maximum[2] - minimum[2]) / 2 + padding[2]
6154
+ ];
6155
+ }
6156
+
6157
+ // dist/tileset-3d/format-3d-tiles/implicit-tiling.js
6158
+ var import_s22 = require("@math.gl/dggs/s2");
6159
+
6160
+ // dist/tileset-3d/helpers/s2-bounding-volume.js
6161
+ var import_s2 = require("@math.gl/dggs/s2");
6162
+ var import_geospatial9 = require("@math.gl/geospatial");
6163
+ function convertS2BoundingVolumeToOBB(s2VolumeInfo) {
6164
+ const [[west, south], [east, north]] = (0, import_s2.getS2Bounds)(s2VolumeInfo.token);
6165
+ const orientedBoundingBox = (0, import_geospatial9.makeOBBFromRegion)([west, south, east, north, s2VolumeInfo.minimumHeight, s2VolumeInfo.maximumHeight], import_geospatial9.Ellipsoid.WGS84, { units: "degrees" });
6166
+ return [...orientedBoundingBox.center, ...orientedBoundingBox.halfAxes];
5149
6167
  }
5150
6168
 
5151
6169
  // dist/tileset-3d/format-3d-tiles/implicit-tiling.js
5152
6170
  var QUADTREE_CHILD_COUNT = 4;
5153
6171
  var OCTREE_CHILD_COUNT = 8;
6172
+ var AVAILABILITY_RANK_CACHE = /* @__PURE__ */ new WeakMap();
5154
6173
  function createImplicitSubtreeReference(descriptor, coordinates) {
5155
6174
  return {
5156
6175
  coordinates,
5157
6176
  subtreeUrl: replaceImplicitUrlTemplate(descriptor.subtreesUrlTemplate, coordinates),
5158
- descriptor
6177
+ descriptor,
6178
+ resource: findImplicitPackageResource(descriptor.resourceFiles, replaceImplicitUrlTemplate(descriptor.subtreesUrlTemplate, coordinates))
5159
6179
  };
5160
6180
  }
5161
6181
  function materializeImplicitSubtree(subtree, reference) {
5162
6182
  const { descriptor } = reference;
5163
6183
  validateImplicitDescriptor(descriptor);
6184
+ validateImplicitSubtree(subtree, descriptor);
5164
6185
  const counters = { tileCount: 0, childSubtreeCount: 0 };
5165
6186
  const root = materializeAvailableTile(subtree, reference, { localLevel: 0, mortonIndex: 0 }, reference.coordinates, counters);
5166
6187
  if (!root) {
@@ -5168,14 +6189,79 @@ function materializeImplicitSubtree(subtree, reference) {
5168
6189
  }
5169
6190
  return { root, ...counters };
5170
6191
  }
5171
- function replaceImplicitUrlTemplate(templateUrl, coordinates) {
6192
+ function validateImplicitSubtree(subtree, descriptor) {
6193
+ const childCount = getImplicitChildCount(descriptor.subdivisionScheme);
6194
+ const tileElementCount = (childCount ** descriptor.subtreeLevels - 1) / (childCount - 1);
6195
+ const childSubtreeElementCount = childCount ** descriptor.subtreeLevels;
6196
+ validateAvailabilityLength(subtree.tileAvailability, tileElementCount, "tile");
6197
+ const contentAvailabilityStreams = Array.isArray(subtree.contentAvailability) ? subtree.contentAvailability : subtree.contentAvailability ? [subtree.contentAvailability] : [];
6198
+ for (const contentAvailability of contentAvailabilityStreams) {
6199
+ validateAvailabilityLength(contentAvailability, tileElementCount, "content");
6200
+ }
6201
+ validateAvailabilityLength(subtree.childSubtreeAvailability, childSubtreeElementCount, "child subtree");
6202
+ const availableTileCount = countAvailableElements(subtree.tileAvailability, tileElementCount);
6203
+ const availableContentCount = contentAvailabilityStreams[0] ? countAvailableElements(contentAvailabilityStreams[0], tileElementCount) : 0;
6204
+ validateAttributeLengths(subtree.tileAttributes, availableTileCount, "tile");
6205
+ validateAttributeLengths(subtree.contentAttributes, availableContentCount, "content");
6206
+ if (subtree.tilePropertyRows && subtree.tilePropertyRows.length !== availableTileCount) {
6207
+ throw new Error("3DTILES_subtree: tile property-table count does not match availability");
6208
+ }
6209
+ if (subtree.contentPropertyRows && subtree.contentPropertyRows.length !== availableContentCount) {
6210
+ throw new Error("3DTILES_subtree: content property-table count does not match availability");
6211
+ }
6212
+ }
6213
+ function validateAvailabilityLength(availability, elementCount, label) {
6214
+ if (availability.constant === 0 || availability.constant === 1) {
6215
+ return;
6216
+ }
6217
+ if (!availability.explicitBitstream || availability.explicitBitstream.byteLength * 8 < elementCount) {
6218
+ throw new Error(`3DTILES_subtree: ${label} availability bitstream is too short`);
6219
+ }
6220
+ }
6221
+ function countAvailableElements(availability, elementCount) {
6222
+ if (availability.constant === 1) {
6223
+ return elementCount;
6224
+ }
6225
+ if (availability.constant === 0) {
6226
+ return 0;
6227
+ }
6228
+ let availableCount = 0;
6229
+ for (let index = 0; index < elementCount; index++) {
6230
+ if (getAvailabilityValue(availability, index)) {
6231
+ availableCount++;
6232
+ }
6233
+ }
6234
+ return availableCount;
6235
+ }
6236
+ function validateAttributeLengths(attributes, availableCount, label) {
6237
+ const componentCounts = {
6238
+ TILE_BOUNDING_BOX: 16,
6239
+ TILE_BOUNDING_SPHERE: 4,
6240
+ TILE_GEOMETRIC_ERROR: 1,
6241
+ TILE_REFINE: 1,
6242
+ TILE_TRANSFORM: 16,
6243
+ CONTENT_BOUNDING_BOX: 16,
6244
+ CONTENT_BOUNDING_SPHERE: 4
6245
+ };
6246
+ for (const [semantic, values] of Object.entries(attributes || {})) {
6247
+ const componentCount = componentCounts[semantic];
6248
+ if (componentCount && values.length !== availableCount * componentCount) {
6249
+ throw new Error(`3DTILES_subtree: ${label} attribute ${semantic} count does not match availability`);
6250
+ }
6251
+ }
6252
+ }
6253
+ function replaceImplicitUrlTemplate(templateUrl, coordinates, properties = {}) {
5172
6254
  const values = {
5173
6255
  level: coordinates.level,
5174
6256
  x: coordinates.x,
5175
6257
  y: coordinates.y,
5176
- z: coordinates.z
6258
+ z: coordinates.z,
6259
+ ...properties
5177
6260
  };
5178
- return templateUrl.replace(/{(level|x|y|z)}/gi, (_match, coordinateName) => String(values[coordinateName.toLowerCase()]));
6261
+ return templateUrl.replace(/{([^{}]+)}/g, (match, propertyName) => {
6262
+ const value = Object.hasOwn(values, propertyName) ? values[propertyName] : values[propertyName.toLowerCase()];
6263
+ return value === void 0 ? match : String(value);
6264
+ });
5179
6265
  }
5180
6266
  function materializeAvailableTile(subtree, reference, localCoordinates, globalCoordinates, counters) {
5181
6267
  const { descriptor } = reference;
@@ -5210,10 +6296,12 @@ function materializeAvailableTile(subtree, reference, localCoordinates, globalCo
5210
6296
  }
5211
6297
  }
5212
6298
  }
5213
- return formatImplicitTileHeader(subtree, descriptor, reference, globalCoordinates, contentAvailability, children);
6299
+ return formatImplicitTileHeader(subtree, descriptor, reference, globalCoordinates, availabilityIndex, contentAvailability, children);
5214
6300
  }
5215
6301
  function createLazyImplicitTileHeader(subtree, descriptor, coordinates) {
5216
- const reference = createImplicitSubtreeReference(descriptor, coordinates);
6302
+ const resourceFiles = mergeImplicitPackageFiles(descriptor.resourceFiles, subtree.resourceFiles);
6303
+ const childDescriptor = resourceFiles ? { ...descriptor, resourceFiles } : descriptor;
6304
+ const reference = createImplicitSubtreeReference(childDescriptor, coordinates);
5217
6305
  return {
5218
6306
  id: getImplicitTileId(reference, coordinates),
5219
6307
  children: [],
@@ -5224,55 +6312,187 @@ function createLazyImplicitTileHeader(subtree, descriptor, coordinates) {
5224
6312
  lodMetricValue: descriptor.rootLodMetricValue / 2 ** coordinates.level,
5225
6313
  refine: descriptor.refine,
5226
6314
  type: TILE_TYPE.EMPTY,
5227
- implicitMetadata: getImplicitSubtreeMetadata(subtree)
6315
+ implicitMetadata: getImplicitSubtreeMetadata(subtree),
6316
+ _scaleGeometricError: descriptor.scaleGeometricError !== false
6317
+ };
6318
+ }
6319
+ function getSubtreeAttributeOverrides(attributes, rowIndex, scope) {
6320
+ var _a, _b;
6321
+ if (!attributes || rowIndex < 0) {
6322
+ return {};
6323
+ }
6324
+ const box = getAttributeComponents(attributes[scope === "tile" ? "TILE_BOUNDING_BOX" : "CONTENT_BOUNDING_BOX"], rowIndex, 16);
6325
+ const sphere = getAttributeComponents(attributes[scope === "tile" ? "TILE_BOUNDING_SPHERE" : "CONTENT_BOUNDING_SPHERE"], rowIndex, 4);
6326
+ const boundingVolume = box ? {
6327
+ box: [
6328
+ box[12],
6329
+ box[13],
6330
+ box[14],
6331
+ box[0] / 2,
6332
+ box[1] / 2,
6333
+ box[2] / 2,
6334
+ box[4] / 2,
6335
+ box[5] / 2,
6336
+ box[6] / 2,
6337
+ box[8] / 2,
6338
+ box[9] / 2,
6339
+ box[10] / 2
6340
+ ]
6341
+ } : sphere ? { sphere } : void 0;
6342
+ if (scope === "content") {
6343
+ return { boundingVolume };
6344
+ }
6345
+ const geometricError = (_a = getAttributeComponents(attributes.TILE_GEOMETRIC_ERROR, rowIndex, 1)) == null ? void 0 : _a[0];
6346
+ if (geometricError !== void 0 && (!Number.isFinite(geometricError) || geometricError < 0)) {
6347
+ throw new Error("3DTILES_subtree: TILE_GEOMETRIC_ERROR must be nonnegative");
6348
+ }
6349
+ const refineValue = (_b = getAttributeComponents(attributes.TILE_REFINE, rowIndex, 1)) == null ? void 0 : _b[0];
6350
+ if (refineValue !== void 0 && refineValue !== 0 && refineValue !== 1) {
6351
+ throw new Error("3DTILES_subtree: TILE_REFINE must be 0 or 1");
6352
+ }
6353
+ return {
6354
+ boundingVolume,
6355
+ geometricError,
6356
+ refine: refineValue === void 0 ? void 0 : refineValue === 0 ? TILE_REFINEMENT.ADD : TILE_REFINEMENT.REPLACE,
6357
+ transform: getAttributeComponents(attributes.TILE_TRANSFORM, rowIndex, 16)
5228
6358
  };
5229
6359
  }
5230
- function formatImplicitTileHeader(subtree, descriptor, reference, coordinates, contentAvailability, children) {
6360
+ function getAttributeComponents(values, rowIndex, componentCount) {
6361
+ if (!values) {
6362
+ return void 0;
6363
+ }
6364
+ const offset = rowIndex * componentCount;
6365
+ if (offset + componentCount > values.length) {
6366
+ throw new Error("3DTILES_subtree: attribute accessor is shorter than availability");
6367
+ }
6368
+ return Array.from({ length: componentCount }, (_unused, componentIndex) => Number(values[offset + componentIndex]));
6369
+ }
6370
+ function getAvailabilityRank(availability, index) {
6371
+ var _a;
6372
+ if (availability.constant === 1) {
6373
+ return index;
6374
+ }
6375
+ if (availability.constant === 0) {
6376
+ return 0;
6377
+ }
6378
+ let ranks = AVAILABILITY_RANK_CACHE.get(availability);
6379
+ if (!ranks) {
6380
+ const bitCount = (((_a = availability.explicitBitstream) == null ? void 0 : _a.byteLength) || 0) * 8;
6381
+ ranks = new Uint32Array(bitCount + 1);
6382
+ for (let candidateIndex = 0; candidateIndex < bitCount; candidateIndex++) {
6383
+ ranks[candidateIndex + 1] = ranks[candidateIndex] + Number(getAvailabilityValue(availability, candidateIndex));
6384
+ }
6385
+ AVAILABILITY_RANK_CACHE.set(availability, ranks);
6386
+ }
6387
+ return ranks[index];
6388
+ }
6389
+ function findImplicitPackageResource(files, resourceUrl) {
5231
6390
  var _a;
5232
- const contentUrlTemplates = ((_a = descriptor.contentUrlTemplates) == null ? void 0 : _a.length) ? descriptor.contentUrlTemplates : [descriptor.contentUrlTemplate];
6391
+ if (!(files == null ? void 0 : files.length)) {
6392
+ return void 0;
6393
+ }
6394
+ const urlWithoutQuery = resourceUrl.split("?")[0];
6395
+ const packageRelativePath = (_a = /^gltf-package:\/\/[^/]+\/(.*)$/.exec(urlWithoutQuery)) == null ? void 0 : _a[1];
6396
+ const exactFileIndex = files.findIndex((file) => {
6397
+ const reference = file.originalUri || file.name;
6398
+ return file.uri === urlWithoutQuery || Boolean(reference && (urlWithoutQuery === reference || packageRelativePath === reference));
6399
+ });
6400
+ if (exactFileIndex >= 0) {
6401
+ return { fileIndex: exactFileIndex, files };
6402
+ }
6403
+ const fileIndex = files.findIndex((file) => {
6404
+ const reference = file.originalUri || file.name;
6405
+ return Boolean(reference && (urlWithoutQuery.endsWith(`/${reference}`) || urlWithoutQuery.endsWith(`:${reference}`)));
6406
+ });
6407
+ return fileIndex < 0 ? void 0 : { fileIndex, files };
6408
+ }
6409
+ function mergeImplicitPackageFiles(parentFiles, childFiles) {
6410
+ if (!(childFiles == null ? void 0 : childFiles.length)) {
6411
+ return parentFiles;
6412
+ }
6413
+ if (!(parentFiles == null ? void 0 : parentFiles.length)) {
6414
+ return childFiles;
6415
+ }
6416
+ const childReferences = new Set(childFiles.flatMap((file) => [file.uri, file.originalUri, file.name].filter(Boolean)));
6417
+ return [
6418
+ ...childFiles,
6419
+ ...parentFiles.filter((file) => ![file.uri, file.originalUri, file.name].some((reference) => reference && childReferences.has(reference)))
6420
+ ];
6421
+ }
6422
+ function formatImplicitTileHeader(subtree, descriptor, reference, coordinates, availabilityIndex, contentAvailability, children) {
6423
+ var _a, _b, _c, _d, _e;
6424
+ const tilePropertyIndex = getAvailabilityRank(subtree.tileAvailability, availabilityIndex);
6425
+ const tileProperties = (_a = subtree.tilePropertyRows) == null ? void 0 : _a[tilePropertyIndex];
6426
+ const primaryContentAvailability = Array.isArray(subtree.contentAvailability) ? subtree.contentAvailability[0] : subtree.contentAvailability;
6427
+ const contentPropertyIndex = primaryContentAvailability && getAvailabilityValue(primaryContentAvailability, availabilityIndex) ? getAvailabilityRank(primaryContentAvailability, availabilityIndex) : -1;
6428
+ const contentProperties = (_b = subtree.contentPropertyRows) == null ? void 0 : _b[contentPropertyIndex];
6429
+ const tileTemplateProperties = ((_c = subtree.tileTemplatePropertyRows) == null ? void 0 : _c[tilePropertyIndex]) || tileProperties;
6430
+ const contentTemplateProperties = ((_d = subtree.contentTemplatePropertyRows) == null ? void 0 : _d[contentPropertyIndex]) || contentProperties;
6431
+ const templateProperties = { ...tileTemplateProperties, ...contentTemplateProperties };
6432
+ const tileAttributes = getSubtreeAttributeOverrides(subtree.tileAttributes, tilePropertyIndex, "tile");
6433
+ const contentAttributes = getSubtreeAttributeOverrides(subtree.contentAttributes, contentPropertyIndex, "content");
6434
+ const contentUrlTemplates = ((_e = descriptor.contentUrlTemplates) == null ? void 0 : _e.length) ? descriptor.contentUrlTemplates : [descriptor.contentUrlTemplate];
5233
6435
  const contentEntries = contentUrlTemplates.map((templateUrl, contentIndex) => {
5234
6436
  if (!contentAvailability[contentIndex] || !templateUrl) {
5235
6437
  return null;
5236
6438
  }
6439
+ const contentUrl2 = replaceImplicitUrlTemplate(templateUrl, coordinates, templateProperties);
6440
+ if (/{[^{}]+}/.test(contentUrl2)) {
6441
+ throw new Error(`Implicit content URI has unresolved template values: ${contentUrl2}`);
6442
+ }
5237
6443
  return {
5238
6444
  contentIndex,
5239
- contentUrl: replaceImplicitUrlTemplate(templateUrl, coordinates)
6445
+ contentUrl: contentUrl2
5240
6446
  };
5241
6447
  }).filter((entry) => Boolean(entry));
5242
6448
  const availableContentUrls = contentEntries.map((entry) => entry.contentUrl);
6449
+ const resourceFiles = mergeImplicitPackageFiles(descriptor.resourceFiles, subtree.resourceFiles);
5243
6450
  const content = contentEntries.map(({ contentIndex, contentUrl: contentUrl2 }) => {
5244
6451
  var _a2;
6452
+ const packageResource = findImplicitPackageResource(resourceFiles, contentUrl2);
5245
6453
  return {
5246
6454
  ...((_a2 = descriptor.contentHeaders) == null ? void 0 : _a2[contentIndex]) || (contentIndex === 0 ? descriptor.contentHeader : {}) || {},
5247
- uri: contentUrl2
6455
+ ...contentAttributes,
6456
+ uri: contentUrl2,
6457
+ ...contentProperties ? { metadata: { properties: contentProperties } } : {},
6458
+ ...packageResource ? { _resource: packageResource } : {}
5248
6459
  };
5249
6460
  });
5250
6461
  const contentUrl = availableContentUrls[0];
5251
- const lodMetricValue = descriptor.rootLodMetricValue / 2 ** coordinates.level;
6462
+ const computedLodMetricValue = descriptor.rootLodMetricValue / 2 ** coordinates.level;
6463
+ const lodMetricValue = tileAttributes.geometricError ?? computedLodMetricValue;
6464
+ const computedBoundingVolume = calculateImplicitBoundingVolume(descriptor.rootBoundingVolume, coordinates, descriptor.subdivisionScheme);
5252
6465
  return {
5253
6466
  id: getImplicitTileId(reference, coordinates),
5254
6467
  children,
5255
6468
  contentUrl,
5256
6469
  content: content.length > 1 ? content : content[0],
6470
+ contents: descriptor.useCanonicalContents && content.length ? content : void 0,
5257
6471
  contentUrls: availableContentUrls,
5258
- refine: descriptor.refine,
6472
+ refine: tileAttributes.refine || descriptor.refine,
5259
6473
  type: getImplicitTileType(contentUrl),
5260
6474
  lodMetricType: descriptor.lodMetricType,
5261
6475
  lodMetricValue,
5262
6476
  geometricError: lodMetricValue,
5263
- boundingVolume: calculateImplicitBoundingVolume(descriptor.rootBoundingVolume, coordinates, descriptor.subdivisionScheme),
5264
- implicitMetadata: getImplicitSubtreeMetadata(subtree)
6477
+ boundingVolume: tileAttributes.boundingVolume || computedBoundingVolume,
6478
+ transform: tileAttributes.transform,
6479
+ transformMatrix: tileAttributes.transform,
6480
+ metadata: tileProperties ? { properties: tileProperties } : void 0,
6481
+ implicitMetadata: getImplicitSubtreeMetadata(subtree, tileProperties, contentProperties),
6482
+ _scaleGeometricError: descriptor.scaleGeometricError !== false
5265
6483
  };
5266
6484
  }
5267
- function getImplicitSubtreeMetadata(subtree) {
5268
- if (!subtree.propertyTables && subtree.tileMetadata === void 0 && !subtree.contentMetadata && subtree.subtreeMetadata === void 0) {
6485
+ function getImplicitSubtreeMetadata(subtree, tileProperties, contentProperties) {
6486
+ if (!subtree.propertyTables && subtree.tileMetadata === void 0 && !subtree.contentMetadata && subtree.subtreeMetadata === void 0 && !tileProperties && !contentProperties) {
5269
6487
  return void 0;
5270
6488
  }
5271
6489
  return {
5272
6490
  propertyTables: subtree.propertyTables,
5273
6491
  tileMetadata: subtree.tileMetadata,
5274
6492
  contentMetadata: subtree.contentMetadata,
5275
- subtreeMetadata: subtree.subtreeMetadata
6493
+ subtreeMetadata: subtree.subtreeMetadata,
6494
+ tileProperties,
6495
+ contentProperties
5276
6496
  };
5277
6497
  }
5278
6498
  function getImplicitTileId(reference, coordinates) {
@@ -5309,12 +6529,15 @@ function getImplicitChildCount(subdivisionScheme) {
5309
6529
  return subdivisionScheme === "OCTREE" ? OCTREE_CHILD_COUNT : QUADTREE_CHILD_COUNT;
5310
6530
  }
5311
6531
  function calculateImplicitBoundingVolume(rootBoundingVolume, coordinates, subdivisionScheme) {
6532
+ var _a;
5312
6533
  if (rootBoundingVolume.region) {
5313
6534
  const [west, south, east, north, minimumHeight, maximumHeight] = rootBoundingVolume.region;
5314
6535
  const divisionCount = 2 ** coordinates.level;
5315
- const longitudeSize = (east - west) / divisionCount;
6536
+ const longitudeSpan = east >= west ? east - west : east + 2 * Math.PI - west;
6537
+ const longitudeSize = longitudeSpan / divisionCount;
5316
6538
  const latitudeSize = (north - south) / divisionCount;
5317
- const childWest = west + longitudeSize * coordinates.x;
6539
+ const childWest = normalizeRegionLongitude(west + longitudeSize * coordinates.x);
6540
+ const childEast = normalizeRegionLongitude(west + longitudeSize * (coordinates.x + 1));
5318
6541
  const childSouth = south + latitudeSize * coordinates.y;
5319
6542
  let childMinimumHeight = minimumHeight;
5320
6543
  let childMaximumHeight = maximumHeight;
@@ -5327,7 +6550,7 @@ function calculateImplicitBoundingVolume(rootBoundingVolume, coordinates, subdiv
5327
6550
  region: [
5328
6551
  childWest,
5329
6552
  childSouth,
5330
- childWest + longitudeSize,
6553
+ childEast,
5331
6554
  childSouth + latitudeSize,
5332
6555
  childMinimumHeight,
5333
6556
  childMaximumHeight
@@ -5336,7 +6559,13 @@ function calculateImplicitBoundingVolume(rootBoundingVolume, coordinates, subdiv
5336
6559
  }
5337
6560
  if (rootBoundingVolume.box) {
5338
6561
  if (rootBoundingVolume.s2VolumeInfo) {
5339
- return { box: [...rootBoundingVolume.box], s2VolumeInfo: rootBoundingVolume.s2VolumeInfo };
6562
+ return calculateS2ImplicitBoundingVolume(rootBoundingVolume, coordinates, subdivisionScheme);
6563
+ }
6564
+ if ((_a = rootBoundingVolume.extensions) == null ? void 0 : _a["3DTILES_shape_cylinder_region"]) {
6565
+ return {
6566
+ ...rootBoundingVolume,
6567
+ box: [...rootBoundingVolume.box]
6568
+ };
5340
6569
  }
5341
6570
  const box = rootBoundingVolume.box;
5342
6571
  const divisionCount = 2 ** coordinates.level;
@@ -5366,6 +6595,41 @@ function calculateImplicitBoundingVolume(rootBoundingVolume, coordinates, subdiv
5366
6595
  }
5367
6596
  throw new Error(`Unsupported implicit 3D Tiles bounding volume: ${JSON.stringify(rootBoundingVolume)}`);
5368
6597
  }
6598
+ function calculateS2ImplicitBoundingVolume(rootBoundingVolume, coordinates, subdivisionScheme) {
6599
+ const rootS2VolumeInfo = rootBoundingVolume.s2VolumeInfo;
6600
+ const rootIndex = (0, import_s22.getS2IndexFromToken)(rootS2VolumeInfo.token);
6601
+ const descendantIndex = (0, import_s22.getS2DescendantIndex)(rootIndex, coordinates.level, coordinates.x, coordinates.y);
6602
+ const divisionCount = 2 ** coordinates.level;
6603
+ const heightSize = (rootS2VolumeInfo.maximumHeight - rootS2VolumeInfo.minimumHeight) / divisionCount;
6604
+ const childS2VolumeInfo = {
6605
+ token: (0, import_s22.getS2TokenFromIndex)(descendantIndex),
6606
+ minimumHeight: rootS2VolumeInfo.minimumHeight + (subdivisionScheme === "OCTREE" ? heightSize * coordinates.z : 0),
6607
+ maximumHeight: rootS2VolumeInfo.minimumHeight + (subdivisionScheme === "OCTREE" ? heightSize * (coordinates.z + 1) : heightSize * divisionCount)
6608
+ };
6609
+ const extensions = rootBoundingVolume.extensions ? { ...rootBoundingVolume.extensions } : void 0;
6610
+ if (extensions == null ? void 0 : extensions["3DTILES_bounding_volume_S2"]) {
6611
+ extensions["3DTILES_bounding_volume_S2"] = childS2VolumeInfo;
6612
+ }
6613
+ if (extensions == null ? void 0 : extensions["3DTILES_shape_s2"]) {
6614
+ extensions["3DTILES_shape_s2"] = childS2VolumeInfo;
6615
+ }
6616
+ return {
6617
+ ...rootBoundingVolume,
6618
+ ...extensions ? { extensions } : {},
6619
+ s2VolumeInfo: childS2VolumeInfo,
6620
+ box: convertS2BoundingVolumeToOBB(childS2VolumeInfo)
6621
+ };
6622
+ }
6623
+ function normalizeRegionLongitude(longitude) {
6624
+ let normalizedLongitude = longitude;
6625
+ while (normalizedLongitude > Math.PI) {
6626
+ normalizedLongitude -= 2 * Math.PI;
6627
+ }
6628
+ while (normalizedLongitude < -Math.PI) {
6629
+ normalizedLongitude += 2 * Math.PI;
6630
+ }
6631
+ return normalizedLongitude;
6632
+ }
5369
6633
  function getImplicitTileType(contentUrl) {
5370
6634
  var _a;
5371
6635
  if (!contentUrl) {
@@ -5434,6 +6698,12 @@ var Tiles3DSource = class {
5434
6698
  queryParams = {};
5435
6699
  /** Final request URLs cached by unmodified tile content URL. */
5436
6700
  tileUrlCache = /* @__PURE__ */ new Map();
6701
+ /** Original content descriptors retained across {@link Tile3D.unloadContent} calls. */
6702
+ tileContentHeaders = /* @__PURE__ */ new WeakMap();
6703
+ /** Source-local namespaces assigned to distinct embedded glTF package file collections. */
6704
+ packageNamespaces = /* @__PURE__ */ new WeakMap();
6705
+ /** Next source-local package namespace. */
6706
+ nextPackageNamespace = 0;
5437
6707
  /** Parsed subtree requests keyed by final source URL for deduplication and LRU reuse. */
5438
6708
  implicitSubtreeCache;
5439
6709
  /** Mutable counters exposed as a defensive snapshot through {@link getImplicitTilingStats}. */
@@ -5448,6 +6718,10 @@ var Tiles3DSource = class {
5448
6718
  extensionsUsed = [];
5449
6719
  resolver;
5450
6720
  rootTileset;
6721
+ /** CRS transformer shared by this source's headers and decoded glTF content. */
6722
+ spatialTransformer;
6723
+ /** Root header after composing source-frame transforms and rebuilding target bounds. */
6724
+ preparedRootHeader;
5451
6725
  /**
5452
6726
  * Creates a 3D Tiles source.
5453
6727
  * @param input Root metadata request, or preloaded root metadata for tests and internal callers.
@@ -5505,8 +6779,8 @@ var Tiles3DSource = class {
5505
6779
  if (!this.asset) {
5506
6780
  throw new Error("Tileset must have an asset property.");
5507
6781
  }
5508
- if (this.asset.version !== "0.0" && this.asset.version !== "1.0" && this.asset.version !== "1.1") {
5509
- throw new Error("The tileset must be 3D Tiles version either 0.0 or 1.0 or 1.1.");
6782
+ if (this.rootTileset.formatVersion !== "2.0-draft" && this.asset.version !== "0.0" && this.asset.version !== "1.0" && this.asset.version !== "1.1") {
6783
+ throw new Error("The tileset must be 3D Tiles version 0.0, 1.0, 1.1, or 2.0-draft.");
5510
6784
  }
5511
6785
  if ("tilesetVersion" in this.asset) {
5512
6786
  this.setQueryParameter("v", this.asset.tilesetVersion);
@@ -5542,12 +6816,26 @@ var Tiles3DSource = class {
5542
6816
  async getRootTileset() {
5543
6817
  return this.getMetadata().tileset;
5544
6818
  }
6819
+ /** Prepare nonlinear CRS state before runtime headers are materialized. */
6820
+ async prepareTileset(tileset) {
6821
+ const spatialReference = tileset.spatialReference;
6822
+ if (spatialReference.status === "unresolved") {
6823
+ throw new Error(spatialReference.warnings[0] || "3D Tiles spatial operations cannot be resolved from the supplied metadata and options");
6824
+ }
6825
+ if (spatialReference.status !== "transformable" && spatialReference.status !== "transformed") {
6826
+ return;
6827
+ }
6828
+ this.spatialTransformer ||= new Tiles3DSpatialTransformer(spatialReference, tileset.options.spatial);
6829
+ tileset.spatialReference = markTilesetSpatialReferenceTransformed(spatialReference);
6830
+ this.preparedRootHeader = this.transformTileHeader(this.getMetadata().tileset.root, new import_core12.Matrix4());
6831
+ }
5545
6832
  /**
5546
6833
  * Builds explicit runtime headers while leaving implicit subtree references lazy.
5547
6834
  */
5548
6835
  initializeTileHeaders(tileset, tilesetJson, parentTile) {
5549
6836
  var _a;
5550
- const rootTile = new Tile3D(tileset, tilesetJson.root, parentTile || void 0);
6837
+ const rootHeader = parentTile ? tilesetJson.root : this.preparedRootHeader || tilesetJson.root;
6838
+ const rootTile = new Tile3D(tileset, rootHeader, parentTile || void 0);
5551
6839
  if (parentTile) {
5552
6840
  parentTile.children.push(rootTile);
5553
6841
  rootTile.depth = parentTile.depth + 1;
@@ -5583,7 +6871,13 @@ var Tiles3DSource = class {
5583
6871
  * Loads binary content or nested tileset JSON for a runtime tile.
5584
6872
  */
5585
6873
  async loadTileContent(tile) {
6874
+ var _a, _b, _c, _d, _e, _f;
5586
6875
  const contentUrls = (tile.contentUrls || [tile.contentUrl]).filter(Boolean);
6876
+ const cachedContentHeaders = this.tileContentHeaders.get(tile);
6877
+ const contentHeaders = cachedContentHeaders || (Array.isArray((_a = tile.header) == null ? void 0 : _a.content) ? tile.header.content : ((_b = tile.header) == null ? void 0 : _b.content) ? [tile.header.content] : []);
6878
+ if (!cachedContentHeaders) {
6879
+ this.tileContentHeaders.set(tile, contentHeaders);
6880
+ }
5587
6881
  const tilesetLoaderOptions = this.loadOptions[this.loader.id] || {};
5588
6882
  const options = {
5589
6883
  ...this.loadOptions,
@@ -5592,10 +6886,28 @@ var Tiles3DSource = class {
5592
6886
  // Content bytes, rather than URL suffixes, distinguish external tilesets from renderable
5593
6887
  // payloads. This is required for signed and extensionless resources.
5594
6888
  isTileset: "auto",
5595
- assetGltfUpAxis: this.asset && this.asset.gltfUpAxis || "Y"
6889
+ assetGltfUpAxis: this.asset && this.asset.gltfUpAxis || "Y",
6890
+ _tilesetOptions: {
6891
+ spatialReference: ((_c = tile.header) == null ? void 0 : _c._spatialReference) || ((_d = tile.tileset) == null ? void 0 : _d.spatialReference),
6892
+ spatialOptions: (_e = tile.tileset) == null ? void 0 : _e.options.spatial,
6893
+ spatialTransform: (_f = tile.header) == null ? void 0 : _f._spatialTransform
6894
+ }
5596
6895
  }
5597
6896
  };
5598
- const contents = await Promise.all(contentUrls.map((contentUrl) => this.loadResourceData(this.getTileUrl(contentUrl), options)));
6897
+ const contents = await Promise.all(contentUrls.map((contentUrl, contentIndex) => {
6898
+ var _a2;
6899
+ const contentHeader = contentHeaders[contentIndex] || {};
6900
+ const vectorExtension = (_a2 = contentHeader.extensions) == null ? void 0 : _a2["3DTILES_content_gltf_vector"];
6901
+ const vectorContent = contentHeader._vectorContent || ((vectorExtension == null ? void 0 : vectorExtension.vector) === true ? { clip: vectorExtension.clip === true } : void 0);
6902
+ const contentOptions = vectorContent ? {
6903
+ ...options,
6904
+ [this.loader.id]: {
6905
+ ...options[this.loader.id],
6906
+ vectorContent
6907
+ }
6908
+ } : options;
6909
+ return this.loadContentResource(this.getTileUrl(contentUrl), contentHeader._resource, contentOptions);
6910
+ }));
5599
6911
  tile.contents = contents;
5600
6912
  tile.content = contents[0] || null;
5601
6913
  const nestedTilesets = contents.filter((content) => (content == null ? void 0 : content.shape) === "tileset3d");
@@ -5606,6 +6918,34 @@ var Tiles3DSource = class {
5606
6918
  nestedTilesets
5607
6919
  };
5608
6920
  }
6921
+ /** Transform a tile header and its eagerly declared descendants. */
6922
+ transformTileHeader(header, parentTransform = new import_core12.Matrix4()) {
6923
+ if (!this.spatialTransformer) {
6924
+ return header;
6925
+ }
6926
+ return this.transformTileHeaderWithTransformer(header, this.spatialTransformer, parentTransform);
6927
+ }
6928
+ /** Rebuilds one header subtree using an explicitly selected CRS transformer. */
6929
+ transformTileHeaderWithTransformer(header, transformer, parentTransform, spatialReference) {
6930
+ const localTransform = header.transform ? new import_core12.Matrix4(header.transform) : new import_core12.Matrix4();
6931
+ const composedTransform = new import_core12.Matrix4(parentTransform).multiplyRight(localTransform);
6932
+ const content = Array.isArray(header.content) ? header.content.map((entry) => this.transformContentHeader(entry, composedTransform, transformer)) : header.content ? this.transformContentHeader(header.content, composedTransform, transformer) : header.content;
6933
+ return {
6934
+ ...header,
6935
+ transform: void 0,
6936
+ boundingVolume: header.boundingVolume ? transformer.transformBoundingVolume(header.boundingVolume, composedTransform) : header.boundingVolume,
6937
+ _spatialReference: spatialReference,
6938
+ _spatialTransform: composedTransform.toArray(),
6939
+ content,
6940
+ children: Array.isArray(header.children) ? header.children.map((child) => this.transformTileHeaderWithTransformer(child, transformer, composedTransform, spatialReference)) : header.children
6941
+ };
6942
+ }
6943
+ transformContentHeader(content, composedTransform, transformer) {
6944
+ return {
6945
+ ...content,
6946
+ boundingVolume: content.boundingVolume ? transformer.transformBoundingVolume(content.boundingVolume, composedTransform) : content.boundingVolume
6947
+ };
6948
+ }
5609
6949
  /**
5610
6950
  * Loads, materializes, and installs exactly one implicit subtree.
5611
6951
  *
@@ -5624,7 +6964,7 @@ var Tiles3DSource = class {
5624
6964
  return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
5625
6965
  }
5626
6966
  const subtreeUrl = this.getTileUrl(reference.subtreeUrl);
5627
- const subtree = await this.loadImplicitSubtreeResource(subtreeUrl);
6967
+ const subtree = await this.loadImplicitSubtreeResource(subtreeUrl, reference.resource);
5628
6968
  if (this.destroyed || tile.isDestroyed()) {
5629
6969
  return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
5630
6970
  }
@@ -5632,8 +6972,11 @@ var Tiles3DSource = class {
5632
6972
  ...reference,
5633
6973
  subtreeUrl
5634
6974
  });
5635
- tile.applyImplicitSubtreeHeader(materializedSubtree.root);
5636
- const materializedTileCount = this.initializeMaterializedChildren(tile.tileset, tile, materializedSubtree.root.children);
6975
+ const nestedSpatialReference = tile.header._spatialReference;
6976
+ const subtreeTransformer = nestedSpatialReference ? new Tiles3DSpatialTransformer(nestedSpatialReference, tile.tileset.options.spatial) : this.spatialTransformer;
6977
+ const materializedRoot = subtreeTransformer ? this.transformTileHeaderWithTransformer(materializedSubtree.root, subtreeTransformer, new import_core12.Matrix4(), nestedSpatialReference) : materializedSubtree.root;
6978
+ tile.applyImplicitSubtreeHeader(materializedRoot);
6979
+ const materializedTileCount = this.initializeMaterializedChildren(tile.tileset, tile, materializedRoot.children);
5637
6980
  this.implicitTilingStats.loadedSubtrees++;
5638
6981
  this.implicitTilingStats.materializedTiles += materializedTileCount;
5639
6982
  return {
@@ -5720,10 +7063,10 @@ var Tiles3DSource = class {
5720
7063
  const { center } = rootTile.boundingVolume;
5721
7064
  let cartographicCenter;
5722
7065
  if (center && (center[0] !== 0 || center[1] !== 0 || center[2] !== 0)) {
5723
- cartographicCenter = new import_core10.Vector3();
5724
- import_geospatial7.Ellipsoid.WGS84.cartesianToCartographic(center, cartographicCenter);
7066
+ cartographicCenter = new import_core12.Vector3();
7067
+ import_geospatial10.Ellipsoid.WGS84.cartesianToCartographic(center, cartographicCenter);
5725
7068
  } else {
5726
- cartographicCenter = new import_core10.Vector3(0, 0, -import_geospatial7.Ellipsoid.WGS84.radii[0]);
7069
+ cartographicCenter = new import_core12.Vector3(0, 0, -import_geospatial10.Ellipsoid.WGS84.radii[0]);
5727
7070
  }
5728
7071
  return {
5729
7072
  asset: this.asset,
@@ -5762,9 +7105,59 @@ var Tiles3DSource = class {
5762
7105
  }
5763
7106
  const nestedTilesets = loadResult.nestedTilesets || (loadResult.nestedTileset ? [loadResult.nestedTileset] : []);
5764
7107
  for (const nestedTileset of nestedTilesets) {
5765
- tileset._initializeTileHeaders(nestedTileset, tile);
7108
+ tileset._initializeTileHeaders(this.prepareNestedTileset(tileset, nestedTileset), tile);
5766
7109
  }
5767
7110
  }
7111
+ /**
7112
+ * Resolves a nested tileset's CRS independently and adapts it to the owning tileset output frame.
7113
+ *
7114
+ * Nested resources may use a different source CRS from their parent. Their headers and glTF
7115
+ * payloads must therefore use a transformer built from the nested descriptor, while the parent
7116
+ * runtime continues to expose one requested output frame to traversal and rendering.
7117
+ */
7118
+ prepareNestedTileset(tileset, nestedTileset) {
7119
+ const nestedSpatialReference = nestedTileset.spatialMetadata;
7120
+ const parentSpatialReference = tileset.spatialReference;
7121
+ if ((nestedSpatialReference == null ? void 0 : nestedSpatialReference.status) === "unresolved" || (nestedSpatialReference == null ? void 0 : nestedSpatialReference.crs.state) === "unknown") {
7122
+ throw new Error(nestedSpatialReference.warnings[0] || "Nested 3D Tiles CRS is explicitly unresolved and cannot be placed in the parent frame");
7123
+ }
7124
+ if (!nestedSpatialReference || !nestedSpatialReference.sourceCrs) {
7125
+ if (this.spatialTransformer && (parentSpatialReference.status === "transformable" || parentSpatialReference.status === "transformed")) {
7126
+ return {
7127
+ ...nestedTileset,
7128
+ spatialMetadata: parentSpatialReference,
7129
+ root: this.transformTileHeaderWithTransformer(nestedTileset.root, this.spatialTransformer, new import_core12.Matrix4(), parentSpatialReference)
7130
+ };
7131
+ }
7132
+ return addNestedSpatialReference(nestedTileset, parentSpatialReference);
7133
+ }
7134
+ const parentCoordinateEpoch = parentSpatialReference.coordinateEpoch;
7135
+ const nestedCoordinateEpoch = nestedSpatialReference.coordinateEpoch;
7136
+ if (parentCoordinateEpoch !== void 0 && nestedCoordinateEpoch !== void 0 && parentCoordinateEpoch !== nestedCoordinateEpoch) {
7137
+ throw new Error(`Nested 3D Tiles coordinate epoch ${nestedCoordinateEpoch} differs from parent epoch ${parentCoordinateEpoch}; epoch-aware transformation is unavailable`);
7138
+ }
7139
+ const outputCrs = parentSpatialReference.targetCrs || parentSpatialReference.sourceCrs;
7140
+ if (!outputCrs) {
7141
+ return addNestedSpatialReference(nestedTileset, nestedSpatialReference);
7142
+ }
7143
+ const nestedOutputReference = applyTilesetSpatialOptions(nestedSpatialReference, {
7144
+ ...tileset.options.spatial,
7145
+ outputCoordinates: "target-crs",
7146
+ targetCrs: outputCrs
7147
+ });
7148
+ if (nestedOutputReference.status === "unresolved") {
7149
+ throw new Error(nestedOutputReference.warnings[0] || "Nested 3D Tiles CRS cannot be resolved in the parent output frame");
7150
+ }
7151
+ if (nestedOutputReference.status === "native") {
7152
+ return addNestedSpatialReference(nestedTileset, nestedOutputReference);
7153
+ }
7154
+ const nestedTransformer = new Tiles3DSpatialTransformer(nestedOutputReference, tileset.options.spatial);
7155
+ return {
7156
+ ...nestedTileset,
7157
+ spatialMetadata: nestedOutputReference,
7158
+ root: this.transformTileHeaderWithTransformer(nestedTileset.root, nestedTransformer, new import_core12.Matrix4(), nestedOutputReference)
7159
+ };
7160
+ }
5768
7161
  /**
5769
7162
  * Loads data through injected core APIs so this module stays independent from `@loaders.gl/core`.
5770
7163
  */
@@ -5792,13 +7185,84 @@ var Tiles3DSource = class {
5792
7185
  }
5793
7186
  return await this.loadWithCoreApi(url, options, loader);
5794
7187
  }
7188
+ /** Loads a URL content reference or parses a buffer-view file retained from a glTF package. */
7189
+ async loadContentResource(url, packageResource, options) {
7190
+ if (!packageResource) {
7191
+ return await this.loadResourceData(url, options);
7192
+ }
7193
+ const file = packageResource.files[packageResource.fileIndex];
7194
+ if (!file) {
7195
+ throw new Error(`3D Tiles package references missing file ${packageResource.fileIndex}`);
7196
+ }
7197
+ if (file.uri) {
7198
+ return await this.loadResourceData(this.getTileUrl(file.uri), options);
7199
+ }
7200
+ if (!this.coreApi) {
7201
+ throw new Error("Tiles3DSource requires an injected coreApi to parse embedded package data");
7202
+ }
7203
+ const data = await this.getPackageFileData(file);
7204
+ const packageNamespace = this.getPackageNamespace(packageResource.files);
7205
+ const packageRootUrl = `gltf-package://${packageNamespace}/${packageResource.fileIndex}`;
7206
+ const fileName = file.name || "content";
7207
+ const directorySeparatorIndex = fileName.lastIndexOf("/");
7208
+ const packageBaseUrl = directorySeparatorIndex >= 0 ? `${packageRootUrl}/${fileName.slice(0, directorySeparatorIndex)}` : packageRootUrl;
7209
+ return await this.coreApi.parse(data, this.loader, options, {
7210
+ url: `${packageRootUrl}/${fileName}`,
7211
+ filename: fileName,
7212
+ baseUrl: packageBaseUrl,
7213
+ loaders: [this.loader],
7214
+ coreApi: this.coreApi,
7215
+ _parse: this.coreApi.parse,
7216
+ _tiles3dPackageFiles: packageResource.files,
7217
+ fetch: async (resource, init) => {
7218
+ const reference = resource.startsWith(`${packageRootUrl}/`) ? resource.slice(packageRootUrl.length + 1) : resource;
7219
+ const referencedFile = packageResource.files.find((packageFile) => packageFile.name === reference || packageFile.originalUri === reference);
7220
+ if (!referencedFile) {
7221
+ throw new Error(`3D Tiles glTF package does not contain file ${reference}`);
7222
+ }
7223
+ if (referencedFile.uri) {
7224
+ return await this.coreApi.fetchFile(this.getTileUrl(referencedFile.uri), init);
7225
+ }
7226
+ const referencedData = await this.getPackageFileData(referencedFile);
7227
+ return new Response(referencedData, {
7228
+ headers: { "content-type": referencedFile.mimeType }
7229
+ });
7230
+ }
7231
+ });
7232
+ }
7233
+ /** Returns a stable source-local namespace for one embedded glTF package file collection. */
7234
+ getPackageNamespace(files) {
7235
+ const existingNamespace = this.packageNamespaces.get(files);
7236
+ if (existingNamespace !== void 0) {
7237
+ return existingNamespace;
7238
+ }
7239
+ const packageNamespace = this.nextPackageNamespace++;
7240
+ this.packageNamespaces.set(files, packageNamespace);
7241
+ return packageNamespace;
7242
+ }
7243
+ /** Resolves a retained package-file byte range without eagerly copying other package files. */
7244
+ async getPackageFileData(file) {
7245
+ if (file.data) {
7246
+ return sliceArrayBuffer(file.data, file.byteOffset, file.byteLength);
7247
+ }
7248
+ if (file.bufferUri && this.coreApi) {
7249
+ const response = await this.coreApi.fetchFile(this.getTileUrl(file.bufferUri));
7250
+ if (!response.ok) {
7251
+ throw new Error(`Failed to fetch 3D Tiles package buffer: HTTP ${response.status}`);
7252
+ }
7253
+ const buffer = await response.arrayBuffer();
7254
+ return sliceArrayBuffer(buffer, file.byteOffset, file.byteLength);
7255
+ }
7256
+ throw new Error("3D Tiles package file has no available byte source");
7257
+ }
5795
7258
  /**
5796
7259
  * Returns a parsed subtree from the LRU cache or starts one source-managed request.
5797
7260
  *
5798
7261
  * @param subtreeUrl - Final subtree URL after query inheritance.
7262
+ * @param packageResource - Optional glTF package file satisfying the subtree reference.
5799
7263
  * @returns Parsed subtree availability data.
5800
7264
  */
5801
- async loadImplicitSubtreeResource(subtreeUrl) {
7265
+ async loadImplicitSubtreeResource(subtreeUrl, packageResource) {
5802
7266
  const cachedSubtree = this.implicitSubtreeCache.get(subtreeUrl);
5803
7267
  if (cachedSubtree) {
5804
7268
  this.implicitTilingStats.cacheHits++;
@@ -5807,14 +7271,15 @@ var Tiles3DSource = class {
5807
7271
  this.implicitTilingStats.requestedSubtrees++;
5808
7272
  const loaderOptions = this.loadOptions[this.loader.id] || {};
5809
7273
  return await this.implicitSubtreeCache.getOrLoad(subtreeUrl, async () => {
5810
- return await this.loadResourceData(subtreeUrl, {
7274
+ const options = {
5811
7275
  ...this.loadOptions,
5812
7276
  [this.loader.id]: {
5813
7277
  ...loaderOptions,
5814
7278
  isTileset: false,
5815
7279
  isSubtree: true
5816
7280
  }
5817
- });
7281
+ };
7282
+ return await (packageResource ? this.loadContentResource(subtreeUrl, packageResource, options) : this.loadResourceData(subtreeUrl, options));
5818
7283
  });
5819
7284
  }
5820
7285
  /**
@@ -5865,10 +7330,22 @@ function normalizeTiles3DRequest(input) {
5865
7330
  coreApi: input.coreApi
5866
7331
  };
5867
7332
  }
7333
+ function addNestedSpatialReference(nestedTileset, spatialReference) {
7334
+ const addToHeader = (header) => ({
7335
+ ...header,
7336
+ _spatialReference: spatialReference,
7337
+ children: Array.isArray(header.children) ? header.children.map(addToHeader) : header.children
7338
+ });
7339
+ return {
7340
+ ...nestedTileset,
7341
+ spatialMetadata: spatialReference,
7342
+ root: addToHeader(nestedTileset.root)
7343
+ };
7344
+ }
5868
7345
 
5869
7346
  // dist/tileset-3d/format-i3s/i3s-source.js
5870
- var import_geospatial10 = require("@math.gl/geospatial");
5871
- var import_core13 = require("@math.gl/core");
7347
+ var import_geospatial12 = require("@math.gl/geospatial");
7348
+ var import_core14 = require("@math.gl/core");
5872
7349
 
5873
7350
  // dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js
5874
7351
  var I3SPendingTilesRegister = class {
@@ -6049,224 +7526,8 @@ var I3STilesetTraverser = class extends TilesetTraverser {
6049
7526
  };
6050
7527
 
6051
7528
  // dist/spatial/i3s-spatial-transformer.js
6052
- var import_core12 = require("@math.gl/core");
6053
- var import_geospatial9 = require("@math.gl/geospatial");
6054
-
6055
- // dist/spatial/spatial-coordinate-transformer.js
6056
- var import_core11 = require("@math.gl/core");
6057
- var import_geospatial8 = require("@math.gl/geospatial");
6058
- var import_proj42 = require("@math.gl/proj4");
6059
-
6060
- // dist/spatial/spatial-resource-registry.js
6061
- var import_geoid = require("@math.gl/geoid");
6062
- var import_proj4 = require("@math.gl/proj4");
6063
- var geoidModels = /* @__PURE__ */ new Map();
6064
- function registerSpatialCrs(name, definition) {
6065
- validateResourceName(name, "CRS");
6066
- import_proj4.Proj4Projection.defineProjectionAliases({ [name]: definition });
6067
- }
6068
- function registerSpatialDatumGrid(name, data) {
6069
- validateResourceName(name, "datum grid");
6070
- import_proj4.Proj4Projection.registerDatumGrid(name, data);
6071
- }
6072
- function registerGeoidModel(name, geoid) {
6073
- validateResourceName(name, "geoid model");
6074
- geoidModels.set(name, geoid);
6075
- }
6076
- function registerGeoidModelFromPgm(name, data, options = {}) {
6077
- const geoid = (0, import_geoid.parsePGM)(data, options);
6078
- registerGeoidModel(name, geoid);
6079
- return geoid;
6080
- }
6081
- function getGeoidModel(name) {
6082
- return geoidModels.get(name);
6083
- }
6084
- function validateResourceName(name, resourceType) {
6085
- if (!name.trim()) {
6086
- throw new Error(`${resourceType} name must not be empty`);
6087
- }
6088
- }
6089
-
6090
- // dist/spatial/spatial-coordinate-transformer.js
6091
- var GEOGRAPHIC_CRS2 = "EPSG:4326";
6092
- var GEOCENTRIC_CRS2 = "EPSG:4978";
6093
- var SpatialCoordinateTransformer = class {
6094
- /** Normalized spatial metadata describing this transformation. */
6095
- spatialReference;
6096
- /** Projection from the source CRS directly to the requested horizontal target CRS. */
6097
- horizontalProjection;
6098
- /** Projection from the source CRS to geographic longitude, latitude, and ellipsoidal height. */
6099
- geographicProjection;
6100
- /** Projection from adjusted geographic coordinates to the requested output CRS. */
6101
- heightOutputProjection;
6102
- /** Whether source coordinates use the WGS84 geocentric frame handled by math.gl. */
6103
- sourceIsGeocentric;
6104
- /** Whether output coordinates use the WGS84 geocentric frame handled by math.gl. */
6105
- outputIsGeocentric;
6106
- /** Whether source coordinates already use conventional WGS84 longitude/latitude order. */
6107
- sourceIsGeographic;
6108
- /** Whether output coordinates use conventional WGS84 longitude/latitude order. */
6109
- outputIsGeographic;
6110
- /** Geoid model used to convert between ellipsoidal and orthometric heights. */
6111
- geoid;
6112
- /**
6113
- * Creates a coordinate transformer.
6114
- *
6115
- * @param spatialReference - Format-discovered and option-normalized spatial metadata.
6116
- * @param options - Application options containing an optional geoid model.
6117
- */
6118
- constructor(spatialReference, options = {}) {
6119
- this.spatialReference = spatialReference;
6120
- validateTransformRequest(spatialReference);
6121
- const outputCrs = spatialReference.targetCrs || spatialReference.sourceCrs;
6122
- this.sourceIsGeocentric = isWgs84Geocentric(spatialReference.sourceCrs);
6123
- this.outputIsGeocentric = isWgs84Geocentric(outputCrs);
6124
- this.sourceIsGeographic = isWgs84Geographic(spatialReference.sourceCrs);
6125
- this.outputIsGeographic = isWgs84Geographic(outputCrs);
6126
- if (spatialReference.sourceCrs && spatialReference.targetCrs && !this.sourceIsGeocentric && !this.outputIsGeocentric) {
6127
- this.horizontalProjection = new import_proj42.Proj4Projection({
6128
- from: getHorizontalProj4Definition(spatialReference.sourceCrs),
6129
- to: getHorizontalProj4Definition(spatialReference.targetCrs),
6130
- enforceAxis: false
6131
- });
6132
- }
6133
- if (requiresHeightTransformation(spatialReference) || this.sourceIsGeocentric || this.outputIsGeocentric) {
6134
- if (!this.sourceIsGeographic && !this.sourceIsGeocentric) {
6135
- this.geographicProjection = new import_proj42.Proj4Projection({
6136
- from: getHorizontalProj4Definition(spatialReference.sourceCrs),
6137
- to: GEOGRAPHIC_CRS2,
6138
- enforceAxis: false
6139
- });
6140
- }
6141
- if (!this.outputIsGeographic && !this.outputIsGeocentric) {
6142
- this.heightOutputProjection = new import_proj42.Proj4Projection({
6143
- from: GEOGRAPHIC_CRS2,
6144
- to: getHorizontalProj4Definition(outputCrs),
6145
- enforceAxis: false
6146
- });
6147
- }
6148
- }
6149
- if (requiresHeightTransformation(spatialReference)) {
6150
- this.geoid = resolveGeoid(options.geoidModel);
6151
- if (!this.geoid) {
6152
- const modelName = typeof options.geoidModel === "string" ? ` "${options.geoidModel}"` : "";
6153
- throw new Error(`Height conversion requires a registered geoid model${modelName}; call registerGeoidModel() or pass a parsed Geoid instance`);
6154
- }
6155
- }
6156
- }
6157
- /**
6158
- * Transforms one `[x, y, z?]` coordinate while retaining additional components.
6159
- *
6160
- * @param coordinate - Source coordinate in the format adapter's normalized `x/y/z` order.
6161
- * @returns A new transformed coordinate array.
6162
- */
6163
- transformPosition(coordinate) {
6164
- if (coordinate.length < 2) {
6165
- throw new Error("A spatial coordinate must contain at least x and y components");
6166
- }
6167
- const result = [...coordinate];
6168
- if (requiresHeightTransformation(this.spatialReference) || this.sourceIsGeocentric || this.outputIsGeocentric) {
6169
- if (result.length < 3 || !Number.isFinite(result[2])) {
6170
- throw new Error("Three-dimensional CRS conversion requires a finite z coordinate");
6171
- }
6172
- const geographic = this.toGeographic(result);
6173
- if (requiresHeightTransformation(this.spatialReference)) {
6174
- const geoidUndulation = this.geoid.getHeight(geographic[1], geographic[0]);
6175
- geographic[2] = transformHeight(geographic[2], geoidUndulation, this.spatialReference.heightReference, this.spatialReference.targetHeightReference);
6176
- }
6177
- const projected = this.fromGeographic(geographic);
6178
- result.splice(0, projected.length, ...projected);
6179
- return result;
6180
- }
6181
- if (this.horizontalProjection) {
6182
- const projected = this.horizontalProjection.project(result.slice(0, 3));
6183
- result.splice(0, projected.length, ...projected);
6184
- }
6185
- return result;
6186
- }
6187
- /** Convert one source coordinate to conventional WGS84 longitude, latitude, and height. */
6188
- toGeographic(coordinate) {
6189
- if (this.sourceIsGeocentric) {
6190
- return Array.from(import_geospatial8.Ellipsoid.WGS84.cartesianToCartographic(new import_core11.Vector3(coordinate)));
6191
- }
6192
- if (this.sourceIsGeographic) {
6193
- return coordinate.slice(0, 3);
6194
- }
6195
- return this.geographicProjection.project(coordinate.slice(0, 3));
6196
- }
6197
- /** Convert one conventional WGS84 geographic coordinate to the selected output CRS. */
6198
- fromGeographic(coordinate) {
6199
- if (this.outputIsGeocentric) {
6200
- return Array.from(import_geospatial8.Ellipsoid.WGS84.cartographicToCartesian(new import_core11.Vector3(coordinate)));
6201
- }
6202
- if (this.outputIsGeographic) {
6203
- return coordinate.slice(0, 3);
6204
- }
6205
- return this.heightOutputProjection.project(coordinate.slice(0, 3));
6206
- }
6207
- };
6208
- function getHorizontalProj4Definition(definition) {
6209
- if (!definition) {
6210
- throw new Error("Cannot construct a projection because the CRS is unknown");
6211
- }
6212
- return (0, import_proj42.toProj4CRSDefinition)(definition, { mode: "horizontal" });
6213
- }
6214
- function validateTransformRequest(spatialReference) {
6215
- if (spatialReference.outputCoordinates === "local-enu") {
6216
- throw new Error("local-enu output requires a dataset-derived local origin and must be created by a tileset source");
6217
- }
6218
- if (spatialReference.outputCoordinates === "target-crs" && !spatialReference.targetCrs) {
6219
- throw new Error("target-crs output requires targetCrs");
6220
- }
6221
- if (!spatialReference.sourceCrs && spatialReference.status === "unresolved") {
6222
- throw new Error("Cannot transform coordinates because the source CRS is unknown");
6223
- }
6224
- if (requiresHeightTransformation(spatialReference) && spatialReference.heightReference === "unknown") {
6225
- throw new Error("Cannot convert heights because the source height reference is unknown");
6226
- }
6227
- if (spatialReference.coordinateFrame === "geocentric" && !isWgs84Geocentric(spatialReference.sourceCrs)) {
6228
- throw new Error("Only EPSG:4978 geocentric coordinates are supported by the current spatial transformer");
6229
- }
6230
- if (spatialReference.status === "unresolved") {
6231
- throw new Error("The requested spatial output cannot be resolved from the available metadata");
6232
- }
6233
- }
6234
- function isWgs84Geocentric(definition) {
6235
- return typeof definition === "string" && normalizeCrsIdentifier2(definition) === GEOCENTRIC_CRS2;
6236
- }
6237
- function isWgs84Geographic(definition) {
6238
- if (typeof definition !== "string") {
6239
- return false;
6240
- }
6241
- const identifier = normalizeCrsIdentifier2(definition);
6242
- return identifier === GEOGRAPHIC_CRS2 || identifier === "EPSG:4979" || identifier === "OGC:CRS84";
6243
- }
6244
- function normalizeCrsIdentifier2(identifier) {
6245
- const normalizedIdentifier = identifier.trim().toUpperCase();
6246
- const ogcMatch = normalizedIdentifier.match(/(?:\/DEF\/CRS\/|URN:OGC:DEF:CRS:)([A-Z0-9_-]+)(?:\/|::)(?:[^/:]*[/:])?([A-Z0-9_.-]+)$/);
6247
- return ogcMatch ? `${ogcMatch[1]}:${ogcMatch[2]}` : normalizedIdentifier;
6248
- }
6249
- function requiresHeightTransformation(spatialReference) {
6250
- const target = spatialReference.targetHeightReference;
6251
- return target !== "native" && target !== spatialReference.heightReference;
6252
- }
6253
- function resolveGeoid(model) {
6254
- return typeof model === "string" ? getGeoidModel(model) : model;
6255
- }
6256
- function transformHeight(height, geoidUndulation, source, target) {
6257
- if (source === target || target === "native") {
6258
- return height;
6259
- }
6260
- if (source === "orthometric" && target === "ellipsoidal") {
6261
- return height + geoidUndulation;
6262
- }
6263
- if (source === "ellipsoidal" && target === "orthometric") {
6264
- return height - geoidUndulation;
6265
- }
6266
- throw new Error(`Unsupported height conversion from ${source} to ${target}`);
6267
- }
6268
-
6269
- // dist/spatial/i3s-spatial-transformer.js
7529
+ var import_core13 = require("@math.gl/core");
7530
+ var import_geospatial11 = require("@math.gl/geospatial");
6270
7531
  var I3SSpatialTransformer = class {
6271
7532
  /** Descriptor attached to output after all adapter-owned spatial values have been transformed. */
6272
7533
  spatialReference;
@@ -6461,7 +7722,7 @@ var I3SSpatialTransformer = class {
6461
7722
  positions[index + 2] = transformed[2] - origin[2];
6462
7723
  }
6463
7724
  const isGeographic = this.targetCoordinateFrame === "geographic";
6464
- const modelMatrix = isGeographic ? new import_core12.Matrix4() : new import_core12.Matrix4().translate(origin);
7725
+ const modelMatrix = isGeographic ? new import_core13.Matrix4() : new import_core13.Matrix4().translate(origin);
6465
7726
  return {
6466
7727
  positions,
6467
7728
  sourcePositions: absoluteSourcePositions,
@@ -6489,7 +7750,7 @@ var I3SSpatialTransformer = class {
6489
7750
  sourcePositions[index + 1],
6490
7751
  sourcePositions[index + 2]
6491
7752
  ]);
6492
- const inputNormal = new import_core12.Vector3([
7753
+ const inputNormal = new import_core13.Vector3([
6493
7754
  normals[index],
6494
7755
  normals[index + 1],
6495
7756
  normals[index + 2]
@@ -6527,12 +7788,12 @@ var I3SSpatialTransformer = class {
6527
7788
  }
6528
7789
  /** Rebuild an I3S bound as a WGS84 ECEF box for generic `Tileset3D` traversal. */
6529
7790
  transformBoundingVolumeToGeocentric(bounds) {
6530
- const cartesianSamples = this.getGeographicBoundSamples(bounds).map((position) => Array.from(import_geospatial9.Ellipsoid.WGS84.cartographicToCartesian(new import_core12.Vector3(position))));
7791
+ const cartesianSamples = this.getGeographicBoundSamples(bounds).map((position) => Array.from(import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position))));
6531
7792
  return { box: getAxisAlignedBox(cartesianSamples) };
6532
7793
  }
6533
7794
  /** Rebuild an I3S bound as WGS84 ECEF after asynchronous elevation placement. */
6534
7795
  async transformBoundingVolumeToGeocentricAsync(bounds) {
6535
- const cartesianSamples = (await this.getGeographicBoundSamplesAsync(bounds)).map((position) => Array.from(import_geospatial9.Ellipsoid.WGS84.cartographicToCartesian(new import_core12.Vector3(position))));
7796
+ const cartesianSamples = (await this.getGeographicBoundSamplesAsync(bounds)).map((position) => Array.from(import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position))));
6536
7797
  return { box: getAxisAlignedBox(cartesianSamples) };
6537
7798
  }
6538
7799
  /** Return a WGS84 geographic MBS used by the I3S screen-threshold calculation. */
@@ -6559,7 +7820,7 @@ var I3SSpatialTransformer = class {
6559
7820
  */
6560
7821
  async transformBoundsAsync(bounds) {
6561
7822
  const geographicSamples = await this.getGeographicBoundSamplesAsync(bounds);
6562
- const cartesianSamples = geographicSamples.map((position) => Array.from(import_geospatial9.Ellipsoid.WGS84.cartographicToCartesian(new import_core12.Vector3(position))));
7823
+ const cartesianSamples = geographicSamples.map((position) => Array.from(import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position))));
6563
7824
  const targetSamples = geographicSamples.map((position) => this.geographicToTargetTransformer.transformPosition(position));
6564
7825
  return {
6565
7826
  boundingVolume: { box: getAxisAlignedBox(cartesianSamples) },
@@ -6682,21 +7943,21 @@ var I3SSpatialTransformer = class {
6682
7943
  if (this.targetCoordinateFrame === "geographic") {
6683
7944
  return ecefVectorToLocalEnu(ecefNormal, geographic).normalize();
6684
7945
  }
6685
- const cartesian = import_geospatial9.Ellipsoid.WGS84.cartographicToCartesian(new import_core12.Vector3(geographic));
7946
+ const cartesian = import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(geographic));
6686
7947
  const jacobianColumns = [];
6687
7948
  for (const direction of getPositiveAxisDirections()) {
6688
- const positiveGeographic = import_geospatial9.Ellipsoid.WGS84.cartesianToCartographic(new import_core12.Vector3(cartesian).add(direction));
6689
- const negativeGeographic = import_geospatial9.Ellipsoid.WGS84.cartesianToCartographic(new import_core12.Vector3(cartesian).subtract(direction));
7949
+ const positiveGeographic = import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(new import_core13.Vector3(cartesian).add(direction));
7950
+ const negativeGeographic = import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(new import_core13.Vector3(cartesian).subtract(direction));
6690
7951
  const positiveTarget = this.geographicToTargetTransformer.transformPosition(positiveGeographic);
6691
7952
  const negativeTarget = this.geographicToTargetTransformer.transformPosition(negativeGeographic);
6692
7953
  jacobianColumns.push((positiveTarget[0] - negativeTarget[0]) / 2, (positiveTarget[1] - negativeTarget[1]) / 2, (positiveTarget[2] - negativeTarget[2]) / 2);
6693
7954
  }
6694
- const normalMatrix = new import_core12.Matrix3(jacobianColumns);
7955
+ const normalMatrix = new import_core13.Matrix3(jacobianColumns);
6695
7956
  if (Math.abs(normalMatrix.determinant()) < 1e-12) {
6696
7957
  throw new Error("Cannot transform an I3S normal through a singular local projection");
6697
7958
  }
6698
7959
  normalMatrix.invert().transpose();
6699
- return new import_core12.Vector3(ecefNormal).transformByMatrix3(normalMatrix).normalize();
7960
+ return new import_core13.Vector3(ecefNormal).transformByMatrix3(normalMatrix).normalize();
6700
7961
  }
6701
7962
  };
6702
7963
  function getElevationProvider(mode, options) {
@@ -6728,17 +7989,17 @@ function getGeographicBoundingSphere(geographicSamples) {
6728
7989
  if (!geographicSamples.length) {
6729
7990
  throw new Error("I3S spatial transformation requires at least one bound sample");
6730
7991
  }
6731
- const cartesianSamples = geographicSamples.map((position) => import_geospatial9.Ellipsoid.WGS84.cartographicToCartesian(new import_core12.Vector3(position)));
6732
- const cartesianCenter = new import_core12.Vector3();
7992
+ const cartesianSamples = geographicSamples.map((position) => import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position)));
7993
+ const cartesianCenter = new import_core13.Vector3();
6733
7994
  for (const sample of cartesianSamples) {
6734
7995
  cartesianCenter.add(sample);
6735
7996
  }
6736
7997
  cartesianCenter.scale(1 / cartesianSamples.length);
6737
7998
  let radius = 0;
6738
7999
  for (const sample of cartesianSamples) {
6739
- radius = Math.max(radius, new import_core12.Vector3(sample).subtract(cartesianCenter).magnitude());
8000
+ radius = Math.max(radius, new import_core13.Vector3(sample).subtract(cartesianCenter).magnitude());
6740
8001
  }
6741
- const geographicCenter = import_geospatial9.Ellipsoid.WGS84.cartesianToCartographic(cartesianCenter);
8002
+ const geographicCenter = import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(cartesianCenter);
6742
8003
  return [geographicCenter[0], geographicCenter[1], geographicCenter[2], radius];
6743
8004
  }
6744
8005
  function getSourceBoundSamples(bounds, sourceFrame, sourceToGeographicTransformer, sourceVerticalUnitScale) {
@@ -6747,28 +8008,28 @@ function getSourceBoundSamples(bounds, sourceFrame, sourceToGeographicTransforme
6747
8008
  const center2 = Array.from(obb.center).slice(0, 3);
6748
8009
  center2[2] *= sourceVerticalUnitScale;
6749
8010
  const halfSize = Array.from(obb.halfSize).slice(0, 3);
6750
- const quaternion = new import_core12.Quaternion().fromArray(Array.from(obb.quaternion));
8011
+ const quaternion = new import_core13.Quaternion().fromArray(Array.from(obb.quaternion));
6751
8012
  const axes = [
6752
- new import_core12.Vector3([halfSize[0], 0, 0]).transformByQuaternion(quaternion),
6753
- new import_core12.Vector3([0, halfSize[1], 0]).transformByQuaternion(quaternion),
6754
- new import_core12.Vector3([0, 0, halfSize[2]]).transformByQuaternion(quaternion)
8013
+ new import_core13.Vector3([halfSize[0], 0, 0]).transformByQuaternion(quaternion),
8014
+ new import_core13.Vector3([0, halfSize[1], 0]).transformByQuaternion(quaternion),
8015
+ new import_core13.Vector3([0, 0, halfSize[2]]).transformByQuaternion(quaternion)
6755
8016
  ];
6756
8017
  if (sourceFrame === "geographic") {
6757
8018
  const geographicCenter = sourceToGeographicTransformer.transformPosition(center2);
6758
- const cartesianCenter = import_geospatial9.Ellipsoid.WGS84.cartographicToCartesian(new import_core12.Vector3(geographicCenter));
8019
+ const cartesianCenter = import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(geographicCenter));
6759
8020
  const cartesianAxes = bounds.normalReferenceFrame === "vertex-reference-frame" ? axes.map((axis) => localEnuVectorToEcef(axis, geographicCenter)) : axes;
6760
8021
  return getBoxSigns().map((signs) => {
6761
- const corner = new import_core12.Vector3(cartesianCenter);
8022
+ const corner = new import_core13.Vector3(cartesianCenter);
6762
8023
  for (let axisIndex = 0; axisIndex < 3; axisIndex++) {
6763
- corner.add(new import_core12.Vector3(cartesianAxes[axisIndex]).scale(signs[axisIndex]));
8024
+ corner.add(new import_core13.Vector3(cartesianAxes[axisIndex]).scale(signs[axisIndex]));
6764
8025
  }
6765
- return Array.from(import_geospatial9.Ellipsoid.WGS84.cartesianToCartographic(corner));
8026
+ return Array.from(import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(corner));
6766
8027
  });
6767
8028
  }
6768
8029
  return getBoxSigns().map((signs) => {
6769
- const corner = new import_core12.Vector3(center2);
8030
+ const corner = new import_core13.Vector3(center2);
6770
8031
  for (let axisIndex = 0; axisIndex < 3; axisIndex++) {
6771
- corner.add(new import_core12.Vector3(axes[axisIndex]).scale(signs[axisIndex]));
8032
+ corner.add(new import_core13.Vector3(axes[axisIndex]).scale(signs[axisIndex]));
6772
8033
  }
6773
8034
  return Array.from(corner);
6774
8035
  });
@@ -6782,10 +8043,10 @@ function getSourceBoundSamples(bounds, sourceFrame, sourceToGeographicTransforme
6782
8043
  const radius = mbs[3];
6783
8044
  if (sourceFrame === "geographic") {
6784
8045
  const geographicCenter = sourceToGeographicTransformer.transformPosition(center);
6785
- const cartesianCenter = import_geospatial9.Ellipsoid.WGS84.cartographicToCartesian(new import_core12.Vector3(geographicCenter));
6786
- return getAxisDirections().map((direction) => Array.from(import_geospatial9.Ellipsoid.WGS84.cartesianToCartographic(new import_core12.Vector3(cartesianCenter).add(new import_core12.Vector3(direction).scale(radius)))));
8046
+ const cartesianCenter = import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(geographicCenter));
8047
+ return getAxisDirections().map((direction) => Array.from(import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(new import_core13.Vector3(cartesianCenter).add(new import_core13.Vector3(direction).scale(radius)))));
6787
8048
  }
6788
- return getAxisDirections().map((direction) => Array.from(new import_core12.Vector3(center).add(new import_core12.Vector3(direction).scale(radius))));
8049
+ return getAxisDirections().map((direction) => Array.from(new import_core13.Vector3(center).add(new import_core13.Vector3(direction).scale(radius))));
6789
8050
  }
6790
8051
  function getBoxSigns() {
6791
8052
  return [-1, 1].flatMap((x) => [-1, 1].flatMap((y) => [-1, 1].map((z) => [x, y, z])));
@@ -6801,18 +8062,18 @@ function getAxisDirections() {
6801
8062
  ];
6802
8063
  }
6803
8064
  function getPositiveAxisDirections() {
6804
- return [new import_core12.Vector3([1, 0, 0]), new import_core12.Vector3([0, 1, 0]), new import_core12.Vector3([0, 0, 1])];
8065
+ return [new import_core13.Vector3([1, 0, 0]), new import_core13.Vector3([0, 1, 0]), new import_core13.Vector3([0, 0, 1])];
6805
8066
  }
6806
8067
  function localEnuVectorToEcef(vector, geographic) {
6807
8068
  const longitude = geographic[0] * Math.PI / 180;
6808
8069
  const latitude = geographic[1] * Math.PI / 180;
6809
- const east = new import_core12.Vector3([-Math.sin(longitude), Math.cos(longitude), 0]);
6810
- const north = new import_core12.Vector3([
8070
+ const east = new import_core13.Vector3([-Math.sin(longitude), Math.cos(longitude), 0]);
8071
+ const north = new import_core13.Vector3([
6811
8072
  -Math.sin(latitude) * Math.cos(longitude),
6812
8073
  -Math.sin(latitude) * Math.sin(longitude),
6813
8074
  Math.cos(latitude)
6814
8075
  ]);
6815
- const up = new import_core12.Vector3([
8076
+ const up = new import_core13.Vector3([
6816
8077
  Math.cos(latitude) * Math.cos(longitude),
6817
8078
  Math.cos(latitude) * Math.sin(longitude),
6818
8079
  Math.sin(latitude)
@@ -6822,18 +8083,18 @@ function localEnuVectorToEcef(vector, geographic) {
6822
8083
  function ecefVectorToLocalEnu(vector, geographic) {
6823
8084
  const longitude = geographic[0] * Math.PI / 180;
6824
8085
  const latitude = geographic[1] * Math.PI / 180;
6825
- const east = new import_core12.Vector3([-Math.sin(longitude), Math.cos(longitude), 0]);
6826
- const north = new import_core12.Vector3([
8086
+ const east = new import_core13.Vector3([-Math.sin(longitude), Math.cos(longitude), 0]);
8087
+ const north = new import_core13.Vector3([
6827
8088
  -Math.sin(latitude) * Math.cos(longitude),
6828
8089
  -Math.sin(latitude) * Math.sin(longitude),
6829
8090
  Math.cos(latitude)
6830
8091
  ]);
6831
- const up = new import_core12.Vector3([
8092
+ const up = new import_core13.Vector3([
6832
8093
  Math.cos(latitude) * Math.cos(longitude),
6833
8094
  Math.cos(latitude) * Math.sin(longitude),
6834
8095
  Math.sin(latitude)
6835
8096
  ]);
6836
- return new import_core12.Vector3([east.dot(vector), north.dot(vector), up.dot(vector)]);
8097
+ return new import_core13.Vector3([east.dot(vector), north.dot(vector), up.dot(vector)]);
6837
8098
  }
6838
8099
  function getAxisAlignedBox(positions) {
6839
8100
  const minimum = [Infinity, Infinity, Infinity];
@@ -6888,8 +8149,10 @@ var EMPTY_CONTENT_FORMATS2 = {
6888
8149
  var I3SSource = class {
6889
8150
  /** I3S format discriminator. */
6890
8151
  type = TILESET_TYPE.I3S;
6891
- /** Loader used for tile metadata and content requests. */
8152
+ /** Primary I3S loader used for root and child metadata requests. */
6892
8153
  loader;
8154
+ /** Worker-capable loader used for binary tile content when supplied by the format loader. */
8155
+ contentLoader;
6893
8156
  /** Root I3S layer URL. */
6894
8157
  url;
6895
8158
  /** Base path used for relative tile resource resolution. */
@@ -6924,6 +8187,7 @@ var I3SSource = class {
6924
8187
  this.rootTileset = isTilesetRequest2(input) ? null : input;
6925
8188
  this.tileset = this.rootTileset;
6926
8189
  this.loader = request.loader;
8190
+ this.contentLoader = getI3SContentLoader(request.loader);
6927
8191
  this.url = request.url;
6928
8192
  this.basePath = request.basePath || path_exports.dirname(request.url);
6929
8193
  this.resolver = request.resolver;
@@ -7040,7 +8304,7 @@ var I3SSource = class {
7040
8304
  isTileHeader: false
7041
8305
  }
7042
8306
  };
7043
- tile.content = await this.loadResourceData(contentUrl, options);
8307
+ tile.content = await this.loadResourceData(contentUrl, options, this.contentLoader);
7044
8308
  return { loaded: true };
7045
8309
  }
7046
8310
  /**
@@ -7095,8 +8359,8 @@ var I3SSource = class {
7095
8359
  getViewState(rootTile) {
7096
8360
  var _a;
7097
8361
  if (this.spatialTransformer && rootTile) {
7098
- const center = new import_core13.Vector3(rootTile.boundingVolume.center);
7099
- const cartographicCenter = new import_core13.Vector3(rootTile.header.i3sLodMbs.slice(0, 3));
8362
+ const center = new import_core14.Vector3(rootTile.boundingVolume.center);
8363
+ const cartographicCenter = new import_core14.Vector3(rootTile.header.i3sLodMbs.slice(0, 3));
7100
8364
  if (this.spatialTransformer.targetCoordinateFrame === "geographic") {
7101
8365
  return {
7102
8366
  boundingVolume: rootTile.boundingVolume,
@@ -7116,9 +8380,9 @@ var I3SSource = class {
7116
8380
  const fullExtent = metadata.tileset.fullExtent;
7117
8381
  if (fullExtent) {
7118
8382
  const { xmin, xmax, ymin, ymax, zmin, zmax } = fullExtent;
7119
- const cartographicCenter = new import_core13.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, zmin + (zmax - zmin) / 2);
7120
- const cartesianCenter = new import_core13.Vector3();
7121
- import_geospatial10.Ellipsoid.WGS84.cartographicToCartesian(cartographicCenter, cartesianCenter);
8383
+ const cartographicCenter = new import_core14.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, zmin + (zmax - zmin) / 2);
8384
+ const cartesianCenter = new import_core14.Vector3();
8385
+ import_geospatial12.Ellipsoid.WGS84.cartographicToCartesian(cartographicCenter, cartesianCenter);
7122
8386
  return {
7123
8387
  cartographicCenter,
7124
8388
  cartesianCenter,
@@ -7128,9 +8392,9 @@ var I3SSource = class {
7128
8392
  const extent = (_a = metadata.tileset.store) == null ? void 0 : _a.extent;
7129
8393
  if (extent) {
7130
8394
  const [xmin, ymin, xmax, ymax] = extent;
7131
- const cartographicCenter = new import_core13.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, 0);
7132
- const cartesianCenter = new import_core13.Vector3();
7133
- import_geospatial10.Ellipsoid.WGS84.cartographicToCartesian(cartographicCenter, cartesianCenter);
8395
+ const cartographicCenter = new import_core14.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, 0);
8396
+ const cartesianCenter = new import_core14.Vector3();
8397
+ import_geospatial12.Ellipsoid.WGS84.cartographicToCartesian(cartographicCenter, cartesianCenter);
7134
8398
  return {
7135
8399
  cartographicCenter,
7136
8400
  cartesianCenter,
@@ -7139,7 +8403,7 @@ var I3SSource = class {
7139
8403
  }
7140
8404
  console.warn("Extent is not defined in the tileset header");
7141
8405
  return {
7142
- cartographicCenter: new import_core13.Vector3(),
8406
+ cartographicCenter: new import_core14.Vector3(),
7143
8407
  zoom: 1
7144
8408
  };
7145
8409
  }
@@ -7171,11 +8435,11 @@ var I3SSource = class {
7171
8435
  /**
7172
8436
  * Loads data through injected core APIs so this module stays independent from `@loaders.gl/core`.
7173
8437
  */
7174
- async loadWithCoreApi(url, options) {
8438
+ async loadWithCoreApi(url, options, loader = this.loader) {
7175
8439
  if (!this.coreApi) {
7176
8440
  throw new Error("I3SSource requires an injected coreApi to load tileset data");
7177
8441
  }
7178
- return await this.coreApi.load(url, this.loader, options);
8442
+ return await this.coreApi.load(url, loader, options);
7179
8443
  }
7180
8444
  /**
7181
8445
  * Loads data through an injected resolver when present, otherwise through the injected core API.
@@ -7202,11 +8466,11 @@ var I3SSource = class {
7202
8466
  /**
7203
8467
  * Loads tile metadata or content through an injected resolver when present, otherwise through the injected core API.
7204
8468
  */
7205
- async loadResourceData(url, options) {
8469
+ async loadResourceData(url, options, loader = this.loader) {
7206
8470
  if (this.resolver) {
7207
- return await this.resolver.loadResource(url, this.loader, options);
8471
+ return await this.resolver.loadResource(url, loader, options);
7208
8472
  }
7209
- return await this.loadWithCoreApi(url, options);
8473
+ return await this.loadWithCoreApi(url, options, loader);
7210
8474
  }
7211
8475
  /** Transform one I3S header bound after target options and elevation providers are available. */
7212
8476
  async transformTileHeader(header) {
@@ -7230,6 +8494,9 @@ var I3SSource = class {
7230
8494
  };
7231
8495
  }
7232
8496
  };
8497
+ function getI3SContentLoader(loader) {
8498
+ return loader.contentLoader || loader;
8499
+ }
7233
8500
  function isTilesetRequest2(input) {
7234
8501
  return Boolean(input && typeof input === "object" && "url" in input && "loader" in input && !("type" in input));
7235
8502
  }
@@ -8456,7 +9723,7 @@ function isAbortError3(error) {
8456
9723
  }
8457
9724
 
8458
9725
  // dist/point-cloud/point-cloud-tileset.js
8459
- var import_core14 = require("@math.gl/core");
9726
+ var import_core15 = require("@math.gl/core");
8460
9727
  var import_culling6 = require("@math.gl/culling");
8461
9728
 
8462
9729
  // dist/point-cloud/point-cloud-tile.js
@@ -8620,7 +9887,7 @@ var PointCloudTileset = class {
8620
9887
  const viewState = await ((_b = (_a = this.dataSource).getViewState) == null ? void 0 : _b.call(_a));
8621
9888
  this.boundingVolume = (viewState == null ? void 0 : viewState.boundingVolume) || this.root.boundingVolume;
8622
9889
  const center = (viewState == null ? void 0 : viewState.cartographicCenter) || this.root.boundingVolume.center;
8623
- this.cartographicCenter = new import_core14.Vector3(center);
9890
+ this.cartographicCenter = new import_core15.Vector3(center);
8624
9891
  this.zoom = (viewState == null ? void 0 : viewState.zoom) || this.estimateZoom(this.boundingVolume);
8625
9892
  }
8626
9893
  /**