@loaders.gl/tiles 5.0.0-alpha.6 → 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 (96) hide show
  1. package/dist/dist.dev.js +5160 -2896
  2. package/dist/dist.min.js +4 -2
  3. package/dist/index.cjs +1509 -454
  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/tile-3d-contracts.d.ts +63 -0
  30. package/dist/tileset-3d/common/tile-3d-contracts.d.ts.map +1 -0
  31. package/dist/tileset-3d/common/tile-3d-contracts.js +51 -0
  32. package/dist/tileset-3d/common/tile-3d-contracts.js.map +1 -0
  33. package/dist/tileset-3d/common/tile-3d.d.ts +29 -5
  34. package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
  35. package/dist/tileset-3d/common/tile-3d.js +147 -18
  36. package/dist/tileset-3d/common/tile-3d.js.map +1 -1
  37. package/dist/tileset-3d/common/tileset-3d.d.ts +14 -0
  38. package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
  39. package/dist/tileset-3d/common/tileset-3d.js +27 -1
  40. package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
  41. package/dist/tileset-3d/common/tileset-observability.d.ts +60 -0
  42. package/dist/tileset-3d/common/tileset-observability.d.ts.map +1 -0
  43. package/dist/tileset-3d/common/tileset-observability.js +39 -0
  44. package/dist/tileset-3d/common/tileset-observability.js.map +1 -0
  45. package/dist/tileset-3d/common/tileset-source.d.ts +3 -0
  46. package/dist/tileset-3d/common/tileset-source.d.ts.map +1 -1
  47. package/dist/tileset-3d/common/tileset-source.js.map +1 -1
  48. package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
  49. package/dist/tileset-3d/common/tileset-traverser.js +4 -3
  50. package/dist/tileset-3d/common/tileset-traverser.js.map +1 -1
  51. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts +47 -1
  52. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts.map +1 -1
  53. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js +327 -30
  54. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js.map +1 -1
  55. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +32 -0
  56. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts.map +1 -1
  57. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js +265 -14
  58. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -1
  59. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts +5 -3
  60. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -1
  61. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js +7 -4
  62. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js.map +1 -1
  63. package/dist/tileset-3d/helpers/bounding-volume.d.ts +7 -3
  64. package/dist/tileset-3d/helpers/bounding-volume.d.ts.map +1 -1
  65. package/dist/tileset-3d/helpers/bounding-volume.js +19 -61
  66. package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -1
  67. package/dist/tileset-3d/helpers/frame-state.d.ts +4 -0
  68. package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
  69. package/dist/tileset-3d/helpers/frame-state.js +6 -1
  70. package/dist/tileset-3d/helpers/frame-state.js.map +1 -1
  71. package/dist/tileset-3d/helpers/s2-bounding-volume.d.ts +17 -0
  72. package/dist/tileset-3d/helpers/s2-bounding-volume.d.ts.map +1 -0
  73. package/dist/tileset-3d/helpers/s2-bounding-volume.js +20 -0
  74. package/dist/tileset-3d/helpers/s2-bounding-volume.js.map +1 -0
  75. package/dist/types.d.ts +2 -0
  76. package/dist/types.d.ts.map +1 -1
  77. package/package.json +13 -12
  78. package/src/index.ts +13 -1
  79. package/src/spatial/format-spatial-reference.ts +127 -53
  80. package/src/spatial/get-spatial-coordinate-frame.ts +126 -31
  81. package/src/spatial/normalize-crs-identifier.ts +26 -0
  82. package/src/spatial/spatial-coordinate-transformer.ts +106 -15
  83. package/src/spatial/tiles-3d-spatial-transformer.ts +206 -0
  84. package/src/tileset-3d/common/tile-3d-contracts.ts +115 -0
  85. package/src/tileset-3d/common/tile-3d.ts +172 -18
  86. package/src/tileset-3d/common/tileset-3d.ts +29 -1
  87. package/src/tileset-3d/common/tileset-observability.ts +100 -0
  88. package/src/tileset-3d/common/tileset-source.ts +3 -0
  89. package/src/tileset-3d/common/tileset-traverser.ts +4 -3
  90. package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +481 -34
  91. package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +387 -14
  92. package/src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts +7 -4
  93. package/src/tileset-3d/helpers/bounding-volume.ts +29 -62
  94. package/src/tileset-3d/helpers/frame-state.ts +6 -1
  95. package/src/tileset-3d/helpers/s2-bounding-volume.ts +35 -0
  96. 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
  });
