@loaders.gl/tiles 5.0.0-alpha.6 → 5.0.0-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +26 -0
  2. package/dist/dist.dev.js +10015 -8172
  3. package/dist/dist.min.js +3 -4
  4. package/dist/index.cjs +1676 -477
  5. package/dist/index.cjs.map +3 -3
  6. package/dist/index.d.ts +10 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +4 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/spatial/format-spatial-reference.d.ts +1 -0
  11. package/dist/spatial/format-spatial-reference.d.ts.map +1 -1
  12. package/dist/spatial/format-spatial-reference.js +104 -46
  13. package/dist/spatial/format-spatial-reference.js.map +1 -1
  14. package/dist/spatial/get-spatial-coordinate-frame.d.ts +12 -1
  15. package/dist/spatial/get-spatial-coordinate-frame.d.ts.map +1 -1
  16. package/dist/spatial/get-spatial-coordinate-frame.js +105 -24
  17. package/dist/spatial/get-spatial-coordinate-frame.js.map +1 -1
  18. package/dist/spatial/normalize-crs-identifier.d.ts +15 -0
  19. package/dist/spatial/normalize-crs-identifier.d.ts.map +1 -0
  20. package/dist/spatial/normalize-crs-identifier.js +22 -0
  21. package/dist/spatial/normalize-crs-identifier.js.map +1 -0
  22. package/dist/spatial/spatial-coordinate-transformer.d.ts +4 -0
  23. package/dist/spatial/spatial-coordinate-transformer.d.ts.map +1 -1
  24. package/dist/spatial/spatial-coordinate-transformer.js +102 -18
  25. package/dist/spatial/spatial-coordinate-transformer.js.map +1 -1
  26. package/dist/spatial/spatial-resource-registry.js +3 -3
  27. package/dist/spatial/spatial-resource-registry.js.map +1 -1
  28. package/dist/spatial/tiles-3d-spatial-transformer.d.ts +32 -0
  29. package/dist/spatial/tiles-3d-spatial-transformer.d.ts.map +1 -0
  30. package/dist/spatial/tiles-3d-spatial-transformer.js +175 -0
  31. package/dist/spatial/tiles-3d-spatial-transformer.js.map +1 -0
  32. package/dist/tileset-3d/common/tile-3d-contracts.d.ts +63 -0
  33. package/dist/tileset-3d/common/tile-3d-contracts.d.ts.map +1 -0
  34. package/dist/tileset-3d/common/tile-3d-contracts.js +51 -0
  35. package/dist/tileset-3d/common/tile-3d-contracts.js.map +1 -0
  36. package/dist/tileset-3d/common/tile-3d.d.ts +33 -5
  37. package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
  38. package/dist/tileset-3d/common/tile-3d.js +179 -29
  39. package/dist/tileset-3d/common/tile-3d.js.map +1 -1
  40. package/dist/tileset-3d/common/tileset-3d.d.ts +15 -25
  41. package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
  42. package/dist/tileset-3d/common/tileset-3d.js +35 -42
  43. package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
  44. package/dist/tileset-3d/common/tileset-content-traversal.d.ts +37 -0
  45. package/dist/tileset-3d/common/tileset-content-traversal.d.ts.map +1 -0
  46. package/dist/tileset-3d/common/tileset-content-traversal.js +80 -0
  47. package/dist/tileset-3d/common/tileset-content-traversal.js.map +1 -0
  48. package/dist/tileset-3d/common/tileset-observability.d.ts +60 -0
  49. package/dist/tileset-3d/common/tileset-observability.d.ts.map +1 -0
  50. package/dist/tileset-3d/common/tileset-observability.js +39 -0
  51. package/dist/tileset-3d/common/tileset-observability.js.map +1 -0
  52. package/dist/tileset-3d/common/tileset-source.d.ts +11 -1
  53. package/dist/tileset-3d/common/tileset-source.d.ts.map +1 -1
  54. package/dist/tileset-3d/common/tileset-source.js.map +1 -1
  55. package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
  56. package/dist/tileset-3d/common/tileset-traverser.js +4 -3
  57. package/dist/tileset-3d/common/tileset-traverser.js.map +1 -1
  58. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts +47 -1
  59. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts.map +1 -1
  60. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js +327 -30
  61. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js.map +1 -1
  62. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +34 -0
  63. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts.map +1 -1
  64. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js +278 -14
  65. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -1
  66. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts +5 -3
  67. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -1
  68. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js +7 -4
  69. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js.map +1 -1
  70. package/dist/tileset-3d/format-i3s/i3s-source.d.ts +4 -2
  71. package/dist/tileset-3d/format-i3s/i3s-source.d.ts.map +1 -1
  72. package/dist/tileset-3d/format-i3s/i3s-source.js +60 -9
  73. package/dist/tileset-3d/format-i3s/i3s-source.js.map +1 -1
  74. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.d.ts.map +1 -1
  75. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js +1 -1
  76. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js.map +1 -1
  77. package/dist/tileset-3d/helpers/bounding-volume.d.ts +7 -3
  78. package/dist/tileset-3d/helpers/bounding-volume.d.ts.map +1 -1
  79. package/dist/tileset-3d/helpers/bounding-volume.js +19 -61
  80. package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -1
  81. package/dist/tileset-3d/helpers/frame-state.d.ts +5 -0
  82. package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
  83. package/dist/tileset-3d/helpers/frame-state.js +11 -5
  84. package/dist/tileset-3d/helpers/frame-state.js.map +1 -1
  85. package/dist/tileset-3d/helpers/s2-bounding-volume.d.ts +17 -0
  86. package/dist/tileset-3d/helpers/s2-bounding-volume.d.ts.map +1 -0
  87. package/dist/tileset-3d/helpers/s2-bounding-volume.js +20 -0
  88. package/dist/tileset-3d/helpers/s2-bounding-volume.js.map +1 -0
  89. package/dist/types.d.ts +2 -0
  90. package/dist/types.d.ts.map +1 -1
  91. package/package.json +11 -12
  92. package/src/index.ts +18 -1
  93. package/src/spatial/format-spatial-reference.ts +127 -53
  94. package/src/spatial/get-spatial-coordinate-frame.ts +126 -31
  95. package/src/spatial/normalize-crs-identifier.ts +26 -0
  96. package/src/spatial/spatial-coordinate-transformer.ts +124 -24
  97. package/src/spatial/spatial-resource-registry.ts +3 -3
  98. package/src/spatial/tiles-3d-spatial-transformer.ts +206 -0
  99. package/src/tileset-3d/common/tile-3d-contracts.ts +115 -0
  100. package/src/tileset-3d/common/tile-3d.ts +208 -32
  101. package/src/tileset-3d/common/tileset-3d.ts +37 -61
  102. package/src/tileset-3d/common/tileset-content-traversal.ts +114 -0
  103. package/src/tileset-3d/common/tileset-observability.ts +100 -0
  104. package/src/tileset-3d/common/tileset-source.ts +19 -1
  105. package/src/tileset-3d/common/tileset-traverser.ts +4 -3
  106. package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +481 -34
  107. package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +405 -14
  108. package/src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts +7 -4
  109. package/src/tileset-3d/format-i3s/i3s-source.ts +84 -13
  110. package/src/tileset-3d/format-i3s/i3s-tileset-traverser.ts +2 -1
  111. package/src/tileset-3d/helpers/bounding-volume.ts +29 -62
  112. package/src/tileset-3d/helpers/frame-state.ts +15 -7
  113. package/src/tileset-3d/helpers/s2-bounding-volume.ts +35 -0
  114. 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,
@@ -61,7 +64,8 @@ __export(index_exports, {
61
64
  registerGeoidModelFromPgm: () => registerGeoidModelFromPgm,
62
65
  registerSpatialCrs: () => registerSpatialCrs,
63
66
  registerSpatialDatumGrid: () => registerSpatialDatumGrid,
64
- replaceImplicitUrlTemplate: () => replaceImplicitUrlTemplate
67
+ replaceImplicitUrlTemplate: () => replaceImplicitUrlTemplate,
68
+ traverseTilesetContents: () => traverseTilesetContents
65
69
  });
66
70
  module.exports = __toCommonJS(index_exports);
67
71
 
@@ -77,6 +81,13 @@ function assert(condition, message) {
77
81
  }
78
82
  }
79
83
 
84
+ // ../loader-utils/src/lib/binary-utils/array-buffer-utils.ts
85
+ function sliceArrayBuffer(arrayBuffer, byteOffset, byteLength) {
86
+ const subArray = byteLength !== void 0 ? new Uint8Array(arrayBuffer).subarray(byteOffset, byteOffset + byteLength) : new Uint8Array(arrayBuffer).subarray(byteOffset);
87
+ const arrayCopy = new Uint8Array(subArray);
88
+ return arrayCopy.buffer;
89
+ }
90
+
80
91
  // ../loader-utils/src/lib/request-utils/request-scheduler.ts
81
92
  var import_stats = require("@probe.gl/stats");
82
93
  var STAT_QUEUED_REQUESTS = "Queued Requests";
@@ -103,6 +114,7 @@ var RequestScheduler = class {
103
114
  /** Tracks the number of active requests and prioritizes/cancels queued requests. */
104
115
  requestQueue = [];
105
116
  requestMap = /* @__PURE__ */ new Map();
117
+ /** Pending arrival debounce or next-tick refill; only new arrivals replace this timer. */
106
118
  updateTimer = null;
107
119
  constructor(props = {}) {
108
120
  this.props = { ...DEFAULT_PROPS, ...props };
@@ -170,20 +182,27 @@ var RequestScheduler = class {
170
182
  isDone = true;
171
183
  this.requestMap.delete(handle);
172
184
  this.activeRequestCount--;
173
- this._issueNewRequests();
185
+ this.scheduleRefill();
174
186
  }
175
187
  };
176
188
  this.activeRequestCount++;
177
189
  return resolve2 ? resolve2({ done }) : Promise.resolve({ done });
178
190
  }
179
- /** We check requests asynchronously, to prevent multiple updates */
191
+ /** Refills freed slots on the next tick while preserving any pending arrival debounce. */
192
+ scheduleRefill() {
193
+ if (this.updateTimer !== null) {
194
+ return;
195
+ }
196
+ this.updateTimer = setTimeout(() => this._issueNewRequestsAsync(), 0);
197
+ }
198
+ /** Restarts the quiet period when a new request enters the queue. */
180
199
  _issueNewRequests() {
181
200
  if (this.updateTimer !== null) {
182
201
  clearTimeout(this.updateTimer);
183
202
  }
184
203
  this.updateTimer = setTimeout(() => this._issueNewRequestsAsync(), this.props.debounceTime);
185
204
  }
