@loaders.gl/tiles 5.0.0-alpha.1 → 5.0.0-alpha.11

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 (205) hide show
  1. package/LICENSE +14 -3
  2. package/README.md +26 -0
  3. package/dist/dist.dev.js +20341 -5081
  4. package/dist/dist.min.js +3 -1
  5. package/dist/index.cjs +7904 -2071
  6. package/dist/index.cjs.map +3 -4
  7. package/dist/index.d.ts +25 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +11 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/point-cloud/point-cloud-tile.d.ts.map +1 -1
  12. package/dist/point-cloud/point-cloud-tile.js +3 -0
  13. package/dist/point-cloud/point-cloud-tile.js.map +1 -1
  14. package/dist/point-cloud/point-cloud-tileset.d.ts +4 -0
  15. package/dist/point-cloud/point-cloud-tileset.d.ts.map +1 -1
  16. package/dist/point-cloud/point-cloud-tileset.js +54 -12
  17. package/dist/point-cloud/point-cloud-tileset.js.map +1 -1
  18. package/dist/point-cloud/types.d.ts +17 -0
  19. package/dist/point-cloud/types.d.ts.map +1 -1
  20. package/dist/point-cloud/types.js +3 -0
  21. package/dist/point-cloud/types.js.map +1 -1
  22. package/dist/raster-set.d.ts +48 -31
  23. package/dist/raster-set.d.ts.map +1 -1
  24. package/dist/raster-set.js +104 -24
  25. package/dist/raster-set.js.map +1 -1
  26. package/dist/spatial/format-spatial-reference.d.ts +68 -0
  27. package/dist/spatial/format-spatial-reference.d.ts.map +1 -0
  28. package/dist/spatial/format-spatial-reference.js +291 -0
  29. package/dist/spatial/format-spatial-reference.js.map +1 -0
  30. package/dist/spatial/get-spatial-coordinate-frame.d.ts +20 -0
  31. package/dist/spatial/get-spatial-coordinate-frame.d.ts.map +1 -0
  32. package/dist/spatial/get-spatial-coordinate-frame.js +139 -0
  33. package/dist/spatial/get-spatial-coordinate-frame.js.map +1 -0
  34. package/dist/spatial/i3s-elevation.d.ts +20 -0
  35. package/dist/spatial/i3s-elevation.d.ts.map +1 -0
  36. package/dist/spatial/i3s-elevation.js +90 -0
  37. package/dist/spatial/i3s-elevation.js.map +1 -0
  38. package/dist/spatial/i3s-spatial-transformer.d.ts +205 -0
  39. package/dist/spatial/i3s-spatial-transformer.d.ts.map +1 -0
  40. package/dist/spatial/i3s-spatial-transformer.js +731 -0
  41. package/dist/spatial/i3s-spatial-transformer.js.map +1 -0
  42. package/dist/spatial/normalize-crs-identifier.d.ts +15 -0
  43. package/dist/spatial/normalize-crs-identifier.d.ts.map +1 -0
  44. package/dist/spatial/normalize-crs-identifier.js +22 -0
  45. package/dist/spatial/normalize-crs-identifier.js.map +1 -0
  46. package/dist/spatial/spatial-coordinate-transformer.d.ts +52 -0
  47. package/dist/spatial/spatial-coordinate-transformer.d.ts.map +1 -0
  48. package/dist/spatial/spatial-coordinate-transformer.js +295 -0
  49. package/dist/spatial/spatial-coordinate-transformer.js.map +1 -0
  50. package/dist/spatial/spatial-resource-registry.d.ts +47 -0
  51. package/dist/spatial/spatial-resource-registry.d.ts.map +1 -0
  52. package/dist/spatial/spatial-resource-registry.js +76 -0
  53. package/dist/spatial/spatial-resource-registry.js.map +1 -0
  54. package/dist/spatial/spatial-types.d.ts +190 -0
  55. package/dist/spatial/spatial-types.d.ts.map +1 -0
  56. package/dist/spatial/spatial-types.js +192 -0
  57. package/dist/spatial/spatial-types.js.map +1 -0
  58. package/dist/spatial/tiles-3d-spatial-transformer.d.ts +32 -0
  59. package/dist/spatial/tiles-3d-spatial-transformer.d.ts.map +1 -0
  60. package/dist/spatial/tiles-3d-spatial-transformer.js +163 -0
  61. package/dist/spatial/tiles-3d-spatial-transformer.js.map +1 -0
  62. package/dist/tileset-2d/adapter.d.ts +2 -3
  63. package/dist/tileset-2d/adapter.d.ts.map +1 -1
  64. package/dist/tileset-2d/tileset-2d.d.ts +2 -3
  65. package/dist/tileset-2d/tileset-2d.d.ts.map +1 -1
  66. package/dist/tileset-2d/tileset-2d.js.map +1 -1
  67. package/dist/tileset-3d/common/indexed-archive-tileset-source.d.ts +3 -3
  68. package/dist/tileset-3d/common/indexed-archive-tileset-source.d.ts.map +1 -1
  69. package/dist/tileset-3d/common/indexed-archive-tileset-source.js +9 -4
  70. package/dist/tileset-3d/common/indexed-archive-tileset-source.js.map +1 -1
  71. package/dist/tileset-3d/common/tile-3d-contracts.d.ts +63 -0
  72. package/dist/tileset-3d/common/tile-3d-contracts.d.ts.map +1 -0
  73. package/dist/tileset-3d/common/tile-3d-contracts.js +51 -0
  74. package/dist/tileset-3d/common/tile-3d-contracts.js.map +1 -0
  75. package/dist/tileset-3d/common/tile-3d.d.ts +176 -5
  76. package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
  77. package/dist/tileset-3d/common/tile-3d.js +523 -89
  78. package/dist/tileset-3d/common/tile-3d.js.map +1 -1
  79. package/dist/tileset-3d/common/tileset-3d.d.ts +199 -9
  80. package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
  81. package/dist/tileset-3d/common/tileset-3d.js +345 -32
  82. package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
  83. package/dist/tileset-3d/common/tileset-cache.d.ts +11 -1
  84. package/dist/tileset-3d/common/tileset-cache.d.ts.map +1 -1
  85. package/dist/tileset-3d/common/tileset-cache.js +11 -3
  86. package/dist/tileset-3d/common/tileset-cache.js.map +1 -1
  87. package/dist/tileset-3d/common/tileset-content-traversal.d.ts +37 -0
  88. package/dist/tileset-3d/common/tileset-content-traversal.d.ts.map +1 -0
  89. package/dist/tileset-3d/common/tileset-content-traversal.js +80 -0
  90. package/dist/tileset-3d/common/tileset-content-traversal.js.map +1 -0
  91. package/dist/tileset-3d/common/tileset-observability.d.ts +60 -0
  92. package/dist/tileset-3d/common/tileset-observability.d.ts.map +1 -0
  93. package/dist/tileset-3d/common/tileset-observability.js +39 -0
  94. package/dist/tileset-3d/common/tileset-observability.js.map +1 -0
  95. package/dist/tileset-3d/common/tileset-source.d.ts +51 -9
  96. package/dist/tileset-3d/common/tileset-source.d.ts.map +1 -1
  97. package/dist/tileset-3d/common/tileset-source.js.map +1 -1
  98. package/dist/tileset-3d/common/tileset-traverser.d.ts +33 -1
  99. package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
  100. package/dist/tileset-3d/common/tileset-traverser.js +61 -14
  101. package/dist/tileset-3d/common/tileset-traverser.js.map +1 -1
  102. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts +182 -0
  103. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts.map +1 -0
  104. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js +681 -0
  105. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js.map +1 -0
  106. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +127 -6
  107. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts.map +1 -1
  108. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js +485 -27
  109. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -1
  110. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts +104 -0
  111. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -1
  112. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js +286 -0
  113. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js.map +1 -1
  114. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.d.ts.map +1 -1
  115. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js +3 -0
  116. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js.map +1 -1
  117. package/dist/tileset-3d/format-i3s/i3s-source.d.ts +23 -6
  118. package/dist/tileset-3d/format-i3s/i3s-source.d.ts.map +1 -1
  119. package/dist/tileset-3d/format-i3s/i3s-source.js +176 -25
  120. package/dist/tileset-3d/format-i3s/i3s-source.js.map +1 -1
  121. package/dist/tileset-3d/format-i3s/i3s-tile-manager.d.ts.map +1 -1
  122. package/dist/tileset-3d/format-i3s/i3s-tile-manager.js +3 -0
  123. package/dist/tileset-3d/format-i3s/i3s-tile-manager.js.map +1 -1
  124. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.d.ts.map +1 -1
  125. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js +4 -1
  126. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js.map +1 -1
  127. package/dist/tileset-3d/helpers/3d-tiles-options.d.ts.map +1 -1
  128. package/dist/tileset-3d/helpers/3d-tiles-options.js +3 -0
  129. package/dist/tileset-3d/helpers/3d-tiles-options.js.map +1 -1
  130. package/dist/tileset-3d/helpers/bounding-volume.d.ts +7 -3
  131. package/dist/tileset-3d/helpers/bounding-volume.d.ts.map +1 -1
  132. package/dist/tileset-3d/helpers/bounding-volume.js +20 -91
  133. package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -1
  134. package/dist/tileset-3d/helpers/frame-state.d.ts +67 -2
  135. package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
  136. package/dist/tileset-3d/helpers/frame-state.js +92 -10
  137. package/dist/tileset-3d/helpers/frame-state.js.map +1 -1
  138. package/dist/tileset-3d/helpers/i3s-lod.d.ts.map +1 -1
  139. package/dist/tileset-3d/helpers/i3s-lod.js +20 -8
  140. package/dist/tileset-3d/helpers/i3s-lod.js.map +1 -1
  141. package/dist/tileset-3d/helpers/s2-bounding-volume.d.ts +17 -0
  142. package/dist/tileset-3d/helpers/s2-bounding-volume.d.ts.map +1 -0
  143. package/dist/tileset-3d/helpers/s2-bounding-volume.js +20 -0
  144. package/dist/tileset-3d/helpers/s2-bounding-volume.js.map +1 -0
  145. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts +61 -9
  146. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts.map +1 -1
  147. package/dist/tileset-3d/helpers/tiles-3d-lod.js +198 -93
  148. package/dist/tileset-3d/helpers/tiles-3d-lod.js.map +1 -1
  149. package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts +134 -0
  150. package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts.map +1 -0
  151. package/dist/tileset-3d/helpers/tiles-3d-request-priority.js +179 -0
  152. package/dist/tileset-3d/helpers/tiles-3d-request-priority.js.map +1 -0
  153. package/dist/types.d.ts +7 -0
  154. package/dist/types.d.ts.map +1 -1
  155. package/dist/types.js +3 -0
  156. package/dist/types.js.map +1 -1
  157. package/dist/utils/doubly-linked-list.d.ts.map +1 -1
  158. package/dist/utils/doubly-linked-list.js +1 -0
  159. package/dist/utils/doubly-linked-list.js.map +1 -1
  160. package/dist/utils/managed-array.d.ts.map +1 -1
  161. package/dist/utils/managed-array.js +1 -0
  162. package/dist/utils/managed-array.js.map +1 -1
  163. package/package.json +12 -10
  164. package/src/index.ts +79 -1
  165. package/src/point-cloud/point-cloud-tile.ts +4 -0
  166. package/src/point-cloud/point-cloud-tileset.ts +70 -12
  167. package/src/point-cloud/types.ts +21 -0
  168. package/src/raster-set.ts +197 -67
  169. package/src/spatial/format-spatial-reference.ts +385 -0
  170. package/src/spatial/get-spatial-coordinate-frame.ts +169 -0
  171. package/src/spatial/i3s-elevation.ts +96 -0
  172. package/src/spatial/i3s-spatial-transformer.ts +1021 -0
  173. package/src/spatial/normalize-crs-identifier.ts +26 -0
  174. package/src/spatial/spatial-coordinate-transformer.ts +377 -0
  175. package/src/spatial/spatial-resource-registry.ts +90 -0
  176. package/src/spatial/spatial-types.ts +422 -0
  177. package/src/spatial/tiles-3d-spatial-transformer.ts +194 -0
  178. package/src/tileset-2d/adapter.ts +2 -3
  179. package/src/tileset-2d/tileset-2d.ts +2 -3
  180. package/src/tileset-3d/common/indexed-archive-tileset-source.ts +13 -13
  181. package/src/tileset-3d/common/tile-3d-contracts.ts +115 -0
  182. package/src/tileset-3d/common/tile-3d.ts +629 -98
  183. package/src/tileset-3d/common/tileset-3d.ts +512 -40
  184. package/src/tileset-3d/common/tileset-cache.ts +15 -7
  185. package/src/tileset-3d/common/tileset-content-traversal.ts +114 -0
  186. package/src/tileset-3d/common/tileset-observability.ts +100 -0
  187. package/src/tileset-3d/common/tileset-source.ts +63 -13
  188. package/src/tileset-3d/common/tileset-traverser.ts +73 -15
  189. package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +1059 -0
  190. package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +674 -28
  191. package/src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts +328 -0
  192. package/src/tileset-3d/format-i3s/i3s-pending-tiles-register.ts +4 -0
  193. package/src/tileset-3d/format-i3s/i3s-source.ts +220 -29
  194. package/src/tileset-3d/format-i3s/i3s-tile-manager.ts +4 -0
  195. package/src/tileset-3d/format-i3s/i3s-tileset-traverser.ts +6 -1
  196. package/src/tileset-3d/helpers/3d-tiles-options.ts +4 -0
  197. package/src/tileset-3d/helpers/bounding-volume.ts +31 -114
  198. package/src/tileset-3d/helpers/frame-state.ts +152 -13
  199. package/src/tileset-3d/helpers/i3s-lod.ts +22 -8
  200. package/src/tileset-3d/helpers/s2-bounding-volume.ts +35 -0
  201. package/src/tileset-3d/helpers/tiles-3d-lod.ts +291 -112
  202. package/src/tileset-3d/helpers/tiles-3d-request-priority.ts +301 -0
  203. package/src/types.ts +11 -0
  204. package/src/utils/doubly-linked-list.ts +2 -0
  205. package/src/utils/managed-array.ts +2 -0