@@ -2661,6 +2751,24 @@ var Tileset3D = class {
2661
2751
  get frameNumber() {
2662
2752
  return this._frameNumber;
2663
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
+ }
2664
2772
  /**
2665
2773
  * Gets or sets the soft target, in bytes, for cached tile content not needed this frame.
2666
2774
  *
@@ -2788,6 +2896,12 @@ var Tileset3D = class {
2788
2896
  return;
2789
2897
  }
2790
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
+ }
2791
2905
  this._cache.reset();
2792
2906
  this._frameNumber++;
2793
2907
  this.traverseCounter = preparedViewports.length;
@@ -2976,21 +3090,21 @@ var Tileset3D = class {
2976
3090
  this.stats.get(MAXIMUM_SSE).count = this.memoryAdjustedScreenSpaceError;
2977
3091
  }
2978
3092
  async _initializeTileSet() {
2979
- var _a, _b, _c;
3093
+ var _a, _b, _c, _d;
2980
3094
  try {
2981
3095
  const initializePromise = this.source.initialize();
2982
3096
  const initialMetadata = this._getSourceMetadata();
2983
3097
  if (initialMetadata) {
2984
3098
  this._syncSourceState(initialMetadata);
2985
3099
  }
2986
- 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")) {
2987
3101
  this.root = this._initializeTileHeaders(initialMetadata.tileset, null);
2988
3102
  this._applyViewState();
2989
3103
  }
2990
3104
  await initializePromise;
2991
3105
  const metadata = this.source.getMetadata();
2992
3106
  this._syncSourceState(metadata);
2993
- 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));
2994
3108
  if (!this.root) {
2995
3109
  this.root = this._initializeTileHeaders(metadata.tileset, null);
2996
3110
  }
@@ -3211,6 +3325,36 @@ function preserveTransformedSpatialReference(current, next) {
3211
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;
3212
3326
  }
3213
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
+
3214
3358
  // dist/tileset-3d/common/indexed-archive-tileset-source.js
3215
3359
  var IndexedArchiveTilesetSource = class {
3216
3360
  /** URL exposed to the tileset source that owns this archive source. */
@@ -3327,6 +3471,47 @@ function stripQueryString(url) {
3327
3471
  var import_core8 = require("@math.gl/core");
3328
3472
  var import_culling4 = require("@math.gl/culling");
3329
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
+
3330
3515
  // dist/tileset-3d/helpers/bounding-volume.js
3331
3516
  var import_core6 = require("@math.gl/core");
3332
3517
  var import_culling3 = require("@math.gl/culling");
@@ -3336,21 +3521,25 @@ function defined(x) {
3336
3521
  }
3337
3522
  var scratchPoint = new import_core6.Vector3();
3338
3523
  var scratchScale = new import_core6.Vector3();
3339
- function createBoundingVolume(boundingVolumeHeader, transform, result) {
3524
+ var identityTransform = new import_core6.Matrix4();
3525
+ function createBoundingVolume(boundingVolumeHeader, transform, result, initialTransform = transform) {
3340
3526
  assert(boundingVolumeHeader, "3D Tile: boundingVolume must be defined");
3341
3527
  if (boundingVolumeHeader.box) {
3342
- return createBox(boundingVolumeHeader.box, transform, result);
3528
+ const boxTransform = boundingVolumeHeader.s2VolumeInfo ? identityTransform : transform;
3529
+ return createBox(boundingVolumeHeader.box, boxTransform, result);
3343
3530
  }
3344
3531
  if (boundingVolumeHeader.region) {
3345
- 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);
3346
3535
  }
3347
3536
  if (boundingVolumeHeader.sphere) {
3348
3537
  return createSphere(boundingVolumeHeader.sphere, transform, result);
3349
3538
  }
3350
3539
  throw new Error("3D Tile: boundingVolume must contain a sphere, region, or box");
3351
3540
  }
3352
- function getCartographicBounds(boundingVolumeHeader, boundingVolume) {
3353
- if (boundingVolumeHeader.box) {
3541
+ function getCartographicBounds(boundingVolumeHeader, boundingVolume, isRegionTransformed = false) {
3542
+ if (boundingVolumeHeader.box || boundingVolumeHeader.region && isRegionTransformed) {
3354
3543
  return orientedBoundingBoxToCartographicBounds(boundingVolume);
3355
3544
  }
3356
3545
  if (boundingVolumeHeader.region) {
@@ -3830,7 +4019,7 @@ var TilesetTraverser = class {
3830
4019
  }
3831
4020
  stack.push(child);
3832
4021
  hasVisibleChild = true;
3833
- } else if (checkRefines || loadSiblings) {
4022
+ } else if ((checkRefines || loadSiblings) && child._inRequestVolume) {
3834
4023
  this.loadTile(child, frameState);
3835
4024
  this.touchTile(child, frameState);
3836
4025
  }
@@ -4017,6 +4206,13 @@ function getContentVisibility(contentVolumes, fallbackVolume, cullingVolume2, vi
4017
4206
  }
4018
4207
  return intersectingVolume ? "intersecting" : "inside";
4019
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
+ }
4020
4216
  function defined2(x) {
4021
4217
  return x !== void 0 && x !== null;
4022
4218
  }
@@ -4044,6 +4240,8 @@ var Tile3D = class {
4044
4240
  * separate ensures transform updates never compound an already-scaled geometric error.
4045
4241
  */
4046
4242
  _unscaledLodMetricValue = 0;
4243
+ /** Whether the source format defines geometric error in transform-local units. */
4244
+ _scaleGeometricError = true;
4047
4245
  /** @todo math.gl is not exporting BoundingVolume base type? */
4048
4246
  boundingVolume = null;
4049
4247
  /**
@@ -4053,6 +4251,14 @@ var Tile3D = class {
4053
4251
  content = null;
4054
4252
  /** Loaded payloads in the same order as {@link contentUrls}; `content` remains the primary payload. */
4055
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;
4056
4262
  /**
4057
4263
  * Metadata entity attached to this tile in the source tileset, if present.
4058
4264
  *
@@ -4119,6 +4325,7 @@ var Tile3D = class {
4119
4325
  get viewerRequestVolume() {
4120
4326
  return this._viewerRequestVolume;
4121
4327
  }
4328
+ /** Accumulated tile transforms from the source JSON, excluding the tileset model matrix. */
4122
4329
  _initialTransform = new import_core8.Matrix4();
4123
4330
  // Used by traverser, cannot be marked private
4124
4331
  _priority = 0;
@@ -4160,15 +4367,14 @@ var Tile3D = class {
4160
4367
  this.type = header.type;
4161
4368
  this.contentUrl = header.contentUrl;
4162
4369
  this.contentUrls = header.contentUrls || (header.contentUrl ? [header.contentUrl] : []);
4163
- this.metadata = header.metadata || null;
4164
- const contentHeaders = Array.isArray(header.content) ? header.content : [header.content];
4165
- this.contentMetadata = contentHeaders.filter(Boolean).map((contentHeader) => contentHeader.metadata || null);
4370
+ this._initializeMetadata(header);
4166
4371
  this.childrenState = header.implicitSubtree ? "unloaded" : "ready";
4167
4372
  this._initializeLodMetric(header);
4168
4373
  this._initializeTransforms(header);
4169
4374
  this._updateLodMetricScale();
4170
4375
  this._initializeBoundingVolumes(header);
4171
4376
  this._initializeContent(header);
4377
+ this._initializeContentEntries(header);
4172
4378
  this._initializeRenderingState(header);
4173
4379
  Object.seal(this);
4174
4380
  }
@@ -4253,12 +4459,12 @@ var Tile3D = class {
4253
4459
  return this._screenSpaceError;
4254
4460
  }
4255
4461
  /**
4256
- * Get bounding box in cartographic coordinates
4462
+ * Get the current world-space bounding volume in cartographic coordinates.
4257
4463
  * @returns [min, max] each in [longitude, latitude, altitude]
4258
4464
  */
4259
4465
  get boundingBox() {
4260
4466
  if (!this._boundingBox) {
4261
- this._boundingBox = getCartographicBounds(this.header.boundingVolume, this.boundingVolume);
4467
+ this._boundingBox = getCartographicBounds(this.header.boundingVolume, this.boundingVolume, !this.computedTransform.equals(this._initialTransform));
4262
4468
  }
4263
4469
  return this._boundingBox;
4264
4470
  }
@@ -4358,6 +4564,7 @@ var Tile3D = class {
4358
4564
  */
4359
4565
  // eslint-disable-next-line max-statements, complexity
4360
4566
  async loadContent() {
4567
+ var _a;
4361
4568
  if (this.hasEmptyContent) {
4362
4569
  return { loaded: false };
4363
4570
  }
@@ -4380,11 +4587,13 @@ var Tile3D = class {
4380
4587
  this.contents = loadResult.contents;
4381
4588
  this.content = this.contents[0] || null;
4382
4589
  }
4590
+ this.vectorContent = ((_a = this.contents.find((content) => Boolean(content == null ? void 0 : content.vectorContent))) == null ? void 0 : _a.vectorContent) || null;
4383
4591
  if (this.tileset.options.contentLoader) {
4384
4592
  await this.tileset.options.contentLoader(this);
4385
4593
  }
4386
4594
  this.contentState = TILE_CONTENT_STATE.READY;
4387
4595
  this._onContentLoaded();
4596
+ this._updateContentEntriesAfterLoad(loadResult);
4388
4597
  return loadResult;
4389
4598
  } catch (error) {
4390
4599
  this.contentState = TILE_CONTENT_STATE.FAILED;
@@ -4447,30 +4656,114 @@ var Tile3D = class {
4447
4656
  /**
4448
4657
  * Replaces a contentless implicit placeholder with its materialized subtree-root header.
4449
4658
  *
4450
- * Transform state is intentionally preserved: the placeholder already owns the composed
4451
- * transform, and reapplying the root transform here would double-transform bounds and geometric
4452
- * 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.
4453
4662
  *
4454
4663
  * @param materializedHeader - Available root header produced from one subtree resource.
4455
4664
  */
4456
4665
  applyImplicitSubtreeHeader(materializedHeader) {
4457
4666
  const existingTransform = this.header.transform;
4458
4667
  const existingTransformMatrix = this.header.transformMatrix;
4668
+ const hasMaterializedTransform = materializedHeader.transform !== void 0;
4459
4669
  this.header = {
4460
4670
  ...this.header,
4461
4671
  ...materializedHeader,
4462
- transform: existingTransform,
4463
- transformMatrix: existingTransformMatrix,
4672
+ transform: hasMaterializedTransform ? materializedHeader.transform : existingTransform,
4673
+ transformMatrix: hasMaterializedTransform ? materializedHeader.transformMatrix : existingTransformMatrix,
4464
4674
  implicitSubtree: void 0
4465
4675
  };
4676
+ if (hasMaterializedTransform) {
4677
+ this._initializeTransforms(this.header);
4678
+ }
4466
4679
  this.refine = this._getRefine(materializedHeader.refine);
4467
4680
  this.type = materializedHeader.type;
4468
4681
  this.contentUrl = materializedHeader.contentUrl;
4469
4682
  this.contentUrls = materializedHeader.contentUrls || (materializedHeader.contentUrl ? [materializedHeader.contentUrl] : []);
4683
+ this._initializeMetadata(this.header);
4470
4684
  this._initializeLodMetric(materializedHeader);
4471
4685
  this._updateLodMetricScale();
4472
4686
  this._initializeBoundingVolumes(this.header);
4687
+ this._boundingBox = void 0;
4473
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);
4474
4767
  }
4475
4768
  // Unloads the tile's content.
4476
4769
  unloadContent() {
@@ -4481,6 +4774,13 @@ var Tile3D = class {
4481
4774
  }
4482
4775
  this.contents = [];
4483
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);
4484
4784
  if (this.header.content && this.header.content.destroy) {
4485
4785
  this.header.content.destroy();
4486
4786
  }
@@ -4577,8 +4877,9 @@ var Tile3D = class {
4577
4877
  * @param frameState Current camera, culling, and optional clipping-plane state.
4578
4878
  * @returns The content visibility classification.
4579
4879
  */
4580
- contentVisibility(frameState) {
4581
- 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);
4582
4883
  }
4583
4884
  /**
4584
4885
  * Computes the (potentially approximate) distance from the closest point of the tile's bounding volume to the camera.
@@ -4625,6 +4926,8 @@ var Tile3D = class {
4625
4926
  // INTERNAL METHODS
4626
4927
  /** Initializes the tile's LOD metric from its header or its nearest available ancestor. */
4627
4928
  _initializeLodMetric(header) {
4929
+ var _a;
4930
+ this._scaleGeometricError = typeof header._scaleGeometricError === "boolean" ? header._scaleGeometricError : ((_a = this.parent) == null ? void 0 : _a._scaleGeometricError) ?? true;
4628
4931
  if ("lodMetricType" in header) {
4629
4932
  this.lodMetricType = header.lodMetricType;
4630
4933
  } else {
@@ -4660,7 +4963,7 @@ var Tile3D = class {
4660
4963
  * I3S `maxScreenThreshold` is already a screen-space metric and must not be transform-scaled.
4661
4964
  */
4662
4965
  _updateLodMetricScale() {
4663
- if (this.lodMetricType !== LOD_METRIC_TYPE.GEOMETRIC_ERROR) {
4966
+ if (this.lodMetricType !== LOD_METRIC_TYPE.GEOMETRIC_ERROR || !this._scaleGeometricError) {
4664
4967
  this.lodMetricValue = this._unscaledLodMetricValue;
4665
4968
  return;
4666
4969
  }
@@ -4740,16 +5043,17 @@ var Tile3D = class {
4740
5043
  }
4741
5044
  }
4742
5045
  _updateBoundingVolume(header) {
4743
- 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);
4744
5048
  if (header.viewerRequestVolume) {
4745
- this._viewerRequestVolume = createBoundingVolume(header.viewerRequestVolume, this.computedTransform, this._viewerRequestVolume);
5049
+ this._viewerRequestVolume = createBoundingVolume(header.viewerRequestVolume, this.computedTransform, this._viewerRequestVolume, this._initialTransform);
4746
5050
  }
4747
5051
  const content = header.content;
4748
5052
  if (content) {
4749
5053
  this._contentBoundingVolumeHeaders = Array.isArray(content) ? content : [content];
4750
5054
  }
4751
5055
  const contentHeaders = content ? Array.isArray(content) ? content : [content] : this._contentBoundingVolumeHeaders;
4752
- 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];
4753
5057
  this._contentBoundingVolume = this._contentBoundingVolumes[0] || null;
4754
5058
  }
4755
5059
  /**
@@ -4769,8 +5073,8 @@ var Tile3D = class {
4769
5073
  };
4770
5074
 
4771
5075
  // dist/tileset-3d/format-3d-tiles/tiles-3d-source.js
4772
- var import_geospatial7 = require("@math.gl/geospatial");
4773
- var import_core10 = require("@math.gl/core");
5076
+ var import_geospatial10 = require("@math.gl/geospatial");
5077
+ var import_core12 = require("@math.gl/core");
4774
5078
 
4775
5079
  // dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js
4776
5080
  var Tileset3DTraverser = class extends TilesetTraverser {
@@ -4980,10 +5284,12 @@ var Tileset3DTraverser = class extends TilesetTraverser {
4980
5284
  return b._distanceToCamera === 0 && a._distanceToCamera === 0 ? b._centerZDepth - a._centerZDepth : b._distanceToCamera - a._distanceToCamera;
4981
5285
  }
4982
5286
  /**
4983
- * 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.
4984
5288
  *
4985
- * The current parent remains the traversal boundary while metadata is loading, which preserves
4986
- * 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
4987
5293
  * progressive-resolution and foveated metrics, so subtree metadata follows the same priority
4988
5294
  * policy as render content. A completion notification lets the owning tileset publish the newly
4989
5295
  * materialized traversal result on the next update.
@@ -4992,7 +5298,9 @@ var Tileset3DTraverser = class extends TilesetTraverser {
4992
5298
  * @param frameState - Current culling and LOD state.
4993
5299
  */
4994
5300
  updateChildTiles(tile, frameState) {
4995
- 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))) {
4996
5304
  const requestKey = `${frameState.viewport.id}:${tile.id}`;
4997
5305
  if (!this.pendingImplicitSubtrees.has(requestKey)) {
4998
5306
  this.pendingImplicitSubtrees.set(requestKey, frameState.frameNumber);
@@ -5183,7 +5491,7 @@ function getI3SSpatialReference(layer) {
5183
5491
  const verticalUnitScale = getI3SVerticalUnitScale(verticalUnit, layer.ZFactor);
5184
5492
  const heightReference = heightModel === "ellipsoidal" ? "ellipsoidal" : heightModel === "gravity_related_height" ? "orthometric" : "unknown";
5185
5493
  const sourceIdentifier = getCrsIdentifier(spatialReference);
5186
- 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}`);
5187
5495
  const units = getI3SCoordinateUnits(coordinateFrame, verticalUnit, layer.ZFactor);
5188
5496
  const warnings = [];
5189
5497
  const elevationMode = getI3SElevationMode((_e = layer.elevationInfo) == null ? void 0 : _e.mode);
@@ -5241,6 +5549,10 @@ function getI3SElevationMode(mode) {
5241
5549
  }
5242
5550
  function get3DTilesSpatialReference(tileset) {
5243
5551
  var _a, _b;
5552
+ const draftSpatialReference = getDraft3DTilesSpatialReference(tileset);
5553
+ if (draftSpatialReference) {
5554
+ return draftSpatialReference;
5555
+ }
5244
5556
  const geocentricCrs = getTilesetSemanticValue(tileset, "TILESET_CRS_GEOCENTRIC");
5245
5557
  const coordinateEpoch = getTilesetSemanticValue(tileset, "TILESET_CRS_COORDINATE_EPOCH");
5246
5558
  const warnings = [];
@@ -5278,6 +5590,87 @@ function get3DTilesSpatialReference(tileset) {
5278
5590
  warnings
5279
5591
  });
5280
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
+ }
5281
5674
  function parseCoordinateEpoch(value) {
5282
5675
  if (typeof value !== "string" && typeof value !== "number") {
5283
5676
  return void 0;
@@ -5302,41 +5695,6 @@ function getI3SVerticalCrsDefinition(spatialReference) {
5302
5695
  function getCrsIdentifier(spatialReference) {
5303
5696
  return (spatialReference == null ? void 0 : spatialReference.latestWkid) ?? (spatialReference == null ? void 0 : spatialReference.wkid);
5304
5697
  }
5305
- function getIdentifierCoordinateFrame(identifier) {
5306
- if (identifier === 4978) {
5307
- return "geocentric";
5308
- }
5309
- if (identifier === 4326 || identifier === 4490 || identifier === 4979) {
5310
- return "geographic";
5311
- }
5312
- return "projected";
5313
- }
5314
- function getWktCoordinateFrame(wkt) {
5315
- var _a;
5316
- if (!wkt) {
5317
- return "unknown";
5318
- }
5319
- const normalizedWkt = wkt.trim().toUpperCase();
5320
- const rootKeyword = (_a = normalizedWkt.match(/^([A-Z][A-Z0-9_]*)\s*[\[(]/)) == null ? void 0 : _a[1];
5321
- if (rootKeyword === "GEOCCS") {
5322
- return "geocentric";
5323
- }
5324
- if (rootKeyword === "GEOGCS" || rootKeyword === "GEOGRAPHICCRS" || rootKeyword === "GEOGRAPHIC2DCRS" || rootKeyword === "GEOGRAPHIC3DCRS") {
5325
- return "geographic";
5326
- }
5327
- if (rootKeyword === "PROJCS" || rootKeyword === "PROJCRS" || rootKeyword === "PROJECTEDCRS") {
5328
- return "projected";
5329
- }
5330
- if (rootKeyword === "GEODCRS" || rootKeyword === "GEODETICCRS") {
5331
- if (/\bCS\s*[\[(]\s*CARTESIAN\s*,\s*3\b/.test(normalizedWkt)) {
5332
- return "geocentric";
5333
- }
5334
- if (/\bCS\s*[\[(]\s*ELLIPSOIDAL\s*,\s*[23]\b/.test(normalizedWkt)) {
5335
- return "geographic";
5336
- }
5337
- }
5338
- return "unknown";
5339
- }
5340
5698
  function getTilesetSemanticValue(tileset, semantic) {
5341
5699
  var _a, _b, _c, _d;
5342
5700
  const classIdentifier = (_a = tileset.metadata) == null ? void 0 : _a.class;
@@ -5354,131 +5712,787 @@ function getTilesetSemanticValue(tileset, semantic) {
5354
5712
  return void 0;
5355
5713
  }
5356
5714
 
5357
- // dist/tileset-3d/format-3d-tiles/implicit-tiling.js
5358
- var QUADTREE_CHILD_COUNT = 4;
5359
- var OCTREE_CHILD_COUNT = 8;
5360
- function createImplicitSubtreeReference(descriptor, coordinates) {
5361
- return {
5362
- coordinates,
5363
- subtreeUrl: replaceImplicitUrlTemplate(descriptor.subtreesUrlTemplate, coordinates),
5364
- descriptor
5365
- };
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 });
5366
5727
  }
5367
- function materializeImplicitSubtree(subtree, reference) {
5368
- const { descriptor } = reference;
5369
- validateImplicitDescriptor(descriptor);
5370
- const counters = { tileCount: 0, childSubtreeCount: 0 };
5371
- const root = materializeAvailableTile(subtree, reference, { localLevel: 0, mortonIndex: 0 }, reference.coordinates, counters);
5372
- if (!root) {
5373
- throw new Error(`Invalid 3D Tiles subtree: root tile is unavailable at ${reference.subtreeUrl}`);
5374
- }
5375
- return { root, ...counters };
5728
+ function registerSpatialDatumGrid(name, data) {
5729
+ validateResourceName(name, "datum grid");
5730
+ import_proj4.Proj4Projection.registerDatumGrid(name, data);
5376
5731
  }
5377
- function replaceImplicitUrlTemplate(templateUrl, coordinates) {
5378
- const values = {
5379
- level: coordinates.level,
5380
- x: coordinates.x,
5381
- y: coordinates.y,
5382
- z: coordinates.z
5383
- };
5384
- return templateUrl.replace(/{(level|x|y|z)}/gi, (_match, coordinateName) => String(values[coordinateName.toLowerCase()]));
5732
+ function registerGeoidModel(name, geoid) {
5733
+ validateResourceName(name, "geoid model");
5734
+ geoidModels.set(name, geoid);
5385
5735
  }
5386
- function materializeAvailableTile(subtree, reference, localCoordinates, globalCoordinates, counters) {
5387
- const { descriptor } = reference;
5388
- const childCount = getImplicitChildCount(descriptor.subdivisionScheme);
5389
- const availabilityIndex = getLevelOffset(childCount, localCoordinates.localLevel) + localCoordinates.mortonIndex;
5390
- if (!getAvailabilityValue(subtree.tileAvailability, availabilityIndex)) {
5391
- return null;
5392
- }
5393
- counters.tileCount++;
5394
- const contentAvailability = getContentAvailability(subtree.contentAvailability, availabilityIndex);
5395
- const children = [];
5396
- if (globalCoordinates.level < descriptor.maximumLevel) {
5397
- if (localCoordinates.localLevel + 1 < descriptor.subtreeLevels) {
5398
- for (let childIndex = 0; childIndex < childCount; childIndex++) {
5399
- const childCoordinates = getChildCoordinates(globalCoordinates, childIndex, descriptor.subdivisionScheme);
5400
- const childHeader = materializeAvailableTile(subtree, reference, {
5401
- localLevel: localCoordinates.localLevel + 1,
5402
- mortonIndex: localCoordinates.mortonIndex * childCount + childIndex
5403
- }, childCoordinates, counters);
5404
- if (childHeader) {
5405
- children.push(childHeader);
5406
- }
5407
- }
5408
- } else {
5409
- for (let childIndex = 0; childIndex < childCount; childIndex++) {
5410
- const childSubtreeIndex = localCoordinates.mortonIndex * childCount + childIndex;
5411
- if (getAvailabilityValue(subtree.childSubtreeAvailability, childSubtreeIndex)) {
5412
- const childCoordinates = getChildCoordinates(globalCoordinates, childIndex, descriptor.subdivisionScheme);
5413
- children.push(createLazyImplicitTileHeader(subtree, descriptor, childCoordinates));
5414
- counters.childSubtreeCount++;
5415
- }
5416
- }
5417
- }
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`);
5418
5747
  }
5419
- return formatImplicitTileHeader(subtree, descriptor, reference, globalCoordinates, contentAvailability, children);
5420
5748
  }
5421
- function createLazyImplicitTileHeader(subtree, descriptor, coordinates) {
5422
- const reference = createImplicitSubtreeReference(descriptor, coordinates);
5423
- return {
5424
- id: getImplicitTileId(reference, coordinates),
5425
- children: [],
5426
- implicitSubtree: reference,
5427
- boundingVolume: calculateImplicitBoundingVolume(descriptor.rootBoundingVolume, coordinates, descriptor.subdivisionScheme),
5428
- geometricError: descriptor.rootLodMetricValue / 2 ** coordinates.level,
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
+ }
5814
+ }
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");
5824
+ }
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;
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;
5844
+ }
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;
5933
+ }
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;
5939
+ }
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);
6053
+ }
6054
+ return this.geographicCoordinateTransformer.transformPosition(sample);
6055
+ }
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];
6167
+ }
6168
+
6169
+ // dist/tileset-3d/format-3d-tiles/implicit-tiling.js
6170
+ var QUADTREE_CHILD_COUNT = 4;
6171
+ var OCTREE_CHILD_COUNT = 8;
6172
+ var AVAILABILITY_RANK_CACHE = /* @__PURE__ */ new WeakMap();
6173
+ function createImplicitSubtreeReference(descriptor, coordinates) {
6174
+ return {
6175
+ coordinates,
6176
+ subtreeUrl: replaceImplicitUrlTemplate(descriptor.subtreesUrlTemplate, coordinates),
6177
+ descriptor,
6178
+ resource: findImplicitPackageResource(descriptor.resourceFiles, replaceImplicitUrlTemplate(descriptor.subtreesUrlTemplate, coordinates))
6179
+ };
6180
+ }
6181
+ function materializeImplicitSubtree(subtree, reference) {
6182
+ const { descriptor } = reference;
6183
+ validateImplicitDescriptor(descriptor);
6184
+ validateImplicitSubtree(subtree, descriptor);
6185
+ const counters = { tileCount: 0, childSubtreeCount: 0 };
6186
+ const root = materializeAvailableTile(subtree, reference, { localLevel: 0, mortonIndex: 0 }, reference.coordinates, counters);
6187
+ if (!root) {
6188
+ throw new Error(`Invalid 3D Tiles subtree: root tile is unavailable at ${reference.subtreeUrl}`);
6189
+ }
6190
+ return { root, ...counters };
6191
+ }
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 = {}) {
6254
+ const values = {
6255
+ level: coordinates.level,
6256
+ x: coordinates.x,
6257
+ y: coordinates.y,
6258
+ z: coordinates.z,
6259
+ ...properties
6260
+ };
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
+ });
6265
+ }
6266
+ function materializeAvailableTile(subtree, reference, localCoordinates, globalCoordinates, counters) {
6267
+ const { descriptor } = reference;
6268
+ const childCount = getImplicitChildCount(descriptor.subdivisionScheme);
6269
+ const availabilityIndex = getLevelOffset(childCount, localCoordinates.localLevel) + localCoordinates.mortonIndex;
6270
+ if (!getAvailabilityValue(subtree.tileAvailability, availabilityIndex)) {
6271
+ return null;
6272
+ }
6273
+ counters.tileCount++;
6274
+ const contentAvailability = getContentAvailability(subtree.contentAvailability, availabilityIndex);
6275
+ const children = [];
6276
+ if (globalCoordinates.level < descriptor.maximumLevel) {
6277
+ if (localCoordinates.localLevel + 1 < descriptor.subtreeLevels) {
6278
+ for (let childIndex = 0; childIndex < childCount; childIndex++) {
6279
+ const childCoordinates = getChildCoordinates(globalCoordinates, childIndex, descriptor.subdivisionScheme);
6280
+ const childHeader = materializeAvailableTile(subtree, reference, {
6281
+ localLevel: localCoordinates.localLevel + 1,
6282
+ mortonIndex: localCoordinates.mortonIndex * childCount + childIndex
6283
+ }, childCoordinates, counters);
6284
+ if (childHeader) {
6285
+ children.push(childHeader);
6286
+ }
6287
+ }
6288
+ } else {
6289
+ for (let childIndex = 0; childIndex < childCount; childIndex++) {
6290
+ const childSubtreeIndex = localCoordinates.mortonIndex * childCount + childIndex;
6291
+ if (getAvailabilityValue(subtree.childSubtreeAvailability, childSubtreeIndex)) {
6292
+ const childCoordinates = getChildCoordinates(globalCoordinates, childIndex, descriptor.subdivisionScheme);
6293
+ children.push(createLazyImplicitTileHeader(subtree, descriptor, childCoordinates));
6294
+ counters.childSubtreeCount++;
6295
+ }
6296
+ }
6297
+ }
6298
+ }
6299
+ return formatImplicitTileHeader(subtree, descriptor, reference, globalCoordinates, availabilityIndex, contentAvailability, children);
6300
+ }
6301
+ function createLazyImplicitTileHeader(subtree, descriptor, coordinates) {
6302
+ const resourceFiles = mergeImplicitPackageFiles(descriptor.resourceFiles, subtree.resourceFiles);
6303
+ const childDescriptor = resourceFiles ? { ...descriptor, resourceFiles } : descriptor;
6304
+ const reference = createImplicitSubtreeReference(childDescriptor, coordinates);
6305
+ return {
6306
+ id: getImplicitTileId(reference, coordinates),
6307
+ children: [],
6308
+ implicitSubtree: reference,
6309
+ boundingVolume: calculateImplicitBoundingVolume(descriptor.rootBoundingVolume, coordinates, descriptor.subdivisionScheme),
6310
+ geometricError: descriptor.rootLodMetricValue / 2 ** coordinates.level,
5429
6311
  lodMetricType: descriptor.lodMetricType,
5430
6312
  lodMetricValue: descriptor.rootLodMetricValue / 2 ** coordinates.level,
5431
6313
  refine: descriptor.refine,
5432
6314
  type: TILE_TYPE.EMPTY,
5433
- 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)
5434
6358
  };