186
- /** Refresh all requests */
205
+ /** Clears timer ownership before updating priorities and dispatching queued requests. */
187
206
  _issueNewRequestsAsync() {
188
207
  if (this.updateTimer !== null) {
189
208
  clearTimeout(this.updateTimer);
@@ -485,6 +504,7 @@ var RangeRequestScheduler = class {
485
504
  /** Optional event callback for range batching diagnostics. */
486
505
  onEvent;
487
506
  pendingRequests = [];
507
+ /** Pending batch window, independent of in-flight transport requests. */
488
508
  flushTimer = null;
489
509
  /** Creates a scheduler for one group of byte-range-addressable resources. */
490
510
  constructor(props = {}) {
@@ -561,7 +581,7 @@ var RangeRequestScheduler = class {
561
581
  }
562
582
  /** Immediately starts loading the currently queued requests. */
563
583
  flush() {
564
- if (this.flushTimer) {
584
+ if (this.flushTimer !== null) {
565
585
  clearTimeout(this.flushTimer);
566
586
  this.flushTimer = null;
567
587
  }
@@ -583,9 +603,9 @@ var RangeRequestScheduler = class {
583
603
  this.fetchMergedRequest(mergedRequest);
584
604
  }
585
605
  }
586
- /** Schedules the next pending-request flush. */
606
+ /** Starts a fixed batch window only when no flush is already scheduled. */
587
607
  scheduleFlush() {
588
- if (this.flushTimer) {
608
+ if (this.flushTimer !== null) {
589
609
  return;
590
610
  }
591
611
  this.flushTimer = setTimeout(() => this.flush(), this.props.batchDelayMs);
@@ -1900,7 +1920,7 @@ function getFrameState(viewport, frameNumber, options = {}) {
1900
1920
  ).normalize();
1901
1921
  commonSpacePlanesToWGS84(viewport);
1902
1922
  const ViewportClass = viewport.constructor;
1903
- const { longitude, latitude, width, bearing, zoom } = viewport;
1923
+ const { longitude, latitude, width, bearing, zoom, position } = viewport;
1904
1924
  const topDownViewport = new ViewportClass({
1905
1925
  longitude,
1906
1926
  latitude,
@@ -1908,15 +1928,17 @@ function getFrameState(viewport, frameNumber, options = {}) {
1908
1928
  width,
1909
1929
  bearing,
1910
1930
  zoom,
1931
+ position,
1911
1932
  pitch: 0
1912
1933
  });
1934
+ const verticalFieldOfView = Number.isFinite(viewport.fovy) && Number(viewport.fovy) > 0 && Number(viewport.fovy) < 180 ? Number(viewport.fovy) * Math.PI / 180 : Math.PI / 3;
1913
1935
  return {
1914
1936
  camera: {
1915
1937
  position: cameraPositionCartesian2,
1916
1938
  direction: cameraDirectionCartesian,
1917
1939
  up: cameraUpCartesian,
1918
1940
  cartographicPosition: cameraPositionCartographic,
1919
- verticalFieldOfView: Number.isFinite(viewport.fovy) && Number(viewport.fovy) > 0 ? Number(viewport.fovy) * Math.PI / 180 : Math.PI / 3,
1941
+ verticalFieldOfView,
1920
1942
  timeSinceMovement: options.timeSinceCameraMovement ?? Number.POSITIVE_INFINITY
1921
1943
  },
1922
1944
  viewport,
@@ -1926,8 +1948,7 @@ function getFrameState(viewport, frameNumber, options = {}) {
1926
1948
  clippingPlanes: viewport.clippingPlanes,
1927
1949
  frameNumber,
1928
1950
  // TODO: This can be the same between updates, what number is unique for between updates?
1929
- sseDenominator: 1.15,
1930
- // Assumes fovy = 60 degrees
1951
+ sseDenominator: 2 * Math.tan(verticalFieldOfView / 2),
1931
1952
  // Tileset3D fills this immediately before traversal because it depends on the root volume.
1932
1953
  dynamicScreenSpaceErrorDensity: 0
1933
1954
  };
@@ -2265,30 +2286,32 @@ function calculateTileRequestPriority(parameters) {
2265
2286
  }
2266
2287
 
2267
2288
  // dist/spatial/spatial-types.js
2268
- var import_crs = require("@math.gl/crs");
2289
+ var import_crs2 = require("@math.gl/crs");
2269
2290
 
2270
2291
  // dist/spatial/get-spatial-coordinate-frame.js
2271
- var GEOGRAPHIC_CRS = "EPSG:4326";
2272
- var GEOCENTRIC_CRS = "EPSG:4978";
2292
+ var import_crs = require("@math.gl/crs");
2293
+
2294
+ // dist/spatial/normalize-crs-identifier.js
2295
+ var WGS84_GEOGRAPHIC_CRS = "EPSG:4326";
2296
+ var WGS84_GEOCENTRIC_CRS = "EPSG:4978";
2273
2297
  function normalizeCrsIdentifier(identifier) {
2274
2298
  const normalizedIdentifier = identifier.trim().toUpperCase();
2275
2299
  const ogcMatch = normalizedIdentifier.match(/(?:\/DEF\/CRS\/|URN:OGC:DEF:CRS:)([A-Z0-9_-]+)(?:\/|::)(?:[^/:]*[/:])?([A-Z0-9_.-]+)$/);
2276
2300
  return ogcMatch ? `${ogcMatch[1]}:${ogcMatch[2]}` : normalizedIdentifier;
2277
2301
  }
2302
+
2303
+ // dist/spatial/get-spatial-coordinate-frame.js
2278
2304
  function getSpatialCoordinateFrame(definition) {
2279
2305
  var _a, _b;
2280
2306
  if (typeof definition === "string") {
2281
- const normalized = normalizeCrsIdentifier(definition);
2282
- if (normalized === GEOCENTRIC_CRS || /^GEOCCS\s*[\[(]/.test(normalized)) {
2283
- return "geocentric";
2307
+ const representation = (0, import_crs.inferCRSRepresentation)(definition);
2308
+ if (representation === "wkt") {
2309
+ return getWktCoordinateFrame(definition);
2284
2310
  }
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";
2311
+ if (representation === "proj-string") {
2312
+ return getProjStringCoordinateFrame(definition);
2290
2313
  }
2291
- return "projected";
2314
+ return getKnownCrsIdentifierCoordinateFrame(definition) || "projected";
2292
2315
  }
2293
2316
  const projJsonDefinition = definition;
2294
2317
  if (projJsonDefinition.type === "BoundCRS" && projJsonDefinition.source_crs) {
@@ -2306,7 +2329,84 @@ function getSpatialCoordinateFrame(definition) {
2306
2329
  if (String(projJsonDefinition.type).includes("Geodetic")) {
2307
2330
  return ((_b = projJsonDefinition.coordinate_system) == null ? void 0 : _b.subtype) === "Cartesian" ? "geocentric" : "geographic";
2308
2331
  }
2309
- return "projected";
2332
+ return "unknown";
2333
+ }
2334
+ function getKnownCrsIdentifierCoordinateFrame(definition) {
2335
+ if (typeof definition !== "string") {
2336
+ return void 0;
2337
+ }
2338
+ const normalized = normalizeCrsIdentifier(definition);
2339
+ if (normalized === WGS84_GEOCENTRIC_CRS) {
2340
+ return "geocentric";
2341
+ }
2342
+ if (normalized === WGS84_GEOGRAPHIC_CRS || normalized === "OGC:CRS84") {
2343
+ return "geographic";
2344
+ }
2345
+ const epsgMatch = /^EPSG:(\d+)$/.exec(normalized);
2346
+ if (!epsgMatch) {
2347
+ return void 0;
2348
+ }
2349
+ const identifier = Number(epsgMatch[1]);
2350
+ if (identifier === 7789) {
2351
+ return "geocentric";
2352
+ }
2353
+ if (identifier === 4490 || identifier === 4979) {
2354
+ return "geographic";
2355
+ }
2356
+ if (identifier === 3857 || identifier >= 32601 && identifier <= 32660 || identifier >= 32701 && identifier <= 32760) {
2357
+ return "projected";
2358
+ }
2359
+ return void 0;
2360
+ }
2361
+ function getWktCoordinateFrame(definition) {
2362
+ let root;
2363
+ try {
2364
+ root = (0, import_crs.parseWKTCRS)(definition).root;
2365
+ } catch {
2366
+ return "unknown";
2367
+ }
2368
+ switch (root.keyword.toUpperCase()) {
2369
+ case "GEOCCS":
2370
+ return "geocentric";
2371
+ case "GEOGCS":
2372
+ case "GEOGCRS":
2373
+ case "GEOGRAPHICCRS":
2374
+ case "GEOGRAPHIC2DCRS":
2375
+ case "GEOGRAPHIC3DCRS":
2376
+ return "geographic";
2377
+ case "PROJCS":
2378
+ case "PROJCRS":
2379
+ case "PROJECTEDCRS":
2380
+ return "projected";
2381
+ case "GEODCRS":
2382
+ case "GEODETICCRS": {
2383
+ const coordinateSystem = root.values.find((value) => value.type === "node" && value.keyword.toUpperCase() === "CS");
2384
+ const subtype = coordinateSystem == null ? void 0 : coordinateSystem.values[0];
2385
+ if ((subtype == null ? void 0 : subtype.type) !== "enumeration" && (subtype == null ? void 0 : subtype.type) !== "string") {
2386
+ return "unknown";
2387
+ }
2388
+ return subtype.value.toUpperCase() === "CARTESIAN" ? "geocentric" : subtype.value.toUpperCase() === "ELLIPSOIDAL" ? "geographic" : "unknown";
2389
+ }
2390
+ default:
2391
+ return "unknown";
2392
+ }
2393
+ }
2394
+ function getProjStringCoordinateFrame(definition) {
2395
+ var _a;
2396
+ try {
2397
+ const projection = (_a = (0, import_crs.parsePROJString)(definition).parameters.find((parameter) => parameter.name.toLowerCase() === "proj")) == null ? void 0 : _a.value;
2398
+ switch (projection == null ? void 0 : projection.toLowerCase()) {
2399
+ case "longlat":
2400
+ case "latlong":
2401
+ return "geographic";
2402
+ case "geocent":
2403
+ return "geocentric";
2404
+ default:
2405
+ return projection ? "projected" : "unknown";
2406
+ }
2407
+ } catch {
2408
+ return "unknown";
2409
+ }
2310
2410
  }
2311
2411
 
2312
2412
  // dist/spatial/spatial-types.js
@@ -2331,11 +2431,11 @@ function createTilesetSpatialReference(discovered, options = {}) {
2331
2431
  const provenance = hasSourceCrsOverride ? "caller-override" : discovered.provenance || "unknown";
2332
2432
  const coordinateFrame = hasSourceCrsOverride && sourceCrs ? getSpatialCoordinateFrame(sourceCrs) : discovered.coordinateFrame;
2333
2433
  const sourceCrsState = hasSourceCrsOverride ? "explicit" : discovered.sourceCrsState || (sourceCrs ? provenance === "format-default" ? "default" : "explicit" : "absent");
2334
- const spatialReference = (0, import_crs.createSpatialReference)({
2434
+ const spatialReference = (0, import_crs2.createSpatialReference)({
2335
2435
  crs: sourceCrs ? {
2336
2436
  state: sourceCrsState === "default" ? "default" : "explicit",
2337
2437
  definition: sourceCrs,
2338
- representation: !hasSourceCrsOverride && discovered.sourceCrsRepresentation || (0, import_crs.inferCRSRepresentation)(sourceCrs),
2438
+ representation: !hasSourceCrsOverride && discovered.sourceCrsRepresentation || (0, import_crs2.inferCRSRepresentation)(sourceCrs),
2339
2439
  provenance,
2340
2440
  alternatives: hasSourceCrsOverride ? void 0 : discovered.sourceCrsAlternatives
2341
2441
  } : {
@@ -2345,7 +2445,7 @@ function createTilesetSpatialReference(discovered, options = {}) {
2345
2445
  vertical: discovered.verticalCrs ? {
2346
2446
  state: "explicit",
2347
2447
  definition: discovered.verticalCrs,
2348
- representation: (0, import_crs.inferCRSRepresentation)(discovered.verticalCrs),
2448
+ representation: (0, import_crs2.inferCRSRepresentation)(discovered.verticalCrs),
2349
2449
  provenance: discovered.provenance || "metadata"
2350
2450
  } : void 0,
2351
2451
  coordinateEpoch: options.coordinateEpoch ?? discovered.coordinateEpoch,
@@ -2402,11 +2502,11 @@ function getKnownCRSDefinition(reference) {
2402
2502
  return reference.state === "explicit" || reference.state === "default" ? reference.definition : void 0;
2403
2503
  }
2404
2504
  function cloneReadonlyCRSDefinition(definition) {
2405
- const spatialReference = (0, import_crs.createSpatialReference)({
2505
+ const spatialReference = (0, import_crs2.createSpatialReference)({
2406
2506
  crs: {
2407
2507
  state: "explicit",
2408
2508
  definition,
2409
- representation: (0, import_crs.inferCRSRepresentation)(definition),
2509
+ representation: (0, import_crs2.inferCRSRepresentation)(definition),
2410
2510
  provenance: "caller-override"
2411
2511
  }
2412
2512
  });
@@ -2453,11 +2553,11 @@ function validateCacheByteLength(value, optionName) {
2453
2553
  return value;
2454
2554
  }
2455
2555
  function resolveCacheBytes(options, fallbackCacheBytes) {
2456
- const cacheBytes = options.cacheBytes ?? (options.maximumMemoryUsage !== void 0 ? options.maximumMemoryUsage * BYTES_PER_MEBIBYTE : fallbackCacheBytes);
2556
+ const cacheBytes = options.cacheBytes ?? fallbackCacheBytes;
2457
2557
  return validateCacheByteLength(cacheBytes, "cacheBytes");
2458
2558
  }
2459
2559
  function resolveMaximumCacheOverflowBytes(options, fallbackOverflowBytes) {
2460
- const maximumCacheOverflowBytes = options.maximumCacheOverflowBytes ?? (options.memoryCacheOverflow !== void 0 ? options.memoryCacheOverflow * BYTES_PER_MEBIBYTE : fallbackOverflowBytes);
2560
+ const maximumCacheOverflowBytes = options.maximumCacheOverflowBytes ?? fallbackOverflowBytes;
2461
2561
  return validateCacheByteLength(maximumCacheOverflowBytes, "maximumCacheOverflowBytes");
2462
2562
  }
2463
2563
  var DEFAULT_PROPS3 = {
@@ -2468,8 +2568,6 @@ var DEFAULT_PROPS3 = {
2468
2568
  maxRequests: 64,
2469
2569
  cacheBytes: DEFAULT_CACHE_BYTES,
2470
2570
  maximumCacheOverflowBytes: DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES,
2471
- maximumMemoryUsage: DEFAULT_CACHE_BYTES / BYTES_PER_MEBIBYTE,
2472
- memoryCacheOverflow: DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES / BYTES_PER_MEBIBYTE,
2473
2571
  maximumTilesSelected: 0,
2474
2572
  debounceTime: 0,
2475
2573
  onTileLoad: () => {
@@ -2613,8 +2711,6 @@ var Tileset3D = class {
2613
2711
  ...suppliedOptions,
2614
2712
  cacheBytes,
2615
2713
  maximumCacheOverflowBytes,
2616
- maximumMemoryUsage: cacheBytes / BYTES_PER_MEBIBYTE,
2617
- memoryCacheOverflow: maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE,
2618
2714
  memoryAdjustedScreenSpaceError: suppliedOptions.memoryAdjustedScreenSpaceError ?? usesTiles3DCacheDefaults
2619
2715
  };
2620
2716
  this.loadOptions = this.options.loadOptions || {};
@@ -2661,6 +2757,24 @@ var Tileset3D = class {
2661
2757
  get frameNumber() {
2662
2758
  return this._frameNumber;
2663
2759
  }
2760
+ /**
2761
+ * Returns the tiles requested during the most recent completed traversal.
2762
+ *
2763
+ * A defensive copy makes request ordering deterministic for diagnostics and prevents callers
2764
+ * from mutating the scheduler's working set.
2765
+ */
2766
+ get requestedTiles() {
2767
+ return this._requestedTiles.slice();
2768
+ }
2769
+ /**
2770
+ * Returns empty tiles considered during the most recent completed traversal.
2771
+ *
2772
+ * Empty tiles can still be useful hierarchy placeholders, but their IDs are included in
2773
+ * observability snapshots so applications can distinguish traversal work from render content.
2774
+ */
2775
+ get emptyTiles() {
2776
+ return this._emptyTiles.slice();
2777
+ }
2664
2778
  /**
2665
2779
  * Gets or sets the soft target, in bytes, for cached tile content not needed this frame.
2666
2780
  *
@@ -2675,7 +2789,6 @@ var Tileset3D = class {
2675
2789
  set cacheBytes(value) {
2676
2790
  this._cacheBytes = validateCacheByteLength(value, "cacheBytes");
2677
2791
  this.options.cacheBytes = this._cacheBytes;
2678
- this.options.maximumMemoryUsage = this._cacheBytes / BYTES_PER_MEBIBYTE;
2679
2792
  }
2680
2793
  /**
2681
2794
  * Gets or sets the additional current-frame headroom, in bytes, before memory-adjusted SSE rises.
@@ -2690,17 +2803,6 @@ var Tileset3D = class {
2690
2803
  set maximumCacheOverflowBytes(value) {
2691
2804
  this._maximumCacheOverflowBytes = validateCacheByteLength(value, "maximumCacheOverflowBytes");
2692
2805
  this.options.maximumCacheOverflowBytes = this._maximumCacheOverflowBytes;
2693
- this.options.memoryCacheOverflow = this._maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE;
2694
- }
2695
- /**
2696
- * Gets or sets the soft cache target in mebibytes.
2697
- * @deprecated Use {@link Tileset3D.cacheBytes}. Assignments remain synchronized for compatibility.
2698
- */
2699
- get maximumMemoryUsage() {
2700
- return this.cacheBytes / BYTES_PER_MEBIBYTE;
2701
- }
2702
- set maximumMemoryUsage(value) {
2703
- this.cacheBytes = validateCacheByteLength(value * BYTES_PER_MEBIBYTE, "maximumMemoryUsage");
2704
2806
  }
2705
2807
  get queryParams() {
2706
2808
  const rootUrl = this.source.getTileUrl(this.url);
@@ -2708,10 +2810,7 @@ var Tileset3D = class {
2708
2810
  return queryIndex >= 0 ? rootUrl.slice(queryIndex + 1) : "";
2709
2811
  }
2710
2812
  /**
2711
- * Updates traversal and runtime options, synchronizing byte-native and deprecated cache values.
2712
- *
2713
- * For each budget, the byte-native option takes precedence when both unit forms are supplied.
2714
- * Unspecified budgets retain their current byte values, avoiding repeated MiB conversion.
2813
+ * Updates traversal and runtime options while retaining unspecified byte budgets.
2715
2814
  *
2716
2815
  * @param props - Partial runtime options to apply.
2717
2816
  */
@@ -2722,9 +2821,7 @@ var Tileset3D = class {
2722
2821
  ...this.options,
2723
2822
  ...props,
2724
2823
  cacheBytes,
2725
- maximumCacheOverflowBytes,
2726
- maximumMemoryUsage: cacheBytes / BYTES_PER_MEBIBYTE,
2727
- memoryCacheOverflow: maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE
2824
+ maximumCacheOverflowBytes
2728
2825
  };
2729
2826
  this._cacheBytes = cacheBytes;
2730
2827
  this._maximumCacheOverflowBytes = maximumCacheOverflowBytes;
@@ -2788,6 +2885,12 @@ var Tileset3D = class {
2788
2885
  return;
2789
2886
  }
2790
2887
  const preparedViewports = viewports instanceof Array ? viewports : [viewports];
2888
+ const activeViewportIds = new Set(preparedViewports.map((viewport) => viewport.id));
2889
+ for (const frameStateId of Object.keys(this.frameStateData)) {
2890
+ if (!activeViewportIds.has(frameStateId)) {
2891
+ delete this.frameStateData[frameStateId];
2892
+ }
2893
+ }
2791
2894
  this._cache.reset();
2792
2895
  this._frameNumber++;
2793
2896
  this.traverseCounter = preparedViewports.length;
@@ -2976,21 +3079,21 @@ var Tileset3D = class {
2976
3079
  this.stats.get(MAXIMUM_SSE).count = this.memoryAdjustedScreenSpaceError;
2977
3080
  }
2978
3081
  async _initializeTileSet() {
2979
- var _a, _b, _c;
3082
+ var _a, _b, _c, _d;
2980
3083
  try {
2981
3084
  const initializePromise = this.source.initialize();
2982
3085
  const initialMetadata = this._getSourceMetadata();
2983
3086
  if (initialMetadata) {
2984
3087
  this._syncSourceState(initialMetadata);
2985
3088
  }
2986
- if (((_a = initialMetadata == null ? void 0 : initialMetadata.tileset) == null ? void 0 : _a.root) && typeof initialMetadata.tileset.root.then !== "function" && !this.source.prepareTileset) {
3089
+ 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
3090
  this.root = this._initializeTileHeaders(initialMetadata.tileset, null);
2988
3091
  this._applyViewState();
2989
3092
  }
2990
3093
  await initializePromise;
2991
3094
  const metadata = this.source.getMetadata();
2992
3095
  this._syncSourceState(metadata);
2993
- await ((_c = (_b = this.source).prepareTileset) == null ? void 0 : _c.call(_b, this));
3096
+ await ((_d = (_c = this.source).prepareTileset) == null ? void 0 : _d.call(_c, this));
2994
3097
  if (!this.root) {
2995
3098
  this.root = this._initializeTileHeaders(metadata.tileset, null);
2996
3099
  }
@@ -3211,6 +3314,98 @@ function preserveTransformedSpatialReference(current, next) {
3211
3314
  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
3315
  }
3213
3316
 
3317
+ // dist/tileset-3d/common/tileset-observability.js
3318
+ function getTileset3DTraversalSnapshot(tileset) {
3319
+ var _a;
3320
+ const selectedTiles = tileset.selectedTiles.slice();
3321
+ const requestedTiles = tileset.requestedTiles.slice();
3322
+ const emptyTiles = tileset.emptyTiles.slice();
3323
+ const getTileIds = (tiles) => tiles.map((tile) => String(tile.id)).sort();
3324
+ const getStatCount = (name) => {
3325
+ const count = tileset.stats.get(name).count;
3326
+ return typeof count === "number" && Number.isFinite(count) ? count : 0;
3327
+ };
3328
+ const implicitTilingSource = tileset.source;
3329
+ const implicitTiling = (_a = implicitTilingSource.getImplicitTilingStats) == null ? void 0 : _a.call(implicitTilingSource);
3330
+ return {
3331
+ frameNumber: tileset.frameNumber,
3332
+ selectedTileIds: getTileIds(selectedTiles),
3333
+ requestedTileIds: getTileIds(requestedTiles),
3334
+ emptyTileIds: getTileIds(emptyTiles),
3335
+ visibleTileCount: selectedTiles.length,
3336
+ renderableTileCount: selectedTiles.filter((tile) => tile.contentAvailable && Boolean(tile.content)).length,
3337
+ loadingTileCount: getStatCount("Tiles Loading"),
3338
+ loadedTileCount: getStatCount("Tiles Loaded"),
3339
+ failedTileCount: getStatCount("Failed Tile Loads"),
3340
+ cachedTileCount: getStatCount("Tiles In Memory"),
3341
+ cacheBytes: tileset.gpuMemoryUsageInBytes,
3342
+ maximumScreenSpaceError: tileset.memoryAdjustedScreenSpaceError,
3343
+ ...implicitTiling ? { implicitTiling } : {}
3344
+ };
3345
+ }
3346
+
3347
+ // dist/tileset-3d/common/tileset-content-traversal.js
3348
+ async function* traverseTilesetContents(tileset, options = {}) {
3349
+ var _a, _b;
3350
+ await tileset.tilesetInitializationPromise;
3351
+ throwIfAborted(options.signal);
3352
+ if (!tileset.root) {
3353
+ throw new Error("Tileset initialization completed without a root tile");
3354
+ }
3355
+ const stack = [tileset.root];
3356
+ const visitedTiles = /* @__PURE__ */ new Set();
3357
+ while (stack.length > 0) {
3358
+ throwIfAborted(options.signal);
3359
+ const tile = stack.pop();
3360
+ if (visitedTiles.has(tile)) {
3361
+ continue;
3362
+ }
3363
+ visitedTiles.add(tile);
3364
+ if (tile.header.implicitSubtree && tile.childrenState !== "ready") {
3365
+ const loadChildren = tileset.source.loadTileChildrenForTraversal;
3366
+ if (!loadChildren) {
3367
+ throw new Error("Tileset source does not support camera-independent subtree loading");
3368
+ }
3369
+ const result = await loadChildren.call(tileset.source, tile, options.signal);
3370
+ if (!result.loaded) {
3371
+ throw new Error(`Unable to load implicit subtree for tile ${tile.id}`);
3372
+ }
3373
+ tile.childrenState = "ready";
3374
+ }
3375
+ if (tileset.type === TILESET_TYPE.I3S && tileset.source.loadTileChildrenForTraversal) {
3376
+ const result = await tileset.source.loadTileChildrenForTraversal(tile, options.signal);
3377
+ if (!result.loaded) {
3378
+ throw new Error(`Unable to load I3S child nodes for tile ${tile.id}`);
3379
+ }
3380
+ }
3381
+ throwIfAborted(options.signal);
3382
+ const loadsContent = tile.contentUrls.length > 0 && !tile.content;
3383
+ try {
3384
+ if (loadsContent) {
3385
+ const loadResult = await tile.loadContentForTraversal();
3386
+ throwIfAborted(options.signal);
3387
+ if (!loadResult.loaded) {
3388
+ throw new Error(`Unable to load tile content for tile ${tile.id}`);
3389
+ }
3390
+ (_b = (_a = tileset.source).onTileLoaded) == null ? void 0 : _b.call(_a, tileset, tile, loadResult);
3391
+ }
3392
+ yield { tile, contents: tile.contentEntries };
3393
+ } finally {
3394
+ if (options.unloadContent && loadsContent) {
3395
+ tile.unloadContent();
3396
+ }
3397
+ }
3398
+ for (let childIndex = tile.children.length - 1; childIndex >= 0; childIndex--) {
3399
+ stack.push(tile.children[childIndex]);
3400
+ }
3401
+ }
3402
+ }
3403
+ function throwIfAborted(signal) {
3404
+ if (signal == null ? void 0 : signal.aborted) {
3405
+ throw signal.reason ?? new DOMException("The operation was aborted", "AbortError");
3406
+ }
3407
+ }
3408
+
3214
3409
  // dist/tileset-3d/common/indexed-archive-tileset-source.js
3215
3410
  var IndexedArchiveTilesetSource = class {
3216
3411
  /** URL exposed to the tileset source that owns this archive source. */
@@ -3327,6 +3522,47 @@ function stripQueryString(url) {
3327
3522
  var import_core8 = require("@math.gl/core");
3328
3523
  var import_culling4 = require("@math.gl/culling");
3329
3524
 
3525
+ // dist/tileset-3d/common/tile-3d-contracts.js
3526
+ function getTile3DFeatureIdSets(payload) {
3527
+ const featureIdSets = [];
3528
+ const visited = /* @__PURE__ */ new Set();
3529
+ const visit = (value) => {
3530
+ var _a, _b;
3531
+ if (!value || typeof value !== "object") {
3532
+ return;
3533
+ }
3534
+ if (visited.has(value)) {
3535
+ return;
3536
+ }
3537
+ visited.add(value);
3538
+ const record = value;
3539
+ const declarations = (_b = (_a = record.extensions) == null ? void 0 : _a.EXT_mesh_features) == null ? void 0 : _b.featureIds;
3540
+ if (Array.isArray(declarations)) {
3541
+ for (const declaration of declarations) {
3542
+ if (!declaration || typeof declaration !== "object") {
3543
+ continue;
3544
+ }
3545
+ const source = declaration.texture ? "texture" : declaration.implicit ? "implicit" : declaration.attribute !== void 0 ? "attribute" : declaration.propertyTable !== void 0 ? "property-table" : "constant";
3546
+ featureIdSets.push({
3547
+ source,
3548
+ attribute: declaration.attribute,
3549
+ propertyTable: declaration.propertyTable,
3550
+ constant: declaration.constant,
3551
+ texture: declaration.texture,
3552
+ implicit: declaration.implicit,
3553
+ values: declaration.values,
3554
+ details: declaration
3555
+ });
3556
+ }
3557
+ }
3558
+ for (const child of Object.values(record)) {
3559
+ visit(child);
3560
+ }
3561
+ };
3562
+ visit(payload);
3563
+ return featureIdSets;
3564
+ }
3565
+
3330
3566
  // dist/tileset-3d/helpers/bounding-volume.js
3331
3567
  var import_core6 = require("@math.gl/core");
3332
3568
  var import_culling3 = require("@math.gl/culling");
@@ -3336,21 +3572,25 @@ function defined(x) {
3336
3572
  }
3337
3573
  var scratchPoint = new import_core6.Vector3();
3338
3574
  var scratchScale = new import_core6.Vector3();
3339
- function createBoundingVolume(boundingVolumeHeader, transform, result) {
3575
+ var identityTransform = new import_core6.Matrix4();
3576
+ function createBoundingVolume(boundingVolumeHeader, transform, result, initialTransform = transform) {
3340
3577
  assert(boundingVolumeHeader, "3D Tile: boundingVolume must be defined");
3341
3578
  if (boundingVolumeHeader.box) {
3342
- return createBox(boundingVolumeHeader.box, transform, result);
3579
+ const boxTransform = boundingVolumeHeader.s2VolumeInfo ? identityTransform : transform;
3580
+ return createBox(boundingVolumeHeader.box, boxTransform, result);
3343
3581
  }
3344
3582
  if (boundingVolumeHeader.region) {
3345
- return (0, import_geospatial4.makeOBBFromRegion)(boundingVolumeHeader.region);
3583
+ const boundingVolume = (0, import_geospatial4.makeOBBFromRegion)(boundingVolumeHeader.region);
3584
+ const regionTransform = transform.equals(initialTransform) ? identityTransform : transform.clone().multiplyRight(initialTransform.clone().invert());
3585
+ return createBox([...boundingVolume.center, ...boundingVolume.halfAxes], regionTransform, result);
3346
3586
  }
3347
3587
  if (boundingVolumeHeader.sphere) {
3348
3588
  return createSphere(boundingVolumeHeader.sphere, transform, result);
3349
3589
  }
3350
3590
  throw new Error("3D Tile: boundingVolume must contain a sphere, region, or box");
3351
3591
  }
3352
- function getCartographicBounds(boundingVolumeHeader, boundingVolume) {
3353
- if (boundingVolumeHeader.box) {
3592
+ function getCartographicBounds(boundingVolumeHeader, boundingVolume, isRegionTransformed = false) {
3593
+ if (boundingVolumeHeader.box || boundingVolumeHeader.region && isRegionTransformed) {
3354
3594
  return orientedBoundingBoxToCartographicBounds(boundingVolume);
3355
3595
  }
3356
3596
  if (boundingVolumeHeader.region) {
@@ -3830,7 +4070,7 @@ var TilesetTraverser = class {
3830
4070
  }
3831
4071
  stack.push(child);
3832
4072
  hasVisibleChild = true;
3833
- } else if (checkRefines || loadSiblings) {
4073
+ } else if ((checkRefines || loadSiblings) && child._inRequestVolume) {
3834
4074
  this.loadTile(child, frameState);
3835
4075
  this.touchTile(child, frameState);
3836
4076
  }
@@ -4017,6 +4257,13 @@ function getContentVisibility(contentVolumes, fallbackVolume, cullingVolume2, vi
4017
4257
  }
4018
4258
  return intersectingVolume ? "intersecting" : "inside";
4019
4259
  }
4260
+ function isNestedTilesetPayload(payload) {
4261
+ if (!payload || typeof payload !== "object") {
4262
+ return false;
4263
+ }
4264
+ const record = payload;
4265
+ return record.shape === "tileset3d" || record.type === "tileset3d" || Boolean(record.tileset);
4266
+ }
4020
4267
  function defined2(x) {
4021
4268
  return x !== void 0 && x !== null;
4022
4269
  }
@@ -4044,6 +4291,8 @@ var Tile3D = class {
4044
4291
  * separate ensures transform updates never compound an already-scaled geometric error.
4045
4292
  */
4046
4293
  _unscaledLodMetricValue = 0;
4294
+ /** Whether the source format defines geometric error in transform-local units. */
4295
+ _scaleGeometricError = true;
4047
4296
  /** @todo math.gl is not exporting BoundingVolume base type? */
4048
4297
  boundingVolume = null;
4049
4298
  /**
@@ -4053,6 +4302,14 @@ var Tile3D = class {
4053
4302
  content = null;
4054
4303
  /** Loaded payloads in the same order as {@link contentUrls}; `content` remains the primary payload. */
4055
4304
  contents = [];
4305
+ /** Ordered renderer-neutral descriptors for each source content entry. */
4306
+ contentEntries = [];
4307
+ /** Raw metadata references inherited by this tile and its content. */
4308
+ metadataContext = {};
4309
+ /** Feature-id declarations normalized from supported content extensions. */
4310
+ featureIdSets = [];
4311
+ /** Decoded 3D Tiles vector topology for the primary loaded content, when present. */
4312
+ vectorContent = null;
4056
4313
  /**
4057
4314
  * Metadata entity attached to this tile in the source tileset, if present.
4058
4315
  *
@@ -4119,6 +4376,7 @@ var Tile3D = class {
4119
4376
  get viewerRequestVolume() {
4120
4377
  return this._viewerRequestVolume;
4121
4378
  }
4379
+ /** Accumulated tile transforms from the source JSON, excluding the tileset model matrix. */
4122
4380
  _initialTransform = new import_core8.Matrix4();
4123
4381
  // Used by traverser, cannot be marked private
4124
4382
  _priority = 0;
@@ -4160,15 +4418,14 @@ var Tile3D = class {
4160
4418
  this.type = header.type;
4161
4419
  this.contentUrl = header.contentUrl;
4162
4420
  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);
4421
+ this._initializeMetadata(header);
4166
4422
  this.childrenState = header.implicitSubtree ? "unloaded" : "ready";
4167
4423
  this._initializeLodMetric(header);
4168
4424
  this._initializeTransforms(header);
4169
4425
  this._updateLodMetricScale();
4170
4426
  this._initializeBoundingVolumes(header);
4171
4427
  this._initializeContent(header);
4428
+ this._initializeContentEntries(header);
4172
4429
  this._initializeRenderingState(header);
4173
4430
  Object.seal(this);
4174
4431
  }
@@ -4253,12 +4510,12 @@ var Tile3D = class {
4253
4510
  return this._screenSpaceError;
4254
4511
  }
4255
4512
  /**
4256
- * Get bounding box in cartographic coordinates
4513
+ * Get the current world-space bounding volume in cartographic coordinates.
4257
4514
  * @returns [min, max] each in [longitude, latitude, altitude]
4258
4515
  */
4259
4516
  get boundingBox() {
4260
4517
  if (!this._boundingBox) {
4261
- this._boundingBox = getCartographicBounds(this.header.boundingVolume, this.boundingVolume);
4518
+ this._boundingBox = getCartographicBounds(this.header.boundingVolume, this.boundingVolume, !this.computedTransform.equals(this._initialTransform));
4262
4519
  }
4263
4520
  return this._boundingBox;
4264
4521
  }
@@ -4375,17 +4632,7 @@ var Tile3D = class {
4375
4632
  return { loaded: false };
4376
4633
  }
4377
4634
  try {
4378
- const loadResult = await this.tileset.source.loadTileContent(this);
4379
- if (loadResult.contents) {
4380
- this.contents = loadResult.contents;
4381
- this.content = this.contents[0] || null;
4382
- }
4383
- if (this.tileset.options.contentLoader) {
4384
- await this.tileset.options.contentLoader(this);
4385
- }
4386
- this.contentState = TILE_CONTENT_STATE.READY;
4387
- this._onContentLoaded();
4388
- return loadResult;
4635
+ return await this._loadContentFromSource();
4389
4636
  } catch (error) {
4390
4637
  this.contentState = TILE_CONTENT_STATE.FAILED;
4391
4638
  throw error;
@@ -4393,6 +4640,39 @@ var Tile3D = class {
4393
4640
  requestToken.done();
4394
4641
  }
4395
4642
  }
4643
+ /** Loads content for complete dataset operations without viewport-based request priority. */
4644
+ async loadContentForTraversal() {
4645
+ if (this.hasEmptyContent) {
4646
+ return { loaded: false };
4647
+ }
4648
+ if (this.content) {
4649
+ return { loaded: true, contents: this.contents };
4650
+ }
4651
+ this.contentState = TILE_CONTENT_STATE.LOADING;
4652
+ try {
4653
+ return await this._loadContentFromSource();
4654
+ } catch (error) {
4655
+ this.contentState = TILE_CONTENT_STATE.FAILED;
4656
+ throw error;
4657
+ }
4658
+ }
4659
+ /** Loads source content and updates the tile's renderer-neutral payload state. */
4660
+ async _loadContentFromSource() {
4661
+ var _a;
4662
+ const loadResult = await this.tileset.source.loadTileContent(this);
4663
+ if (loadResult.contents) {
4664
+ this.contents = loadResult.contents;
4665
+ this.content = this.contents[0] || null;
4666
+ }
4667
+ this.vectorContent = ((_a = this.contents.find((content) => Boolean(content == null ? void 0 : content.vectorContent))) == null ? void 0 : _a.vectorContent) || null;
4668
+ if (this.tileset.options.contentLoader) {
4669
+ await this.tileset.options.contentLoader(this);
4670
+ }
4671
+ this.contentState = TILE_CONTENT_STATE.READY;
4672
+ this._onContentLoaded();
4673
+ this._updateContentEntriesAfterLoad(loadResult);
4674
+ return loadResult;
4675
+ }
4396
4676
  /**
4397
4677
  * Requests and installs this tile's lazy child-header group through its source.
4398
4678
  *
@@ -4447,30 +4727,114 @@ var Tile3D = class {
4447
4727
  /**
4448
4728
  * Replaces a contentless implicit placeholder with its materialized subtree-root header.
4449
4729
  *
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.
4730
+ * The placeholder transform is preserved when the subtree omits a root transform. An explicit
4731
+ * subtree-root transform replaces it and is recomposed against the runtime parent. Content,
4732
+ * metadata, bounds, and raw geometric error are reinitialized from the available header.
4453
4733
  *
4454
4734
  * @param materializedHeader - Available root header produced from one subtree resource.
4455
4735
  */
4456
4736
  applyImplicitSubtreeHeader(materializedHeader) {
4457
4737
  const existingTransform = this.header.transform;
4458
4738
  const existingTransformMatrix = this.header.transformMatrix;
4739
+ const hasMaterializedTransform = materializedHeader.transform !== void 0;
4459
4740
  this.header = {
4460
4741
  ...this.header,
4461
4742
  ...materializedHeader,
4462
- transform: existingTransform,
4463
- transformMatrix: existingTransformMatrix,
4743
+ transform: hasMaterializedTransform ? materializedHeader.transform : existingTransform,
4744
+ transformMatrix: hasMaterializedTransform ? materializedHeader.transformMatrix : existingTransformMatrix,
4464
4745
  implicitSubtree: void 0
4465
4746
  };
4747
+ if (hasMaterializedTransform) {
4748
+ this._initializeTransforms(this.header);
4749
+ }
4466
4750
  this.refine = this._getRefine(materializedHeader.refine);
4467
4751
  this.type = materializedHeader.type;
4468
4752
  this.contentUrl = materializedHeader.contentUrl;
4469
4753
  this.contentUrls = materializedHeader.contentUrls || (materializedHeader.contentUrl ? [materializedHeader.contentUrl] : []);
4754
+ this._initializeMetadata(this.header);
4470
4755
  this._initializeLodMetric(materializedHeader);
4471
4756
  this._updateLodMetricScale();
4472
4757
  this._initializeBoundingVolumes(this.header);
4758
+ this._boundingBox = void 0;
4473
4759
  this._initializeContent(this.header);
4760
+ this._initializeContentEntries(this.header);
4761
+ }
4762
+ /** Refreshes tile and content metadata from a source or materialized tile header. */
4763
+ _initializeMetadata(header) {
4764
+ var _a;
4765
+ this.metadata = header.metadata || null;
4766
+ const contentHeaders = Array.isArray(header.content) ? header.content : [header.content];
4767
+ this.contentMetadata = contentHeaders.filter(Boolean).map((contentHeader) => contentHeader.metadata || null);
4768
+ const firstContentHeader = contentHeaders.find(Boolean) || {};
4769
+ const groupReference = firstContentHeader.group;
4770
+ const group = typeof groupReference === "number" ? ((_a = this.tileset.groups) == null ? void 0 : _a[groupReference]) || null : groupReference || null;
4771
+ const metadataContext = {
4772
+ tileset: this.tileset.metadata || null,
4773
+ group,
4774
+ tile: this.metadata,
4775
+ content: this.contentMetadata[0] || null,
4776
+ subtree: header.implicitMetadata || null
4777
+ };
4778
+ if (this.tileset.groups) {
4779
+ metadataContext.groups = this.tileset.groups;
4780
+ }
4781
+ this.metadataContext = metadataContext;
4782
+ }
4783
+ /** Updates payload and renderability while retaining immutable metadata descriptors. */
4784
+ _updateContentEntriesAfterLoad(loadResult) {
4785
+ if (loadResult.contentEntries) {
4786
+ this.contentEntries = loadResult.contentEntries.map((entry, index) => {
4787
+ var _a;
4788
+ return {
4789
+ ...entry,
4790
+ index,
4791
+ boundingVolume: entry.boundingVolume || this._contentBoundingVolumes[index] || this.boundingVolume,
4792
+ payload: entry.payload ?? this.contents[index] ?? null,
4793
+ featureIds: ((_a = entry.featureIds) == null ? void 0 : _a.length) ? entry.featureIds : getTile3DFeatureIdSets(entry.payload ?? this.contents[index]),
4794
+ renderable: Boolean(entry.renderable !== false && (entry.payload ?? this.contents[index]) && !isNestedTilesetPayload(entry.payload ?? this.contents[index]))
4795
+ };
4796
+ });
4797
+ this.featureIdSets = this.contentEntries.flatMap((entry) => entry.featureIds);
4798
+ return;
4799
+ }
4800
+ this.contentEntries = this.contentEntries.map((entry, index) => ({
4801
+ ...entry,
4802
+ payload: this.contents[index] ?? null,
4803
+ featureIds: getTile3DFeatureIdSets(this.contents[index]),
4804
+ renderable: Boolean(this.contents[index]) && !isNestedTilesetPayload(this.contents[index])
4805
+ }));
4806
+ this.featureIdSets = this.contentEntries.flatMap((entry) => entry.featureIds);
4807
+ }
4808
+ /** Returns one ordered content descriptor, or null when the index is out of range. */
4809
+ getContentEntry(index) {
4810
+ return this.contentEntries[index] || null;
4811
+ }
4812
+ /** Returns whether an ordered content entry currently has renderable payload. */
4813
+ isContentRenderable(index) {
4814
+ var _a;
4815
+ return Boolean((_a = this.contentEntries[index]) == null ? void 0 : _a.renderable);
4816
+ }
4817
+ /** Initializes additive renderer-neutral descriptors from a normalized tile header. */
4818
+ _initializeContentEntries(tileHeader) {
4819
+ const headers = Array.isArray(tileHeader.content) ? tileHeader.content : tileHeader.content ? [tileHeader.content] : [];
4820
+ this.contentEntries = headers.map((contentHeader, index) => {
4821
+ const normalizedContentHeader = contentHeader || {};
4822
+ return {
4823
+ index,
4824
+ uri: this.contentUrls[index] || normalizedContentHeader.uri || normalizedContentHeader.url,
4825
+ group: normalizedContentHeader.group,
4826
+ type: normalizedContentHeader.type,
4827
+ payload: null,
4828
+ metadata: normalizedContentHeader.metadata || null,
4829
+ boundingVolume: this._contentBoundingVolumes[index] || this.boundingVolume,
4830
+ featureIds: this._getFeatureIdSets(normalizedContentHeader),
4831
+ renderable: false
4832
+ };
4833
+ });
4834
+ }
4835
+ /** Extracts renderer-neutral feature-id declarations without decoding property values. */
4836
+ _getFeatureIdSets(contentHeader) {
4837
+ return getTile3DFeatureIdSets(contentHeader);
4474
4838
  }
4475
4839
  // Unloads the tile's content.
4476
4840
  unloadContent() {
@@ -4481,6 +4845,13 @@ var Tile3D = class {
4481
4845
  }
4482
4846
  this.contents = [];
4483
4847
  this.content = null;
4848
+ this.vectorContent = null;
4849
+ this.contentEntries = this.contentEntries.map((entry) => ({
4850
+ ...entry,
4851
+ payload: null,
4852
+ renderable: false
4853
+ }));
4854
+ this.featureIdSets = this.contentEntries.flatMap((entry) => entry.featureIds);
4484
4855
  if (this.header.content && this.header.content.destroy) {
4485
4856
  this.header.content.destroy();
4486
4857
  }
@@ -4577,8 +4948,9 @@ var Tile3D = class {
4577
4948
  * @param frameState Current camera, culling, and optional clipping-plane state.
4578
4949
  * @returns The content visibility classification.
4579
4950
  */
4580
- contentVisibility(frameState) {
4581
- return getContentVisibility(this._contentBoundingVolumes, this.boundingVolume, frameState.cullingVolume, this._visibilityPlaneMask, frameState.clippingPlanes);
4951
+ contentVisibility(frameState, contentIndex) {
4952
+ const contentVolumes = contentIndex === void 0 ? this._contentBoundingVolumes : this._contentBoundingVolumes[contentIndex] ? [this._contentBoundingVolumes[contentIndex]] : [];
4953
+ return getContentVisibility(contentVolumes, this.boundingVolume, frameState.cullingVolume, this._visibilityPlaneMask, frameState.clippingPlanes);
4582
4954
  }
4583
4955
  /**
4584
4956
  * Computes the (potentially approximate) distance from the closest point of the tile's bounding volume to the camera.
@@ -4625,6 +4997,8 @@ var Tile3D = class {
4625
4997
  // INTERNAL METHODS
4626
4998
  /** Initializes the tile's LOD metric from its header or its nearest available ancestor. */
4627
4999
  _initializeLodMetric(header) {
5000
+ var _a;
5001
+ this._scaleGeometricError = typeof header._scaleGeometricError === "boolean" ? header._scaleGeometricError : ((_a = this.parent) == null ? void 0 : _a._scaleGeometricError) ?? true;
4628
5002
  if ("lodMetricType" in header) {
4629
5003
  this.lodMetricType = header.lodMetricType;
4630
5004
  } else {
@@ -4660,7 +5034,7 @@ var Tile3D = class {
4660
5034
  * I3S `maxScreenThreshold` is already a screen-space metric and must not be transform-scaled.
4661
5035
  */
4662
5036
  _updateLodMetricScale() {
4663
- if (this.lodMetricType !== LOD_METRIC_TYPE.GEOMETRIC_ERROR) {
5037
+ if (this.lodMetricType !== LOD_METRIC_TYPE.GEOMETRIC_ERROR || !this._scaleGeometricError) {
4664
5038
  this.lodMetricValue = this._unscaledLodMetricValue;
4665
5039
  return;
4666
5040
  }
@@ -4740,16 +5114,17 @@ var Tile3D = class {
4740
5114
  }
4741
5115
  }
4742
5116
  _updateBoundingVolume(header) {
4743
- this.boundingVolume = createBoundingVolume(header.boundingVolume, this.computedTransform, this.boundingVolume);
5117
+ this._boundingBox = void 0;
5118
+ this.boundingVolume = createBoundingVolume(header.boundingVolume, this.computedTransform, this.boundingVolume, this._initialTransform);
4744
5119
  if (header.viewerRequestVolume) {
4745
- this._viewerRequestVolume = createBoundingVolume(header.viewerRequestVolume, this.computedTransform, this._viewerRequestVolume);
5120
+ this._viewerRequestVolume = createBoundingVolume(header.viewerRequestVolume, this.computedTransform, this._viewerRequestVolume, this._initialTransform);
4746
5121
  }
4747
5122
  const content = header.content;
4748
5123
  if (content) {
4749
5124
  this._contentBoundingVolumeHeaders = Array.isArray(content) ? content : [content];
4750
5125
  }
4751
5126
  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];
5127
+ 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
5128
  this._contentBoundingVolume = this._contentBoundingVolumes[0] || null;
4754
5129
  }
4755
5130
  /**
@@ -4769,8 +5144,8 @@ var Tile3D = class {
4769
5144
  };
4770
5145
 
4771
5146
  // 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");
5147
+ var import_geospatial10 = require("@math.gl/geospatial");
5148
+ var import_core12 = require("@math.gl/core");
4774
5149
 
4775
5150
  // dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js
4776
5151
  var Tileset3DTraverser = class extends TilesetTraverser {
@@ -4980,10 +5355,12 @@ var Tileset3DTraverser = class extends TilesetTraverser {
4980
5355
  return b._distanceToCamera === 0 && a._distanceToCamera === 0 ? b._centerZDepth - a._centerZDepth : b._distanceToCamera - a._distanceToCamera;
4981
5356
  }
4982
5357
  /**
4983
- * Starts an implicit subtree request only after culling, request-volume, and SSE eligibility.
5358
+ * Starts an implicit subtree request after culling and request-volume eligibility.
4984
5359
  *
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
5360
+ * The first subtree of an implicit hierarchy materializes independently of SSE because its
5361
+ * placeholder has no render content. Deeper subtree boundaries remain SSE-gated. The current
5362
+ * parent remains the traversal boundary while metadata is loading, which preserves REPLACE
5363
+ * coverage and ADD accumulation. Request scheduling reads the tile's existing
4987
5364
  * progressive-resolution and foveated metrics, so subtree metadata follows the same priority
4988
5365
  * policy as render content. A completion notification lets the owning tileset publish the newly
4989
5366
  * materialized traversal result on the next update.
@@ -4992,7 +5369,9 @@ var Tileset3DTraverser = class extends TilesetTraverser {
4992
5369
  * @param frameState - Current culling and LOD state.
4993
5370
  */
4994
5371
  updateChildTiles(tile, frameState) {
4995
- if (tile.hasUnloadedChildren && tile.isVisibleAndInRequestVolume && this.shouldRefine(tile, frameState)) {
5372
+ var _a;
5373
+ const isImplicitHierarchyRoot = ((_a = tile.header.implicitSubtree) == null ? void 0 : _a.coordinates.level) === 0;
5374
+ if (tile.hasUnloadedChildren && tile.isVisibleAndInRequestVolume && (isImplicitHierarchyRoot || this.shouldRefine(tile, frameState))) {
4996
5375
  const requestKey = `${frameState.viewport.id}:${tile.id}`;
4997
5376
  if (!this.pendingImplicitSubtrees.has(requestKey)) {
4998
5377
  this.pendingImplicitSubtrees.set(requestKey, frameState.frameNumber);
@@ -5183,7 +5562,7 @@ function getI3SSpatialReference(layer) {
5183
5562
  const verticalUnitScale = getI3SVerticalUnitScale(verticalUnit, layer.ZFactor);
5184
5563
  const heightReference = heightModel === "ellipsoidal" ? "ellipsoidal" : heightModel === "gravity_related_height" ? "orthometric" : "unknown";
5185
5564
  const sourceIdentifier = getCrsIdentifier(spatialReference);
5186
- const coordinateFrame = sourceIdentifier === void 0 ? getWktCoordinateFrame(spatialReference == null ? void 0 : spatialReference.wkt) : getIdentifierCoordinateFrame(sourceIdentifier);
5565
+ const coordinateFrame = sourceIdentifier === void 0 ? sourceCrs ? getSpatialCoordinateFrame(sourceCrs) : "unknown" : getSpatialCoordinateFrame(`EPSG:${sourceIdentifier}`);
5187
5566
  const units = getI3SCoordinateUnits(coordinateFrame, verticalUnit, layer.ZFactor);
5188
5567
  const warnings = [];
5189
5568
  const elevationMode = getI3SElevationMode((_e = layer.elevationInfo) == null ? void 0 : _e.mode);
@@ -5241,6 +5620,10 @@ function getI3SElevationMode(mode) {
5241
5620
  }
5242
5621
  function get3DTilesSpatialReference(tileset) {
5243
5622
  var _a, _b;
5623
+ const draftSpatialReference = getDraft3DTilesSpatialReference(tileset);
5624
+ if (draftSpatialReference) {
5625
+ return draftSpatialReference;
5626
+ }
5244
5627
  const geocentricCrs = getTilesetSemanticValue(tileset, "TILESET_CRS_GEOCENTRIC");
5245
5628
  const coordinateEpoch = getTilesetSemanticValue(tileset, "TILESET_CRS_COORDINATE_EPOCH");
5246
5629
  const warnings = [];
@@ -5278,6 +5661,87 @@ function get3DTilesSpatialReference(tileset) {
5278
5661
  warnings
5279
5662
  });
5280
5663
  }
5664
+ function getDraft3DTilesSpatialReference(tileset) {
5665
+ var _a, _b, _c;
5666
+ const extension = (_a = tileset.extensions) == null ? void 0 : _a.EXT_geospatial_crs;
5667
+ if (!extension) {
5668
+ return void 0;
5669
+ }
5670
+ const warnings = [];
5671
+ let sourceCrs;
5672
+ let verticalCrs;
5673
+ let epoch;
5674
+ let representation;
5675
+ if (extension.format === "wkid") {
5676
+ const wkidExtension = (_b = extension.extensions) == null ? void 0 : _b.EXT_geospatial_crs_wkid;
5677
+ if (typeof (wkidExtension == null ? void 0 : wkidExtension.authority) === "string" && Number.isInteger(wkidExtension.wkid)) {
5678
+ sourceCrs = `${wkidExtension.authority}:${wkidExtension.wkid}`;
5679
+ representation = "identifier";
5680
+ if (Number.isInteger(wkidExtension.vcsWkid)) {
5681
+ verticalCrs = `${wkidExtension.authority}:${wkidExtension.vcsWkid}`;
5682
+ }
5683
+ epoch = parseCoordinateEpoch(wkidExtension.epoch);
5684
+ if (wkidExtension.epoch !== void 0 && epoch === void 0) {
5685
+ warnings.push("EXT_geospatial_crs_wkid epoch is not a finite decimal year");
5686
+ }
5687
+ } else {
5688
+ warnings.push("EXT_geospatial_crs_wkid requires string authority and integer wkid");
5689
+ }
5690
+ } else if (extension.format === "wkt2") {
5691
+ const wktExtension = (_c = extension.extensions) == null ? void 0 : _c.EXT_geospatial_crs_wkt2;
5692
+ if (typeof (wktExtension == null ? void 0 : wktExtension.wkt2) === "string" && wktExtension.wkt2.trim()) {
5693
+ sourceCrs = wktExtension.wkt2;
5694
+ representation = "wkt";
5695
+ } else {
5696
+ warnings.push("EXT_geospatial_crs_wkt2 requires a nonempty wkt2 string");
5697
+ }
5698
+ } else {
5699
+ warnings.push(`Unsupported EXT_geospatial_crs format ${String(extension.format)}`);
5700
+ }
5701
+ const coordinateFrame = representation === "wkt" ? getSpatialCoordinateFrame(sourceCrs) : getKnownCrsIdentifierCoordinateFrame(sourceCrs) || "unknown";
5702
+ const verticalSemantics = getDraftVerticalCrsSemantics(verticalCrs);
5703
+ if (verticalCrs && !verticalSemantics) {
5704
+ warnings.push(`Unsupported vertical CRS ${String(verticalCrs)}; vertical unit and height reference are unknown`);
5705
+ }
5706
+ const verticalUnit = verticalCrs ? verticalSemantics == null ? void 0 : verticalSemantics.unit : "meter";
5707
+ return createTilesetSpatialReference({
5708
+ sourceCrs,
5709
+ sourceCrsState: sourceCrs ? "explicit" : "unknown",
5710
+ sourceCrsRepresentation: representation,
5711
+ verticalCrs,
5712
+ coordinateEpoch: epoch,
5713
+ units: coordinateFrame === "geocentric" ? ["meter", "meter", "meter"] : coordinateFrame === "geographic" && verticalUnit ? ["degree", "degree", verticalUnit] : void 0,
5714
+ verticalUnitScale: verticalCrs ? (verticalSemantics == null ? void 0 : verticalSemantics.verticalUnitScale) ?? Number.NaN : 1,
5715
+ heightReference: verticalCrs ? (verticalSemantics == null ? void 0 : verticalSemantics.heightReference) ?? "unknown" : sourceCrs ? "ellipsoidal" : "unknown",
5716
+ coordinateFrame,
5717
+ axisOrder: sourceCrs ? "xyz" : "unknown",
5718
+ provenance: "metadata",
5719
+ warnings
5720
+ });
5721
+ }
5722
+ function getDraftVerticalCrsSemantics(verticalCrs) {
5723
+ if (typeof verticalCrs !== "string") {
5724
+ return void 0;
5725
+ }
5726
+ const epsgMatch = /^EPSG:(\d+)$/i.exec(verticalCrs);
5727
+ if (!epsgMatch) {
5728
+ return void 0;
5729
+ }
5730
+ switch (Number(epsgMatch[1])) {
5731
+ case 5702:
5732
+ // NGVD29 height (ftUS)
5733
+ case 6360:
5734
+ return {
5735
+ unit: "us-foot",
5736
+ verticalUnitScale: 1200 / 3937,
5737
+ heightReference: "orthometric"
5738
+ };
5739
+ case 5703:
5740
+ return { unit: "meter", verticalUnitScale: 1, heightReference: "orthometric" };
5741
+ default:
5742
+ return void 0;
5743
+ }
5744
+ }
5281
5745
  function parseCoordinateEpoch(value) {
5282
5746
  if (typeof value !== "string" && typeof value !== "number") {
5283
5747
  return void 0;
@@ -5302,41 +5766,6 @@ function getI3SVerticalCrsDefinition(spatialReference) {
5302
5766
  function getCrsIdentifier(spatialReference) {
5303
5767
  return (spatialReference == null ? void 0 : spatialReference.latestWkid) ?? (spatialReference == null ? void 0 : spatialReference.wkid);
5304
5768
  }
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
5769
  function getTilesetSemanticValue(tileset, semantic) {
5341
5770
  var _a, _b, _c, _d;
5342
5771
  const classIdentifier = (_a = tileset.metadata) == null ? void 0 : _a.class;
@@ -5354,39 +5783,568 @@ function getTilesetSemanticValue(tileset, semantic) {
5354
5783
  return void 0;
5355
5784
  }
5356
5785
 
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
- };
5786
+ // dist/spatial/spatial-coordinate-transformer.js
5787
+ var import_core10 = require("@math.gl/core");
5788
+ var import_geospatial7 = require("@math.gl/geospatial");
5789
+ var import_proj42 = require("@math.gl/proj4");
5790
+
5791
+ // dist/spatial/spatial-resource-registry.js
5792
+ var import_geoid = require("@math.gl/geoid");
5793
+ var import_proj4 = require("@math.gl/proj4");
5794
+ var geoidModels = /* @__PURE__ */ new Map();
5795
+ function registerSpatialCrs(name, definition) {
5796
+ validateResourceName(name, "CRS");
5797
+ import_proj4.Projection.defineProjectionAliases({ [name]: definition });
5366
5798
  }
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 };
5799
+ function registerSpatialDatumGrid(name, data) {
5800
+ validateResourceName(name, "datum grid");
5801
+ import_proj4.Projection.registerDatumGrid(name, data);
5376
5802
  }
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()]));
5803
+ function registerGeoidModel(name, geoid) {
5804
+ validateResourceName(name, "geoid model");
5805
+ geoidModels.set(name, geoid);
5385
5806
  }
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;
5807
+ function registerGeoidModelFromPgm(name, data, options = {}) {
5808
+ const geoid = (0, import_geoid.parsePGM)(data, options);
5809
+ registerGeoidModel(name, geoid);
5810
+ return geoid;
5811
+ }
5812
+ function getGeoidModel(name) {
5813
+ return geoidModels.get(name);
5814
+ }
5815
+ function validateResourceName(name, resourceType) {
5816
+ if (!name.trim()) {
5817
+ throw new Error(`${resourceType} name must not be empty`);
5818
+ }
5819
+ }
5820
+
5821
+ // dist/spatial/spatial-coordinate-transformer.js
5822
+ var SpatialCoordinateTransformer = class {
5823
+ /** Normalized spatial metadata describing this transformation. */
5824
+ spatialReference;
5825
+ /** Projection from the source CRS directly to the requested horizontal target CRS. */
5826
+ horizontalProjection;
5827
+ /** Projection from the source CRS to geographic longitude, latitude, and ellipsoidal height. */
5828
+ geographicProjection;
5829
+ /** Projection from adjusted geographic coordinates to the requested output CRS. */
5830
+ heightOutputProjection;
5831
+ /** Whether source coordinates use the WGS84 geocentric frame handled by math.gl. */
5832
+ sourceIsGeocentric;
5833
+ /** Whether output coordinates use the WGS84 geocentric frame handled by math.gl. */
5834
+ outputIsGeocentric;
5835
+ /** Whether source coordinates already use conventional WGS84 longitude/latitude order. */
5836
+ sourceIsGeographic;
5837
+ /** Whether output coordinates use conventional WGS84 longitude/latitude order. */
5838
+ outputIsGeographic;
5839
+ /** Geoid model used to convert between ellipsoidal and orthometric heights. */
5840
+ geoid;
5841
+ /**
5842
+ * Creates a coordinate transformer.
5843
+ *
5844
+ * @param spatialReference - Format-discovered and option-normalized spatial metadata.
5845
+ * @param options - Application options containing an optional geoid model.
5846
+ */
5847
+ constructor(spatialReference, options = {}) {
5848
+ this.spatialReference = spatialReference;
5849
+ validateTransformRequest(spatialReference);
5850
+ const outputCrs = spatialReference.targetCrs || spatialReference.sourceCrs;
5851
+ this.sourceIsGeocentric = isWgs84Geocentric(spatialReference.sourceCrs);
5852
+ this.outputIsGeocentric = isWgs84Geocentric(outputCrs);
5853
+ this.sourceIsGeographic = isWgs84Geographic(spatialReference.sourceCrs);
5854
+ this.outputIsGeographic = isWgs84Geographic(outputCrs);
5855
+ if (spatialReference.sourceCrs && spatialReference.targetCrs && !this.sourceIsGeocentric && !this.outputIsGeocentric) {
5856
+ this.horizontalProjection = new import_proj42.Projection({
5857
+ from: getHorizontalProj4Definition(spatialReference.sourceCrs),
5858
+ to: getHorizontalProj4Definition(spatialReference.targetCrs),
5859
+ enforceAxis: false
5860
+ });
5861
+ }
5862
+ if (requiresHeightTransformation(spatialReference) || this.sourceIsGeocentric || this.outputIsGeocentric) {
5863
+ if (!this.sourceIsGeographic && !this.sourceIsGeocentric) {
5864
+ this.geographicProjection = new import_proj42.Projection({
5865
+ from: getHorizontalProj4Definition(spatialReference.sourceCrs),
5866
+ to: WGS84_GEOGRAPHIC_CRS,
5867
+ enforceAxis: false
5868
+ });
5869
+ }
5870
+ if (!this.outputIsGeographic && !this.outputIsGeocentric) {
5871
+ this.heightOutputProjection = new import_proj42.Projection({
5872
+ from: WGS84_GEOGRAPHIC_CRS,
5873
+ to: getHorizontalProj4Definition(outputCrs),
5874
+ enforceAxis: false
5875
+ });
5876
+ }
5877
+ }
5878
+ if (requiresHeightTransformation(spatialReference)) {
5879
+ this.geoid = resolveGeoid(options.geoidModel);
5880
+ if (!this.geoid) {
5881
+ const modelName = typeof options.geoidModel === "string" ? ` "${options.geoidModel}"` : "";
5882
+ throw new Error(`Height conversion requires a registered geoid model${modelName}; call registerGeoidModel() or pass a parsed Geoid instance`);
5883
+ }
5884
+ }
5885
+ }
5886
+ /**
5887
+ * Transforms one `[x, y, z?]` coordinate while retaining additional components.
5888
+ *
5889
+ * @param coordinate - Source coordinate in the format adapter's normalized `x/y/z` order.
5890
+ * @returns A new transformed coordinate array.
5891
+ */
5892
+ transformPosition(coordinate) {
5893
+ if (coordinate.length < 2) {
5894
+ throw new Error("A spatial coordinate must contain at least x and y components");
5895
+ }
5896
+ const result = [...coordinate];
5897
+ if (requiresHeightTransformation(this.spatialReference) || this.sourceIsGeocentric || this.outputIsGeocentric) {
5898
+ if (result.length < 3 || !Number.isFinite(result[2])) {
5899
+ throw new Error("Three-dimensional CRS conversion requires a finite z coordinate");
5900
+ }
5901
+ const geographic = this.toGeographic(result);
5902
+ if (requiresHeightTransformation(this.spatialReference)) {
5903
+ const geoidUndulation = this.geoid.getHeight(geographic[1], geographic[0]);
5904
+ geographic[2] = transformHeight(geographic[2], geoidUndulation, this.spatialReference.heightReference, this.spatialReference.targetHeightReference);
5905
+ }
5906
+ const projected = this.fromGeographic(geographic);
5907
+ result.splice(0, projected.length, ...projected);
5908
+ return result;
5909
+ }
5910
+ if (this.horizontalProjection) {
5911
+ const projected = this.horizontalProjection.project(result.slice(0, 3));
5912
+ result.splice(0, projected.length, ...projected);
5913
+ }
5914
+ return result;
5915
+ }
5916
+ /** Transform a packed sequence of xyz positions, preserving the input array type only when safe. */
5917
+ transformPositions(positions) {
5918
+ if (positions.length % 3 !== 0) {
5919
+ throw new Error("Packed spatial positions must contain a multiple of three components");
5920
+ }
5921
+ const transformedPositions = new Float64Array(positions.length);
5922
+ for (let index = 0; index < positions.length; index += 3) {
5923
+ const transformed = this.transformPosition([
5924
+ Number(positions[index]),
5925
+ Number(positions[index + 1]),
5926
+ Number(positions[index + 2])
5927
+ ]);
5928
+ transformedPositions[index] = transformed[0];
5929
+ transformedPositions[index + 1] = transformed[1];
5930
+ transformedPositions[index + 2] = transformed[2];
5931
+ }
5932
+ return transformedPositions;
5933
+ }
5934
+ /** Transform packed xyz normals using a local finite-difference tangent approximation. */
5935
+ transformNormals(normals, positions) {
5936
+ if (normals.length !== positions.length || normals.length % 3 !== 0) {
5937
+ throw new Error("Spatial normals and positions must have matching xyz component counts");
5938
+ }
5939
+ const transformedNormals = new Float32Array(normals.length);
5940
+ const epsilon = 1e-5;
5941
+ for (let index = 0; index < normals.length; index += 3) {
5942
+ const position = [
5943
+ Number(positions[index]),
5944
+ Number(positions[index + 1]),
5945
+ Number(positions[index + 2])
5946
+ ];
5947
+ const normal = [
5948
+ Number(normals[index]),
5949
+ Number(normals[index + 1]),
5950
+ Number(normals[index + 2])
5951
+ ];
5952
+ const jacobian = getFiniteDifferenceJacobian(this, position, epsilon);
5953
+ const transformedNormal = inverseTransposeMultiply(jacobian, normal);
5954
+ const length = Math.hypot(...transformedNormal);
5955
+ if (length > 0 && Number.isFinite(length)) {
5956
+ transformedNormals[index] = transformedNormal[0] / length;
5957
+ transformedNormals[index + 1] = transformedNormal[1] / length;
5958
+ transformedNormals[index + 2] = transformedNormal[2] / length;
5959
+ } else {
5960
+ transformedNormals[index] = normal[0];
5961
+ transformedNormals[index + 1] = normal[1];
5962
+ transformedNormals[index + 2] = normal[2];
5963
+ }
5964
+ }
5965
+ return transformedNormals;
5966
+ }
5967
+ /** Convert one source coordinate to conventional WGS84 longitude, latitude, and height. */
5968
+ toGeographic(coordinate) {
5969
+ if (this.sourceIsGeocentric) {
5970
+ return Array.from(import_geospatial7.Ellipsoid.WGS84.cartesianToCartographic(new import_core10.Vector3(coordinate)));
5971
+ }
5972
+ if (this.sourceIsGeographic) {
5973
+ return coordinate.slice(0, 3);
5974
+ }
5975
+ return this.geographicProjection.project(coordinate.slice(0, 3));
5976
+ }
5977
+ /** Convert one conventional WGS84 geographic coordinate to the selected output CRS. */
5978
+ fromGeographic(coordinate) {
5979
+ if (this.outputIsGeocentric) {
5980
+ return Array.from(import_geospatial7.Ellipsoid.WGS84.cartographicToCartesian(new import_core10.Vector3(coordinate)));
5981
+ }
5982
+ if (this.outputIsGeographic) {
5983
+ return coordinate.slice(0, 3);
5984
+ }
5985
+ return this.heightOutputProjection.project(coordinate.slice(0, 3));
5986
+ }
5987
+ };
5988
+ function getFiniteDifferenceJacobian(transformer, position, epsilon) {
5989
+ const columns = [];
5990
+ for (let axis = 0; axis < 3; axis++) {
5991
+ const plus = position.slice();
5992
+ const minus = position.slice();
5993
+ plus[axis] += epsilon;
5994
+ minus[axis] -= epsilon;
5995
+ const plusPosition = transformer.transformPosition(plus);
5996
+ const minusPosition = transformer.transformPosition(minus);
5997
+ columns.push([
5998
+ (plusPosition[0] - minusPosition[0]) / (2 * epsilon),
5999
+ (plusPosition[1] - minusPosition[1]) / (2 * epsilon),
6000
+ (plusPosition[2] - minusPosition[2]) / (2 * epsilon)
6001
+ ]);
6002
+ }
6003
+ return columns;
6004
+ }
6005
+ function inverseTransposeMultiply(jacobianColumns, vector) {
6006
+ const [a, b, c] = jacobianColumns;
6007
+ 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]);
6008
+ if (!Number.isFinite(determinant) || Math.abs(determinant) < 1e-15) {
6009
+ return vector;
6010
+ }
6011
+ const inverseTranspose = [
6012
+ [b[1] * c[2] - b[2] * c[1], a[2] * c[1] - a[1] * c[2], a[1] * b[2] - a[2] * b[1]],
6013
+ [b[2] * c[0] - b[0] * c[2], a[0] * c[2] - a[2] * c[0], a[2] * b[0] - a[0] * b[2]],
6014
+ [b[0] * c[1] - b[1] * c[0], a[1] * c[0] - a[0] * c[1], a[0] * b[1] - a[1] * b[0]]
6015
+ ];
6016
+ return inverseTranspose.map((row) => (row[0] * vector[0] + row[1] * vector[1] + row[2] * vector[2]) / determinant);
6017
+ }
6018
+ function getHorizontalProj4Definition(definition) {
6019
+ if (!definition) {
6020
+ throw new Error("Cannot construct a projection because the CRS is unknown");
6021
+ }
6022
+ if (typeof definition !== "string" && definition.type === "CompoundCRS") {
6023
+ const horizontalComponents = definition.components.filter((component) => component.type !== "VerticalCRS");
6024
+ if (horizontalComponents.length !== 1) {
6025
+ throw new Error("CompoundCRS requires exactly one horizontal component");
6026
+ }
6027
+ return getHorizontalProj4Definition(horizontalComponents[0]);
6028
+ }
6029
+ return definition;
6030
+ }
6031
+ function validateTransformRequest(spatialReference) {
6032
+ if (spatialReference.outputCoordinates === "local-enu") {
6033
+ throw new Error("local-enu output requires a dataset-derived local origin and must be created by a tileset source");
6034
+ }
6035
+ if (spatialReference.outputCoordinates === "target-crs" && !spatialReference.targetCrs) {
6036
+ throw new Error("target-crs output requires targetCrs");
6037
+ }
6038
+ if (!spatialReference.sourceCrs && spatialReference.status === "unresolved") {
6039
+ throw new Error("Cannot transform coordinates because the source CRS is unknown");
6040
+ }
6041
+ if (requiresHeightTransformation(spatialReference) && spatialReference.heightReference === "unknown") {
6042
+ throw new Error("Cannot convert heights because the source height reference is unknown");
6043
+ }
6044
+ if (spatialReference.coordinateFrame === "geocentric" && !isWgs84Geocentric(spatialReference.sourceCrs)) {
6045
+ throw new Error("Only EPSG:4978 geocentric coordinates are supported by the current spatial transformer");
6046
+ }
6047
+ if (spatialReference.status === "unresolved") {
6048
+ throw new Error("The requested spatial output cannot be resolved from the available metadata");
6049
+ }
6050
+ }
6051
+ function isWgs84Geocentric(definition) {
6052
+ return typeof definition === "string" && normalizeCrsIdentifier(definition) === WGS84_GEOCENTRIC_CRS;
6053
+ }
6054
+ function isWgs84Geographic(definition) {
6055
+ if (typeof definition !== "string") {
6056
+ return false;
6057
+ }
6058
+ const identifier = normalizeCrsIdentifier(definition);
6059
+ return identifier === WGS84_GEOGRAPHIC_CRS || identifier === "EPSG:4979" || identifier === "OGC:CRS84";
6060
+ }
6061
+ function requiresHeightTransformation(spatialReference) {
6062
+ const target = spatialReference.targetHeightReference;
6063
+ return target !== "native" && target !== spatialReference.heightReference;
6064
+ }
6065
+ function resolveGeoid(model) {
6066
+ return typeof model === "string" ? getGeoidModel(model) : model;
6067
+ }
6068
+ function transformHeight(height, geoidUndulation, source, target) {
6069
+ if (source === target || target === "native") {
6070
+ return height;
6071
+ }
6072
+ if (source === "orthometric" && target === "ellipsoidal") {
6073
+ return height + geoidUndulation;
6074
+ }
6075
+ if (source === "ellipsoidal" && target === "orthometric") {
6076
+ return height - geoidUndulation;
6077
+ }
6078
+ throw new Error(`Unsupported height conversion from ${source} to ${target}`);
6079
+ }
6080
+
6081
+ // dist/spatial/tiles-3d-spatial-transformer.js
6082
+ var import_core11 = require("@math.gl/core");
6083
+ var import_geospatial8 = require("@math.gl/geospatial");
6084
+ var Tiles3DSpatialTransformer = class {
6085
+ /** Normalized reference describing the completed operation. */
6086
+ spatialReference;
6087
+ coordinateTransformer;
6088
+ geographicCoordinateTransformer;
6089
+ /** Creates a 3D Tiles transformer for a requested target CRS. */
6090
+ constructor(spatialReference, options = {}) {
6091
+ this.coordinateTransformer = new SpatialCoordinateTransformer(spatialReference, options);
6092
+ const geographicSpatialReference = {
6093
+ ...spatialReference,
6094
+ sourceCrs: "EPSG:4326",
6095
+ targetCrs: spatialReference.targetCrs || spatialReference.sourceCrs,
6096
+ coordinateFrame: "geographic",
6097
+ axisOrder: "xy"
6098
+ };
6099
+ this.geographicCoordinateTransformer = new SpatialCoordinateTransformer(geographicSpatialReference, options);
6100
+ this.spatialReference = spatialReference;
6101
+ }
6102
+ /** Transform packed POSITION values into the target CRS. */
6103
+ transformPositions(positions) {
6104
+ return this.coordinateTransformer.transformPositions(positions);
6105
+ }
6106
+ /** Transform packed NORMAL values using the corresponding source positions. */
6107
+ transformNormals(normals, positions) {
6108
+ return this.coordinateTransformer.transformNormals(normals, positions);
6109
+ }
6110
+ /** Rebuild a conservative target-frame volume from a source volume. */
6111
+ transformBoundingVolume(volume, sourceTransform) {
6112
+ const transform = sourceTransform ? new import_core11.Matrix4(Array.from(sourceTransform)) : void 0;
6113
+ const samples = volume.region ? getRegionSamples(volume.region) : getVolumeSamples(volume).map((sample) => transform ? Array.from(transform.transformAsPoint(sample)) : sample);
6114
+ if (!samples.length) {
6115
+ return volume;
6116
+ }
6117
+ const transformed = volume.region ? samples.map((sample) => this.transformRegionSample(sample)) : samples.map((sample) => this.coordinateTransformer.transformPosition(sample));
6118
+ return { box: createAxisAlignedBox(transformed) };
6119
+ }
6120
+ /** Transforms one WGS84 region sample into the selected output frame. */
6121
+ transformRegionSample(sample) {
6122
+ const [longitudeDegrees, latitudeDegrees, height] = sample;
6123
+ const targetCrs = this.spatialReference.targetCrs || this.spatialReference.sourceCrs;
6124
+ if (targetCrs && getSpatialCoordinateFrame(targetCrs) === "geocentric") {
6125
+ const cartesian = import_geospatial8.Ellipsoid.WGS84.cartographicToCartesian([
6126
+ longitudeDegrees,
6127
+ latitudeDegrees,
6128
+ height
6129
+ ]);
6130
+ return Array.from(cartesian);
6131
+ }
6132
+ return this.geographicCoordinateTransformer.transformPosition(sample);
6133
+ }
6134
+ };
6135
+ function getRegionSamples(region) {
6136
+ if (region.length < 6 || region.some((value) => !Number.isFinite(value))) {
6137
+ return [];
6138
+ }
6139
+ const [west, south, east, north, minimumHeight, maximumHeight] = region;
6140
+ const longitudeEnd = east < west ? east + Math.PI * 2 : east;
6141
+ const longitudes = getIntervalSamples(west, longitudeEnd);
6142
+ const latitudes = getIntervalSamples(south, north);
6143
+ const samples = [];
6144
+ for (const longitude of longitudes) {
6145
+ for (const latitude of latitudes) {
6146
+ for (const height of [minimumHeight, maximumHeight]) {
6147
+ samples.push([longitude * 180 / Math.PI, latitude * 180 / Math.PI, height]);
6148
+ }
6149
+ }
6150
+ }
6151
+ return samples;
6152
+ }
6153
+ function getIntervalSamples(start, end) {
6154
+ const samples = [start, (start + end) / 2, end];
6155
+ const angularStep = Math.PI / 2;
6156
+ const firstCriticalIndex = Math.ceil(start / angularStep);
6157
+ const lastCriticalIndex = Math.floor(end / angularStep);
6158
+ for (let criticalIndex = firstCriticalIndex; criticalIndex <= lastCriticalIndex; criticalIndex++) {
6159
+ samples.push(criticalIndex * angularStep);
6160
+ }
6161
+ return samples;
6162
+ }
6163
+ function getVolumeSamples(volume) {
6164
+ if (volume.box && volume.box.length >= 12) {
6165
+ const center = volume.box.slice(0, 3);
6166
+ const axes = [volume.box.slice(3, 6), volume.box.slice(6, 9), volume.box.slice(9, 12)];
6167
+ const samples = [];
6168
+ for (let firstIndex = 0; firstIndex <= 4; firstIndex++) {
6169
+ for (let secondIndex = 0; secondIndex <= 4; secondIndex++) {
6170
+ for (let thirdIndex = 0; thirdIndex <= 4; thirdIndex++) {
6171
+ const first = firstIndex / 2 - 1;
6172
+ const second = secondIndex / 2 - 1;
6173
+ const third = thirdIndex / 2 - 1;
6174
+ samples.push([
6175
+ center[0] + first * axes[0][0] + second * axes[1][0] + third * axes[2][0],
6176
+ center[1] + first * axes[0][1] + second * axes[1][1] + third * axes[2][1],
6177
+ center[2] + first * axes[0][2] + second * axes[1][2] + third * axes[2][2]
6178
+ ]);
6179
+ }
6180
+ }
6181
+ }
6182
+ return samples;
6183
+ }
6184
+ if (volume.sphere && volume.sphere.length >= 4) {
6185
+ const [x, y, z, radius] = volume.sphere;
6186
+ const samples = [
6187
+ [x - radius, y, z],
6188
+ [x + radius, y, z],
6189
+ [x, y - radius, z],
6190
+ [x, y + radius, z],
6191
+ [x, y, z - radius],
6192
+ [x, y, z + radius]
6193
+ ];
6194
+ for (let longitudeIndex = 0; longitudeIndex < 16; longitudeIndex++) {
6195
+ const longitude = longitudeIndex * Math.PI * 2 / 16;
6196
+ samples.push([x + radius * Math.cos(longitude), y + radius * Math.sin(longitude), z]);
6197
+ }
6198
+ return samples;
6199
+ }
6200
+ return [];
6201
+ }
6202
+ function createAxisAlignedBox(samples) {
6203
+ const minimum = [Infinity, Infinity, Infinity];
6204
+ const maximum = [-Infinity, -Infinity, -Infinity];
6205
+ for (const sample of samples) {
6206
+ for (let axis = 0; axis < 3; axis++) {
6207
+ minimum[axis] = Math.min(minimum[axis], sample[axis]);
6208
+ maximum[axis] = Math.max(maximum[axis], sample[axis]);
6209
+ }
6210
+ }
6211
+ const padding = [
6212
+ Math.max((maximum[0] - minimum[0]) * 0.01, 1e-9),
6213
+ Math.max((maximum[1] - minimum[1]) * 0.01, 1e-9),
6214
+ Math.max((maximum[2] - minimum[2]) * 0.01, 1e-9)
6215
+ ];
6216
+ const center = [
6217
+ (minimum[0] + maximum[0]) / 2,
6218
+ (minimum[1] + maximum[1]) / 2,
6219
+ (minimum[2] + maximum[2]) / 2
6220
+ ];
6221
+ return [
6222
+ ...center,
6223
+ (maximum[0] - minimum[0]) / 2 + padding[0],
6224
+ 0,
6225
+ 0,
6226
+ 0,
6227
+ (maximum[1] - minimum[1]) / 2 + padding[1],
6228
+ 0,
6229
+ 0,
6230
+ 0,
6231
+ (maximum[2] - minimum[2]) / 2 + padding[2]
6232
+ ];
6233
+ }
6234
+
6235
+ // dist/tileset-3d/format-3d-tiles/implicit-tiling.js
6236
+ var import_s22 = require("@math.gl/dggs/s2");
6237
+
6238
+ // dist/tileset-3d/helpers/s2-bounding-volume.js
6239
+ var import_s2 = require("@math.gl/dggs/s2");
6240
+ var import_geospatial9 = require("@math.gl/geospatial");
6241
+ function convertS2BoundingVolumeToOBB(s2VolumeInfo) {
6242
+ const [[west, south], [east, north]] = (0, import_s2.getS2Bounds)(s2VolumeInfo.token);
6243
+ const orientedBoundingBox = (0, import_geospatial9.makeOBBFromRegion)([west, south, east, north, s2VolumeInfo.minimumHeight, s2VolumeInfo.maximumHeight], import_geospatial9.Ellipsoid.WGS84, { units: "degrees" });
6244
+ return [...orientedBoundingBox.center, ...orientedBoundingBox.halfAxes];
6245
+ }
6246
+
6247
+ // dist/tileset-3d/format-3d-tiles/implicit-tiling.js
6248
+ var QUADTREE_CHILD_COUNT = 4;
6249
+ var OCTREE_CHILD_COUNT = 8;
6250
+ var AVAILABILITY_RANK_CACHE = /* @__PURE__ */ new WeakMap();
6251
+ function createImplicitSubtreeReference(descriptor, coordinates) {
6252
+ return {
6253
+ coordinates,
6254
+ subtreeUrl: replaceImplicitUrlTemplate(descriptor.subtreesUrlTemplate, coordinates),
6255
+ descriptor,
6256
+ resource: findImplicitPackageResource(descriptor.resourceFiles, replaceImplicitUrlTemplate(descriptor.subtreesUrlTemplate, coordinates))
6257
+ };
6258
+ }
6259
+ function materializeImplicitSubtree(subtree, reference) {
6260
+ const { descriptor } = reference;
6261
+ validateImplicitDescriptor(descriptor);
6262
+ validateImplicitSubtree(subtree, descriptor);
6263
+ const counters = { tileCount: 0, childSubtreeCount: 0 };
6264
+ const root = materializeAvailableTile(subtree, reference, { localLevel: 0, mortonIndex: 0 }, reference.coordinates, counters);
6265
+ if (!root) {
6266
+ throw new Error(`Invalid 3D Tiles subtree: root tile is unavailable at ${reference.subtreeUrl}`);
6267
+ }
6268
+ return { root, ...counters };
6269
+ }
6270
+ function validateImplicitSubtree(subtree, descriptor) {
6271
+ const childCount = getImplicitChildCount(descriptor.subdivisionScheme);
6272
+ const tileElementCount = (childCount ** descriptor.subtreeLevels - 1) / (childCount - 1);
6273
+ const childSubtreeElementCount = childCount ** descriptor.subtreeLevels;
6274
+ validateAvailabilityLength(subtree.tileAvailability, tileElementCount, "tile");
6275
+ const contentAvailabilityStreams = Array.isArray(subtree.contentAvailability) ? subtree.contentAvailability : subtree.contentAvailability ? [subtree.contentAvailability] : [];
6276
+ for (const contentAvailability of contentAvailabilityStreams) {
6277
+ validateAvailabilityLength(contentAvailability, tileElementCount, "content");
6278
+ }
6279
+ validateAvailabilityLength(subtree.childSubtreeAvailability, childSubtreeElementCount, "child subtree");
6280
+ const availableTileCount = countAvailableElements(subtree.tileAvailability, tileElementCount);
6281
+ const availableContentCount = contentAvailabilityStreams[0] ? countAvailableElements(contentAvailabilityStreams[0], tileElementCount) : 0;
6282
+ validateAttributeLengths(subtree.tileAttributes, availableTileCount, "tile");
6283
+ validateAttributeLengths(subtree.contentAttributes, availableContentCount, "content");
6284
+ if (subtree.tilePropertyRows && subtree.tilePropertyRows.length !== availableTileCount) {
6285
+ throw new Error("3DTILES_subtree: tile property-table count does not match availability");
6286
+ }
6287
+ if (subtree.contentPropertyRows && subtree.contentPropertyRows.length !== availableContentCount) {
6288
+ throw new Error("3DTILES_subtree: content property-table count does not match availability");
6289
+ }
6290
+ }
6291
+ function validateAvailabilityLength(availability, elementCount, label) {
6292
+ if (availability.constant === 0 || availability.constant === 1) {
6293
+ return;
6294
+ }
6295
+ if (!availability.explicitBitstream || availability.explicitBitstream.byteLength * 8 < elementCount) {
6296
+ throw new Error(`3DTILES_subtree: ${label} availability bitstream is too short`);
6297
+ }
6298
+ }
6299
+ function countAvailableElements(availability, elementCount) {
6300
+ if (availability.constant === 1) {
6301
+ return elementCount;
6302
+ }
6303
+ if (availability.constant === 0) {
6304
+ return 0;
6305
+ }
6306
+ let availableCount = 0;
6307
+ for (let index = 0; index < elementCount; index++) {
6308
+ if (getAvailabilityValue(availability, index)) {
6309
+ availableCount++;
6310
+ }
6311
+ }
6312
+ return availableCount;
6313
+ }
6314
+ function validateAttributeLengths(attributes, availableCount, label) {
6315
+ const componentCounts = {
6316
+ TILE_BOUNDING_BOX: 16,
6317
+ TILE_BOUNDING_SPHERE: 4,
6318
+ TILE_GEOMETRIC_ERROR: 1,
6319
+ TILE_REFINE: 1,
6320
+ TILE_TRANSFORM: 16,
6321
+ CONTENT_BOUNDING_BOX: 16,
6322
+ CONTENT_BOUNDING_SPHERE: 4
6323
+ };
6324
+ for (const [semantic, values] of Object.entries(attributes || {})) {
6325
+ const componentCount = componentCounts[semantic];
6326
+ if (componentCount && values.length !== availableCount * componentCount) {
6327
+ throw new Error(`3DTILES_subtree: ${label} attribute ${semantic} count does not match availability`);
6328
+ }
6329
+ }
6330
+ }
6331
+ function replaceImplicitUrlTemplate(templateUrl, coordinates, properties = {}) {
6332
+ const values = {
6333
+ level: coordinates.level,
6334
+ x: coordinates.x,
6335
+ y: coordinates.y,
6336
+ z: coordinates.z,
6337
+ ...properties
6338
+ };
6339
+ return templateUrl.replace(/{([^{}]+)}/g, (match, propertyName) => {
6340
+ const value = Object.hasOwn(values, propertyName) ? values[propertyName] : values[propertyName.toLowerCase()];
6341
+ return value === void 0 ? match : String(value);
6342
+ });
6343
+ }
6344
+ function materializeAvailableTile(subtree, reference, localCoordinates, globalCoordinates, counters) {
6345
+ const { descriptor } = reference;
6346
+ const childCount = getImplicitChildCount(descriptor.subdivisionScheme);
6347
+ const availabilityIndex = getLevelOffset(childCount, localCoordinates.localLevel) + localCoordinates.mortonIndex;
5390
6348
  if (!getAvailabilityValue(subtree.tileAvailability, availabilityIndex)) {
5391
6349
  return null;
5392
6350
  }
@@ -5416,10 +6374,12 @@ function materializeAvailableTile(subtree, reference, localCoordinates, globalCo
5416
6374
  }
5417
6375
  }
5418
6376
  }
5419
- return formatImplicitTileHeader(subtree, descriptor, reference, globalCoordinates, contentAvailability, children);
6377
+ return formatImplicitTileHeader(subtree, descriptor, reference, globalCoordinates, availabilityIndex, contentAvailability, children);
5420
6378
  }
5421
6379
  function createLazyImplicitTileHeader(subtree, descriptor, coordinates) {
5422
- const reference = createImplicitSubtreeReference(descriptor, coordinates);
6380
+ const resourceFiles = mergeImplicitPackageFiles(descriptor.resourceFiles, subtree.resourceFiles);
6381
+ const childDescriptor = resourceFiles ? { ...descriptor, resourceFiles } : descriptor;
6382
+ const reference = createImplicitSubtreeReference(childDescriptor, coordinates);
5423
6383
  return {
5424
6384
  id: getImplicitTileId(reference, coordinates),
5425
6385
  children: [],
@@ -5430,55 +6390,187 @@ function createLazyImplicitTileHeader(subtree, descriptor, coordinates) {
5430
6390
  lodMetricValue: descriptor.rootLodMetricValue / 2 ** coordinates.level,
5431
6391
  refine: descriptor.refine,
5432
6392
  type: TILE_TYPE.EMPTY,
5433
- implicitMetadata: getImplicitSubtreeMetadata(subtree)
6393
+ implicitMetadata: getImplicitSubtreeMetadata(subtree),
6394
+ _scaleGeometricError: descriptor.scaleGeometricError !== false
6395
+ };
6396
+ }
6397
+ function getSubtreeAttributeOverrides(attributes, rowIndex, scope) {
6398
+ var _a, _b;
6399
+ if (!attributes || rowIndex < 0) {
6400
+ return {};
6401
+ }
6402
+ const box = getAttributeComponents(attributes[scope === "tile" ? "TILE_BOUNDING_BOX" : "CONTENT_BOUNDING_BOX"], rowIndex, 16);
6403
+ const sphere = getAttributeComponents(attributes[scope === "tile" ? "TILE_BOUNDING_SPHERE" : "CONTENT_BOUNDING_SPHERE"], rowIndex, 4);
6404
+ const boundingVolume = box ? {
6405
+ box: [
6406
+ box[12],
6407
+ box[13],
6408
+ box[14],
6409
+ box[0] / 2,
6410
+ box[1] / 2,
6411
+ box[2] / 2,
6412
+ box[4] / 2,
6413
+ box[5] / 2,
6414
+ box[6] / 2,
6415
+ box[8] / 2,
6416
+ box[9] / 2,
6417
+ box[10] / 2
6418
+ ]
6419
+ } : sphere ? { sphere } : void 0;
6420
+ if (scope === "content") {
6421
+ return { boundingVolume };
6422
+ }
6423
+ const geometricError = (_a = getAttributeComponents(attributes.TILE_GEOMETRIC_ERROR, rowIndex, 1)) == null ? void 0 : _a[0];
6424
+ if (geometricError !== void 0 && (!Number.isFinite(geometricError) || geometricError < 0)) {
6425
+ throw new Error("3DTILES_subtree: TILE_GEOMETRIC_ERROR must be nonnegative");
6426
+ }
6427
+ const refineValue = (_b = getAttributeComponents(attributes.TILE_REFINE, rowIndex, 1)) == null ? void 0 : _b[0];
6428
+ if (refineValue !== void 0 && refineValue !== 0 && refineValue !== 1) {
6429
+ throw new Error("3DTILES_subtree: TILE_REFINE must be 0 or 1");
6430
+ }
6431
+ return {
6432
+ boundingVolume,
6433
+ geometricError,
6434
+ refine: refineValue === void 0 ? void 0 : refineValue === 0 ? TILE_REFINEMENT.ADD : TILE_REFINEMENT.REPLACE,
6435
+ transform: getAttributeComponents(attributes.TILE_TRANSFORM, rowIndex, 16)
5434
6436
  };
5435
6437
  }
5436
- function formatImplicitTileHeader(subtree, descriptor, reference, coordinates, contentAvailability, children) {
6438
+ function getAttributeComponents(values, rowIndex, componentCount) {
6439
+ if (!values) {
6440
+ return void 0;
6441
+ }
6442
+ const offset = rowIndex * componentCount;
6443
+ if (offset + componentCount > values.length) {
6444
+ throw new Error("3DTILES_subtree: attribute accessor is shorter than availability");
6445
+ }
6446
+ return Array.from({ length: componentCount }, (_unused, componentIndex) => Number(values[offset + componentIndex]));
6447
+ }
6448
+ function getAvailabilityRank(availability, index) {
5437
6449
  var _a;
5438
- const contentUrlTemplates = ((_a = descriptor.contentUrlTemplates) == null ? void 0 : _a.length) ? descriptor.contentUrlTemplates : [descriptor.contentUrlTemplate];
6450
+ if (availability.constant === 1) {
6451
+ return index;
6452
+ }
6453
+ if (availability.constant === 0) {
6454
+ return 0;
6455
+ }
6456
+ let ranks = AVAILABILITY_RANK_CACHE.get(availability);
6457
+ if (!ranks) {
6458
+ const bitCount = (((_a = availability.explicitBitstream) == null ? void 0 : _a.byteLength) || 0) * 8;
6459
+ ranks = new Uint32Array(bitCount + 1);
6460
+ for (let candidateIndex = 0; candidateIndex < bitCount; candidateIndex++) {
6461
+ ranks[candidateIndex + 1] = ranks[candidateIndex] + Number(getAvailabilityValue(availability, candidateIndex));
6462
+ }
6463
+ AVAILABILITY_RANK_CACHE.set(availability, ranks);
6464
+ }
6465
+ return ranks[index];
6466
+ }
6467
+ function findImplicitPackageResource(files, resourceUrl) {
6468
+ var _a;
6469
+ if (!(files == null ? void 0 : files.length)) {
6470
+ return void 0;
6471
+ }
6472
+ const urlWithoutQuery = resourceUrl.split("?")[0];
6473
+ const packageRelativePath = (_a = /^gltf-package:\/\/[^/]+\/(.*)$/.exec(urlWithoutQuery)) == null ? void 0 : _a[1];
6474
+ const exactFileIndex = files.findIndex((file) => {
6475
+ const reference = file.originalUri || file.name;
6476
+ return file.uri === urlWithoutQuery || Boolean(reference && (urlWithoutQuery === reference || packageRelativePath === reference));
6477
+ });
6478
+ if (exactFileIndex >= 0) {
6479
+ return { fileIndex: exactFileIndex, files };
6480
+ }
6481
+ const fileIndex = files.findIndex((file) => {
6482
+ const reference = file.originalUri || file.name;
6483
+ return Boolean(reference && (urlWithoutQuery.endsWith(`/${reference}`) || urlWithoutQuery.endsWith(`:${reference}`)));
6484
+ });
6485
+ return fileIndex < 0 ? void 0 : { fileIndex, files };
6486
+ }
6487
+ function mergeImplicitPackageFiles(parentFiles, childFiles) {
6488
+ if (!(childFiles == null ? void 0 : childFiles.length)) {
6489
+ return parentFiles;
6490
+ }
6491
+ if (!(parentFiles == null ? void 0 : parentFiles.length)) {
6492
+ return childFiles;
6493
+ }
6494
+ const childReferences = new Set(childFiles.flatMap((file) => [file.uri, file.originalUri, file.name].filter(Boolean)));
6495
+ return [
6496
+ ...childFiles,
6497
+ ...parentFiles.filter((file) => ![file.uri, file.originalUri, file.name].some((reference) => reference && childReferences.has(reference)))
6498
+ ];
6499
+ }
6500
+ function formatImplicitTileHeader(subtree, descriptor, reference, coordinates, availabilityIndex, contentAvailability, children) {
6501
+ var _a, _b, _c, _d, _e;
6502
+ const tilePropertyIndex = getAvailabilityRank(subtree.tileAvailability, availabilityIndex);
6503
+ const tileProperties = (_a = subtree.tilePropertyRows) == null ? void 0 : _a[tilePropertyIndex];
6504
+ const primaryContentAvailability = Array.isArray(subtree.contentAvailability) ? subtree.contentAvailability[0] : subtree.contentAvailability;
6505
+ const contentPropertyIndex = primaryContentAvailability && getAvailabilityValue(primaryContentAvailability, availabilityIndex) ? getAvailabilityRank(primaryContentAvailability, availabilityIndex) : -1;
6506
+ const contentProperties = (_b = subtree.contentPropertyRows) == null ? void 0 : _b[contentPropertyIndex];
6507
+ const tileTemplateProperties = ((_c = subtree.tileTemplatePropertyRows) == null ? void 0 : _c[tilePropertyIndex]) || tileProperties;
6508
+ const contentTemplateProperties = ((_d = subtree.contentTemplatePropertyRows) == null ? void 0 : _d[contentPropertyIndex]) || contentProperties;
6509
+ const templateProperties = { ...tileTemplateProperties, ...contentTemplateProperties };
6510
+ const tileAttributes = getSubtreeAttributeOverrides(subtree.tileAttributes, tilePropertyIndex, "tile");
6511
+ const contentAttributes = getSubtreeAttributeOverrides(subtree.contentAttributes, contentPropertyIndex, "content");
6512
+ const contentUrlTemplates = ((_e = descriptor.contentUrlTemplates) == null ? void 0 : _e.length) ? descriptor.contentUrlTemplates : [descriptor.contentUrlTemplate];
5439
6513
  const contentEntries = contentUrlTemplates.map((templateUrl, contentIndex) => {
5440
6514
  if (!contentAvailability[contentIndex] || !templateUrl) {
5441
6515
  return null;
5442
6516
  }
6517
+ const contentUrl2 = replaceImplicitUrlTemplate(templateUrl, coordinates, templateProperties);
6518
+ if (/{[^{}]+}/.test(contentUrl2)) {
6519
+ throw new Error(`Implicit content URI has unresolved template values: ${contentUrl2}`);
6520
+ }
5443
6521
  return {
5444
6522
  contentIndex,
5445
- contentUrl: replaceImplicitUrlTemplate(templateUrl, coordinates)
6523
+ contentUrl: contentUrl2
5446
6524
  };
5447
6525
  }).filter((entry) => Boolean(entry));
5448
6526
  const availableContentUrls = contentEntries.map((entry) => entry.contentUrl);
6527
+ const resourceFiles = mergeImplicitPackageFiles(descriptor.resourceFiles, subtree.resourceFiles);
5449
6528
  const content = contentEntries.map(({ contentIndex, contentUrl: contentUrl2 }) => {
5450
6529
  var _a2;
6530
+ const packageResource = findImplicitPackageResource(resourceFiles, contentUrl2);
5451
6531
  return {
5452
6532
  ...((_a2 = descriptor.contentHeaders) == null ? void 0 : _a2[contentIndex]) || (contentIndex === 0 ? descriptor.contentHeader : {}) || {},
5453
- uri: contentUrl2
6533
+ ...contentAttributes,
6534
+ uri: contentUrl2,
6535
+ ...contentProperties ? { metadata: { properties: contentProperties } } : {},
6536
+ ...packageResource ? { _resource: packageResource } : {}
5454
6537
  };
5455
6538
  });
5456
6539
  const contentUrl = availableContentUrls[0];
5457
- const lodMetricValue = descriptor.rootLodMetricValue / 2 ** coordinates.level;
6540
+ const computedLodMetricValue = descriptor.rootLodMetricValue / 2 ** coordinates.level;
6541
+ const lodMetricValue = tileAttributes.geometricError ?? computedLodMetricValue;
6542
+ const computedBoundingVolume = calculateImplicitBoundingVolume(descriptor.rootBoundingVolume, coordinates, descriptor.subdivisionScheme);
5458
6543
  return {
5459
6544
  id: getImplicitTileId(reference, coordinates),
5460
6545
  children,
5461
6546
  contentUrl,
5462
6547
  content: content.length > 1 ? content : content[0],
6548
+ contents: descriptor.useCanonicalContents && content.length ? content : void 0,
5463
6549
  contentUrls: availableContentUrls,
5464
- refine: descriptor.refine,
6550
+ refine: tileAttributes.refine || descriptor.refine,
5465
6551
  type: getImplicitTileType(contentUrl),
5466
6552
  lodMetricType: descriptor.lodMetricType,
5467
6553
  lodMetricValue,
5468
6554
  geometricError: lodMetricValue,
5469
- boundingVolume: calculateImplicitBoundingVolume(descriptor.rootBoundingVolume, coordinates, descriptor.subdivisionScheme),
5470
- implicitMetadata: getImplicitSubtreeMetadata(subtree)
6555
+ boundingVolume: tileAttributes.boundingVolume || computedBoundingVolume,
6556
+ transform: tileAttributes.transform,
6557
+ transformMatrix: tileAttributes.transform,
6558
+ metadata: tileProperties ? { properties: tileProperties } : void 0,
6559
+ implicitMetadata: getImplicitSubtreeMetadata(subtree, tileProperties, contentProperties),
6560
+ _scaleGeometricError: descriptor.scaleGeometricError !== false
5471
6561
  };
5472
6562
  }
5473
- function getImplicitSubtreeMetadata(subtree) {
5474
- if (!subtree.propertyTables && subtree.tileMetadata === void 0 && !subtree.contentMetadata && subtree.subtreeMetadata === void 0) {
6563
+ function getImplicitSubtreeMetadata(subtree, tileProperties, contentProperties) {
6564
+ if (!subtree.propertyTables && subtree.tileMetadata === void 0 && !subtree.contentMetadata && subtree.subtreeMetadata === void 0 && !tileProperties && !contentProperties) {
5475
6565
  return void 0;
5476
6566
  }
5477
6567
  return {
5478
6568
  propertyTables: subtree.propertyTables,
5479
6569
  tileMetadata: subtree.tileMetadata,
5480
6570
  contentMetadata: subtree.contentMetadata,
5481
- subtreeMetadata: subtree.subtreeMetadata
6571
+ subtreeMetadata: subtree.subtreeMetadata,
6572
+ tileProperties,
6573
+ contentProperties
5482
6574
  };
5483
6575
  }
5484
6576
  function getImplicitTileId(reference, coordinates) {
@@ -5515,12 +6607,15 @@ function getImplicitChildCount(subdivisionScheme) {
5515
6607
  return subdivisionScheme === "OCTREE" ? OCTREE_CHILD_COUNT : QUADTREE_CHILD_COUNT;
5516
6608
  }
5517
6609
  function calculateImplicitBoundingVolume(rootBoundingVolume, coordinates, subdivisionScheme) {
6610
+ var _a;
5518
6611
  if (rootBoundingVolume.region) {
5519
6612
  const [west, south, east, north, minimumHeight, maximumHeight] = rootBoundingVolume.region;
5520
6613
  const divisionCount = 2 ** coordinates.level;
5521
- const longitudeSize = (east - west) / divisionCount;
6614
+ const longitudeSpan = east >= west ? east - west : east + 2 * Math.PI - west;
6615
+ const longitudeSize = longitudeSpan / divisionCount;
5522
6616
  const latitudeSize = (north - south) / divisionCount;
5523
- const childWest = west + longitudeSize * coordinates.x;
6617
+ const childWest = normalizeRegionLongitude(west + longitudeSize * coordinates.x);
6618
+ const childEast = normalizeRegionLongitude(west + longitudeSize * (coordinates.x + 1));
5524
6619
  const childSouth = south + latitudeSize * coordinates.y;
5525
6620
  let childMinimumHeight = minimumHeight;
5526
6621
  let childMaximumHeight = maximumHeight;
@@ -5533,7 +6628,7 @@ function calculateImplicitBoundingVolume(rootBoundingVolume, coordinates, subdiv
5533
6628
  region: [
5534
6629
  childWest,
5535
6630
  childSouth,
5536
- childWest + longitudeSize,
6631
+ childEast,
5537
6632
  childSouth + latitudeSize,
5538
6633
  childMinimumHeight,
5539
6634
  childMaximumHeight
@@ -5542,7 +6637,13 @@ function calculateImplicitBoundingVolume(rootBoundingVolume, coordinates, subdiv
5542
6637
  }
5543
6638
  if (rootBoundingVolume.box) {
5544
6639
  if (rootBoundingVolume.s2VolumeInfo) {
5545
- return { box: [...rootBoundingVolume.box], s2VolumeInfo: rootBoundingVolume.s2VolumeInfo };
6640
+ return calculateS2ImplicitBoundingVolume(rootBoundingVolume, coordinates, subdivisionScheme);
6641
+ }
6642
+ if ((_a = rootBoundingVolume.extensions) == null ? void 0 : _a["3DTILES_shape_cylinder_region"]) {
6643
+ return {
6644
+ ...rootBoundingVolume,
6645
+ box: [...rootBoundingVolume.box]
6646
+ };
5546
6647
  }
5547
6648
  const box = rootBoundingVolume.box;
5548
6649
  const divisionCount = 2 ** coordinates.level;
@@ -5572,6 +6673,41 @@ function calculateImplicitBoundingVolume(rootBoundingVolume, coordinates, subdiv
5572
6673
  }
5573
6674
  throw new Error(`Unsupported implicit 3D Tiles bounding volume: ${JSON.stringify(rootBoundingVolume)}`);
5574
6675
  }
6676
+ function calculateS2ImplicitBoundingVolume(rootBoundingVolume, coordinates, subdivisionScheme) {
6677
+ const rootS2VolumeInfo = rootBoundingVolume.s2VolumeInfo;
6678
+ const rootIndex = (0, import_s22.getS2IndexFromToken)(rootS2VolumeInfo.token);
6679
+ const descendantIndex = (0, import_s22.getS2DescendantIndex)(rootIndex, coordinates.level, coordinates.x, coordinates.y);
6680
+ const divisionCount = 2 ** coordinates.level;
6681
+ const heightSize = (rootS2VolumeInfo.maximumHeight - rootS2VolumeInfo.minimumHeight) / divisionCount;
6682
+ const childS2VolumeInfo = {
6683
+ token: (0, import_s22.getS2TokenFromIndex)(descendantIndex),
6684
+ minimumHeight: rootS2VolumeInfo.minimumHeight + (subdivisionScheme === "OCTREE" ? heightSize * coordinates.z : 0),
6685
+ maximumHeight: rootS2VolumeInfo.minimumHeight + (subdivisionScheme === "OCTREE" ? heightSize * (coordinates.z + 1) : heightSize * divisionCount)
6686
+ };
6687
+ const extensions = rootBoundingVolume.extensions ? { ...rootBoundingVolume.extensions } : void 0;
6688
+ if (extensions == null ? void 0 : extensions["3DTILES_bounding_volume_S2"]) {
6689
+ extensions["3DTILES_bounding_volume_S2"] = childS2VolumeInfo;
6690
+ }
6691
+ if (extensions == null ? void 0 : extensions["3DTILES_shape_s2"]) {
6692
+ extensions["3DTILES_shape_s2"] = childS2VolumeInfo;
6693
+ }
6694
+ return {
6695
+ ...rootBoundingVolume,
6696
+ ...extensions ? { extensions } : {},
6697
+ s2VolumeInfo: childS2VolumeInfo,
6698
+ box: convertS2BoundingVolumeToOBB(childS2VolumeInfo)
6699
+ };
6700
+ }
6701
+ function normalizeRegionLongitude(longitude) {
6702
+ let normalizedLongitude = longitude;
6703
+ while (normalizedLongitude > Math.PI) {
6704
+ normalizedLongitude -= 2 * Math.PI;
6705
+ }
6706
+ while (normalizedLongitude < -Math.PI) {
6707
+ normalizedLongitude += 2 * Math.PI;
6708
+ }
6709
+ return normalizedLongitude;
6710
+ }
5575
6711
  function getImplicitTileType(contentUrl) {
5576
6712
  var _a;
5577
6713
  if (!contentUrl) {
@@ -5640,6 +6776,12 @@ var Tiles3DSource = class {
5640
6776
  queryParams = {};
5641
6777
  /** Final request URLs cached by unmodified tile content URL. */
5642
6778
  tileUrlCache = /* @__PURE__ */ new Map();
6779
+ /** Original content descriptors retained across {@link Tile3D.unloadContent} calls. */
6780
+ tileContentHeaders = /* @__PURE__ */ new WeakMap();
6781
+ /** Source-local namespaces assigned to distinct embedded glTF package file collections. */
6782
+ packageNamespaces = /* @__PURE__ */ new WeakMap();
6783
+ /** Next source-local package namespace. */
6784
+ nextPackageNamespace = 0;
5643
6785
  /** Parsed subtree requests keyed by final source URL for deduplication and LRU reuse. */
5644
6786
  implicitSubtreeCache;
5645
6787
  /** Mutable counters exposed as a defensive snapshot through {@link getImplicitTilingStats}. */
@@ -5654,6 +6796,10 @@ var Tiles3DSource = class {
5654
6796
  extensionsUsed = [];
5655
6797
  resolver;
5656
6798
  rootTileset;
6799
+ /** CRS transformer shared by this source's headers and decoded glTF content. */
6800
+ spatialTransformer;
6801
+ /** Root header after composing source-frame transforms and rebuilding target bounds. */
6802
+ preparedRootHeader;
5657
6803
  /**
5658
6804
  * Creates a 3D Tiles source.
5659
6805
  * @param input Root metadata request, or preloaded root metadata for tests and internal callers.
@@ -5711,8 +6857,8 @@ var Tiles3DSource = class {
5711
6857
  if (!this.asset) {
5712
6858
  throw new Error("Tileset must have an asset property.");
5713
6859
  }
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.");
6860
+ if (this.rootTileset.formatVersion !== "2.0-draft" && this.asset.version !== "0.0" && this.asset.version !== "1.0" && this.asset.version !== "1.1") {
6861
+ throw new Error("The tileset must be 3D Tiles version 0.0, 1.0, 1.1, or 2.0-draft.");
5716
6862
  }
5717
6863
  if ("tilesetVersion" in this.asset) {
5718
6864
  this.setQueryParameter("v", this.asset.tilesetVersion);
@@ -5748,12 +6894,26 @@ var Tiles3DSource = class {
5748
6894
  async getRootTileset() {
5749
6895
  return this.getMetadata().tileset;
5750
6896
  }
6897
+ /** Prepare nonlinear CRS state before runtime headers are materialized. */
6898
+ async prepareTileset(tileset) {
6899
+ const spatialReference = tileset.spatialReference;
6900
+ if (spatialReference.status === "unresolved") {
6901
+ throw new Error(spatialReference.warnings[0] || "3D Tiles spatial operations cannot be resolved from the supplied metadata and options");
6902
+ }
6903
+ if (spatialReference.status !== "transformable" && spatialReference.status !== "transformed") {
6904
+ return;
6905
+ }
6906
+ this.spatialTransformer ||= new Tiles3DSpatialTransformer(spatialReference, tileset.options.spatial);
6907
+ tileset.spatialReference = markTilesetSpatialReferenceTransformed(spatialReference);
6908
+ this.preparedRootHeader = this.transformTileHeader(this.getMetadata().tileset.root, new import_core12.Matrix4());
6909
+ }
5751
6910
  /**
5752
6911
  * Builds explicit runtime headers while leaving implicit subtree references lazy.
5753
6912
  */
5754
6913
  initializeTileHeaders(tileset, tilesetJson, parentTile) {
5755
6914
  var _a;
5756
- const rootTile = new Tile3D(tileset, tilesetJson.root, parentTile || void 0);
6915
+ const rootHeader = parentTile ? tilesetJson.root : this.preparedRootHeader || tilesetJson.root;
6916
+ const rootTile = new Tile3D(tileset, rootHeader, parentTile || void 0);
5757
6917
  if (parentTile) {
5758
6918
  parentTile.children.push(rootTile);
5759
6919
  rootTile.depth = parentTile.depth + 1;
@@ -5789,7 +6949,13 @@ var Tiles3DSource = class {
5789
6949
  * Loads binary content or nested tileset JSON for a runtime tile.
5790
6950
  */
5791
6951
  async loadTileContent(tile) {
6952
+ var _a, _b, _c, _d, _e, _f;
5792
6953
  const contentUrls = (tile.contentUrls || [tile.contentUrl]).filter(Boolean);
6954
+ const cachedContentHeaders = this.tileContentHeaders.get(tile);
6955
+ 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] : []);
6956
+ if (!cachedContentHeaders) {
6957
+ this.tileContentHeaders.set(tile, contentHeaders);
6958
+ }
5793
6959
  const tilesetLoaderOptions = this.loadOptions[this.loader.id] || {};
5794
6960
  const options = {
5795
6961
  ...this.loadOptions,
@@ -5798,10 +6964,28 @@ var Tiles3DSource = class {
5798
6964
  // Content bytes, rather than URL suffixes, distinguish external tilesets from renderable
5799
6965
  // payloads. This is required for signed and extensionless resources.
5800
6966
  isTileset: "auto",
5801
- assetGltfUpAxis: this.asset && this.asset.gltfUpAxis || "Y"
6967
+ assetGltfUpAxis: this.asset && this.asset.gltfUpAxis || "Y",
6968
+ _tilesetOptions: {
6969
+ spatialReference: ((_c = tile.header) == null ? void 0 : _c._spatialReference) || ((_d = tile.tileset) == null ? void 0 : _d.spatialReference),
6970
+ spatialOptions: (_e = tile.tileset) == null ? void 0 : _e.options.spatial,
6971
+ spatialTransform: (_f = tile.header) == null ? void 0 : _f._spatialTransform
6972
+ }
5802
6973
  }
5803
6974
  };
5804
- const contents = await Promise.all(contentUrls.map((contentUrl) => this.loadResourceData(this.getTileUrl(contentUrl), options)));
6975
+ const contents = await Promise.all(contentUrls.map((contentUrl, contentIndex) => {
6976
+ var _a2;
6977
+ const contentHeader = contentHeaders[contentIndex] || {};
6978
+ const vectorExtension = (_a2 = contentHeader.extensions) == null ? void 0 : _a2["3DTILES_content_gltf_vector"];
6979
+ const vectorContent = contentHeader._vectorContent || ((vectorExtension == null ? void 0 : vectorExtension.vector) === true ? { clip: vectorExtension.clip === true } : void 0);
6980
+ const contentOptions = vectorContent ? {
6981
+ ...options,
6982
+ [this.loader.id]: {
6983
+ ...options[this.loader.id],
6984
+ vectorContent
6985
+ }
6986
+ } : options;
6987
+ return this.loadContentResource(this.getTileUrl(contentUrl), contentHeader._resource, contentOptions);
6988
+ }));
5805
6989
  tile.contents = contents;
5806
6990
  tile.content = contents[0] || null;
5807
6991
  const nestedTilesets = contents.filter((content) => (content == null ? void 0 : content.shape) === "tileset3d");
@@ -5812,6 +6996,34 @@ var Tiles3DSource = class {
5812
6996
  nestedTilesets
5813
6997
  };
5814
6998
  }
6999
+ /** Transform a tile header and its eagerly declared descendants. */
7000
+ transformTileHeader(header, parentTransform = new import_core12.Matrix4()) {
7001
+ if (!this.spatialTransformer) {
7002
+ return header;
7003
+ }
7004
+ return this.transformTileHeaderWithTransformer(header, this.spatialTransformer, parentTransform);
7005
+ }
7006
+ /** Rebuilds one header subtree using an explicitly selected CRS transformer. */
7007
+ transformTileHeaderWithTransformer(header, transformer, parentTransform, spatialReference) {
7008
+ const localTransform = header.transform ? new import_core12.Matrix4(header.transform) : new import_core12.Matrix4();
7009
+ const composedTransform = new import_core12.Matrix4(parentTransform).multiplyRight(localTransform);
7010
+ 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;
7011
+ return {
7012
+ ...header,
7013
+ transform: void 0,
7014
+ boundingVolume: header.boundingVolume ? transformer.transformBoundingVolume(header.boundingVolume, composedTransform) : header.boundingVolume,
7015
+ _spatialReference: spatialReference,
7016
+ _spatialTransform: composedTransform.toArray(),
7017
+ content,
7018
+ children: Array.isArray(header.children) ? header.children.map((child) => this.transformTileHeaderWithTransformer(child, transformer, composedTransform, spatialReference)) : header.children
7019
+ };
7020
+ }
7021
+ transformContentHeader(content, composedTransform, transformer) {
7022
+ return {
7023
+ ...content,
7024
+ boundingVolume: content.boundingVolume ? transformer.transformBoundingVolume(content.boundingVolume, composedTransform) : content.boundingVolume
7025
+ };
7026
+ }
5815
7027
  /**
5816
7028
  * Loads, materializes, and installs exactly one implicit subtree.
5817
7029
  *
@@ -5830,7 +7042,7 @@ var Tiles3DSource = class {
5830
7042
  return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
5831
7043
  }
5832
7044
  const subtreeUrl = this.getTileUrl(reference.subtreeUrl);
5833
- const subtree = await this.loadImplicitSubtreeResource(subtreeUrl);
7045
+ const subtree = await this.loadImplicitSubtreeResource(subtreeUrl, reference.resource);
5834
7046
  if (this.destroyed || tile.isDestroyed()) {
5835
7047
  return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
5836
7048
  }
@@ -5838,8 +7050,11 @@ var Tiles3DSource = class {
5838
7050
  ...reference,
5839
7051
  subtreeUrl
5840
7052
  });
5841
- tile.applyImplicitSubtreeHeader(materializedSubtree.root);
5842
- const materializedTileCount = this.initializeMaterializedChildren(tile.tileset, tile, materializedSubtree.root.children);
7053
+ const nestedSpatialReference = tile.header._spatialReference;
7054
+ const subtreeTransformer = nestedSpatialReference ? new Tiles3DSpatialTransformer(nestedSpatialReference, tile.tileset.options.spatial) : this.spatialTransformer;
7055
+ const materializedRoot = subtreeTransformer ? this.transformTileHeaderWithTransformer(materializedSubtree.root, subtreeTransformer, new import_core12.Matrix4(), nestedSpatialReference) : materializedSubtree.root;
7056
+ tile.applyImplicitSubtreeHeader(materializedRoot);
7057
+ const materializedTileCount = this.initializeMaterializedChildren(tile.tileset, tile, materializedRoot.children);
5843
7058
  this.implicitTilingStats.loadedSubtrees++;
5844
7059
  this.implicitTilingStats.materializedTiles += materializedTileCount;
5845
7060
  return {
@@ -5848,6 +7063,13 @@ var Tiles3DSource = class {
5848
7063
  childSubtreeCount: materializedSubtree.childSubtreeCount
5849
7064
  };
5850
7065
  }
7066
+ /** Loads an implicit subtree for dataset traversal without camera state. */
7067
+ async loadTileChildrenForTraversal(tile, signal) {
7068
+ throwIfTraversalAborted(signal);
7069
+ const result = await this.loadTileChildren(tile, {});
7070
+ throwIfTraversalAborted(signal);
7071
+ return result;
7072
+ }
5851
7073
  /**
5852
7074
  * Returns a snapshot of implicit-subtree request, cache, and materialization counters.
5853
7075
  *
@@ -5926,10 +7148,10 @@ var Tiles3DSource = class {
5926
7148
  const { center } = rootTile.boundingVolume;
5927
7149
  let cartographicCenter;
5928
7150
  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);
7151
+ cartographicCenter = new import_core12.Vector3();
7152
+ import_geospatial10.Ellipsoid.WGS84.cartesianToCartographic(center, cartographicCenter);
5931
7153
  } else {
5932
- cartographicCenter = new import_core10.Vector3(0, 0, -import_geospatial7.Ellipsoid.WGS84.radii[0]);
7154
+ cartographicCenter = new import_core12.Vector3(0, 0, -import_geospatial10.Ellipsoid.WGS84.radii[0]);
5933
7155
  }
5934
7156
  return {
5935
7157
  asset: this.asset,
@@ -5960,16 +7182,66 @@ var Tiles3DSource = class {
5960
7182
  if (extensionsRemoved.includes("KHR_draco_mesh_compression")) {
5961
7183
  this.contentFormats.draco = true;
5962
7184
  }
5963
- if (extensionsRemoved.includes("EXT_meshopt_compression")) {
5964
- this.contentFormats.meshopt = true;
7185
+ if (extensionsRemoved.includes("EXT_meshopt_compression")) {
7186
+ this.contentFormats.meshopt = true;
7187
+ }
7188
+ if (extensionsRemoved.includes("KHR_texture_basisu")) {
7189
+ this.contentFormats.ktx2 = true;
7190
+ }
7191
+ const nestedTilesets = loadResult.nestedTilesets || (loadResult.nestedTileset ? [loadResult.nestedTileset] : []);
7192
+ for (const nestedTileset of nestedTilesets) {
7193
+ tileset._initializeTileHeaders(this.prepareNestedTileset(tileset, nestedTileset), tile);
7194
+ }
7195
+ }
7196
+ /**
7197
+ * Resolves a nested tileset's CRS independently and adapts it to the owning tileset output frame.
7198
+ *
7199
+ * Nested resources may use a different source CRS from their parent. Their headers and glTF
7200
+ * payloads must therefore use a transformer built from the nested descriptor, while the parent
7201
+ * runtime continues to expose one requested output frame to traversal and rendering.
7202
+ */
7203
+ prepareNestedTileset(tileset, nestedTileset) {
7204
+ const nestedSpatialReference = nestedTileset.spatialMetadata;
7205
+ const parentSpatialReference = tileset.spatialReference;
7206
+ if ((nestedSpatialReference == null ? void 0 : nestedSpatialReference.status) === "unresolved" || (nestedSpatialReference == null ? void 0 : nestedSpatialReference.crs.state) === "unknown") {
7207
+ throw new Error(nestedSpatialReference.warnings[0] || "Nested 3D Tiles CRS is explicitly unresolved and cannot be placed in the parent frame");
7208
+ }
7209
+ if (!nestedSpatialReference || !nestedSpatialReference.sourceCrs) {
7210
+ if (this.spatialTransformer && (parentSpatialReference.status === "transformable" || parentSpatialReference.status === "transformed")) {
7211
+ return {
7212
+ ...nestedTileset,
7213
+ spatialMetadata: parentSpatialReference,
7214
+ root: this.transformTileHeaderWithTransformer(nestedTileset.root, this.spatialTransformer, new import_core12.Matrix4(), parentSpatialReference)
7215
+ };
7216
+ }
7217
+ return addNestedSpatialReference(nestedTileset, parentSpatialReference);
7218
+ }
7219
+ const parentCoordinateEpoch = parentSpatialReference.coordinateEpoch;
7220
+ const nestedCoordinateEpoch = nestedSpatialReference.coordinateEpoch;
7221
+ if (parentCoordinateEpoch !== void 0 && nestedCoordinateEpoch !== void 0 && parentCoordinateEpoch !== nestedCoordinateEpoch) {
7222
+ throw new Error(`Nested 3D Tiles coordinate epoch ${nestedCoordinateEpoch} differs from parent epoch ${parentCoordinateEpoch}; epoch-aware transformation is unavailable`);
5965
7223
  }
5966
- if (extensionsRemoved.includes("KHR_texture_basisu")) {
5967
- this.contentFormats.ktx2 = true;
7224
+ const outputCrs = parentSpatialReference.targetCrs || parentSpatialReference.sourceCrs;
7225
+ if (!outputCrs) {
7226
+ return addNestedSpatialReference(nestedTileset, nestedSpatialReference);
5968
7227
  }
5969
- const nestedTilesets = loadResult.nestedTilesets || (loadResult.nestedTileset ? [loadResult.nestedTileset] : []);
5970
- for (const nestedTileset of nestedTilesets) {
5971
- tileset._initializeTileHeaders(nestedTileset, tile);
7228
+ const nestedOutputReference = applyTilesetSpatialOptions(nestedSpatialReference, {
7229
+ ...tileset.options.spatial,
7230
+ outputCoordinates: "target-crs",
7231
+ targetCrs: outputCrs
7232
+ });
7233
+ if (nestedOutputReference.status === "unresolved") {
7234
+ throw new Error(nestedOutputReference.warnings[0] || "Nested 3D Tiles CRS cannot be resolved in the parent output frame");
5972
7235
  }
7236
+ if (nestedOutputReference.status === "native") {
7237
+ return addNestedSpatialReference(nestedTileset, nestedOutputReference);
7238
+ }
7239
+ const nestedTransformer = new Tiles3DSpatialTransformer(nestedOutputReference, tileset.options.spatial);
7240
+ return {
7241
+ ...nestedTileset,
7242
+ spatialMetadata: nestedOutputReference,
7243
+ root: this.transformTileHeaderWithTransformer(nestedTileset.root, nestedTransformer, new import_core12.Matrix4(), nestedOutputReference)
7244
+ };
5973
7245
  }
5974
7246
  /**
5975
7247
  * Loads data through injected core APIs so this module stays independent from `@loaders.gl/core`.
@@ -5998,13 +7270,84 @@ var Tiles3DSource = class {
5998
7270
  }
5999
7271
  return await this.loadWithCoreApi(url, options, loader);
6000
7272
  }
7273
+ /** Loads a URL content reference or parses a buffer-view file retained from a glTF package. */
7274
+ async loadContentResource(url, packageResource, options) {
7275
+ if (!packageResource) {
7276
+ return await this.loadResourceData(url, options);
7277
+ }
7278
+ const file = packageResource.files[packageResource.fileIndex];
7279
+ if (!file) {
7280
+ throw new Error(`3D Tiles package references missing file ${packageResource.fileIndex}`);
7281
+ }
7282
+ if (file.uri) {
7283
+ return await this.loadResourceData(this.getTileUrl(file.uri), options);
7284
+ }
7285
+ if (!this.coreApi) {
7286
+ throw new Error("Tiles3DSource requires an injected coreApi to parse embedded package data");
7287
+ }
7288
+ const data = await this.getPackageFileData(file);
7289
+ const packageNamespace = this.getPackageNamespace(packageResource.files);
7290
+ const packageRootUrl = `gltf-package://${packageNamespace}/${packageResource.fileIndex}`;
7291
+ const fileName = file.name || "content";
7292
+ const directorySeparatorIndex = fileName.lastIndexOf("/");
7293
+ const packageBaseUrl = directorySeparatorIndex >= 0 ? `${packageRootUrl}/${fileName.slice(0, directorySeparatorIndex)}` : packageRootUrl;
7294
+ return await this.coreApi.parse(data, this.loader, options, {
7295
+ url: `${packageRootUrl}/${fileName}`,
7296
+ filename: fileName,
7297
+ baseUrl: packageBaseUrl,
7298
+ loaders: [this.loader],
7299
+ coreApi: this.coreApi,
7300
+ _parse: this.coreApi.parse,
7301
+ _tiles3dPackageFiles: packageResource.files,
7302
+ fetch: async (resource, init) => {
7303
+ const reference = resource.startsWith(`${packageRootUrl}/`) ? resource.slice(packageRootUrl.length + 1) : resource;
7304
+ const referencedFile = packageResource.files.find((packageFile) => packageFile.name === reference || packageFile.originalUri === reference);
7305
+ if (!referencedFile) {
7306
+ throw new Error(`3D Tiles glTF package does not contain file ${reference}`);
7307
+ }
7308
+ if (referencedFile.uri) {
7309
+ return await this.coreApi.fetchFile(this.getTileUrl(referencedFile.uri), init);
7310
+ }
7311
+ const referencedData = await this.getPackageFileData(referencedFile);
7312
+ return new Response(referencedData, {
7313
+ headers: { "content-type": referencedFile.mimeType }
7314
+ });
7315
+ }
7316
+ });
7317
+ }
7318
+ /** Returns a stable source-local namespace for one embedded glTF package file collection. */
7319
+ getPackageNamespace(files) {
7320
+ const existingNamespace = this.packageNamespaces.get(files);
7321
+ if (existingNamespace !== void 0) {
7322
+ return existingNamespace;
7323
+ }
7324
+ const packageNamespace = this.nextPackageNamespace++;
7325
+ this.packageNamespaces.set(files, packageNamespace);
7326
+ return packageNamespace;
7327
+ }
7328
+ /** Resolves a retained package-file byte range without eagerly copying other package files. */
7329
+ async getPackageFileData(file) {
7330
+ if (file.data) {
7331
+ return sliceArrayBuffer(file.data, file.byteOffset, file.byteLength);
7332
+ }
7333
+ if (file.bufferUri && this.coreApi) {
7334
+ const response = await this.coreApi.fetchFile(this.getTileUrl(file.bufferUri));
7335
+ if (!response.ok) {
7336
+ throw new Error(`Failed to fetch 3D Tiles package buffer: HTTP ${response.status}`);
7337
+ }
7338
+ const buffer = await response.arrayBuffer();
7339
+ return sliceArrayBuffer(buffer, file.byteOffset, file.byteLength);
7340
+ }
7341
+ throw new Error("3D Tiles package file has no available byte source");
7342
+ }
6001
7343
  /**
6002
7344
  * Returns a parsed subtree from the LRU cache or starts one source-managed request.
6003
7345
  *
6004
7346
  * @param subtreeUrl - Final subtree URL after query inheritance.
7347
+ * @param packageResource - Optional glTF package file satisfying the subtree reference.
6005
7348
  * @returns Parsed subtree availability data.
6006
7349
  */
6007
- async loadImplicitSubtreeResource(subtreeUrl) {
7350
+ async loadImplicitSubtreeResource(subtreeUrl, packageResource) {
6008
7351
  const cachedSubtree = this.implicitSubtreeCache.get(subtreeUrl);
6009
7352
  if (cachedSubtree) {
6010
7353
  this.implicitTilingStats.cacheHits++;
@@ -6013,14 +7356,15 @@ var Tiles3DSource = class {
6013
7356
  this.implicitTilingStats.requestedSubtrees++;
6014
7357
  const loaderOptions = this.loadOptions[this.loader.id] || {};
6015
7358
  return await this.implicitSubtreeCache.getOrLoad(subtreeUrl, async () => {
6016
- return await this.loadResourceData(subtreeUrl, {
7359
+ const options = {
6017
7360
  ...this.loadOptions,
6018
7361
  [this.loader.id]: {
6019
7362
  ...loaderOptions,
6020
7363
  isTileset: false,
6021
7364
  isSubtree: true
6022
7365
  }
6023
- });
7366
+ };
7367
+ return await (packageResource ? this.loadContentResource(subtreeUrl, packageResource, options) : this.loadResourceData(subtreeUrl, options));
6024
7368
  });
6025
7369
  }
6026
7370
  /**
@@ -6053,6 +7397,11 @@ var Tiles3DSource = class {
6053
7397
  return materializedTileCount;
6054
7398
  }
6055
7399
  };
7400
+ function throwIfTraversalAborted(signal) {
7401
+ if (signal == null ? void 0 : signal.aborted) {
7402
+ throw signal.reason ?? new DOMException("The operation was aborted", "AbortError");
7403
+ }
7404
+ }
6056
7405
  function isTilesetRequest(input) {
6057
7406
  return Boolean(input && typeof input === "object" && "url" in input && "loader" in input && !("type" in input));
6058
7407
  }
@@ -6071,10 +7420,22 @@ function normalizeTiles3DRequest(input) {
6071
7420
  coreApi: input.coreApi
6072
7421
  };
6073
7422
  }
7423
+ function addNestedSpatialReference(nestedTileset, spatialReference) {
7424
+ const addToHeader = (header) => ({
7425
+ ...header,
7426
+ _spatialReference: spatialReference,
7427
+ children: Array.isArray(header.children) ? header.children.map(addToHeader) : header.children
7428
+ });
7429
+ return {
7430
+ ...nestedTileset,
7431
+ spatialMetadata: spatialReference,
7432
+ root: addToHeader(nestedTileset.root)
7433
+ };
7434
+ }
6074
7435
 
6075
7436
  // dist/tileset-3d/format-i3s/i3s-source.js
6076
- var import_geospatial10 = require("@math.gl/geospatial");
6077
- var import_core13 = require("@math.gl/core");
7437
+ var import_geospatial12 = require("@math.gl/geospatial");
7438
+ var import_core14 = require("@math.gl/core");
6078
7439
 
6079
7440
  // dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js
6080
7441
  var I3SPendingTilesRegister = class {
@@ -6210,7 +7571,10 @@ var I3STilesetTraverser = class extends TilesetTraverser {
6210
7571
  const childTiles = tile.children;
6211
7572
  for (const child of children) {
6212
7573
  const extendedId = `${child.id}-${frameState.viewport.id}`;
6213
- const childTile = childTiles && childTiles.find((t) => t.id === extendedId);
7574
+ const childTile = childTiles && childTiles.find((t) => {
7575
+ var _a;
7576
+ return t.id === extendedId || ((_a = t.header) == null ? void 0 : _a.id) === child.id;
7577
+ });
6214
7578
  if (!childTile) {
6215
7579
  const request = () => this._loadTile(tile, child.id, frameState);
6216
7580
  const cachedRequest = this._tileManager.find(extendedId);
@@ -6255,224 +7619,8 @@ var I3STilesetTraverser = class extends TilesetTraverser {
6255
7619
  };
6256
7620
 
6257
7621
  // 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
7622
+ var import_core13 = require("@math.gl/core");
7623
+ var import_geospatial11 = require("@math.gl/geospatial");
6476
7624
  var I3SSpatialTransformer = class {
6477
7625
  /** Descriptor attached to output after all adapter-owned spatial values have been transformed. */
6478
7626
  spatialReference;
@@ -6667,7 +7815,7 @@ var I3SSpatialTransformer = class {
6667
7815
  positions[index + 2] = transformed[2] - origin[2];
6668
7816
  }
6669
7817
  const isGeographic = this.targetCoordinateFrame === "geographic";
6670
- const modelMatrix = isGeographic ? new import_core12.Matrix4() : new import_core12.Matrix4().translate(origin);
7818
+ const modelMatrix = isGeographic ? new import_core13.Matrix4() : new import_core13.Matrix4().translate(origin);
6671
7819
  return {
6672
7820
  positions,
6673
7821
  sourcePositions: absoluteSourcePositions,
@@ -6695,7 +7843,7 @@ var I3SSpatialTransformer = class {
6695
7843
  sourcePositions[index + 1],
6696
7844
  sourcePositions[index + 2]
6697
7845
  ]);
6698
- const inputNormal = new import_core12.Vector3([
7846
+ const inputNormal = new import_core13.Vector3([
6699
7847
  normals[index],
6700
7848
  normals[index + 1],
6701
7849
  normals[index + 2]
@@ -6733,12 +7881,12 @@ var I3SSpatialTransformer = class {
6733
7881
  }
6734
7882
  /** Rebuild an I3S bound as a WGS84 ECEF box for generic `Tileset3D` traversal. */
6735
7883
  transformBoundingVolumeToGeocentric(bounds) {
6736
- const cartesianSamples = this.getGeographicBoundSamples(bounds).map((position) => Array.from(import_geospatial9.Ellipsoid.WGS84.cartographicToCartesian(new import_core12.Vector3(position))));
7884
+ const cartesianSamples = this.getGeographicBoundSamples(bounds).map((position) => Array.from(import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position))));
6737
7885
  return { box: getAxisAlignedBox(cartesianSamples) };
6738
7886
  }
6739
7887
  /** Rebuild an I3S bound as WGS84 ECEF after asynchronous elevation placement. */
6740
7888
  async transformBoundingVolumeToGeocentricAsync(bounds) {
6741
- const cartesianSamples = (await this.getGeographicBoundSamplesAsync(bounds)).map((position) => Array.from(import_geospatial9.Ellipsoid.WGS84.cartographicToCartesian(new import_core12.Vector3(position))));
7889
+ const cartesianSamples = (await this.getGeographicBoundSamplesAsync(bounds)).map((position) => Array.from(import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position))));
6742
7890
  return { box: getAxisAlignedBox(cartesianSamples) };
6743
7891
  }
6744
7892
  /** Return a WGS84 geographic MBS used by the I3S screen-threshold calculation. */
@@ -6765,7 +7913,7 @@ var I3SSpatialTransformer = class {
6765
7913
  */
6766
7914
  async transformBoundsAsync(bounds) {
6767
7915
  const geographicSamples = await this.getGeographicBoundSamplesAsync(bounds);
6768
- const cartesianSamples = geographicSamples.map((position) => Array.from(import_geospatial9.Ellipsoid.WGS84.cartographicToCartesian(new import_core12.Vector3(position))));
7916
+ const cartesianSamples = geographicSamples.map((position) => Array.from(import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position))));
6769
7917
  const targetSamples = geographicSamples.map((position) => this.geographicToTargetTransformer.transformPosition(position));
6770
7918
  return {
6771
7919
  boundingVolume: { box: getAxisAlignedBox(cartesianSamples) },
@@ -6888,21 +8036,21 @@ var I3SSpatialTransformer = class {
6888
8036
  if (this.targetCoordinateFrame === "geographic") {
6889
8037
  return ecefVectorToLocalEnu(ecefNormal, geographic).normalize();
6890
8038
  }
6891
- const cartesian = import_geospatial9.Ellipsoid.WGS84.cartographicToCartesian(new import_core12.Vector3(geographic));
8039
+ const cartesian = import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(geographic));
6892
8040
  const jacobianColumns = [];
6893
8041
  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));
8042
+ const positiveGeographic = import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(new import_core13.Vector3(cartesian).add(direction));
8043
+ const negativeGeographic = import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(new import_core13.Vector3(cartesian).subtract(direction));
6896
8044
  const positiveTarget = this.geographicToTargetTransformer.transformPosition(positiveGeographic);
6897
8045
  const negativeTarget = this.geographicToTargetTransformer.transformPosition(negativeGeographic);
6898
8046
  jacobianColumns.push((positiveTarget[0] - negativeTarget[0]) / 2, (positiveTarget[1] - negativeTarget[1]) / 2, (positiveTarget[2] - negativeTarget[2]) / 2);
6899
8047
  }
6900
- const normalMatrix = new import_core12.Matrix3(jacobianColumns);
8048
+ const normalMatrix = new import_core13.Matrix3(jacobianColumns);
6901
8049
  if (Math.abs(normalMatrix.determinant()) < 1e-12) {
6902
8050
  throw new Error("Cannot transform an I3S normal through a singular local projection");
6903
8051
  }
6904
8052
  normalMatrix.invert().transpose();
6905
- return new import_core12.Vector3(ecefNormal).transformByMatrix3(normalMatrix).normalize();
8053
+ return new import_core13.Vector3(ecefNormal).transformByMatrix3(normalMatrix).normalize();
6906
8054
  }
6907
8055
  };
6908
8056
  function getElevationProvider(mode, options) {
@@ -6934,17 +8082,17 @@ function getGeographicBoundingSphere(geographicSamples) {
6934
8082
  if (!geographicSamples.length) {
6935
8083
  throw new Error("I3S spatial transformation requires at least one bound sample");
6936
8084
  }
6937
- const cartesianSamples = geographicSamples.map((position) => import_geospatial9.Ellipsoid.WGS84.cartographicToCartesian(new import_core12.Vector3(position)));
6938
- const cartesianCenter = new import_core12.Vector3();
8085
+ const cartesianSamples = geographicSamples.map((position) => import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(position)));
8086
+ const cartesianCenter = new import_core13.Vector3();
6939
8087
  for (const sample of cartesianSamples) {
6940
8088
  cartesianCenter.add(sample);
6941
8089
  }
6942
8090
  cartesianCenter.scale(1 / cartesianSamples.length);
6943
8091
  let radius = 0;
6944
8092
  for (const sample of cartesianSamples) {
6945
- radius = Math.max(radius, new import_core12.Vector3(sample).subtract(cartesianCenter).magnitude());
8093
+ radius = Math.max(radius, new import_core13.Vector3(sample).subtract(cartesianCenter).magnitude());
6946
8094
  }
6947
- const geographicCenter = import_geospatial9.Ellipsoid.WGS84.cartesianToCartographic(cartesianCenter);
8095
+ const geographicCenter = import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(cartesianCenter);
6948
8096
  return [geographicCenter[0], geographicCenter[1], geographicCenter[2], radius];
6949
8097
  }
6950
8098
  function getSourceBoundSamples(bounds, sourceFrame, sourceToGeographicTransformer, sourceVerticalUnitScale) {
@@ -6953,28 +8101,28 @@ function getSourceBoundSamples(bounds, sourceFrame, sourceToGeographicTransforme
6953
8101
  const center2 = Array.from(obb.center).slice(0, 3);
6954
8102
  center2[2] *= sourceVerticalUnitScale;
6955
8103
  const halfSize = Array.from(obb.halfSize).slice(0, 3);
6956
- const quaternion = new import_core12.Quaternion().fromArray(Array.from(obb.quaternion));
8104
+ const quaternion = new import_core13.Quaternion().fromArray(Array.from(obb.quaternion));
6957
8105
  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)
8106
+ new import_core13.Vector3([halfSize[0], 0, 0]).transformByQuaternion(quaternion),
8107
+ new import_core13.Vector3([0, halfSize[1], 0]).transformByQuaternion(quaternion),
8108
+ new import_core13.Vector3([0, 0, halfSize[2]]).transformByQuaternion(quaternion)
6961
8109
  ];
6962
8110
  if (sourceFrame === "geographic") {
6963
8111
  const geographicCenter = sourceToGeographicTransformer.transformPosition(center2);
6964
- const cartesianCenter = import_geospatial9.Ellipsoid.WGS84.cartographicToCartesian(new import_core12.Vector3(geographicCenter));
8112
+ const cartesianCenter = import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(geographicCenter));
6965
8113
  const cartesianAxes = bounds.normalReferenceFrame === "vertex-reference-frame" ? axes.map((axis) => localEnuVectorToEcef(axis, geographicCenter)) : axes;
6966
8114
  return getBoxSigns().map((signs) => {
6967
- const corner = new import_core12.Vector3(cartesianCenter);
8115
+ const corner = new import_core13.Vector3(cartesianCenter);
6968
8116
  for (let axisIndex = 0; axisIndex < 3; axisIndex++) {
6969
- corner.add(new import_core12.Vector3(cartesianAxes[axisIndex]).scale(signs[axisIndex]));
8117
+ corner.add(new import_core13.Vector3(cartesianAxes[axisIndex]).scale(signs[axisIndex]));
6970
8118
  }
6971
- return Array.from(import_geospatial9.Ellipsoid.WGS84.cartesianToCartographic(corner));
8119
+ return Array.from(import_geospatial11.Ellipsoid.WGS84.cartesianToCartographic(corner));
6972
8120
  });
6973
8121
  }
6974
8122
  return getBoxSigns().map((signs) => {
6975
- const corner = new import_core12.Vector3(center2);
8123
+ const corner = new import_core13.Vector3(center2);
6976
8124
  for (let axisIndex = 0; axisIndex < 3; axisIndex++) {
6977
- corner.add(new import_core12.Vector3(axes[axisIndex]).scale(signs[axisIndex]));
8125
+ corner.add(new import_core13.Vector3(axes[axisIndex]).scale(signs[axisIndex]));
6978
8126
  }
6979
8127
  return Array.from(corner);
6980
8128
  });
@@ -6988,10 +8136,10 @@ function getSourceBoundSamples(bounds, sourceFrame, sourceToGeographicTransforme
6988
8136
  const radius = mbs[3];
6989
8137
  if (sourceFrame === "geographic") {
6990
8138
  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)))));
8139
+ const cartesianCenter = import_geospatial11.Ellipsoid.WGS84.cartographicToCartesian(new import_core13.Vector3(geographicCenter));
8140
+ 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
8141
  }
6994
- return getAxisDirections().map((direction) => Array.from(new import_core12.Vector3(center).add(new import_core12.Vector3(direction).scale(radius))));
8142
+ return getAxisDirections().map((direction) => Array.from(new import_core13.Vector3(center).add(new import_core13.Vector3(direction).scale(radius))));
6995
8143
  }
6996
8144
  function getBoxSigns() {
6997
8145
  return [-1, 1].flatMap((x) => [-1, 1].flatMap((y) => [-1, 1].map((z) => [x, y, z])));
@@ -7007,18 +8155,18 @@ function getAxisDirections() {
7007
8155
  ];
7008
8156
  }
7009
8157
  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])];
8158
+ return [new import_core13.Vector3([1, 0, 0]), new import_core13.Vector3([0, 1, 0]), new import_core13.Vector3([0, 0, 1])];
7011
8159
  }
7012
8160
  function localEnuVectorToEcef(vector, geographic) {
7013
8161
  const longitude = geographic[0] * Math.PI / 180;
7014
8162
  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([
8163
+ const east = new import_core13.Vector3([-Math.sin(longitude), Math.cos(longitude), 0]);
8164
+ const north = new import_core13.Vector3([
7017
8165
  -Math.sin(latitude) * Math.cos(longitude),
7018
8166
  -Math.sin(latitude) * Math.sin(longitude),
7019
8167
  Math.cos(latitude)
7020
8168
  ]);
7021
- const up = new import_core12.Vector3([
8169
+ const up = new import_core13.Vector3([
7022
8170
  Math.cos(latitude) * Math.cos(longitude),
7023
8171
  Math.cos(latitude) * Math.sin(longitude),
7024
8172
  Math.sin(latitude)
@@ -7028,18 +8176,18 @@ function localEnuVectorToEcef(vector, geographic) {
7028
8176
  function ecefVectorToLocalEnu(vector, geographic) {
7029
8177
  const longitude = geographic[0] * Math.PI / 180;
7030
8178
  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([
8179
+ const east = new import_core13.Vector3([-Math.sin(longitude), Math.cos(longitude), 0]);
8180
+ const north = new import_core13.Vector3([
7033
8181
  -Math.sin(latitude) * Math.cos(longitude),
7034
8182
  -Math.sin(latitude) * Math.sin(longitude),
7035
8183
  Math.cos(latitude)
7036
8184
  ]);
7037
- const up = new import_core12.Vector3([
8185
+ const up = new import_core13.Vector3([
7038
8186
  Math.cos(latitude) * Math.cos(longitude),
7039
8187
  Math.cos(latitude) * Math.sin(longitude),
7040
8188
  Math.sin(latitude)
7041
8189
  ]);
7042
- return new import_core12.Vector3([east.dot(vector), north.dot(vector), up.dot(vector)]);
8190
+ return new import_core13.Vector3([east.dot(vector), north.dot(vector), up.dot(vector)]);
7043
8191
  }
7044
8192
  function getAxisAlignedBox(positions) {
7045
8193
  const minimum = [Infinity, Infinity, Infinity];
@@ -7250,7 +8398,17 @@ var I3SSource = class {
7250
8398
  }
7251
8399
  };
7252
8400
  tile.content = await this.loadResourceData(contentUrl, options, this.contentLoader);
7253
- return { loaded: true };
8401
+ const contentEntry = {
8402
+ index: 0,
8403
+ uri: tile.contentUrl,
8404
+ type: tile.header.layerType,
8405
+ payload: tile.content,
8406
+ metadata: tile.header.metadata || null,
8407
+ boundingVolume: tile.boundingVolume,
8408
+ featureIds: tile.featureIdSets,
8409
+ renderable: Boolean(tile.content)
8410
+ };
8411
+ return { loaded: true, contents: [tile.content], contentEntries: [contentEntry] };
7254
8412
  }
7255
8413
  /**
7256
8414
  * Loads child tile metadata on demand from node pages or `/nodes/{id}` resources.
@@ -7273,6 +8431,38 @@ var I3SSource = class {
7273
8431
  const header = await this.loadResourceData(nodeUrl, options);
7274
8432
  return await this.transformTileHeader(header);
7275
8433
  }
8434
+ /** Loads all declared I3S child headers without camera or visibility state. */
8435
+ async loadTileChildrenForTraversal(parentTile, signal) {
8436
+ const childHeaders = parentTile.header.children || [];
8437
+ let loadedChildren = 0;
8438
+ const orderedChildren = [];
8439
+ for (const childHeader of childHeaders) {
8440
+ throwIfTraversalAborted2(signal);
8441
+ const childId = childHeader.id;
8442
+ if (typeof childId !== "string" && typeof childId !== "number") {
8443
+ throw new Error("I3S child header is missing its node ID");
8444
+ }
8445
+ const existingChild = parentTile.children.find((tile) => {
8446
+ var _a;
8447
+ return tile.id === String(childId) || ((_a = tile.header) == null ? void 0 : _a.id) === childId;
8448
+ });
8449
+ if (existingChild) {
8450
+ orderedChildren.push(existingChild);
8451
+ continue;
8452
+ }
8453
+ const childTileHeader = await this.loadChildTileHeader(parentTile, String(childId), null);
8454
+ throwIfTraversalAborted2(signal);
8455
+ const childTile = new Tile3D(parentTile.tileset, childTileHeader, parentTile, String(childId));
8456
+ orderedChildren.push(childTile);
8457
+ loadedChildren++;
8458
+ }
8459
+ const orderedChildSet = new Set(orderedChildren);
8460
+ parentTile.children = [
8461
+ ...orderedChildren,
8462
+ ...parentTile.children.filter((childTile) => !orderedChildSet.has(childTile))
8463
+ ];
8464
+ return { loaded: true, tileCount: loadedChildren, childSubtreeCount: 0 };
8465
+ }
7276
8466
  /**
7277
8467
  * Resolves an I3S request URL with source-managed query parameters.
7278
8468
  */
@@ -7283,20 +8473,21 @@ var I3SSource = class {
7283
8473
  if (!Object.keys(this.queryParams).length) {
7284
8474
  return tilePath;
7285
8475
  }
7286
- const queryDelimiterIndex = tilePath.indexOf("?");
7287
- if (queryDelimiterIndex === -1) {
7288
- return `${tilePath}?${new URLSearchParams(this.queryParams).toString()}`;
7289
- }
7290
- const existingQuery = tilePath.slice(queryDelimiterIndex + 1);
7291
- const existingQueryKeys = new Set(existingQuery.split("&").map((parameter) => parameter.split("=", 1)[0]));
8476
+ const hashIndex = tilePath.indexOf("#");
8477
+ const hash = hashIndex >= 0 ? tilePath.slice(hashIndex) : "";
8478
+ const tilePathWithoutHash = hashIndex >= 0 ? tilePath.slice(0, hashIndex) : tilePath;
8479
+ const queryDelimiterIndex = tilePathWithoutHash.indexOf("?");
8480
+ const path = queryDelimiterIndex === -1 ? tilePathWithoutHash : tilePathWithoutHash.slice(0, queryDelimiterIndex);
8481
+ const existingQuery = queryDelimiterIndex === -1 ? "" : tilePathWithoutHash.slice(queryDelimiterIndex + 1);
8482
+ const existingQueryParams = new URLSearchParams(existingQuery);
7292
8483
  const queryParams = new URLSearchParams();
7293
8484
  for (const [key, value] of Object.entries(this.queryParams)) {
7294
- if (!existingQueryKeys.has(key)) {
8485
+ if (!existingQueryParams.has(key)) {
7295
8486
  queryParams.set(key, value);
7296
8487
  }
7297
8488
  }
7298
8489
  const queryString = queryParams.toString();
7299
- return queryString ? `${tilePath}${existingQuery ? "&" : ""}${queryString}` : tilePath;
8490
+ return `${path}${existingQuery ? `?${existingQuery}` : ""}${queryString ? `${existingQuery ? "&" : "?"}${queryString}` : ""}${hash}`;
7300
8491
  }
7301
8492
  /**
7302
8493
  * Derives the default view state from full extent or store extent metadata.
@@ -7304,8 +8495,8 @@ var I3SSource = class {
7304
8495
  getViewState(rootTile) {
7305
8496
  var _a;
7306
8497
  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));
8498
+ const center = new import_core14.Vector3(rootTile.boundingVolume.center);
8499
+ const cartographicCenter = new import_core14.Vector3(rootTile.header.i3sLodMbs.slice(0, 3));
7309
8500
  if (this.spatialTransformer.targetCoordinateFrame === "geographic") {
7310
8501
  return {
7311
8502
  boundingVolume: rootTile.boundingVolume,
@@ -7325,9 +8516,9 @@ var I3SSource = class {
7325
8516
  const fullExtent = metadata.tileset.fullExtent;
7326
8517
  if (fullExtent) {
7327
8518
  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);
8519
+ const cartographicCenter = new import_core14.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, zmin + (zmax - zmin) / 2);
8520
+ const cartesianCenter = new import_core14.Vector3();
8521
+ import_geospatial12.Ellipsoid.WGS84.cartographicToCartesian(cartographicCenter, cartesianCenter);
7331
8522
  return {
7332
8523
  cartographicCenter,
7333
8524
  cartesianCenter,
@@ -7337,9 +8528,9 @@ var I3SSource = class {
7337
8528
  const extent = (_a = metadata.tileset.store) == null ? void 0 : _a.extent;
7338
8529
  if (extent) {
7339
8530
  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);
8531
+ const cartographicCenter = new import_core14.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, 0);
8532
+ const cartesianCenter = new import_core14.Vector3();
8533
+ import_geospatial12.Ellipsoid.WGS84.cartographicToCartesian(cartographicCenter, cartesianCenter);
7343
8534
  return {
7344
8535
  cartographicCenter,
7345
8536
  cartesianCenter,
@@ -7348,7 +8539,7 @@ var I3SSource = class {
7348
8539
  }
7349
8540
  console.warn("Extent is not defined in the tileset header");
7350
8541
  return {
7351
- cartographicCenter: new import_core13.Vector3(),
8542
+ cartographicCenter: new import_core14.Vector3(),
7352
8543
  zoom: 1
7353
8544
  };
7354
8545
  }
@@ -7400,6 +8591,9 @@ var I3SSource = class {
7400
8591
  * @param loadOptions Loader options that may contain an ArcGIS token.
7401
8592
  */
7402
8593
  initializeQueryParams(loadOptions) {
8594
+ for (const [key, value] of Object.entries(loadOptions.searchParams || {})) {
8595
+ this.queryParams[key] = String(value);
8596
+ }
7403
8597
  const i3sOptions = loadOptions.i3s;
7404
8598
  if (i3sOptions && typeof i3sOptions === "object" && "token" in i3sOptions) {
7405
8599
  const token = i3sOptions.token;
@@ -7439,6 +8633,11 @@ var I3SSource = class {
7439
8633
  };
7440
8634
  }
7441
8635
  };
8636
+ function throwIfTraversalAborted2(signal) {
8637
+ if (signal == null ? void 0 : signal.aborted) {
8638
+ throw signal.reason ?? new DOMException("The operation was aborted", "AbortError");
8639
+ }
8640
+ }
7442
8641
  function getI3SContentLoader(loader) {
7443
8642
  return loader.contentLoader || loader;
7444
8643
  }
@@ -8668,7 +9867,7 @@ function isAbortError3(error) {
8668
9867
  }
8669
9868
 
8670
9869
  // dist/point-cloud/point-cloud-tileset.js
8671
- var import_core14 = require("@math.gl/core");
9870
+ var import_core15 = require("@math.gl/core");
8672
9871
  var import_culling6 = require("@math.gl/culling");
8673
9872
 
8674
9873
  // dist/point-cloud/point-cloud-tile.js
@@ -8832,7 +10031,7 @@ var PointCloudTileset = class {
8832
10031
  const viewState = await ((_b = (_a = this.dataSource).getViewState) == null ? void 0 : _b.call(_a));
8833
10032
  this.boundingVolume = (viewState == null ? void 0 : viewState.boundingVolume) || this.root.boundingVolume;
8834
10033
  const center = (viewState == null ? void 0 : viewState.cartographicCenter) || this.root.boundingVolume.center;
8835
- this.cartographicCenter = new import_core14.Vector3(center);
10034
+ this.cartographicCenter = new import_core15.Vector3(center);
8836
10035
  this.zoom = (viewState == null ? void 0 : viewState.zoom) || this.estimateZoom(this.boundingVolume);
8837
10036
  }
8838
10037
  /**