@@ -5,13 +5,15 @@
5
5
  import {path} from '@loaders.gl/loader-utils';
6
6
  import {Ellipsoid} from '@math.gl/geospatial';
7
7
  import {Vector3} from '@math.gl/core';
8
- import type {CoreAPI, LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils';
8
+ import type {CoreAPI, Loader, LoaderOptions} from '@loaders.gl/loader-utils';
9
9
  import type {Tile3D} from '../common/tile-3d';
10
10
  import {Tile3D as Tile3DNode} from '../common/tile-3d';
11
+ import type {Tile3DContent} from '../common/tile-3d-contracts';
11
12
  import {I3STilesetTraverser} from './i3s-tileset-traverser';
12
13
  import type {Tileset3D} from '../common/tileset-3d';
13
14
  import type {FrameState} from '../helpers/frame-state';
14
15
  import type {
16
+ TileChildrenLoadResult,
15
17
  TileContentLoadResult,
16
18
  TilesetContentFormats,
17
19
  TilesetJSON,
@@ -25,6 +27,8 @@ import type {
25
27
  import {getZoomFromExtent, getZoomFromFullExtent} from '../helpers/zoom';
26
28
  import {TILESET_TYPE} from '../../constants';
27
29
  import type {TilesetTraverser, TilesetTraverserProps} from '../common/tileset-traverser';
30
+ import {getI3SSpatialReference} from '../../spatial/format-spatial-reference';
31
+ import {I3SSpatialTransformer} from '../../spatial/i3s-spatial-transformer';
28
32
 
29
33
  const EMPTY_CONTENT_FORMATS: TilesetContentFormats = {
30
34
  draco: false,
@@ -39,8 +43,10 @@ const EMPTY_CONTENT_FORMATS: TilesetContentFormats = {
39
43
  export class I3SSource implements Tileset3DSource {
40
44
  /** I3S format discriminator. */
41
45
  readonly type = TILESET_TYPE.I3S;
42
- /** Loader used for tile metadata and content requests. */
43
- readonly loader: LoaderWithParser;
46
+ /** Primary I3S loader used for root and child metadata requests. */
47
+ readonly loader: Loader;
48
+ /** Worker-capable loader used for binary tile content when supplied by the format loader. */
49
+ readonly contentLoader: Loader;
44
50
  /** Root I3S layer URL. */
45
51
  readonly url: string;
46
52
  /** Base path used for relative tile resource resolution. */
@@ -63,6 +69,10 @@ export class I3SSource implements Tileset3DSource {
63
69
  private readonly queryParams: Record<string, string> = {};
64
70
  private readonly resolver?: TilesetSourceResolver;
65
71
  private rootTileset: TilesetJSON;
72
+ /** Spatial adapter created lazily after Tileset3D applies target options. */
73
+ private spatialTransformer?: I3SSpatialTransformer;
74
+ /** Root header prepared after asynchronous elevation sampling. */
75
+ private preparedRootHeader?: any;
66
76
 
67
77
  /**
68
78
  * Creates an I3S source.
@@ -74,11 +84,13 @@ export class I3SSource implements Tileset3DSource {
74
84
  this.rootTileset = isTilesetRequest(input) ? null : input;
75
85
  this.tileset = this.rootTileset;
76
86
  this.loader = request.loader;
87
+ this.contentLoader = getI3SContentLoader(request.loader);
77
88
  this.url = request.url;
78
89
  this.basePath = request.basePath || path.dirname(request.url);
79
90
  this.resolver = request.resolver;
80
91
  this.coreApi = request.coreApi;
81
92
  this.loadOptions = loadOptions;
93
+ this.initializeQueryParams(loadOptions);
82
94
  }
83
95
 
84
96
  /**
@@ -86,7 +98,10 @@ export class I3SSource implements Tileset3DSource {
86
98
  */
87
99
  async initialize(): Promise<void> {
88
100
  if (!this.rootTileset) {
89
- this.rootTileset = await this.loadRootData(this.url, this.loadOptions);
101
+ this.rootTileset = await this.loadRootData(this.getTileUrl(this.url), {
102
+ ...this.loadOptions,
103
+ i3s: {...(this.loadOptions.i3s as Record<string, unknown>), isTileset: true}
104
+ });
90
105
  }
91
106
  this.tileset = this.rootTileset;
92
107
 
@@ -94,14 +109,6 @@ export class I3SSource implements Tileset3DSource {
94
109
  this.rootTileset.root = await this.rootTileset.root;
95
110
  }
96
111
 
97
- const i3sOptions = this.loadOptions.i3s;
98
- if (i3sOptions && typeof i3sOptions === 'object' && 'token' in i3sOptions) {
99
- const token = (i3sOptions as Record<string, unknown>).token;
100
- if (typeof token === 'string') {
101
- this.queryParams.token = token;
102
- }
103
- }
104
-
105
112
  this.metadata = {
106
113
  type: this.type,
107
114
  loader: this.loader,
@@ -110,7 +117,8 @@ export class I3SSource implements Tileset3DSource {
110
117
  tileset: this.rootTileset,
111
118
  lodMetricType: this.rootTileset.lodMetricType,
112
119
  lodMetricValue: this.rootTileset.lodMetricValue,
113
- refine: this.rootTileset.root?.refine
120
+ refine: this.rootTileset.root?.refine,
121
+ spatialReference: getI3SSpatialReference(this.rootTileset)
114
122
  };
115
123
  }
116
124
 
@@ -131,6 +139,26 @@ export class I3SSource implements Tileset3DSource {
131
139
  return this.getMetadata().tileset;
132
140
  }
133
141
 
142
+ /** Prepare option-dependent spatial state and the asynchronously placed root bound. */
143
+ async prepareTileset(tileset: Tileset3D): Promise<void> {
144
+ const spatialReference = tileset.spatialReference;
145
+ if (spatialReference.status === 'unresolved') {
146
+ throw new Error(
147
+ spatialReference.warnings[0] ||
148
+ 'I3S spatial operations cannot be resolved from the supplied metadata and options'
149
+ );
150
+ }
151
+ if (spatialReference.status !== 'transformable' && spatialReference.status !== 'transformed') {
152
+ return;
153
+ }
154
+ this.spatialTransformer ||= new I3SSpatialTransformer(
155
+ spatialReference,
156
+ tileset.options.spatial
157
+ );
158
+ tileset.spatialReference = this.spatialTransformer.spatialReference;
159
+ this.preparedRootHeader = await this.transformTileHeader(this.getMetadata().tileset.root);
160
+ }
161
+
134
162
  /**
135
163
  * Creates the runtime root tile for an I3S subtree.
136
164
  */
@@ -139,7 +167,11 @@ export class I3SSource implements Tileset3DSource {
139
167
  tilesetJson: TilesetJSON,
140
168
  parentTile?: Tile3D | null
141
169
  ): Tile3D {
142
- const rootTile = new Tile3DNode(tileset, tilesetJson.root, parentTile || undefined);
170
+ const rootHeader = parentTile ? tilesetJson.root : this.preparedRootHeader || tilesetJson.root;
171
+ if (this.spatialTransformer && !this.preparedRootHeader) {
172
+ throw new Error('I3S spatial root header was not prepared before tile initialization');
173
+ }
174
+ const rootTile = new Tile3DNode(tileset, rootHeader, parentTile || undefined);
143
175
  if (parentTile) {
144
176
  parentTile.children.push(rootTile);
145
177
  rootTile.depth = parentTile.depth + 1;
@@ -170,23 +202,39 @@ export class I3SSource implements Tileset3DSource {
170
202
  _tileOptions: {
171
203
  attributeUrls: tile.header.attributeUrls,
172
204
  textureUrl: tile.header.textureUrl,
205
+ textureUrls: tile.header.textureUrls,
173
206
  textureFormat: tile.header.textureFormat,
174
207
  textureLoaderOptions: tile.header.textureLoaderOptions,
175
208
  materialDefinition: tile.header.materialDefinition,
209
+ layerType: tile.header.layerType,
210
+ pointRenderer: tile.header.pointRenderer,
211
+ pointSymbol: tile.header.pointSymbol,
176
212
  isDracoGeometry: tile.header.isDracoGeometry,
177
213
  mbs: tile.header.mbs
178
214
  },
179
215
  _tilesetOptions: {
180
216
  store: metadata.tileset.store,
181
217
  attributeStorageInfo: metadata.tileset.attributeStorageInfo,
182
- fields: metadata.tileset.fields
218
+ fields: metadata.tileset.fields,
219
+ spatialReference: tile.tileset.spatialReference,
220
+ spatialOptions: tile.tileset.options.spatial
183
221
  },
184
222
  isTileHeader: false
185
223
  }
186
224
  };
187
225
 
188
- tile.content = await this.loadResourceData(contentUrl, options);
189
- return {loaded: true};
226
+ tile.content = await this.loadResourceData(contentUrl, options, this.contentLoader);
227
+ const contentEntry: Tile3DContent = {
228
+ index: 0,
229
+ uri: tile.contentUrl,
230
+ type: tile.header.layerType,
231
+ payload: tile.content,
232
+ metadata: tile.header.metadata || null,
233
+ boundingVolume: tile.boundingVolume,
234
+ featureIds: tile.featureIdSets,
235
+ renderable: Boolean(tile.content)
236
+ };
237
+ return {loaded: true, contents: [tile.content], contentEntries: [contentEntry]};
190
238
  }
191
239
 
192
240
  /**
@@ -195,11 +243,12 @@ export class I3SSource implements Tileset3DSource {
195
243
  async loadChildTileHeader(
196
244
  _parentTile: Tile3D,
197
245
  childId: string,
198
- _frameState: FrameState
246
+ _frameState: FrameState | null
199
247
  ): Promise<any> {
200
248
  const metadata = this.getMetadata();
201
- if (metadata.tileset.nodePages) {
202
- return await metadata.tileset.nodePagesTile.formTileFromNodePages(childId);
249
+ if (metadata.tileset.nodePages || metadata.tileset.pointNodePages) {
250
+ const header = await metadata.tileset.nodePagesTile.formTileFromNodePages(childId);
251
+ return await this.transformTileHeader(header);
203
252
  }
204
253
 
205
254
  const nodeUrl = this.getTileUrl(`${this.url}/nodes/${childId}`);
@@ -215,7 +264,52 @@ export class I3SSource implements Tileset3DSource {
215
264
  }
216
265
  };
217
266
 
218
- return await this.loadResourceData(nodeUrl, options);
267
+ const header = await this.loadResourceData(nodeUrl, options);
268
+ return await this.transformTileHeader(header);
269
+ }
270
+
271
+ /** Loads all declared I3S child headers without camera or visibility state. */
272
+ async loadTileChildrenForTraversal(
273
+ parentTile: Tile3D,
274
+ signal?: AbortSignal
275
+ ): Promise<TileChildrenLoadResult> {
276
+ const childHeaders = parentTile.header.children || [];
277
+ let loadedChildren = 0;
278
+ const orderedChildren: Tile3D[] = [];
279
+
280
+ for (const childHeader of childHeaders) {
281
+ throwIfTraversalAborted(signal);
282
+ const childId = childHeader.id;
283
+ if (typeof childId !== 'string' && typeof childId !== 'number') {
284
+ throw new Error('I3S child header is missing its node ID');
285
+ }
286
+ const existingChild = parentTile.children.find(
287
+ tile => tile.id === String(childId) || tile.header?.id === childId
288
+ );
289
+ if (existingChild) {
290
+ orderedChildren.push(existingChild);
291
+ continue;
292
+ }
293
+
294
+ const childTileHeader = await this.loadChildTileHeader(parentTile, String(childId), null);
295
+ throwIfTraversalAborted(signal);
296
+ const childTile = new Tile3DNode(
297
+ parentTile.tileset,
298
+ childTileHeader,
299
+ parentTile,
300
+ String(childId)
301
+ );
302
+ orderedChildren.push(childTile);
303
+ loadedChildren++;
304
+ }
305
+
306
+ const orderedChildSet = new Set(orderedChildren);
307
+ parentTile.children = [
308
+ ...orderedChildren,
309
+ ...parentTile.children.filter(childTile => !orderedChildSet.has(childTile))
310
+ ];
311
+
312
+ return {loaded: true, tileCount: loadedChildren, childSubtreeCount: 0};
219
313
  }
220
314
 
221
315
  /**
@@ -230,14 +324,52 @@ export class I3SSource implements Tileset3DSource {
230
324
  return tilePath;
231
325
  }
232
326
 
233
- const queryParams = new URLSearchParams(this.queryParams).toString();
234
- return `${tilePath}${tilePath.includes('?') ? '&' : '?'}${queryParams}`;
327
+ const hashIndex = tilePath.indexOf('#');
328
+ const hash = hashIndex >= 0 ? tilePath.slice(hashIndex) : '';
329
+ const tilePathWithoutHash = hashIndex >= 0 ? tilePath.slice(0, hashIndex) : tilePath;
330
+ const queryDelimiterIndex = tilePathWithoutHash.indexOf('?');
331
+ const path =
332
+ queryDelimiterIndex === -1
333
+ ? tilePathWithoutHash
334
+ : tilePathWithoutHash.slice(0, queryDelimiterIndex);
335
+ const existingQuery =
336
+ queryDelimiterIndex === -1 ? '' : tilePathWithoutHash.slice(queryDelimiterIndex + 1);
337
+ const existingQueryParams = new URLSearchParams(existingQuery);
338
+ const queryParams = new URLSearchParams();
339
+ for (const [key, value] of Object.entries(this.queryParams)) {
340
+ if (!existingQueryParams.has(key)) {
341
+ queryParams.set(key, value);
342
+ }
343
+ }
344
+
345
+ const queryString = queryParams.toString();
346
+ return `${path}${existingQuery ? `?${existingQuery}` : ''}${
347
+ queryString ? `${existingQuery ? '&' : '?'}${queryString}` : ''
348
+ }${hash}`;
235
349
  }
236
350
 
237
351
  /**
238
352
  * Derives the default view state from full extent or store extent metadata.
239
353
  */
240
- getViewState(_rootTile: Tile3D | null): TilesetSourceViewState {
354
+ getViewState(rootTile: Tile3D | null): TilesetSourceViewState {
355
+ if (this.spatialTransformer && rootTile) {
356
+ const center = new Vector3(rootTile.boundingVolume.center);
357
+ const cartographicCenter = new Vector3(rootTile.header.i3sLodMbs.slice(0, 3));
358
+ if (this.spatialTransformer.targetCoordinateFrame === 'geographic') {
359
+ return {
360
+ boundingVolume: rootTile.boundingVolume,
361
+ cartographicCenter,
362
+ cartesianCenter: center,
363
+ zoom: 1
364
+ };
365
+ }
366
+ return {
367
+ boundingVolume: rootTile.boundingVolume,
368
+ cartographicCenter,
369
+ cartesianCenter: center,
370
+ zoom: 1
371
+ };
372
+ }
241
373
  const metadata = this.getMetadata();
242
374
  const fullExtent = metadata.tileset.fullExtent;
243
375
  if (fullExtent) {
@@ -306,12 +438,16 @@ export class I3SSource implements Tileset3DSource {
306
438
  /**
307
439
  * Loads data through injected core APIs so this module stays independent from `@loaders.gl/core`.
308
440
  */
309
- private async loadWithCoreApi(url: string, options: LoaderOptions): Promise<any> {
441
+ private async loadWithCoreApi(
442
+ url: string,
443
+ options: LoaderOptions,
444
+ loader: Loader = this.loader
445
+ ): Promise<any> {
310
446
  if (!this.coreApi) {
311
447
  throw new Error('I3SSource requires an injected coreApi to load tileset data');
312
448
  }
313
449
 
314
- return await this.coreApi.load(url, this.loader, options);
450
+ return await this.coreApi.load(url, loader, options);
315
451
  }
316
452
 
317
453
  /**
@@ -325,18 +461,73 @@ export class I3SSource implements Tileset3DSource {
325
461
  return await this.loadWithCoreApi(url, options);
326
462
  }
327
463
 
464
+ /**
465
+ * Initializes query parameters before any URL-backed resource is requested.
466
+ * @param loadOptions Loader options that may contain an ArcGIS token.
467
+ */
468
+ private initializeQueryParams(loadOptions: LoaderOptions): void {
469
+ for (const [key, value] of Object.entries(loadOptions.searchParams || {})) {
470
+ this.queryParams[key] = String(value);
471
+ }
472
+
473
+ const i3sOptions = loadOptions.i3s;
474
+ if (i3sOptions && typeof i3sOptions === 'object' && 'token' in i3sOptions) {
475
+ const token = (i3sOptions as Record<string, unknown>).token;
476
+ if (typeof token === 'string') {
477
+ this.queryParams.token = token;
478
+ }
479
+ }
480
+ }
481
+
328
482
  /**
329
483
  * Loads tile metadata or content through an injected resolver when present, otherwise through the injected core API.
330
484
  */
331
- private async loadResourceData(url: string, options: LoaderOptions): Promise<any> {
485
+ private async loadResourceData(
486
+ url: string,
487
+ options: LoaderOptions,
488
+ loader: Loader = this.loader
489
+ ): Promise<any> {
332
490
  if (this.resolver) {
333
- return await this.resolver.loadResource(url, this.loader, options);
491
+ return await this.resolver.loadResource(url, loader, options);
334
492
  }
335
493
 
336
- return await this.loadWithCoreApi(url, options);
494
+ return await this.loadWithCoreApi(url, options, loader);
495
+ }
496
+
497
+ /** Transform one I3S header bound after target options and elevation providers are available. */
498
+ private async transformTileHeader(header: any): Promise<any> {
499
+ if (!this.spatialTransformer) {
500
+ return header;
501
+ }
502
+ const sourceBounds = {
503
+ mbs: header.mbs,
504
+ obb: header.obb,
505
+ normalReferenceFrame: this.getMetadata().tileset.store?.normalReferenceFrame
506
+ };
507
+ const transformedBounds = await this.spatialTransformer.transformBoundsAsync(sourceBounds);
508
+ return {
509
+ ...header,
510
+ // Tileset3D traversal and culling operate in WGS84 ECEF independently of content output.
511
+ boundingVolume: transformedBounds.boundingVolume,
512
+ // Preserve the renderer/output-frame bound for applications and content coordination.
513
+ spatialBoundingVolume: transformedBounds.spatialBoundingVolume,
514
+ i3sLodMbs: transformedBounds.i3sLodMbs
515
+ };
337
516
  }
338
517
  }
339
518
 
519
+ /** Throws a cancellation reason before or after source-managed traversal work. */
520
+ function throwIfTraversalAborted(signal?: AbortSignal): void {
521
+ if (signal?.aborted) {
522
+ throw signal.reason ?? new DOMException('The operation was aborted', 'AbortError');
523
+ }
524
+ }
525
+
526
+ /** Returns the format-provided binary content loader, falling back for custom legacy loaders. */
527
+ function getI3SContentLoader(loader: Loader): Loader {
528
+ return (loader as Loader & {contentLoader?: Loader}).contentLoader || loader;
529
+ }
530
+
340
531
  function isTilesetRequest(input: TilesetSourceInput): input is TilesetSourceRequest {
341
532
  return Boolean(
342
533
  input && typeof input === 'object' && 'url' in input && 'loader' in input && !('type' in input)
@@ -1,3 +1,7 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
1
5
  import {FrameState} from '../helpers/frame-state';
2
6
  import {I3SPendingTilesRegister} from './i3s-pending-tiles-register';
3
7
 
@@ -1,3 +1,7 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
1
5
  import {TilesetTraverser} from '../common/tileset-traverser';
2
6
 
3
7
  import {getLodStatus} from '../helpers/i3s-lod';
@@ -34,7 +38,8 @@ export class I3STilesetTraverser extends TilesetTraverser {
34
38
  for (const child of children) {
35
39
  const extendedId = `${child.id}-${frameState.viewport.id}`;
36
40
  // if child tile is not fetched
37
- const childTile = childTiles && childTiles.find(t => t.id === extendedId);
41
+ const childTile =
42
+ childTiles && childTiles.find(t => t.id === extendedId || t.header?.id === child.id);
38
43
  if (!childTile) {
39
44
  const request = () => this._loadTile(tile, child.id, frameState);
40
45
  const cachedRequest = this._tileManager.find(extendedId);
@@ -1,3 +1,7 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
1
5
  import type {Tileset3D} from '../common/tileset-3d';
2
6
 
3
7
  export function get3dTilesOptions(tileset: Tileset3D): {assetGltfUpAxis: 'X' | 'Y' | 'Z'} {
@@ -1,10 +1,12 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+
1
3
  // This file is derived from the Cesium code base under Apache 2 license
2
4
  // See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md
3
5
 
4
6
  /* eslint-disable */
5
7
  import {Quaternion, Vector3, Matrix3, Matrix4, degrees} from '@math.gl/core';
6
8
  import {BoundingSphere, OrientedBoundingBox} from '@math.gl/culling';
7
- import {Ellipsoid} from '@math.gl/geospatial';
9
+ import {Ellipsoid, makeOBBFromRegion} from '@math.gl/geospatial';
8
10
  import {assert} from '@loaders.gl/loader-utils';
9
11
 
10
12
  // const scratchProjectedBoundingSphere = new BoundingSphere();
@@ -16,12 +18,7 @@ function defined(x) {
16
18
  // const scratchMatrix = new Matrix3();
17
19
  const scratchPoint = new Vector3();
18
20
  const scratchScale = new Vector3();
19
- const scratchNorthWest = new Vector3();
20
- const scratchSouthEast = new Vector3();
21
- const scratchCenter = new Vector3();
22
- const scratchXAxis = new Vector3();
23
- const scratchYAxis = new Vector3();
24
- const scratchZAxis = new Vector3();
21
+ const identityTransform = new Matrix4();
25
22
  // const scratchRectangle = new Rectangle();
26
23
  // const scratchOrientedBoundingBox = new OrientedBoundingBox();
27
24
  // const scratchTransform = new Matrix4();
@@ -29,20 +26,39 @@ const scratchZAxis = new Vector3();
29
26
  /**
30
27
  * Create a bounding volume from the tile's bounding volume header.
31
28
  * @param {Object} boundingVolumeHeader The tile's bounding volume header.
32
- * @param {Matrix4} transform The transform to apply to the bounding volume.
29
+ * @param {Matrix4} transform The current computed tile transform, including application transforms.
33
30
  * @param [result] The object onto which to store the result.
31
+ * @param initialTransform The accumulated JSON tile transform, excluded from region transforms.
32
+ * Defaults to transform so callers without application transforms keep geospatial regions fixed.
34
33
  * @returns The modified result parameter or a new TileBoundingVolume instance if none was provided.
35
34
  */
36
- export function createBoundingVolume(boundingVolumeHeader, transform, result?) {
35
+ export function createBoundingVolume(
36
+ boundingVolumeHeader,
37
+ transform: Matrix4,
38
+ result?,
39
+ initialTransform: Matrix4 = transform
40
+ ) {
37
41
  assert(boundingVolumeHeader, '3D Tile: boundingVolume must be defined');
38
42
 
39
43
  // boundingVolume schema:
40
44
  // https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/specification/schema/boundingVolume.schema.json
41
45
  if (boundingVolumeHeader.box) {
42
- return createBox(boundingVolumeHeader.box, transform, result);
46
+ // S2 cells and regions are geospatially anchored. Their conservative ECEF boxes are already
47
+ // world-space volumes and must not receive the tile/node transform a second time.
48
+ const boxTransform = boundingVolumeHeader.s2VolumeInfo ? identityTransform : transform;
49
+ return createBox(boundingVolumeHeader.box, boxTransform, result);
43
50
  }
44
51
  if (boundingVolumeHeader.region) {
45
- return createObbFromRegion(boundingVolumeHeader.region);
52
+ const boundingVolume = makeOBBFromRegion(boundingVolumeHeader.region);
53
+ // Regions are already in world coordinates. Apply only the change from the JSON transform.
54
+ const regionTransform = transform.equals(initialTransform)
55
+ ? identityTransform
56
+ : transform.clone().multiplyRight(initialTransform.clone().invert());
57
+ return createBox(
58
+ [...boundingVolume.center, ...boundingVolume.halfAxes],
59
+ regionTransform,
60
+ result
61
+ );
46
62
  }
47
63
 
48
64
  if (boundingVolumeHeader.sphere) {
@@ -59,15 +75,17 @@ export type CartographicBounds = [min: number[], max: number[]];
59
75
  * Calculate the cartographic bounding box the tile's bounding volume.
60
76
  * @param {Object} boundingVolumeHeader The tile's bounding volume header.
61
77
  * @param {BoundingVolume} boundingVolume The bounding volume.
78
+ * @param isRegionTransformed Whether application transforms moved the region from its JSON bounds.
62
79
  * @returns {CartographicBounds}
63
80
  */
64
81
  export function getCartographicBounds(
65
82
  boundingVolumeHeader,
66
- boundingVolume: OrientedBoundingBox | BoundingSphere
83
+ boundingVolume: OrientedBoundingBox | BoundingSphere,
84
+ isRegionTransformed = false
67
85
  ): CartographicBounds {
68
86
  // boundingVolume schema:
69
87
  // https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/specification/schema/boundingVolume.schema.json
70
- if (boundingVolumeHeader.box) {
88
+ if (boundingVolumeHeader.box || (boundingVolumeHeader.region && isRegionTransformed)) {
71
89
  return orientedBoundingBoxToCartographicBounds(boundingVolume as OrientedBoundingBox);
72
90
  }
73
91
  if (boundingVolumeHeader.region) {
@@ -146,61 +164,6 @@ function createBox(box, transform, result?) {
146
164
  return new OrientedBoundingBox(center, halfAxes);
147
165
  }
148
166
 
149
- /*
150
- function createBoxFromTransformedRegion(region, transform, initialTransform, result) {
151
- const rectangle = Rectangle.unpack(region, 0, scratchRectangle);
152
- const minimumHeight = region[4];
153
- const maximumHeight = region[5];
154
-
155
- const orientedBoundingBox = OrientedBoundingBox.fromRectangle(
156
- rectangle,
157
- minimumHeight,
158
- maximumHeight,
159
- Ellipsoid.WGS84,
160
- scratchOrientedBoundingBox
161
- );
162
- const center = orientedBoundingBox.center;
163
- const halfAxes = orientedBoundingBox.halfAxes;
164
-
165
- // A region bounding volume is not transformed by the transform in the tileset JSON,
166
- // but may be transformed by additional transforms applied in Cesium.
167
- // This is why the transform is calculated as the difference between the initial transform and the current transform.
168
- transform = Matrix4.multiplyTransformation(
169
- transform,
170
- Matrix4.inverseTransformation(initialTransform, scratchTransform),
171
- scratchTransform
172
- );
173
- center = Matrix4.multiplyByPoint(transform, center, center);
174
- const rotationScale = Matrix4.getRotation(transform, scratchMatrix);
175
- halfAxes = Matrix3.multiply(rotationScale, halfAxes, halfAxes);
176
-
177
- if (defined(result) && result instanceof TileOrientedBoundingBox) {
178
- result.update(center, halfAxes);
179
- return result;
180
- }
181
-
182
- return new TileOrientedBoundingBox(center, halfAxes);
183
- }
184
-
185
- function createRegion(region, transform, initialTransform, result) {
186
- if (!Matrix4.equalsEpsilon(transform, initialTransform, CesiumMath.EPSILON8)) {
187
- return createBoxFromTransformedRegion(region, transform, initialTransform, result);
188
- }
189
-
190
- if (defined(result)) {
191
- return result;
192
- }
193
-
194
- const rectangleRegion = Rectangle.unpack(region, 0, scratchRectangle);
195
-
196
- return new TileBoundingRegion({
197
- rectangle: rectangleRegion,
198
- minimumHeight: region[4],
199
- maximumHeight: region[5]
200
- });
201
- }
202
- */
203
-
204
167
  function createSphere(sphere, transform, result?) {
205
168
  // Find the transformed center
206
169
  const center = new Vector3(sphere[0], sphere[1], sphere[2]);
@@ -219,52 +182,6 @@ function createSphere(sphere, transform, result?) {
219
182
  return new BoundingSphere(center, radius);
220
183
  }
221
184
 
222
- /**
223
- * Create OrientedBoundingBox instance from region 3D tiles bounding volume
224
- * @param region - region 3D tiles bounding volume
225
- * @returns OrientedBoundingBox instance
226
- */
227
- function createObbFromRegion(region: number[]): OrientedBoundingBox {
228
- // [west, south, east, north, minimum height, maximum height]
229
- // Latitudes and longitudes are in the WGS 84 datum as defined in EPSG 4979 and are in radians.
230
- // Heights are in meters above (or below) the WGS 84 ellipsoid.
231
- const [west, south, east, north, minHeight, maxHeight] = region;
232
-
233
- const northWest = Ellipsoid.WGS84.cartographicToCartesian(
234
- [degrees(west), degrees(north), minHeight],
235
- scratchNorthWest
236
- );
237
- const southEast = Ellipsoid.WGS84.cartographicToCartesian(
238
- [degrees(east), degrees(south), maxHeight],
239
- scratchSouthEast
240
- );
241
- const centerInCartesian = new Vector3().addVectors(northWest, southEast).multiplyByScalar(0.5);
242
- Ellipsoid.WGS84.cartesianToCartographic(centerInCartesian, scratchCenter);
243
-
244
- Ellipsoid.WGS84.cartographicToCartesian(
245
- [degrees(east), scratchCenter[1], scratchCenter[2]],
246
- scratchXAxis
247
- );
248
- Ellipsoid.WGS84.cartographicToCartesian(
249
- [scratchCenter[0], degrees(north), scratchCenter[2]],
250
- scratchYAxis
251
- );
252
- Ellipsoid.WGS84.cartographicToCartesian(
253
- [scratchCenter[0], scratchCenter[1], maxHeight],
254
- scratchZAxis
255
- );
256
-
257
- return createBox(
258
- [
259
- ...centerInCartesian,
260
- ...scratchXAxis.subtract(centerInCartesian),
261
- ...scratchYAxis.subtract(centerInCartesian),
262
- ...scratchZAxis.subtract(centerInCartesian)
263
- ],
264
- new Matrix4()
265
- );
266
- }
267
-
268
185
  /**
269
186
  * Convert a bounding volume defined by OrientedBoundingBox to cartographic bounds
270
187
  * @returns {CartographicBounds}