5435
6359
  }
5436
- 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) {
5437
6390
  var _a;
5438
- 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];
5439
6435
  const contentEntries = contentUrlTemplates.map((templateUrl, contentIndex) => {
5440
6436
  if (!contentAvailability[contentIndex] || !templateUrl) {
5441
6437
  return null;
5442
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
+ }
5443
6443
  return {
5444
6444
  contentIndex,
5445
- contentUrl: replaceImplicitUrlTemplate(templateUrl, coordinates)
6445
+ contentUrl: contentUrl2
5446
6446
  };
5447
6447
  }).filter((entry) => Boolean(entry));
5448
6448
  const availableContentUrls = contentEntries.map((entry) => entry.contentUrl);
6449
+ const resourceFiles = mergeImplicitPackageFiles(descriptor.resourceFiles, subtree.resourceFiles);
5449
6450
  const content = contentEntries.map(({ contentIndex, contentUrl: contentUrl2 }) => {
5450
6451
  var _a2;
6452
+ const packageResource = findImplicitPackageResource(resourceFiles, contentUrl2);
5451
6453
  return {
5452
6454
  ...((_a2 = descriptor.contentHeaders) == null ? void 0 : _a2[contentIndex]) || (contentIndex === 0 ? descriptor.contentHeader : {}) || {},
5453
- uri: contentUrl2
6455
+ ...contentAttributes,
6456
+ uri: contentUrl2,
6457
+ ...contentProperties ? { metadata: { properties: contentProperties } } : {},
6458
+ ...packageResource ? { _resource: packageResource } : {}
5454
6459
  };
5455
6460
  });
