@loaders.gl/tiles 4.4.5 → 5.0.0-alpha.2

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 (227) hide show
  1. package/LICENSE +14 -3
  2. package/README.md +110 -1
  3. package/dist/dist.dev.js +6588 -2057
  4. package/dist/dist.min.js +1 -1
  5. package/dist/image-set.d.ts +94 -0
  6. package/dist/image-set.d.ts.map +1 -0
  7. package/dist/image-set.js +243 -0
  8. package/dist/image-set.js.map +1 -0
  9. package/dist/index.cjs +6872 -2160
  10. package/dist/index.cjs.map +3 -4
  11. package/dist/index.d.ts +33 -9
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +18 -8
  14. package/dist/index.js.map +1 -1
  15. package/dist/point-cloud/point-cloud-tile.d.ts +47 -0
  16. package/dist/point-cloud/point-cloud-tile.d.ts.map +1 -0
  17. package/dist/point-cloud/point-cloud-tile.js +70 -0
  18. package/dist/point-cloud/point-cloud-tile.js.map +1 -0
  19. package/dist/point-cloud/point-cloud-tileset.d.ts +131 -0
  20. package/dist/point-cloud/point-cloud-tileset.d.ts.map +1 -0
  21. package/dist/point-cloud/point-cloud-tileset.js +510 -0
  22. package/dist/point-cloud/point-cloud-tileset.js.map +1 -0
  23. package/dist/point-cloud/types.d.ts +64 -0
  24. package/dist/point-cloud/types.d.ts.map +1 -0
  25. package/dist/point-cloud/types.js +5 -0
  26. package/dist/point-cloud/types.js.map +1 -0
  27. package/dist/raster-set.d.ts +113 -0
  28. package/dist/raster-set.d.ts.map +1 -0
  29. package/dist/raster-set.js +274 -0
  30. package/dist/raster-set.js.map +1 -0
  31. package/dist/tileset-2d/adapter.d.ts +38 -0
  32. package/dist/tileset-2d/adapter.d.ts.map +1 -0
  33. package/dist/tileset-2d/adapter.js +5 -0
  34. package/dist/tileset-2d/adapter.js.map +1 -0
  35. package/dist/tileset-2d/index.d.ts +7 -0
  36. package/dist/tileset-2d/index.d.ts.map +1 -0
  37. package/dist/tileset-2d/index.js +6 -0
  38. package/dist/tileset-2d/index.js.map +1 -0
  39. package/dist/tileset-2d/tile-2d-header.d.ts +68 -0
  40. package/dist/tileset-2d/tile-2d-header.d.ts.map +1 -0
  41. package/dist/tileset-2d/tile-2d-header.js +181 -0
  42. package/dist/tileset-2d/tile-2d-header.js.map +1 -0
  43. package/dist/tileset-2d/tileset-2d.d.ts +201 -0
  44. package/dist/tileset-2d/tileset-2d.d.ts.map +1 -0
  45. package/dist/tileset-2d/tileset-2d.js +532 -0
  46. package/dist/tileset-2d/tileset-2d.js.map +1 -0
  47. package/dist/tileset-2d/types.d.ts +44 -0
  48. package/dist/tileset-2d/types.d.ts.map +1 -0
  49. package/dist/tileset-2d/types.js +5 -0
  50. package/dist/tileset-2d/types.js.map +1 -0
  51. package/dist/tileset-2d.d.ts +2 -0
  52. package/dist/tileset-2d.d.ts.map +1 -0
  53. package/dist/tileset-2d.js +5 -0
  54. package/dist/tileset-2d.js.map +1 -0
  55. package/dist/tileset-3d/common/indexed-archive-tileset-source.d.ts +60 -0
  56. package/dist/tileset-3d/common/indexed-archive-tileset-source.d.ts.map +1 -0
  57. package/dist/tileset-3d/common/indexed-archive-tileset-source.js +125 -0
  58. package/dist/tileset-3d/common/indexed-archive-tileset-source.js.map +1 -0
  59. package/dist/{tileset → tileset-3d/common}/tile-3d.d.ts +133 -29
  60. package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -0
  61. package/dist/{tileset → tileset-3d/common}/tile-3d.js +282 -77
  62. package/dist/tileset-3d/common/tile-3d.js.map +1 -0
  63. package/dist/tileset-3d/common/tileset-3d.d.ts +317 -0
  64. package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -0
  65. package/dist/tileset-3d/common/tileset-3d.js +807 -0
  66. package/dist/tileset-3d/common/tileset-3d.js.map +1 -0
  67. package/dist/tileset-3d/common/tileset-cache.d.ts +29 -0
  68. package/dist/tileset-3d/common/tileset-cache.d.ts.map +1 -0
  69. package/dist/{tileset → tileset-3d/common}/tileset-cache.js +12 -4
  70. package/dist/tileset-3d/common/tileset-cache.js.map +1 -0
  71. package/dist/tileset-3d/common/tileset-source.d.ts +204 -0
  72. package/dist/tileset-3d/common/tileset-source.d.ts.map +1 -0
  73. package/dist/tileset-3d/common/tileset-source.js +17 -0
  74. package/dist/tileset-3d/common/tileset-source.js.map +1 -0
  75. package/dist/{tileset → tileset-3d/common}/tileset-traverser.d.ts +15 -2
  76. package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -0
  77. package/dist/{tileset → tileset-3d/common}/tileset-traverser.js +30 -3
  78. package/dist/tileset-3d/common/tileset-traverser.js.map +1 -0
  79. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts +111 -0
  80. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts.map +1 -0
  81. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js +340 -0
  82. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js.map +1 -0
  83. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +189 -0
  84. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts.map +1 -0
  85. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js +510 -0
  86. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -0
  87. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts +43 -0
  88. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -0
  89. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js +119 -0
  90. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js.map +1 -0
  91. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.d.ts.map +1 -0
  92. package/dist/{tileset → tileset-3d}/format-i3s/i3s-pending-tiles-register.js +3 -0
  93. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js.map +1 -0
  94. package/dist/tileset-3d/format-i3s/i3s-source.d.ts +99 -0
  95. package/dist/tileset-3d/format-i3s/i3s-source.d.ts.map +1 -0
  96. package/dist/tileset-3d/format-i3s/i3s-source.js +299 -0
  97. package/dist/tileset-3d/format-i3s/i3s-source.js.map +1 -0
  98. package/dist/tileset-3d/format-i3s/i3s-tile-manager.d.ts.map +1 -0
  99. package/dist/{tileset → tileset-3d}/format-i3s/i3s-tile-manager.js +5 -2
  100. package/dist/tileset-3d/format-i3s/i3s-tile-manager.js.map +1 -0
  101. package/dist/{tileset → tileset-3d}/format-i3s/i3s-tileset-traverser.d.ts +2 -2
  102. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.d.ts.map +1 -0
  103. package/dist/{tileset → tileset-3d}/format-i3s/i3s-tileset-traverser.js +17 -23
  104. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js.map +1 -0
  105. package/dist/{tileset → tileset-3d}/helpers/3d-tiles-options.d.ts +1 -1
  106. package/dist/tileset-3d/helpers/3d-tiles-options.d.ts.map +1 -0
  107. package/dist/{tileset → tileset-3d}/helpers/3d-tiles-options.js +3 -0
  108. package/dist/tileset-3d/helpers/3d-tiles-options.js.map +1 -0
  109. package/dist/tileset-3d/helpers/bounding-volume.d.ts.map +1 -0
  110. package/dist/{tileset → tileset-3d}/helpers/bounding-volume.js +1 -0
  111. package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -0
  112. package/dist/tileset-3d/helpers/frame-state.d.ts +86 -0
  113. package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -0
  114. package/dist/{tileset → tileset-3d}/helpers/frame-state.js +84 -9
  115. package/dist/tileset-3d/helpers/frame-state.js.map +1 -0
  116. package/dist/{tileset → tileset-3d}/helpers/i3s-lod.d.ts +1 -1
  117. package/dist/tileset-3d/helpers/i3s-lod.d.ts.map +1 -0
  118. package/dist/{tileset → tileset-3d}/helpers/i3s-lod.js +1 -1
  119. package/dist/tileset-3d/helpers/i3s-lod.js.map +1 -0
  120. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts +72 -0
  121. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts.map +1 -0
  122. package/dist/tileset-3d/helpers/tiles-3d-lod.js +257 -0
  123. package/dist/tileset-3d/helpers/tiles-3d-lod.js.map +1 -0
  124. package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts +134 -0
  125. package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts.map +1 -0
  126. package/dist/tileset-3d/helpers/tiles-3d-request-priority.js +179 -0
  127. package/dist/tileset-3d/helpers/tiles-3d-request-priority.js.map +1 -0
  128. package/dist/{tileset → tileset-3d}/helpers/transform-utils.d.ts +1 -1
  129. package/dist/tileset-3d/helpers/transform-utils.d.ts.map +1 -0
  130. package/dist/tileset-3d/helpers/transform-utils.js.map +1 -0
  131. package/dist/tileset-3d/helpers/zoom.d.ts.map +1 -0
  132. package/dist/tileset-3d/helpers/zoom.js.map +1 -0
  133. package/dist/types.d.ts +9 -0
  134. package/dist/types.d.ts.map +1 -1
  135. package/dist/types.js +3 -0
  136. package/dist/types.js.map +1 -1
  137. package/dist/utils/doubly-linked-list.d.ts.map +1 -1
  138. package/dist/utils/doubly-linked-list.js +1 -0
  139. package/dist/utils/doubly-linked-list.js.map +1 -1
  140. package/dist/utils/managed-array.d.ts.map +1 -1
  141. package/dist/utils/managed-array.js +1 -0
  142. package/dist/utils/managed-array.js.map +1 -1
  143. package/package.json +7 -6
  144. package/src/image-set.ts +328 -0
  145. package/src/index.ts +99 -9
  146. package/src/point-cloud/point-cloud-tile.ts +80 -0
  147. package/src/point-cloud/point-cloud-tileset.ts +713 -0
  148. package/src/point-cloud/types.ts +82 -0
  149. package/src/raster-set.ts +412 -0
  150. package/src/tileset-2d/adapter.ts +47 -0
  151. package/src/tileset-2d/index.ts +33 -0
  152. package/src/tileset-2d/tile-2d-header.ts +224 -0
  153. package/src/tileset-2d/tileset-2d.ts +716 -0
  154. package/src/tileset-2d/types.ts +39 -0
  155. package/src/tileset-2d.ts +5 -0
  156. package/src/tileset-3d/common/indexed-archive-tileset-source.ts +205 -0
  157. package/src/{tileset → tileset-3d/common}/tile-3d.ts +348 -93
  158. package/src/tileset-3d/common/tileset-3d.ts +1087 -0
  159. package/src/{tileset → tileset-3d/common}/tileset-cache.ts +16 -8
  160. package/src/tileset-3d/common/tileset-source.ts +255 -0
  161. package/src/{tileset → tileset-3d/common}/tileset-traverser.ts +32 -4
  162. package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +534 -0
  163. package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +632 -0
  164. package/src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts +140 -0
  165. package/src/{tileset → tileset-3d}/format-i3s/i3s-pending-tiles-register.ts +4 -0
  166. package/src/tileset-3d/format-i3s/i3s-source.ts +366 -0
  167. package/src/{tileset → tileset-3d}/format-i3s/i3s-tile-manager.ts +6 -2
  168. package/src/{tileset → tileset-3d}/format-i3s/i3s-tileset-traverser.ts +19 -24
  169. package/src/{tileset → tileset-3d}/helpers/3d-tiles-options.ts +5 -1
  170. package/src/{tileset → tileset-3d}/helpers/bounding-volume.ts +2 -0
  171. package/src/tileset-3d/helpers/frame-state.ts +311 -0
  172. package/src/{tileset → tileset-3d}/helpers/i3s-lod.ts +2 -2
  173. package/src/tileset-3d/helpers/tiles-3d-lod.ts +370 -0
  174. package/src/tileset-3d/helpers/tiles-3d-request-priority.ts +301 -0
  175. package/src/{tileset → tileset-3d}/helpers/transform-utils.ts +1 -1
  176. package/src/types.ts +13 -0
  177. package/src/utils/doubly-linked-list.ts +2 -0
  178. package/src/utils/managed-array.ts +2 -0
  179. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.d.ts +0 -7
  180. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.d.ts.map +0 -1
  181. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.js +0 -53
  182. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.js.map +0 -1
  183. package/dist/tileset/format-i3s/i3s-pending-tiles-register.d.ts.map +0 -1
  184. package/dist/tileset/format-i3s/i3s-pending-tiles-register.js.map +0 -1
  185. package/dist/tileset/format-i3s/i3s-tile-manager.d.ts.map +0 -1
  186. package/dist/tileset/format-i3s/i3s-tile-manager.js.map +0 -1
  187. package/dist/tileset/format-i3s/i3s-tileset-traverser.d.ts.map +0 -1
  188. package/dist/tileset/format-i3s/i3s-tileset-traverser.js.map +0 -1
  189. package/dist/tileset/helpers/3d-tiles-options.d.ts.map +0 -1
  190. package/dist/tileset/helpers/3d-tiles-options.js.map +0 -1
  191. package/dist/tileset/helpers/bounding-volume.d.ts.map +0 -1
  192. package/dist/tileset/helpers/bounding-volume.js.map +0 -1
  193. package/dist/tileset/helpers/frame-state.d.ts +0 -28
  194. package/dist/tileset/helpers/frame-state.d.ts.map +0 -1
  195. package/dist/tileset/helpers/frame-state.js.map +0 -1
  196. package/dist/tileset/helpers/i3s-lod.d.ts.map +0 -1
  197. package/dist/tileset/helpers/i3s-lod.js.map +0 -1
  198. package/dist/tileset/helpers/tiles-3d-lod.d.ts +0 -8
  199. package/dist/tileset/helpers/tiles-3d-lod.d.ts.map +0 -1
  200. package/dist/tileset/helpers/tiles-3d-lod.js +0 -113
  201. package/dist/tileset/helpers/tiles-3d-lod.js.map +0 -1
  202. package/dist/tileset/helpers/transform-utils.d.ts.map +0 -1
  203. package/dist/tileset/helpers/transform-utils.js.map +0 -1
  204. package/dist/tileset/helpers/zoom.d.ts.map +0 -1
  205. package/dist/tileset/helpers/zoom.js.map +0 -1
  206. package/dist/tileset/tile-3d.d.ts.map +0 -1
  207. package/dist/tileset/tile-3d.js.map +0 -1
  208. package/dist/tileset/tileset-3d.d.ts +0 -284
  209. package/dist/tileset/tileset-3d.d.ts.map +0 -1
  210. package/dist/tileset/tileset-3d.js +0 -814
  211. package/dist/tileset/tileset-3d.js.map +0 -1
  212. package/dist/tileset/tileset-cache.d.ts +0 -19
  213. package/dist/tileset/tileset-cache.d.ts.map +0 -1
  214. package/dist/tileset/tileset-cache.js.map +0 -1
  215. package/dist/tileset/tileset-traverser.d.ts.map +0 -1
  216. package/dist/tileset/tileset-traverser.js.map +0 -1
  217. package/src/tileset/format-3d-tiles/tileset-3d-traverser.ts +0 -63
  218. package/src/tileset/helpers/frame-state.ts +0 -183
  219. package/src/tileset/helpers/tiles-3d-lod.ts +0 -149
  220. package/src/tileset/tileset-3d.ts +0 -1037
  221. /package/dist/{tileset → tileset-3d}/format-i3s/i3s-pending-tiles-register.d.ts +0 -0
  222. /package/dist/{tileset → tileset-3d}/format-i3s/i3s-tile-manager.d.ts +0 -0
  223. /package/dist/{tileset → tileset-3d}/helpers/bounding-volume.d.ts +0 -0
  224. /package/dist/{tileset → tileset-3d}/helpers/transform-utils.js +0 -0
  225. /package/dist/{tileset → tileset-3d}/helpers/zoom.d.ts +0 -0
  226. /package/dist/{tileset → tileset-3d}/helpers/zoom.js +0 -0
  227. /package/src/{tileset → tileset-3d}/helpers/zoom.ts +0 -0