5456
6461
  const contentUrl = availableContentUrls[0];
5457
- 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);
5458
6465
  return {
5459
6466
  id: getImplicitTileId(reference, coordinates),
5460
6467
  children,
5461
6468
  contentUrl,
5462
6469
  content: content.length > 1 ? content : content[0],
6470
+ contents: descriptor.useCanonicalContents && content.length ? content : void 0,
5463
6471
  contentUrls: availableContentUrls,
5464
- refine: descriptor.refine,
6472
+ refine: tileAttributes.refine || descriptor.refine,
5465
6473
  type: getImplicitTileType(contentUrl),
5466
6474
  lodMetricType: descriptor.lodMetricType,
5467
6475
  lodMetricValue,
5468
6476
  geometricError: lodMetricValue,
5469
- boundingVolume: calculateImplicitBoundingVolume(descriptor.rootBoundingVolume, coordinates, descriptor.subdivisionScheme),
5470
- 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
5471
6483
  };
5472
6484
  }
5473
- function getImplicitSubtreeMetadata(subtree) {
5474
- 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) {
5475
6487
  return void 0;
5476
6488
  }
5477
6489
  return {
5478
6490
  propertyTables: subtree.propertyTables,
5479
6491
  tileMetadata: subtree.tileMetadata,
5480
6492
  contentMetadata: subtree.contentMetadata,
5481
- subtreeMetadata: subtree.subtreeMetadata
6493
+ subtreeMetadata: subtree.subtreeMetadata,
6494
+ tileProperties,
6495
+ contentProperties
5482
6496
  };
5483
6497
  }
5484
6498
  function getImplicitTileId(reference, coordinates) {
@@ -5515,12 +6529,15 @@ function getImplicitChildCount(subdivisionScheme) {
5515
6529
  return subdivisionScheme === "OCTREE" ? OCTREE_CHILD_COUNT : QUADTREE_CHILD_COUNT;
5516
6530
  }
5517
6531
  function calculateImplicitBoundingVolume(rootBoundingVolume, coordinates, subdivisionScheme) {
6532
+ var _a;
5518
6533
  if (rootBoundingVolume.region) {
5519
6534
  const [west, south, east, north, minimumHeight, maximumHeight] = rootBoundingVolume.region;
5520
6535
  const divisionCount = 2 ** coordinates.level;
5521
- const longitudeSize = (east - west) / divisionCount;
6536
+ const longitudeSpan = east >= west ? east - west : east + 2 * Math.PI - west;
6537
+ const longitudeSize = longitudeSpan / divisionCount;
5522
6538
  const latitudeSize = (north - south) / divisionCount;
5523
- const childWest = west + longitudeSize * coordinates.x;
6539
+ const childWest = normalizeRegionLongitude(west + longitudeSize * coordinates.x);
6540
+ const childEast = normalizeRegionLongitude(west + longitudeSize * (coordinates.x + 1));
5524
6541
  const childSouth = south + latitudeSize * coordinates.y;
5525
6542
  let childMinimumHeight = minimumHeight;
5526
6543
  let childMaximumHeight = maximumHeight;
@@ -5533,7 +6550,7 @@ function calculateImplicitBoundingVolume(rootBoundingVolume, coordinates, subdiv
5533
6550
  region: [
5534
6551
  childWest,
5535
6552
  childSouth,
5536
- childWest + longitudeSize,
6553
+ childEast,
5537
6554
  childSouth + latitudeSize,
5538
6555
  childMinimumHeight,
5539
6556
  childMaximumHeight
@@ -5542,7 +6559,13 @@ function calculateImplicitBoundingVolume(rootBoundingVolume, coordinates, subdiv
5542
6559
  }
5543
6560
  if (rootBoundingVolume.box) {
5544
6561
  if (rootBoundingVolume.s2VolumeInfo) {
5545
- 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
+ };
5546
6569
  }
5547
6570
  const box = rootBoundingVolume.box;
5548
6571
  const divisionCount = 2 ** coordinates.level;
@@ -5572,6 +6595,41 @@ function calculateImplicitBoundingVolume(rootBoundingVolume, coordinates, subdiv
5572
6595
  }
5573
6596
  throw new Error(`Unsupported implicit 3D Tiles bounding volume: ${JSON.stringify(rootBoundingVolume)}`);
5574
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
+ }
5575
6633
  function getImplicitTileType(contentUrl) {
5576
6634
  var _a;
5577
6635
  if (!contentUrl) {
@@ -5640,6 +6698,12 @@ var Tiles3DSource = class {
5640
6698
  queryParams = {};
5641
6699
  /** Final request URLs cached by unmodified tile content URL. */
5642
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;
5643
6707
  /** Parsed subtree requests keyed by final source URL for deduplication and LRU reuse. */
5644
6708
  implicitSubtreeCache;
5645
6709
  /** Mutable counters exposed as a defensive snapshot through {@link getImplicitTilingStats}. */
@@ -5654,6 +6718,10 @@ var Tiles3DSource = class {
5654
6718
  extensionsUsed = [];
5655
6719
  resolver;
5656
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;
5657
6725
  /**
5658
6726
  * Creates a 3D Tiles source.
5659
6727
  * @param input Root metadata request, or preloaded root metadata for tests and internal callers.
@@ -5711,8 +6779,8 @@ var Tiles3DSource = class {
5711
6779
  if (!this.asset) {
5712
6780
  throw new Error("Tileset must have an asset property.");
5713
6781
  }
5714
- if (this.asset.version !== "0.0" && this.asset.version !== "1.0" && this.asset.version !== "1.1") {
5715
- 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.");
5716
6784
  }
5717
6785
  if ("tilesetVersion" in this.asset) {
5718
6786
  this.setQueryParameter("v", this.asset.tilesetVersion);
@@ -5748,12 +6816,26 @@ var Tiles3DSource = class {
5748
6816
  async getRootTileset() {
5749
6817
  return this.getMetadata().tileset;
5750
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
+ }
5751
6832
  /**
5752
6833
  * Builds explicit runtime headers while leaving implicit subtree references lazy.
5753
6834
  */
5754
6835
  initializeTileHeaders(tileset, tilesetJson, parentTile) {
5755
6836
  var _a;
5756
- 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);
5757
6839
  if (parentTile) {
5758
6840
  parentTile.children.push(rootTile);
5759
6841
  rootTile.depth = parentTile.depth + 1;
@@ -5789,7 +6871,13 @@ var Tiles3DSource = class {
5789
6871
  * Loads binary content or nested tileset JSON for a runtime tile.
5790
6872
  */
5791
6873
  async loadTileContent(tile) {
6874
+ var _a, _b, _c, _d, _e, _f;
5792
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
+ }
5793
6881
  const tilesetLoaderOptions = this.loadOptions[this.loader.id] || {};
5794
6882
  const options = {
5795
6883
  ...this.loadOptions,
@@ -5798,18 +6886,64 @@ var Tiles3DSource = class {
5798
6886
  // Content bytes, rather than URL suffixes, distinguish external tilesets from renderable
5799
6887
  // payloads. This is required for signed and extensionless resources.
5800
6888
  isTileset: "auto",
5801
- 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
+ }
5802
6895
  }
5803
6896
  };
5804
- 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
+ }));
5805
6911
  tile.contents = contents;
5806
6912
  tile.content = contents[0] || null;
5807
6913
  const nestedTilesets = contents.filter((content) => (content == null ? void 0 : content.shape) === "tileset3d");
5808
6914
  return {
5809
- loaded: true,
5810
- contents,
5811
- nestedTileset: nestedTilesets[0],
5812
- nestedTilesets
6915
+ loaded: true,
6916
+ contents,
6917
+ nestedTileset: nestedTilesets[0],
6918
+ nestedTilesets
6919
+ };
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
5813
6947
  };
5814
6948
  }
5815
6949
  /**
@@ -5830,7 +6964,7 @@ var Tiles3DSource = class {
5830
6964
  return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
5831
6965
  }
5832
6966
  const subtreeUrl = this.getTileUrl(reference.subtreeUrl);
5833
- const subtree = await this.loadImplicitSubtreeResource(subtreeUrl);
6967
+ const subtree = await this.loadImplicitSubtreeResource(subtreeUrl, reference.resource);
5834
6968
  if (this.destroyed || tile.isDestroyed()) {
5835
6969
  return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
5836
6970
  }
@@ -5838,8 +6972,11 @@ var Tiles3DSource = class {
5838
6972
  ...reference,
5839
6973
  subtreeUrl
5840
6974
  });
5841
- tile.applyImplicitSubtreeHeader(materializedSubtree.root);
5842
- 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);
5843
6980
  this.implicitTilingStats.loadedSubtrees++;
5844
6981
  this.implicitTilingStats.materializedTiles += materializedTileCount;
5845
6982
  return {
@@ -5926,10 +7063,10 @@ var Tiles3DSource = class {
5926
7063
  const { center } = rootTile.boundingVolume;
5927
7064
  let cartographicCenter;
5928
7065
  if (center && (center[0] !== 0 || center[1] !== 0 || center[2] !== 0)) {
5929
- cartographicCenter = new import_core10.Vector3();
5930
- import_geospatial7.Ellipsoid.WGS84.cartesianToCartographic(center, cartographicCenter);
7066
+ cartographicCenter = new import_core12.Vector3();
7067
+ import_geospatial10.Ellipsoid.WGS84.cartesianToCartographic(center, cartographicCenter);
5931
7068
  } else {
5932
- 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]);
5933
7070
  }
5934
7071
  return {
5935
7072
  asset: this.asset,
@@ -5968,8 +7105,58 @@ var Tiles3DSource = class {
5968
7105
  }
5969
7106
  const nestedTilesets = loadResult.nestedTilesets || (loadResult.nestedTileset ? [loadResult.nestedTileset] : []);
5970
7107
  for (const nestedTileset of nestedTilesets) {
5971
- tileset._initializeTileHeaders(nestedTileset, tile);
7108
+ tileset._initializeTileHeaders(this.prepareNestedTileset(tileset, nestedTileset), tile);
7109
+ }
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);
5972
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
+ };
5973
7160
  }
5974
7161
  /**
5975
7162
  * Loads data through injected core APIs so this module stays independent from `@loaders.gl/core`.
@@ -5998,13 +7185,84 @@ var Tiles3DSource = class {
5998
7185
  }
5999
7186
  return await this.loadWithCoreApi(url, options, loader);
6000
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
+ }
6001
7258
  /**
6002
7259
  * Returns a parsed subtree from the LRU cache or starts one source-managed request.
6003
7260
  *
6004
7261
  * @param subtreeUrl - Final subtree URL after query inheritance.
7262
+ * @param packageResource - Optional glTF package file satisfying the subtree reference.
6005
7263
  * @returns Parsed subtree availability data.
6006
7264
  */
6007
- async loadImplicitSubtreeResource(subtreeUrl) {
7265
+ async loadImplicitSubtreeResource(subtreeUrl, packageResource) {
6008
7266
  const cachedSubtree = this.implicitSubtreeCache.get(subtreeUrl);
6009
7267
  if (cachedSubtree) {
6010
7268
  this.implicitTilingStats.cacheHits++;
@@ -6013,14 +7271,15 @@ var Tiles3DSource = class {
6013
7271
  this.implicitTilingStats.requestedSubtrees++;
6014
7272
  const loaderOptions = this.loadOptions[this.loader.id] || {};
6015
7273
  return await this.implicitSubtreeCache.getOrLoad(subtreeUrl, async () => {
6016
- return await this.loadResourceData(subtreeUrl, {
7274
+ const options = {
6017
7275
  ...this.loadOptions,
6018
7276
  [this.loader.id]: {
6019
7277
  ...loaderOptions,
6020
7278
  isTileset: false,
6021
7279
  isSubtree: true
6022
7280
  }
6023
- });
7281
+ };
7282
+ return await (packageResource ? this.loadContentResource(subtreeUrl, packageResource, options) : this.loadResourceData(subtreeUrl, options));
6024
7283
  });
6025
7284
  }
6026
7285
  /**
@@ -6071,10 +7330,22 @@ function normalizeTiles3DRequest(input) {
6071
7330
  coreApi: input.coreApi
6072
7331
  };
6073
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
+ }
6074
7345
 
6075
7346
  // dist/tileset-3d/format-i3s/i3s-source.js
6076
- var import_geospatial10 = require("@math.gl/geospatial");
6077
- var import_core13 = require("@math.gl/core");
7347
+ var import_geospatial12 = require("@math.gl/geospatial");
7348
+ var import_core14 = require("@math.gl/core");
6078
7349
 
6079
7350
  // dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js
6080
7351
  var I3SPendingTilesRegister = class {
@@ -6255,224 +7526,8 @@ var I3STilesetTraverser = class extends TilesetTraverser {
6255
7526
  };
6256
7527
 
6257
7528
  // dist/spatial/i3s-spatial-transformer.js
6258
- var import_core12 = require("@math.gl/core");
6259
- var import_geospatial9 = require("@math.gl/geospatial");
6260
-
6261
- // dist/spatial/spatial-coordinate-transformer.js
6262
- var import_core11 = require("@math.gl/core");
6263
- var import_geospatial8 = require("@math.gl/geospatial");
6264
- var import_proj42 = require("@math.gl/proj4");
6265
-
6266
- // dist/spatial/spatial-resource-registry.js
6267
- var import_geoid = require("@math.gl/geoid");
6268
- var import_proj4 = require("@math.gl/proj4");
6269
- var geoidModels = /* @__PURE__ */ new Map();
6270
- function registerSpatialCrs(name, definition) {
6271
- validateResourceName(name, "CRS");
6272
- import_proj4.Proj4Projection.defineProjectionAliases({ [name]: definition });
6273
- }
6274
- function registerSpatialDatumGrid(name, data) {
6275
- validateResourceName(name, "datum grid");
6276
- import_proj4.Proj4Projection.registerDatumGrid(name, data);
6277
- }
6278
- function registerGeoidModel(name, geoid) {
6279
- validateResourceName(name, "geoid model");
6280
- geoidModels.set(name, geoid);
6281
- }
6282
- function registerGeoidModelFromPgm(name, data, options = {}) {
6283
- const geoid = (0, import_geoid.parsePGM)(data, options);
6284
- registerGeoidModel(name, geoid);
6285
- return geoid;
6286
- }
6287
- function getGeoidModel(name) {
6288
- return geoidModels.get(name);
6289
- }
6290
- function validateResourceName(name, resourceType) {
6291
- if (!name.trim()) {
6292
- throw new Error(`${resourceType} name must not be empty`);
6293
- }
6294
- }
6295
-
6296
- // dist/spatial/spatial-coordinate-transformer.js
6297
- var GEOGRAPHIC_CRS2 = "EPSG:4326";
6298
- var GEOCENTRIC_CRS2 = "EPSG:4978";
6299
- var SpatialCoordinateTransformer = class {
6300
- /** Normalized spatial metadata describing this transformation. */
6301
- spatialReference;
6302
- /** Projection from the source CRS directly to the requested horizontal target CRS. */
6303
- horizontalProjection;
6304
- /** Projection from the source CRS to geographic longitude, latitude, and ellipsoidal height. */
6305
- geographicProjection;
6306
- /** Projection from adjusted geographic coordinates to the requested output CRS. */
6307
- heightOutputProjection;
6308
- /** Whether source coordinates use the WGS84 geocentric frame handled by math.gl. */
6309
- sourceIsGeocentric;
6310
- /** Whether output coordinates use the WGS84 geocentric frame handled by math.gl. */
6311
- outputIsGeocentric;
6312
- /** Whether source coordinates already use conventional WGS84 longitude/latitude order. */
6313
- sourceIsGeographic;
6314
- /** Whether output coordinates use conventional WGS84 longitude/latitude order. */
6315
- outputIsGeographic;
6316
- /** Geoid model used to convert between ellipsoidal and orthometric heights. */
6317
- geoid;
6318
- /**
6319
- * Creates a coordinate transformer.
6320
- *
6321
- * @param spatialReference - Format-discovered and option-normalized spatial metadata.
6322
- * @param options - Application options containing an optional geoid model.
6323
- */
6324
- constructor(spatialReference, options = {}) {
6325
- this.spatialReference = spatialReference;
6326
- validateTransformRequest(spatialReference);
6327
- const outputCrs = spatialReference.targetCrs || spatialReference.sourceCrs;
6328
- this.sourceIsGeocentric = isWgs84Geocentric(spatialReference.sourceCrs);
6329
- this.outputIsGeocentric = isWgs84Geocentric(outputCrs);
6330
- this.sourceIsGeographic = isWgs84Geographic(spatialReference.sourceCrs);
6331
- this.outputIsGeographic = isWgs84Geographic(outputCrs);
6332
- if (spatialReference.sourceCrs && spatialReference.targetCrs && !this.sourceIsGeocentric && !this.outputIsGeocentric) {
6333
- this.horizontalProjection = new import_proj42.Proj4Projection({
6334
- from: getHorizontalProj4Definition(spatialReference.sourceCrs),
6335
- to: getHorizontalProj4Definition(spatialReference.targetCrs),
6336
- enforceAxis: false
6337
- });
6338
- }
6339
- if (requiresHeightTransformation(spatialReference) || this.sourceIsGeocentric || this.outputIsGeocentric) {
6340
- if (!this.sourceIsGeographic && !this.sourceIsGeocentric) {
6341
- this.geographicProjection = new import_proj42.Proj4Projection({
6342
- from: getHorizontalProj4Definition(spatialReference.sourceCrs),
6343
- to: GEOGRAPHIC_CRS2,
6344
- enforceAxis: false
6345
- });
6346
- }
6347
- if (!this.outputIsGeographic && !this.outputIsGeocentric) {
6348
- this.heightOutputProjection = new import_proj42.Proj4Projection({
6349
- from: GEOGRAPHIC_CRS2,
6350
- to: getHorizontalProj4Definition(outputCrs),
6351
- enforceAxis: false
6352
- });
6353
- }
6354
- }
6355
- if (requiresHeightTransformation(spatialReference)) {
6356
- this.geoid = resolveGeoid(options.geoidModel);
6357
- if (!this.geoid) {
6358
- const modelName = typeof options.geoidModel === "string" ? ` "${options.geoidModel}"` : "";
6359
- throw new Error(`Height conversion requires a registered geoid model${modelName}; call registerGeoidModel() or pass a parsed Geoid instance`);
6360
- }
6361
- }
6362
- }
6363
- /**
6364
- * Transforms one `[x, y, z?]` coordinate while retaining additional components.
6365
- *
6366
- * @param coordinate - Source coordinate in the format adapter's normalized `x/y/z` order.
6367
- * @returns A new transformed coordinate array.
6368
- */
6369
- transformPosition(coordinate) {
6370
- if (coordinate.length < 2) {
6371
- throw new Error("A spatial coordinate must contain at least x and y components");
6372
- }
6373
- const result = [...coordinate];
6374
- if (requiresHeightTransformation(this.spatialReference) || this.sourceIsGeocentric || this.outputIsGeocentric) {
6375
- if (result.length < 3 || !Number.isFinite(result[2])) {
6376
- throw new Error("Three-dimensional CRS conversion requires a finite z coordinate");
6377
- }
6378
- const geographic = this.toGeographic(result);
6379
- if (requiresHeightTransformation(this.spatialReference)) {
6380
- const geoidUndulation = this.geoid.getHeight(geographic[1], geographic[0]);
6381
- geographic[2] = transformHeight(geographic[2], geoidUndulation, this.spatialReference.heightReference, this.spatialReference.targetHeightReference);
6382
- }
6383
- const projected = this.fromGeographic(geographic);
6384
- result.splice(0, projected.length, ...projected);
6385
- return result;
6386
- }
6387
- if (this.horizontalProjection) {
6388
- const projected = this.horizontalProjection.project(result.slice(0, 3));
6389
- result.splice(0, projected.length, ...projected);
6390
- }
6391
- return result;
6392
- }
6393
- /** Convert one source coordinate to conventional WGS84 longitude, latitude, and height. */
6394
- toGeographic(coordinate) {
6395
- if (this.sourceIsGeocentric) {
6396
- return Array.from(import_geospatial8.Ellipsoid.WGS84.cartesianToCartographic(new import_core11.Vector3(coordinate)));
6397
- }
6398
- if (this.sourceIsGeographic) {
6399
- return coordinate.slice(0, 3);
6400
- }
6401
- return this.geographicProjection.project(coordinate.slice(0, 3));
6402
- }
6403
- /** Convert one conventional WGS84 geographic coordinate to the selected output CRS. */
6404
- fromGeographic(coordinate) {
6405
- if (this.outputIsGeocentric) {
6406
- return Array.from(import_geospatial8.Ellipsoid.WGS84.cartographicToCartesian(new import_core11.Vector3(coordinate)));
6407
- }
6408
- if (this.outputIsGeographic) {
6409
- return coordinate.slice(0, 3);
6410
- }
6411
- return this.heightOutputProjection.project(coordinate.slice(0, 3));
6412
- }
6413
- };
6414
- function getHorizontalProj4Definition(definition) {
6415
- if (!definition) {
6416
- throw new Error("Cannot construct a projection because the CRS is unknown");
6417
- }
6418
- return (0, import_proj42.toProj4CRSDefinition)(definition, { mode: "horizontal" });
6419
- }
6420
- function validateTransformRequest(spatialReference) {
6421
- if (spatialReference.outputCoordinates === "local-enu") {
6422
- throw new Error("local-enu output requires a dataset-derived local origin and must be created by a tileset source");
6423
- }
6424
- if (spatialReference.outputCoordinates === "target-crs" && !spatialReference.targetCrs) {
6425
- throw new Error("target-crs output requires targetCrs");
6426
- }
6427
- if (!spatialReference.sourceCrs && spatialReference.status === "unresolved") {
6428
- throw new Error("Cannot transform coordinates because the source CRS is unknown");
6429
- }
6430
- if (requiresHeightTransformation(spatialReference) && spatialReference.heightReference === "unknown") {
6431
- throw new Error("Cannot convert heights because the source height reference is unknown");
6432
- }
6433
- if (spatialReference.coordinateFrame === "geocentric" && !isWgs84Geocentric(spatialReference.sourceCrs)) {
6434
- throw new Error("Only EPSG:4978 geocentric coordinates are supported by the current spatial transformer");
6435
- }
6436
- if (spatialReference.status === "unresolved") {
6437
- throw new Error("The requested spatial output cannot be resolved from the available metadata");
6438
- }
6439
- }
6440
- function isWgs84Geocentric(definition) {
6441
- return typeof definition === "string" && normalizeCrsIdentifier2(definition) === GEOCENTRIC_CRS2;
6442
- }
6443
- function isWgs84Geographic(definition) {
6444
- if (typeof definition !== "string") {
6445
- return false;
6446
- }
6447
- const identifier = normalizeCrsIdentifier2(definition);
6448
- return identifier === GEOGRAPHIC_CRS2 || identifier === "EPSG:4979" || identifier === "OGC:CRS84";
6449
- }
6450
- function normalizeCrsIdentifier2(identifier) {
6451
- const normalizedIdentifier = identifier.trim().toUpperCase();
6452
- const ogcMatch = normalizedIdentifier.match(/(?:\/DEF\/CRS\/|URN:OGC:DEF:CRS:)([A-Z0-9_-]+)(?:\/|::)(?:[^/:]*[/:])?([A-Z0-9_.-]+)$/);
6453
- return ogcMatch ? `${ogcMatch[1]}:${ogcMatch[2]}` : normalizedIdentifier;
6454
- }
6455
- function requiresHeightTransformation(spatialReference) {
6456
- const target = spatialReference.targetHeightReference;
6457
- return target !== "native" && target !== spatialReference.heightReference;
6458
- }
6459
- function resolveGeoid(model) {
6460
- return typeof model === "string" ? getGeoidModel(model) : model;
6461
- }
6462
- function transformHeight(height, geoidUndulation, source, target) {
6463
- if (source === target || target === "native") {
6464
- return height;
6465
- }
6466
- if (source === "orthometric" && target === "ellipsoidal") {
6467
- return height + geoidUndulation;
6468
- }
6469
- if (source === "ellipsoidal" && target === "orthometric") {
6470
- return height - geoidUndulation;
6471
- }
6472
- throw new Error(`Unsupported height conversion from ${source} to ${target}`);
6473
- }
6474
-
6475
- // dist/spatial/i3s-spatial-transformer.js
7529
+ var import_core13 = require("@math.gl/core");
7530
+ var import_geospatial11 = require("@math.gl/geospatial");
6476
7531
  var I3SSpatialTransformer = class {
6477
7532
  /** Descriptor attached to output after all adapter-owned spatial values have been transformed. */
6478
7533
  spatialReference;
@@ -6667,7 +7722,7 @@ var I3SSpatialTransformer = class {
6667
7722
  positions[index + 2] = transformed[2] - origin[2];
6668
7723
  }
6669
7724
  const isGeographic = this.targetCoordinateFrame === "geographic";
6670
- 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);
6671
7726
  return {
6672
7727
  positions,
6673
7728
  sourcePositions: absoluteSourcePositions,
@@ -6695,7 +7750,7 @@ var I3SSpatialTransformer = class {
6695
7750
  sourcePositions[index + 1],
6696
7751
  sourcePositions[index + 2]
6697
7752
  ]);
6698
- const inputNormal = new import_core12.Vector3([
7753
+ const inputNormal = new import_core13.Vector3([
6699
7754
  normals[index],
6700
7755
  normals[index + 1],
6701
7756
  normals[index + 2]
@@ -6733,12 +7788,12 @@ var I3SSpatialTransformer = class {
6733
7788
  }
6734
7789
  /** Rebuild an I3S bound as a WGS84 ECEF box for generic `Tileset3D` traversal. */
6735
7790
  transformBoundingVolumeToGeocentric(bounds) {
6736
- 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))));
6737
7792
  return { box: getAxisAlignedBox(cartesianSamples) };
6738
7793
  }
6739
7794
  /** Rebuild an I3S bound as WGS84 ECEF after asynchronous elevation placement. */
6740
7795
  async transformBoundingVolumeToGeocentricAsync(bounds) {
6741
- 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))));
6742
7797
  return { box: getAxisAlignedBox(cartesianSamples) };
6743
7798
  }
6744
7799
  /** Return a WGS84 geographic MBS used by the I3S screen-threshold calculation. */
@@ -6765,7 +7820,7 @@ var I3SSpatialTransformer = class {
6765
7820
  */
6766
7821
  async transformBoundsAsync(bounds) {
6767
7822
  const geographicSamples = await this.getGeographicBoundSamplesAsync(bounds);
6768
- 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))));
6769
7824
  const targetSamples = geographicSamples.map((position) => this.geographicToTargetTransformer.transformPosition(position));
6770
7825
  return {
6771
7826
  boundingVolume: { box: getAxisAlignedBox(cartesianSamples) },
@@ -6888,21 +7943,21 @@ var I3SSpatialTransformer = class {
6888
7943
  if (this.targetCoordinateFrame === "geographic") {
6889
7944
  return ecefVectorToLocalEnu(ecefNormal, geographic).normalize();
6890
7945
  }
6891
- 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));
6892
7947
  const jacobianColumns = [];
6893
7948
  for (const direction of getPositiveAxisDirections()) {
6894
- const positiveGeographic = import_geospatial9.Ellipsoid.WGS84.cartesianToCartographic(new import_core12.Vector3(cartesian).add(direction));
6895
- 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));
6896
7951
  const positiveTarget = this.geographicToTargetTransformer.transformPosition(positiveGeographic);