@@ -0,0 +1,632 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {path} from '@loaders.gl/loader-utils';
6
+ import {Ellipsoid} from '@math.gl/geospatial';
7
+ import {Vector3} from '@math.gl/core';
8
+ import type {CoreAPI, LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils';
9
+ import type {Tile3D} from '../common/tile-3d';
10
+ import {Tileset3DTraverser} from './tileset-3d-traverser';
11
+ import type {Tileset3D} from '../common/tileset-3d';
12
+ import type {
13
+ TileChildrenLoadResult,
14
+ TileContentLoadResult,
15
+ TilesetContentFormats,
16
+ TilesetJSON,
17
+ Tileset3DSource,
18
+ TilesetSourceInput,
19
+ TilesetSourceMetadata,
20
+ TilesetSourceRequest,
21
+ TilesetSourceResolver,
22
+ TilesetSourceViewState
23
+ } from '../common/tileset-source';
24
+ import {Tile3D as Tile3DNode} from '../common/tile-3d';
25
+ import {getZoomFromBoundingVolume} from '../helpers/zoom';
26
+ import {TILESET_TYPE} from '../../constants';
27
+ import type {TilesetTraverser, TilesetTraverserProps} from '../common/tileset-traverser';
28
+ import type {FrameState} from '../helpers/frame-state';
29
+ import {
30
+ materializeImplicitSubtree,
31
+ type ImplicitSubtreeReference,
32
+ type ParsedImplicitSubtree
33
+ } from './implicit-tiling';
34
+
35
+ const EMPTY_CONTENT_FORMATS: TilesetContentFormats = {
36
+ draco: false,
37
+ meshopt: false,
38
+ dds: false,
39
+ ktx2: false
40
+ };
41
+
42
+ const DEFAULT_MAXIMUM_CACHED_SUBTREES = 32;
43
+
44
+ /** Diagnostics for source-managed implicit subtree loading. */
45
+ export type ImplicitTilingStats = {
46
+ /** Subtree resources requested from the resolver or core API. */
47
+ requestedSubtrees: number;
48
+ /** Successful subtree materializations, including parsed-cache hits. */
49
+ loadedSubtrees: number;
50
+ /** Requests served by the source's parsed-subtree cache. */
51
+ cacheHits: number;
52
+ /** Parsed subtree resources currently retained for reuse. */
53
+ cachedSubtrees: number;
54
+ /** Subtree resource requests currently in flight. */
55
+ pendingSubtrees: number;
56
+ /** Runtime headers created below already-existing subtree-root placeholders. */
57
+ materializedTiles: number;
58
+ };
59
+
60
+ /**
61
+ * {@link Tileset3DSource} implementation for 3D Tiles datasets.
62
+ */
63
+ export class Tiles3DSource implements Tileset3DSource {
64
+ /** 3D Tiles format discriminator. */
65
+ readonly type = TILESET_TYPE.TILES3D;
66
+ /** Loader used for tile content requests. */
67
+ readonly loader: LoaderWithParser;
68
+ /** Root tileset URL. */
69
+ readonly url: string;
70
+ /** Base path used for relative tile resource resolution. */
71
+ readonly basePath: string;
72
+ /** Parsed root tileset payload. */
73
+ tileset: TilesetJSON | null = null;
74
+ /** Loader options forwarded to content requests. */
75
+ readonly loadOptions: LoaderOptions;
76
+ /** Core API used for root and tile-content loads when injected by the caller. */
77
+ coreApi?: CoreAPI;
78
+ /** Aggregate content-format flags discovered during streaming. */
79
+ readonly contentFormats: TilesetContentFormats = {...EMPTY_CONTENT_FORMATS};
80
+
81
+ /** Top-level 3D Tiles asset metadata. */
82
+ asset?: Record<string, any>;
83
+ /** Top-level per-feature properties metadata. */
84
+ properties?: any;
85
+ /** Top-level application-specific metadata. */
86
+ extras?: any;
87
+ /** Source-provided attribution metadata. */
88
+ credits?: any;
89
+ /** Normalized source metadata after initialization. */
90
+ metadata?: TilesetSourceMetadata;
91
+
92
+ private readonly queryParams: Record<string, string> = {};
93
+ /** Final request URLs cached by unmodified tile content URL. */
94
+ private readonly tileUrlCache: Map<string, string> = new Map();
95
+ /** Parsed subtree promises keyed by final source URL for request deduplication and LRU reuse. */
96
+ private readonly implicitSubtreeCache: Map<string, Promise<ParsedImplicitSubtree>> = new Map();
97
+ /** URLs whose subtree resource requests have not settled. */
98
+ private readonly pendingImplicitSubtreeUrls: Set<string> = new Set();
99
+ /** Maximum number of settled parsed subtrees retained by this source. */
100
+ private readonly maximumCachedSubtrees: number;
101
+ /** Mutable counters exposed as a defensive snapshot through {@link getImplicitTilingStats}. */
102
+ private readonly implicitTilingStats: Omit<
103
+ ImplicitTilingStats,
104
+ 'cachedSubtrees' | 'pendingSubtrees'
105
+ > = {
106
+ requestedSubtrees: 0,
107
+ loadedSubtrees: 0,
108
+ cacheHits: 0,
109
+ materializedTiles: 0
110
+ };
111
+ /** Whether the owning tileset has released this source. */
112
+ private destroyed = false;
113
+ private readonly extensionsUsed: string[] = [];
114
+ private readonly resolver?: TilesetSourceResolver;
115
+ private rootTileset: TilesetJSON;
116
+
117
+ /**
118
+ * Creates a 3D Tiles source.
119
+ * @param input Root metadata request, or preloaded root metadata for tests and internal callers.
120
+ * @param loadOptions Loader options forwarded to tile requests.
121
+ */
122
+ constructor(input: TilesetSourceInput, loadOptions: LoaderOptions = {}) {
123
+ const request = normalizeTiles3DRequest(input);
124
+ this.rootTileset = isTilesetRequest(input) ? null : input;
125
+ this.tileset = this.rootTileset;
126
+ this.loader = request.loader;
127
+ this.url = request.url;
128
+ this.basePath = request.basePath || path.dirname(request.url);
129
+ this.resolver = request.resolver;
130
+ this.coreApi = request.coreApi;
131
+ this.loadOptions = loadOptions;
132
+ const maximumCachedSubtrees = Number(
133
+ (loadOptions['3d-tiles'] as Record<string, unknown> | undefined)?.maximumCachedSubtrees
134
+ );
135
+ this.maximumCachedSubtrees = Number.isFinite(maximumCachedSubtrees)
136
+ ? Math.max(0, Math.floor(maximumCachedSubtrees))
137
+ : DEFAULT_MAXIMUM_CACHED_SUBTREES;
138
+ }
139
+
140
+ /**
141
+ * Releases URL and parsed-subtree caches and blocks late subtree installation.
142
+ *
143
+ * The injected resolver or core API remains responsible for transport-level abort signals. A
144
+ * request that cannot be aborted may finish parsing, but it will not mutate destroyed tiles.
145
+ */
146
+ destroy(): void {
147
+ this.destroyed = true;
148
+ this.tileUrlCache.clear();
149
+ this.implicitSubtreeCache.clear();
150
+ this.pendingImplicitSubtreeUrls.clear();
151
+ }
152
+
153
+ /**
154
+ * Fetches and validates top-level metadata, then normalizes common fields used by {@link Tileset3D}.
155
+ */
156
+ async initialize(): Promise<void> {
157
+ if (!this.rootTileset) {
158
+ const loaderOptions = (this.loadOptions[this.loader.id] as Record<string, unknown>) || {};
159
+ this.rootTileset = await this.loadRootData(this.url, {
160
+ ...this.loadOptions,
161
+ [this.loader.id]: {
162
+ ...loaderOptions,
163
+ isTileset: true
164
+ }
165
+ });
166
+ }
167
+ this.tileset = this.rootTileset;
168
+
169
+ if (this.rootTileset.queryString) {
170
+ const searchParams = new URLSearchParams(this.rootTileset.queryString);
171
+ for (const [parameterName, parameterValue] of searchParams.entries()) {
172
+ this.setQueryParameter(parameterName, parameterValue);
173
+ }
174
+ }
175
+
176
+ this.asset = this.rootTileset.asset;
177
+ if (!this.asset) {
178
+ throw new Error('Tileset must have an asset property.');
179
+ }
180
+ if (
181
+ this.asset.version !== '0.0' &&
182
+ this.asset.version !== '1.0' &&
183
+ this.asset.version !== '1.1'
184
+ ) {
185
+ throw new Error('The tileset must be 3D Tiles version either 0.0 or 1.0 or 1.1.');
186
+ }
187
+
188
+ if ('tilesetVersion' in this.asset) {
189
+ this.setQueryParameter('v', this.asset.tilesetVersion);
190
+ }
191
+
192
+ this.properties = this.rootTileset.properties;
193
+ this.extras = this.rootTileset.extras;
194
+ this.credits = {attributions: []};
195
+ this.extensionsUsed.splice(
196
+ 0,
197
+ this.extensionsUsed.length,
198
+ ...(this.rootTileset.extensionsUsed || [])
199
+ );
200
+ this.metadata = {
201
+ type: this.type,
202
+ loader: this.loader,
203
+ url: this.url,
204
+ basePath: this.basePath,
205
+ tileset: this.rootTileset,
206
+ lodMetricType: this.rootTileset.lodMetricType,
207
+ lodMetricValue: this.rootTileset.lodMetricValue,
208
+ refine: this.rootTileset.root?.refine
209
+ };
210
+ }
211
+
212
+ /**
213
+ * Returns normalized source metadata after initialization.
214
+ */
215
+ getMetadata(): TilesetSourceMetadata {
216
+ if (!this.metadata) {
217
+ throw new Error('Tiles3DSource has not been initialized');
218
+ }
219
+ return this.metadata;
220
+ }
221
+
222
+ /**
223
+ * Returns the root 3D Tiles payload.
224
+ */
225
+ async getRootTileset(): Promise<TilesetJSON> {
226
+ return this.getMetadata().tileset;
227
+ }
228
+
229
+ /**
230
+ * Builds explicit runtime headers while leaving implicit subtree references lazy.
231
+ */
232
+ initializeTileHeaders(
233
+ tileset: Tileset3D,
234
+ tilesetJson: TilesetJSON,
235
+ parentTile?: Tile3D | null
236
+ ): Tile3D {
237
+ const rootTile = new Tile3DNode(tileset, tilesetJson.root, parentTile || undefined);
238
+
239
+ if (parentTile) {
240
+ parentTile.children.push(rootTile);
241
+ rootTile.depth = parentTile.depth + 1;
242
+ }
243
+
244
+ const stack: Tile3D[] = [rootTile];
245
+ while (stack.length > 0) {
246
+ const tile = stack.pop() as Tile3D;
247
+ tileset.stats.get('Tiles In Tileset(s)').incrementCount();
248
+ const children = tile.header.children || [];
249
+ for (const childHeader of children) {
250
+ const childTile = new Tile3DNode(tileset, childHeader, tile);
251
+ if (childTile.contentUrl?.includes('?session=')) {
252
+ const url = new URL(childTile.contentUrl);
253
+ const session = url.searchParams.get('session');
254
+ if (session) {
255
+ this.setQueryParameter('session', session);
256
+ }
257
+ }
258
+ tile.children.push(childTile);
259
+ childTile.depth = tile.depth + 1;
260
+ stack.push(childTile);
261
+ }
262
+ }
263
+
264
+ return rootTile;
265
+ }
266
+
267
+ /**
268
+ * Creates the standard 3D Tiles traverser.
269
+ */
270
+ createTraverser(options: TilesetTraverserProps): TilesetTraverser {
271
+ return new Tileset3DTraverser(options);
272
+ }
273
+
274
+ /**
275
+ * Loads binary content or nested tileset JSON for a runtime tile.
276
+ */
277
+ async loadTileContent(tile: Tile3D): Promise<TileContentLoadResult> {
278
+ const contentUrl = this.getTileUrl(tile.contentUrl);
279
+ const tilesetLoaderOptions =
280
+ (this.loadOptions[this.loader.id] as Record<string, unknown>) || {};
281
+ const options = {
282
+ ...this.loadOptions,
283
+ [this.loader.id]: {
284
+ ...tilesetLoaderOptions,
285
+ // Content bytes, rather than URL suffixes, distinguish external tilesets from renderable
286
+ // payloads. This is required for signed and extensionless resources.
287
+ isTileset: 'auto',
288
+ assetGltfUpAxis: (this.asset && this.asset.gltfUpAxis) || 'Y'
289
+ }
290
+ };
291
+
292
+ const content = await this.loadResourceData(contentUrl, options);
293
+ tile.content = content;
294
+
295
+ return {
296
+ loaded: true,
297
+ nestedTileset: content?.shape === 'tileset3d' ? content : undefined
298
+ };
299
+ }
300
+
301
+ /**
302
+ * Loads, materializes, and installs exactly one implicit subtree.
303
+ *
304
+ * Final URLs pass through the same query inheritance and archive/custom resolver path as render
305
+ * content. Parsed resources are deduplicated by final URL, while the pure materializer creates
306
+ * lazy placeholders for every available child subtree instead of recursively fetching them.
307
+ *
308
+ * @param tile - Existing contentless subtree-root placeholder.
309
+ * @param frameState - View state that made this request eligible; priority is consumed earlier.
310
+ * @returns Counts describing the installed subtree.
311
+ */
312
+ async loadTileChildren(tile: Tile3D, frameState: FrameState): Promise<TileChildrenLoadResult> {
313
+ void frameState;
314
+ const reference = tile.header.implicitSubtree as ImplicitSubtreeReference | undefined;
315
+ if (!reference) {
316
+ return {loaded: false, tileCount: 0, childSubtreeCount: 0};
317
+ }
318
+
319
+ const subtreeUrl = this.getTileUrl(reference.subtreeUrl);
320
+ const subtree = await this.loadImplicitSubtreeResource(subtreeUrl);
321
+ if (this.destroyed || tile.isDestroyed()) {
322
+ return {loaded: false, tileCount: 0, childSubtreeCount: 0};
323
+ }
324
+ const materializedSubtree = materializeImplicitSubtree(subtree, {
325
+ ...reference,
326
+ subtreeUrl
327
+ });
328
+
329
+ tile.applyImplicitSubtreeHeader(materializedSubtree.root);
330
+ const materializedTileCount = this.initializeMaterializedChildren(
331
+ tile.tileset,
332
+ tile,
333
+ materializedSubtree.root.children
334
+ );
335
+ this.implicitTilingStats.loadedSubtrees++;
336
+ this.implicitTilingStats.materializedTiles += materializedTileCount;
337
+
338
+ return {
339
+ loaded: true,
340
+ tileCount: materializedSubtree.tileCount,
341
+ childSubtreeCount: materializedSubtree.childSubtreeCount
342
+ };
343
+ }
344
+
345
+ /**
346
+ * Returns a snapshot of implicit-subtree request, cache, and materialization counters.
347
+ *
348
+ * @returns Immutable-by-convention diagnostic values for runtime inspection.
349
+ */
350
+ getImplicitTilingStats(): ImplicitTilingStats {
351
+ return {
352
+ ...this.implicitTilingStats,
353
+ cachedSubtrees: this.implicitSubtreeCache.size - this.pendingImplicitSubtreeUrls.size,
354
+ pendingSubtrees: this.pendingImplicitSubtreeUrls.size
355
+ };
356
+ }
357
+
358
+ /**
359
+ * Resolves a tile content URL with source-managed query parameters.
360
+ *
361
+ * Existing per-resource parameters take precedence over inherited root, version, and session
362
+ * values. Completed URLs are cached by the original tile path; {@link setQueryParameter}
363
+ * invalidates the cache before changed source state can be observed.
364
+ *
365
+ * @param tilePath - Unmodified absolute content URL or data URL from the tile header.
366
+ * @returns Content URL with any missing source parameters appended.
367
+ */
368
+ getTileUrl(tilePath: string): string {
369
+ if (tilePath.startsWith('data:')) {
370
+ return tilePath;
371
+ }
372
+
373
+ const cachedTileUrl = this.tileUrlCache.get(tilePath);
374
+ if (cachedTileUrl) {
375
+ return cachedTileUrl;
376
+ }
377
+
378
+ if (!Object.keys(this.queryParams).length) {
379
+ this.tileUrlCache.set(tilePath, tilePath);
380
+ return tilePath;
381
+ }
382
+
383
+ const [pathWithoutQuery, existingQuery = ''] = tilePath.split('?');
384
+ const mergedQueryParams = new URLSearchParams(existingQuery);
385
+
386
+ for (const [key, value] of Object.entries(this.queryParams)) {
387
+ if (!mergedQueryParams.has(key)) {
388
+ mergedQueryParams.set(key, value);
389
+ }
390
+ }
391
+
392
+ const queryParams = mergedQueryParams.toString();
393
+ const tileUrl = queryParams ? `${pathWithoutQuery}?${queryParams}` : pathWithoutQuery;
394
+ this.tileUrlCache.set(tilePath, tileUrl);
395
+ return tileUrl;
396
+ }
397
+
398
+ /**
399
+ * Updates an inherited source query parameter and invalidates derived request URLs.
400
+ *
401
+ * Root tokens and tileset versions normally settle during initialization. Some providers expose
402
+ * a session parameter on a child URL, so invalidation is required to prevent URLs cached earlier
403
+ * in header construction from retaining stale authentication state.
404
+ *
405
+ * @param parameterName - Query parameter name.
406
+ * @param parameterValue - Query parameter value.
407
+ */
408
+ private setQueryParameter(parameterName: string, parameterValue: string): void {
409
+ if (this.queryParams[parameterName] === parameterValue) {
410
+ return;
411
+ }
412
+ this.queryParams[parameterName] = parameterValue;
413
+ this.tileUrlCache.clear();
414
+ }
415
+
416
+ /**
417
+ * Derives the default view state from the root bounding volume.
418
+ */
419
+ getViewState(rootTile: Tile3D | null): TilesetSourceViewState {
420
+ if (!rootTile) {
421
+ return {
422
+ asset: this.asset,
423
+ properties: this.properties,
424
+ extras: this.extras,
425
+ credits: this.credits
426
+ };
427
+ }
428
+
429
+ const {center} = rootTile.boundingVolume;
430
+ let cartographicCenter: Vector3;
431
+ if (center && (center[0] !== 0 || center[1] !== 0 || center[2] !== 0)) {
432
+ cartographicCenter = new Vector3();
433
+ Ellipsoid.WGS84.cartesianToCartographic(center, cartographicCenter);
434
+ } else {
435
+ cartographicCenter = new Vector3(0, 0, -Ellipsoid.WGS84.radii[0]);
436
+ }
437
+
438
+ return {
439
+ asset: this.asset,
440
+ properties: this.properties,
441
+ extras: this.extras,
442
+ credits: this.credits,
443
+ boundingVolume: rootTile.boundingVolume,
444
+ cartographicCenter,
445
+ cartesianCenter: center,
446
+ zoom: getZoomFromBoundingVolume(rootTile.boundingVolume, cartographicCenter)
447
+ };
448
+ }
449
+
450
+ /**
451
+ * Checks whether the root tileset declares the given extension.
452
+ */
453
+ hasExtension(extensionName: string): boolean {
454
+ return this.extensionsUsed.includes(extensionName);
455
+ }
456
+
457
+ /**
458
+ * Updates content-format flags and installs nested tileset subtrees.
459
+ */
460
+ onTileLoaded(tileset: Tileset3D, tile: Tile3D, loadResult: TileContentLoadResult): void {
461
+ const {extensionsRemoved = []} = tile.content?.gltf || {};
462
+ if (extensionsRemoved.includes('KHR_draco_mesh_compression')) {
463
+ this.contentFormats.draco = true;
464
+ }
465
+ if (extensionsRemoved.includes('EXT_meshopt_compression')) {
466
+ this.contentFormats.meshopt = true;
467
+ }
468
+ if (extensionsRemoved.includes('KHR_texture_basisu')) {
469
+ this.contentFormats.ktx2 = true;
470
+ }
471
+
472
+ if (loadResult.nestedTileset) {
473
+ tileset._initializeTileHeaders(loadResult.nestedTileset, tile);
474
+ }
475
+ }
476
+
477
+ /**
478
+ * Loads data through injected core APIs so this module stays independent from `@loaders.gl/core`.
479
+ */
480
+ private async loadWithCoreApi(
481
+ url: string,
482
+ options: LoaderOptions,
483
+ loader: LoaderWithParser = this.loader
484
+ ): Promise<any> {
485
+ if (!this.coreApi) {
486
+ throw new Error('Tiles3DSource requires an injected coreApi to load tileset data');
487
+ }
488
+
489
+ return await this.coreApi.load(url, loader, options);
490
+ }
491
+
492
+ /**
493
+ * Loads data through an injected resolver when present, otherwise through the injected core API.
494
+ */
495
+ private async loadRootData(url: string, options: LoaderOptions): Promise<any> {
496
+ if (this.resolver) {
497
+ return await this.resolver.loadRoot(url, this.loader, options);
498
+ }
499
+
500
+ return await this.loadWithCoreApi(url, options);
501
+ }
502
+
503
+ /**
504
+ * Loads an arbitrary source resource through an injected resolver or core API.
505
+ */
506
+ private async loadResourceData(
507
+ url: string,
508
+ options: LoaderOptions,
509
+ loader: LoaderWithParser = this.loader
510
+ ): Promise<any> {
511
+ if (this.resolver) {
512
+ return await this.resolver.loadResource(url, loader, options);
513
+ }
514
+
515
+ return await this.loadWithCoreApi(url, options, loader);
516
+ }
517
+
518
+ /**
519
+ * Returns a parsed subtree from the LRU cache or starts one source-managed request.
520
+ *
521
+ * @param subtreeUrl - Final subtree URL after query inheritance.
522
+ * @returns Parsed subtree availability data.
523
+ */
524
+ private async loadImplicitSubtreeResource(subtreeUrl: string): Promise<ParsedImplicitSubtree> {
525
+ const cachedSubtree = this.implicitSubtreeCache.get(subtreeUrl);
526
+ if (cachedSubtree) {
527
+ this.implicitTilingStats.cacheHits++;
528
+ this.implicitSubtreeCache.delete(subtreeUrl);
529
+ this.implicitSubtreeCache.set(subtreeUrl, cachedSubtree);
530
+ return await cachedSubtree;
531
+ }
532
+
533
+ this.implicitTilingStats.requestedSubtrees++;
534
+ this.pendingImplicitSubtreeUrls.add(subtreeUrl);
535
+ const loaderOptions = (this.loadOptions[this.loader.id] as Record<string, unknown>) || {};
536
+ const subtreePromise = this.loadResourceData(subtreeUrl, {
537
+ ...this.loadOptions,
538
+ [this.loader.id]: {
539
+ ...loaderOptions,
540
+ isTileset: false,
541
+ isSubtree: true
542
+ }
543
+ }) as Promise<ParsedImplicitSubtree>;
544
+ this.implicitSubtreeCache.set(subtreeUrl, subtreePromise);
545
+
546
+ try {
547
+ return await subtreePromise;
548
+ } catch (error) {
549
+ this.implicitSubtreeCache.delete(subtreeUrl);
550
+ throw error;
551
+ } finally {
552
+ this.pendingImplicitSubtreeUrls.delete(subtreeUrl);
553
+ this.trimImplicitSubtreeCache();
554
+ }
555
+ }
556
+
557
+ /**
558
+ * Instantiates every header represented by one materialized subtree.
559
+ *
560
+ * @param tileset - Owning runtime tileset.
561
+ * @param parentTile - Existing materialized parent.
562
+ * @param childHeaders - Headers to install below the parent.
563
+ * @returns Number of newly allocated runtime tile nodes.
564
+ */
565
+ private initializeMaterializedChildren(
566
+ tileset: Tileset3D,
567
+ parentTile: Tile3D,
568
+ childHeaders: Record<string, any>[]
569
+ ): number {
570
+ let materializedTileCount = 0;
571
+ const stack: Array<{parentTile: Tile3D; childHeaders: Record<string, any>[]}> = [
572
+ {parentTile, childHeaders}
573
+ ];
574
+ while (stack.length > 0) {
575
+ const entry = stack.pop()!;
576
+ for (const childHeader of entry.childHeaders) {
577
+ const childTile = new Tile3DNode(tileset, childHeader, entry.parentTile);
578
+ entry.parentTile.children.push(childTile);
579
+ childTile.depth = entry.parentTile.depth + 1;
580
+ tileset.stats.get('Tiles In Tileset(s)').incrementCount();
581
+ materializedTileCount++;
582
+ if (childHeader.children?.length) {
583
+ stack.push({parentTile: childTile, childHeaders: childHeader.children});
584
+ }
585
+ }
586
+ }
587
+ return materializedTileCount;
588
+ }
589
+
590
+ /** Evicts least-recently-used settled subtree entries until the configured bound is met. */
591
+ private trimImplicitSubtreeCache(): void {
592
+ if (this.implicitSubtreeCache.size <= this.maximumCachedSubtrees) {
593
+ return;
594
+ }
595
+ for (const subtreeUrl of this.implicitSubtreeCache.keys()) {
596
+ if (!this.pendingImplicitSubtreeUrls.has(subtreeUrl)) {
597
+ this.implicitSubtreeCache.delete(subtreeUrl);
598
+ }
599
+ if (this.implicitSubtreeCache.size <= this.maximumCachedSubtrees) {
600
+ break;
601
+ }
602
+ }
603
+ }
604
+ }
605
+
606
+ function isTilesetRequest(input: TilesetSourceInput): input is TilesetSourceRequest {
607
+ return Boolean(
608
+ input && typeof input === 'object' && 'url' in input && 'loader' in input && !('type' in input)
609
+ );
610
+ }
611
+
612
+ /**
613
+ * Normalizes constructor input into a URL request descriptor.
614
+ * @param input Constructor input for {@link Tiles3DSource}.
615
+ * @returns A normalized request with a resolved base path.
616
+ */
617
+ function normalizeTiles3DRequest(input: TilesetSourceInput): TilesetSourceRequest {
618
+ if (isTilesetRequest(input)) {
619
+ return {
620
+ ...input,
621
+ basePath: input.basePath || path.dirname(input.url)
622
+ };
623
+ }
624
+
625
+ return {
626
+ url: input.url,
627
+ loader: input.loader,
628
+ basePath: input.basePath || path.dirname(input.url),
629
+ resolver: (input as TilesetSourceRequest).resolver,
630
+ coreApi: (input as TilesetSourceRequest).coreApi
631
+ };
632
+ }