6897
7952
  const negativeTarget = this.geographicToTargetTransformer.transformPosition(negativeGeographic);
6898
7953
  jacobianColumns.push((positiveTarget[0] - negativeTarget[0]) / 2, (positiveTarget[1] - negativeTarget[1]) / 2, (positiveTarget[2] - negativeTarget[2]) / 2);
6899
7954
  }
6900
- const normalMatrix = new import_core12.Matrix3(jacobianColumns);
7955
+ const normalMatrix = new import_core13.Matrix3(jacobianColumns);
6901
7956
  if (Math.abs(normalMatrix.determinant()) < 1e-12) {
6902
7957
  throw new Error("Cannot transform an I3S normal through a singular local projection");
6903
7958
  }
6904
7959
  normalMatrix.invert().transpose();
6905
- return new import_core12.Vector3(ecefNormal).transformByMatrix3(normalMatrix).normalize();
7960
+ return new import_core13.Vector3(ecefNormal).transformByMatrix3(normalMatrix).normalize();
6906
7961
  }
6907
7962
  };
6908
7963
  function getElevationProvider(mode, options) {
@@ -6934,17 +7989,17 @@ function getGeographicBoundingSphere(geographicSamples) {
6934
7989
  if (!geographicSamples.length) {
6935
7990
  throw new Error("I3S spatial transformation requires at least one bound sample");
6936
7991
  }
6937
- const cartesianSamples = geographicSamples.map((position) => import_geospatial9.Ellipsoid.WGS84.cartographicToCartesian(new import_core12.Vector3(position)));
6938
- 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();
6939
7994
  for (const sample of cartesianSamples) {
6940
7995
  cartesianCenter.add(sample);
6941
7996
  }
6942
7997
  cartesianCenter.scale(1 / cartesianSamples.length);
6943
7998
  let radius = 0;
6944
7999
  for (const sample of cartesianSamples) {
6945
- 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());
6946
8001
  }
6947
- const geographicCenter = import_geospatial9.Ellipsoid.WGS84.cartesianToCartographic(cartesianCenter);
8002
+ const geographicCenter = import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(cartesianCenter);
6948
8003
  return [geographicCenter[0], geographicCenter[1], geographicCenter[2], radius];
6949
8004
  }
6950
8005
  function getSourceBoundSamples(bounds, sourceFrame, sourceToGeographicTransformer, sourceVerticalUnitScale) {
@@ -6953,28 +8008,28 @@ function getSourceBoundSamples(bounds, sourceFrame, sourceToGeographicTransforme
6953
8008
  const center2 = Array.from(obb.center).slice(0, 3);
6954
8009
  center2[2] *= sourceVerticalUnitScale;
6955
8010
  const halfSize = Array.from(obb.halfSize).slice(0, 3);
6956
- const quaternion = new import_core12.Quaternion().fromArray(Array.from(obb.quaternion));
8011
+ const quaternion = new import_core13.Quaternion().fromArray(Array.from(obb.quaternion));
6957
8012
  const axes = [
6958
- new import_core12.Vector3([halfSize[0], 0, 0]).transformByQuaternion(quaternion),
6959
- new import_core12.Vector3([0, halfSize[1], 0]).transformByQuaternion(quaternion),
6960
- 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)
6961
8016
  ];
6962
8017
  if (sourceFrame === "geographic") {
6963
8018
  const geographicCenter = sourceToGeographicTransformer.transformPosition(center2);
6964
- 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));
6965
8020
  const cartesianAxes = bounds.normalReferenceFrame === "vertex-reference-frame" ? axes.map((axis) => localEnuVectorToEcef(axis, geographicCenter)) : axes;
6966
8021
  return getBoxSigns().map((signs) => {
6967
- const corner = new import_core12.Vector3(cartesianCenter);
8022
+ const corner = new import_core13.Vector3(cartesianCenter);
6968
8023
  for (let axisIndex = 0; axisIndex < 3; axisIndex++) {
6969
- corner.add(new import_core12.Vector3(cartesianAxes[axisIndex]).scale(signs[axisIndex]));
8024
+ corner.add(new import_core13.Vector3(cartesianAxes[axisIndex]).scale(signs[axisIndex]));
6970
8025
  }
6971
- return Array.from(import_geospatial9.Ellipsoid.WGS84.cartesianToCartographic(corner));
8026
+ return Array.from(import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(corner));
6972
8027
  });
6973
8028
  }
6974
8029
  return getBoxSigns().map((signs) => {
6975
- const corner = new import_core12.Vector3(center2);
8030
+ const corner = new import_core13.Vector3(center2);
6976
8031
  for (let axisIndex = 0; axisIndex < 3; axisIndex++) {
6977
- corner.add(new import_core12.Vector3(axes[axisIndex]).scale(signs[axisIndex]));
8032
+ corner.add(new import_core13.Vector3(axes[axisIndex]).scale(signs[axisIndex]));
6978
8033
  }
6979
8034
  return Array.from(corner);
6980
8035
  });
@@ -6988,10 +8043,10 @@ function getSourceBoundSamples(bounds, sourceFrame, sourceToGeographicTransforme
6988
8043
  const radius = mbs[3];
6989
8044
  if (sourceFrame === "geographic") {
6990
8045
  const geographicCenter = sourceToGeographicTransformer.transformPosition(center);
6991
- const cartesianCenter = import_geospatial9.Ellipsoid.WGS84.cartographicToCartesian(new import_core12.Vector3(geographicCenter));
6992
- 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)))));
6993
8048
  }
6994
- 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))));
6995
8050
  }
6996
8051
  function getBoxSigns() {
6997
8052
  return [-1, 1].flatMap((x) => [-1, 1].flatMap((y) => [-1, 1].map((z) => [x, y, z])));
@@ -7007,18 +8062,18 @@ function getAxisDirections() {
7007
8062
  ];
7008
8063
  }
7009
8064
  function getPositiveAxisDirections() {
7010
- 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])];
7011
8066
  }
7012
8067
  function localEnuVectorToEcef(vector, geographic) {
7013
8068
  const longitude = geographic[0] * Math.PI / 180;
7014
8069
  const latitude = geographic[1] * Math.PI / 180;
7015
- const east = new import_core12.Vector3([-Math.sin(longitude), Math.cos(longitude), 0]);
7016
- 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([
7017
8072
  -Math.sin(latitude) * Math.cos(longitude),
7018
8073
  -Math.sin(latitude) * Math.sin(longitude),
7019
8074
  Math.cos(latitude)
7020
8075
  ]);
7021
- const up = new import_core12.Vector3([
8076
+ const up = new import_core13.Vector3([
7022
8077
  Math.cos(latitude) * Math.cos(longitude),
7023
8078
  Math.cos(latitude) * Math.sin(longitude),
7024
8079
  Math.sin(latitude)
@@ -7028,18 +8083,18 @@ function localEnuVectorToEcef(vector, geographic) {
7028
8083
  function ecefVectorToLocalEnu(vector, geographic) {
7029
8084
  const longitude = geographic[0] * Math.PI / 180;
7030
8085
  const latitude = geographic[1] * Math.PI / 180;
7031
- const east = new import_core12.Vector3([-Math.sin(longitude), Math.cos(longitude), 0]);
7032
- 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([
7033
8088
  -Math.sin(latitude) * Math.cos(longitude),
7034
8089
  -Math.sin(latitude) * Math.sin(longitude),
7035
8090
  Math.cos(latitude)
7036
8091
  ]);
7037
- const up = new import_core12.Vector3([
8092
+ const up = new import_core13.Vector3([
7038
8093
  Math.cos(latitude) * Math.cos(longitude),
7039
8094
  Math.cos(latitude) * Math.sin(longitude),
7040
8095
  Math.sin(latitude)
7041
8096
  ]);
7042
- 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)]);
7043
8098
  }
7044
8099
  function getAxisAlignedBox(positions) {
7045
8100
  const minimum = [Infinity, Infinity, Infinity];
@@ -7304,8 +8359,8 @@ var I3SSource = class {
7304
8359
  getViewState(rootTile) {
7305
8360
  var _a;
7306
8361
  if (this.spatialTransformer && rootTile) {
7307
- const center = new import_core13.Vector3(rootTile.boundingVolume.center);
7308
- 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));
7309
8364
  if (this.spatialTransformer.targetCoordinateFrame === "geographic") {
7310
8365
  return {
7311
8366
  boundingVolume: rootTile.boundingVolume,
@@ -7325,9 +8380,9 @@ var I3SSource = class {
7325
8380
  const fullExtent = metadata.tileset.fullExtent;
7326
8381
  if (fullExtent) {
7327
8382
  const { xmin, xmax, ymin, ymax, zmin, zmax } = fullExtent;
7328
- const cartographicCenter = new import_core13.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, zmin + (zmax - zmin) / 2);
7329
- const cartesianCenter = new import_core13.Vector3();
7330
- 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);
7331
8386
  return {
7332
8387
  cartographicCenter,
7333
8388
  cartesianCenter,
@@ -7337,9 +8392,9 @@ var I3SSource = class {
7337
8392
  const extent = (_a = metadata.tileset.store) == null ? void 0 : _a.extent;
7338
8393
  if (extent) {
7339
8394
  const [xmin, ymin, xmax, ymax] = extent;
7340
- const cartographicCenter = new import_core13.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, 0);
7341
- const cartesianCenter = new import_core13.Vector3();
7342
- 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);
7343
8398
  return {
7344
8399
  cartographicCenter,
7345
8400
  cartesianCenter,
@@ -7348,7 +8403,7 @@ var I3SSource = class {
7348
8403
  }
7349
8404
  console.warn("Extent is not defined in the tileset header");
7350
8405
  return {
7351
- cartographicCenter: new import_core13.Vector3(),
8406
+ cartographicCenter: new import_core14.Vector3(),
7352
8407
  zoom: 1
7353
8408
  };
7354
8409
  }
@@ -8668,7 +9723,7 @@ function isAbortError3(error) {
8668
9723
  }
8669
9724
 
8670
9725
  // dist/point-cloud/point-cloud-tileset.js
8671
- var import_core14 = require("@math.gl/core");
9726
+ var import_core15 = require("@math.gl/core");
8672
9727
  var import_culling6 = require("@math.gl/culling");
8673
9728
 
8674
9729
  // dist/point-cloud/point-cloud-tile.js
@@ -8832,7 +9887,7 @@ var PointCloudTileset = class {
8832
9887
  const viewState = await ((_b = (_a = this.dataSource).getViewState) == null ? void 0 : _b.call(_a));
8833
9888
  this.boundingVolume = (viewState == null ? void 0 : viewState.boundingVolume) || this.root.boundingVolume;
8834
9889
  const center = (viewState == null ? void 0 : viewState.cartographicCenter) || this.root.boundingVolume.center;
8835
- this.cartographicCenter = new import_core14.Vector3(center);
9890
+ this.cartographicCenter = new import_core15.Vector3(center);
8836
9891
  this.zoom = (viewState == null ? void 0 : viewState.zoom) || this.estimateZoom(this.boundingVolume);
8837
9892
  }
8838
9893
  /**