@loaders.gl/tiles 4.0.0-alpha.1 → 4.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 (198) hide show
  1. package/dist/bundle.d.ts +2 -0
  2. package/dist/bundle.d.ts.map +1 -0
  3. package/dist/bundle.js +2 -2
  4. package/dist/constants.d.ts +38 -0
  5. package/dist/constants.d.ts.map +1 -0
  6. package/dist/constants.js +37 -30
  7. package/dist/dist.min.js +9163 -0
  8. package/dist/es5/bundle.js +6 -0
  9. package/dist/es5/bundle.js.map +1 -0
  10. package/dist/es5/constants.js +48 -0
  11. package/dist/es5/constants.js.map +1 -0
  12. package/dist/es5/index.js +93 -0
  13. package/dist/es5/index.js.map +1 -0
  14. package/dist/es5/tileset/format-3d-tiles/tileset-3d-traverser.js +70 -0
  15. package/dist/es5/tileset/format-3d-tiles/tileset-3d-traverser.js.map +1 -0
  16. package/dist/es5/tileset/format-i3s/i3s-pending-tiles-register.js +45 -0
  17. package/dist/es5/tileset/format-i3s/i3s-pending-tiles-register.js.map +1 -0
  18. package/dist/es5/tileset/format-i3s/i3s-tile-manager.js +84 -0
  19. package/dist/es5/tileset/format-i3s/i3s-tile-manager.js.map +1 -0
  20. package/dist/es5/tileset/format-i3s/i3s-tileset-traverser.js +143 -0
  21. package/dist/es5/tileset/format-i3s/i3s-tileset-traverser.js.map +1 -0
  22. package/dist/es5/tileset/helpers/3d-tiles-options.js +12 -0
  23. package/dist/es5/tileset/helpers/3d-tiles-options.js.map +1 -0
  24. package/dist/es5/tileset/helpers/bounding-volume.js +176 -0
  25. package/dist/es5/tileset/helpers/bounding-volume.js.map +1 -0
  26. package/dist/es5/tileset/helpers/frame-state.js +129 -0
  27. package/dist/es5/tileset/helpers/frame-state.js.map +1 -0
  28. package/dist/es5/tileset/helpers/i3s-lod.js +60 -0
  29. package/dist/es5/tileset/helpers/i3s-lod.js.map +1 -0
  30. package/dist/es5/tileset/helpers/tiles-3d-lod.js +103 -0
  31. package/dist/es5/tileset/helpers/tiles-3d-lod.js.map +1 -0
  32. package/dist/es5/tileset/helpers/transform-utils.js +50 -0
  33. package/dist/es5/tileset/helpers/transform-utils.js.map +1 -0
  34. package/dist/es5/tileset/helpers/zoom.js +63 -0
  35. package/dist/es5/tileset/helpers/zoom.js.map +1 -0
  36. package/dist/es5/tileset/tile-3d.js +536 -0
  37. package/dist/es5/tileset/tile-3d.js.map +1 -0
  38. package/dist/es5/tileset/tileset-3d.js +855 -0
  39. package/dist/es5/tileset/tileset-3d.js.map +1 -0
  40. package/dist/es5/tileset/tileset-cache.js +82 -0
  41. package/dist/es5/tileset/tileset-cache.js.map +1 -0
  42. package/dist/es5/tileset/tileset-traverser.js +321 -0
  43. package/dist/es5/tileset/tileset-traverser.js.map +1 -0
  44. package/dist/es5/types.js +2 -0
  45. package/dist/es5/types.js.map +1 -0
  46. package/dist/es5/utils/doubly-linked-list-node.js +21 -0
  47. package/dist/es5/utils/doubly-linked-list-node.js.map +1 -0
  48. package/dist/es5/utils/doubly-linked-list.js +88 -0
  49. package/dist/es5/utils/doubly-linked-list.js.map +1 -0
  50. package/dist/es5/utils/managed-array.js +126 -0
  51. package/dist/es5/utils/managed-array.js.map +1 -0
  52. package/dist/esm/bundle.js +4 -0
  53. package/dist/esm/bundle.js.map +1 -0
  54. package/dist/esm/constants.js +36 -0
  55. package/dist/esm/constants.js.map +1 -0
  56. package/dist/esm/index.js +10 -0
  57. package/dist/esm/index.js.map +1 -0
  58. package/dist/{tileset/traversers → esm/tileset/format-3d-tiles}/tileset-3d-traverser.js +2 -13
  59. package/dist/esm/tileset/format-3d-tiles/tileset-3d-traverser.js.map +1 -0
  60. package/dist/esm/tileset/format-i3s/i3s-pending-tiles-register.js +26 -0
  61. package/dist/esm/tileset/format-i3s/i3s-pending-tiles-register.js.map +1 -0
  62. package/dist/esm/tileset/format-i3s/i3s-tile-manager.js +79 -0
  63. package/dist/esm/tileset/format-i3s/i3s-tile-manager.js.map +1 -0
  64. package/dist/{tileset/traversers → esm/tileset/format-i3s}/i3s-tileset-traverser.js +16 -28
  65. package/dist/esm/tileset/format-i3s/i3s-tileset-traverser.js.map +1 -0
  66. package/dist/esm/tileset/helpers/3d-tiles-options.js +6 -0
  67. package/dist/esm/tileset/helpers/3d-tiles-options.js.map +1 -0
  68. package/dist/esm/tileset/helpers/bounding-volume.js +155 -0
  69. package/dist/esm/tileset/helpers/bounding-volume.js.map +1 -0
  70. package/dist/esm/tileset/helpers/frame-state.js +109 -0
  71. package/dist/esm/tileset/helpers/frame-state.js.map +1 -0
  72. package/dist/esm/tileset/helpers/i3s-lod.js +53 -0
  73. package/dist/esm/tileset/helpers/i3s-lod.js.map +1 -0
  74. package/dist/esm/tileset/helpers/tiles-3d-lod.js +100 -0
  75. package/dist/{tileset → esm/tileset}/helpers/tiles-3d-lod.js.map +1 -1
  76. package/dist/esm/tileset/helpers/transform-utils.js +50 -0
  77. package/dist/esm/tileset/helpers/transform-utils.js.map +1 -0
  78. package/dist/esm/tileset/helpers/zoom.js +55 -0
  79. package/dist/esm/tileset/helpers/zoom.js.map +1 -0
  80. package/dist/esm/tileset/tile-3d.js +414 -0
  81. package/dist/esm/tileset/tile-3d.js.map +1 -0
  82. package/dist/esm/tileset/tileset-3d.js +606 -0
  83. package/dist/esm/tileset/tileset-3d.js.map +1 -0
  84. package/dist/esm/tileset/tileset-cache.js +57 -0
  85. package/dist/esm/tileset/tileset-cache.js.map +1 -0
  86. package/dist/{tileset/traversers → esm/tileset}/tileset-traverser.js +29 -88
  87. package/dist/esm/tileset/tileset-traverser.js.map +1 -0
  88. package/dist/esm/types.js +2 -0
  89. package/dist/esm/types.js.map +1 -0
  90. package/dist/esm/utils/doubly-linked-list-node.js +12 -0
  91. package/dist/esm/utils/doubly-linked-list-node.js.map +1 -0
  92. package/dist/esm/utils/doubly-linked-list.js +65 -0
  93. package/dist/esm/utils/doubly-linked-list.js.map +1 -0
  94. package/dist/esm/utils/managed-array.js +87 -0
  95. package/dist/esm/utils/managed-array.js.map +1 -0
  96. package/dist/index.d.ts +11 -0
  97. package/dist/index.d.ts.map +1 -0
  98. package/dist/index.js +26 -9
  99. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.d.ts +7 -0
  100. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -0
  101. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.js +54 -0
  102. package/dist/tileset/format-i3s/i3s-pending-tiles-register.d.ts +27 -0
  103. package/dist/tileset/format-i3s/i3s-pending-tiles-register.d.ts.map +1 -0
  104. package/dist/tileset/format-i3s/i3s-pending-tiles-register.js +47 -0
  105. package/dist/tileset/format-i3s/i3s-tile-manager.d.ts +34 -0
  106. package/dist/tileset/format-i3s/i3s-tile-manager.d.ts.map +1 -0
  107. package/dist/tileset/format-i3s/i3s-tile-manager.js +80 -0
  108. package/dist/tileset/format-i3s/i3s-tileset-traverser.d.ts +25 -0
  109. package/dist/tileset/format-i3s/i3s-tileset-traverser.d.ts.map +1 -0
  110. package/dist/tileset/format-i3s/i3s-tileset-traverser.js +92 -0
  111. package/dist/tileset/helpers/3d-tiles-options.d.ts +5 -0
  112. package/dist/tileset/helpers/3d-tiles-options.d.ts.map +1 -0
  113. package/dist/tileset/helpers/3d-tiles-options.js +8 -5
  114. package/dist/tileset/helpers/bounding-volume.d.ts +19 -0
  115. package/dist/tileset/helpers/bounding-volume.d.ts.map +1 -0
  116. package/dist/tileset/helpers/bounding-volume.js +274 -69
  117. package/dist/tileset/helpers/frame-state.d.ts +28 -0
  118. package/dist/tileset/helpers/frame-state.d.ts.map +1 -0
  119. package/dist/tileset/helpers/frame-state.js +131 -49
  120. package/dist/tileset/helpers/i3s-lod.d.ts +20 -0
  121. package/dist/tileset/helpers/i3s-lod.d.ts.map +1 -0
  122. package/dist/tileset/helpers/i3s-lod.js +82 -105
  123. package/dist/tileset/helpers/tiles-3d-lod.d.ts +8 -0
  124. package/dist/tileset/helpers/tiles-3d-lod.d.ts.map +1 -0
  125. package/dist/tileset/helpers/tiles-3d-lod.js +112 -100
  126. package/dist/tileset/helpers/transform-utils.d.ts +2 -0
  127. package/dist/tileset/helpers/transform-utils.d.ts.map +1 -0
  128. package/dist/tileset/helpers/transform-utils.js +51 -56
  129. package/dist/tileset/helpers/zoom.d.ts +46 -0
  130. package/dist/tileset/helpers/zoom.d.ts.map +1 -0
  131. package/dist/tileset/helpers/zoom.js +83 -30
  132. package/dist/tileset/tile-3d.d.ts +222 -0
  133. package/dist/tileset/tile-3d.d.ts.map +1 -0
  134. package/dist/tileset/tile-3d.js +607 -531
  135. package/dist/tileset/tileset-3d.d.ts +264 -0
  136. package/dist/tileset/tileset-3d.d.ts.map +1 -0
  137. package/dist/tileset/tileset-3d.js +693 -652
  138. package/dist/tileset/tileset-cache.d.ts +19 -0
  139. package/dist/tileset/tileset-cache.d.ts.map +1 -0
  140. package/dist/tileset/tileset-cache.js +66 -73
  141. package/dist/tileset/tileset-traverser.d.ts +57 -0
  142. package/dist/tileset/tileset-traverser.d.ts.map +1 -0
  143. package/dist/tileset/tileset-traverser.js +300 -0
  144. package/dist/types.d.ts +34 -0
  145. package/dist/types.d.ts.map +1 -0
  146. package/dist/types.js +2 -0
  147. package/dist/utils/doubly-linked-list-node.d.ts +11 -0
  148. package/dist/utils/doubly-linked-list-node.d.ts.map +1 -0
  149. package/dist/utils/doubly-linked-list-node.js +17 -8
  150. package/dist/utils/doubly-linked-list.d.ts +30 -0
  151. package/dist/utils/doubly-linked-list.d.ts.map +1 -0
  152. package/dist/utils/doubly-linked-list.js +91 -72
  153. package/dist/utils/managed-array.d.ts +85 -0
  154. package/dist/utils/managed-array.d.ts.map +1 -0
  155. package/dist/utils/managed-array.js +144 -103
  156. package/package.json +13 -10
  157. package/src/constants.ts +38 -18
  158. package/src/index.ts +7 -4
  159. package/src/tileset/{traversers → format-3d-tiles}/tileset-3d-traverser.ts +4 -2
  160. package/src/tileset/format-i3s/i3s-pending-tiles-register.ts +44 -0
  161. package/src/tileset/format-i3s/i3s-tile-manager.ts +101 -0
  162. package/src/tileset/{traversers → format-i3s}/i3s-tileset-traverser.ts +27 -15
  163. package/src/tileset/helpers/3d-tiles-options.ts +3 -1
  164. package/src/tileset/helpers/bounding-volume.ts +136 -0
  165. package/src/tileset/helpers/frame-state.ts +102 -18
  166. package/src/tileset/helpers/i3s-lod.ts +75 -96
  167. package/src/tileset/helpers/tiles-3d-lod.ts +2 -0
  168. package/src/tileset/helpers/transform-utils.ts +2 -0
  169. package/src/tileset/helpers/zoom.ts +84 -9
  170. package/src/tileset/tile-3d.ts +139 -123
  171. package/src/tileset/tileset-3d.ts +379 -252
  172. package/src/tileset/tileset-cache.ts +26 -17
  173. package/src/tileset/{traversers/tileset-traverser.ts → tileset-traverser.ts} +71 -72
  174. package/src/types.ts +36 -0
  175. package/src/utils/{doubly-linked-list-node.js → doubly-linked-list-node.ts} +7 -2
  176. package/src/utils/{doubly-linked-list.js → doubly-linked-list.ts} +5 -8
  177. package/src/utils/{managed-array.js → managed-array.ts} +5 -2
  178. package/dist/bundle.js.map +0 -1
  179. package/dist/constants.js.map +0 -1
  180. package/dist/index.js.map +0 -1
  181. package/dist/tileset/helpers/3d-tiles-options.js.map +0 -1
  182. package/dist/tileset/helpers/bounding-volume.js.map +0 -1
  183. package/dist/tileset/helpers/frame-state.js.map +0 -1
  184. package/dist/tileset/helpers/i3s-lod.js.map +0 -1
  185. package/dist/tileset/helpers/transform-utils.js.map +0 -1
  186. package/dist/tileset/helpers/zoom.js.map +0 -1
  187. package/dist/tileset/tile-3d.js.map +0 -1
  188. package/dist/tileset/tileset-3d.js.map +0 -1
  189. package/dist/tileset/tileset-cache.js.map +0 -1
  190. package/dist/tileset/traversers/i3s-tile-manager.js +0 -45
  191. package/dist/tileset/traversers/i3s-tile-manager.js.map +0 -1
  192. package/dist/tileset/traversers/i3s-tileset-traverser.js.map +0 -1
  193. package/dist/tileset/traversers/tileset-3d-traverser.js.map +0 -1
  194. package/dist/tileset/traversers/tileset-traverser.js.map +0 -1
  195. package/dist/utils/doubly-linked-list-node.js.map +0 -1
  196. package/dist/utils/doubly-linked-list.js.map +0 -1
  197. package/dist/utils/managed-array.js.map +0 -1
  198. package/src/tileset/traversers/i3s-tile-manager.ts +0 -39
@@ -1,61 +1,48 @@
1
+ // loaders.gl, MIT license
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
- /*
5
-
6
- The Tileset loading and rendering flow is as below,
7
- A rendered (i.e. deck.gl `Tile3DLayer`) triggers `tileset.update()` after a `tileset` is loaded
8
- `tileset` starts traversing the tile tree and update `requestTiles` (tiles of which content need
9
- to be fetched) and `selectedTiles` (tiles ready for rendering under the current viewport).
10
- `Tile3DLayer` will update rendering based on `selectedTiles`.
11
- `Tile3DLayer` also listens to `onTileLoad` callback and trigger another round of `update and then traversal`
12
- when new tiles are loaded.
13
-
14
- As I3S tileset have stored `tileHeader` file (metadata) and tile content files (geometry, texture, ...) separately.
15
- During each traversal, it issues `tilHeader` requests if that `tileHeader` is not yet fetched,
16
- after the tile header is fulfilled, it will resume the traversal starting from the tile just fetched (not root).
17
-
18
- Tile3DLayer
19
- |
20
- await load(tileset)
21
- |
22
- tileset.update()
23
- | async load tileHeader
24
- tileset.traverse() -------------------------- Queued
25
- | resume traversal after fetched |
26
- |----------------------------------------|
27
- |
28
- | async load tile content
29
- tilset.requestedTiles ----------------------------- RequestScheduler
30
- |
31
- tilset.selectedTiles (ready for rendering) |
32
- | Listen to |
33
- Tile3DLayer ----------- onTileLoad ----------------------|
34
- | | notify new tile is available
35
- updateLayers |
36
- tileset.update // trigger another round of update
37
- */
38
-
39
6
  import {Matrix4, Vector3} from '@math.gl/core';
40
7
  import {Ellipsoid} from '@math.gl/geospatial';
41
8
  import {Stats} from '@probe.gl/stats';
42
- import {
43
- RequestScheduler,
44
- assert,
45
- path,
46
- LoaderWithParser,
47
- LoaderOptions
48
- } from '@loaders.gl/loader-utils';
49
- import TilesetCache from './tileset-cache';
9
+ import {RequestScheduler, path, LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils';
10
+ import {TilesetCache} from './tileset-cache';
50
11
  import {calculateTransformProps} from './helpers/transform-utils';
51
- import {FrameState, getFrameState} from './helpers/frame-state';
52
- import {getZoomFromBoundingVolume} from './helpers/zoom';
53
- import Tile3D from './tile-3d';
54
- import Tileset3DTraverser from './traversers/tileset-3d-traverser';
55
- import TilesetTraverser from './traversers/tileset-traverser';
56
- import I3SetTraverser from './traversers/i3s-tileset-traverser';
12
+ import {FrameState, getFrameState, limitSelectedTiles} from './helpers/frame-state';
13
+ import {getZoomFromBoundingVolume, getZoomFromExtent, getZoomFromFullExtent} from './helpers/zoom';
14
+
15
+ import type {GeospatialViewport, Viewport} from '../types';
16
+ import {Tile3D} from './tile-3d';
57
17
  import {TILESET_TYPE} from '../constants';
58
18
 
19
+ import {TilesetTraverser} from './tileset-traverser';
20
+
21
+ // TODO - these should be moved into their respective modules
22
+ import {Tileset3DTraverser} from './format-3d-tiles/tileset-3d-traverser';
23
+ import {I3STilesetTraverser} from './format-i3s/i3s-tileset-traverser';
24
+
25
+ export type TilesetJSON = any;
26
+
27
+ /*
28
+ export type TilesetJSON = {
29
+ loader;
30
+ // could be 3d tiles, i3s
31
+ type: 'I3S' | '3DTILES';
32
+ /** The url to the top level tileset JSON file. *
33
+ url: string;
34
+ basePath?: string;
35
+ // Geometric error when the tree is not rendered at all
36
+ lodMetricType: string;
37
+ lodMetricValue: number;
38
+ root: {
39
+ refine: string;
40
+ [key: string]: unknown;
41
+ },
42
+ [key: string]: unknown;
43
+ };
44
+ */
45
+
59
46
  export type Tileset3DProps = {
60
47
  // loading
61
48
  throttleRequests?: boolean;
@@ -64,6 +51,8 @@ export type Tileset3DProps = {
64
51
  loadTiles?: boolean;
65
52
  basePath?: string;
66
53
  maximumMemoryUsage?: number;
54
+ maximumTilesSelected?: number;
55
+ debounceTime?: number;
67
56
 
68
57
  // Metadata
69
58
  description?: string;
@@ -90,78 +79,63 @@ export type Tileset3DProps = {
90
79
  type Props = {
91
80
  description: string;
92
81
  ellipsoid: object;
82
+ /** A 4x4 transformation matrix this transforms the entire tileset. */
93
83
  modelMatrix: Matrix4;
84
+ /** Set to false to disable network request throttling */
94
85
  throttleRequests: boolean;
86
+ /** Number of simultaneous requsts, if throttleRequests is true */
87
+ maxRequests: number;
95
88
  maximumMemoryUsage: number;
96
- onTileLoad: (tile: Tile3D) => any;
97
- onTileUnload: (tile: Tile3D) => any;
98
- onTileError: (tile: Tile3D, message: string, url: string) => any;
89
+ /** Maximum number limit of tiles selected for show. 0 means no limit */
90
+ maximumTilesSelected: number;
91
+ /** Delay time before the tileset traversal. It prevents traversal requests spam.*/
92
+ debounceTime: number;
93
+ /** Callback. Indicates this a tile's content was loaded */
94
+ onTileLoad: (tile: Tile3D) => void;
95
+ /** Callback. Indicates this a tile's content was unloaded (cache full) */
96
+ onTileUnload: (tile: Tile3D) => void;
97
+ /** Callback. Indicates this a tile's content failed to load */
98
+ onTileError: (tile: Tile3D, message: string, url: string) => void;
99
+ /** Callback. Allows post-process selectedTiles right after traversal. */
99
100
  onTraversalComplete: (selectedTiles: Tile3D[]) => Tile3D[];
101
+ /** The maximum screen space error used to drive level of detail refinement. */
100
102
  maximumScreenSpaceError: number;
101
- viewportTraversersMap: any;
103
+ viewportTraversersMap: Record<string, any> | null;
102
104
  attributions: string[];
103
- maxRequests: number;
104
105
  loadTiles: boolean;
105
106
  loadOptions: LoaderOptions;
106
107
  updateTransforms: boolean;
108
+ /** View distance scale modifier */
107
109
  viewDistanceScale: number;
108
110
  basePath: string;
111
+ /** Optional async tile content loader */
109
112
  contentLoader?: (tile: Tile3D) => Promise<void>;
110
- i3s: {[key: string]: any};
113
+ /** @todo I3S specific knowledge should be moved to I3S module */
114
+ i3s: Record<string, any>;
111
115
  };
112
116
 
113
117
  const DEFAULT_PROPS: Props = {
114
118
  description: '',
115
-
116
119
  ellipsoid: Ellipsoid.WGS84,
117
- // A 4x4 transformation matrix this transforms the entire tileset.
118
120
  modelMatrix: new Matrix4(),
119
-
120
- // Set to false to disable network request throttling
121
121
  throttleRequests: true,
122
-
123
- // Number of simultaneous requsts, if throttleRequests is true
124
122
  maxRequests: 64,
125
-
126
123
  maximumMemoryUsage: 32,
127
-
128
- /**
129
- * Callback. Indicates this a tile's content was loaded
130
- * @param tile {TileHeader}
131
- */
124
+ maximumTilesSelected: 0,
125
+ debounceTime: 0,
132
126
  onTileLoad: () => {},
133
- /**
134
- * Callback. Indicates this a tile's content was unloaded
135
- * @param tile {TileHeader}
136
- */
137
127
  onTileUnload: () => {},
138
128
  onTileError: () => {},
139
- /**
140
- * Callback. Allows post-process selectedTiles right after traversal.
141
- * @param selectedTiles {TileHeader[]}
142
- * @returns TileHeader[] - output array of tiles to return to deck.gl
143
- */
144
129
  onTraversalComplete: (selectedTiles: Tile3D[]) => selectedTiles,
145
-
146
- // Optional async tile content loader
147
130
  contentLoader: undefined,
148
-
149
- // View distance scale modifier
150
131
  viewDistanceScale: 1.0,
151
-
152
- // TODO CESIUM
153
- // The maximum screen space error used to drive level of detail refinement.
154
132
  maximumScreenSpaceError: 8,
155
-
156
133
  loadTiles: true,
157
134
  updateTransforms: true,
158
135
  viewportTraversersMap: null,
159
-
160
136
  loadOptions: {fetch: {}},
161
-
162
137
  attributions: [],
163
138
  basePath: '',
164
-
165
139
  i3s: {}
166
140
  };
167
141
 
@@ -174,16 +148,49 @@ const TILES_LOADED = 'Tiles Loaded';
174
148
  const TILES_LOADING = 'Tiles Loading';
175
149
  const TILES_UNLOADED = 'Tiles Unloaded';
176
150
  const TILES_LOAD_FAILED = 'Failed Tile Loads';
177
- const POINTS_COUNT = 'Points';
151
+ const POINTS_COUNT = 'Points/Vertices';
178
152
  const TILES_GPU_MEMORY = 'Tile Memory Use';
179
153
 
180
- export default class Tileset3D {
154
+ /**
155
+ * The Tileset loading and rendering flow is as below,
156
+ * A rendered (i.e. deck.gl `Tile3DLayer`) triggers `tileset.update()` after a `tileset` is loaded
157
+ * `tileset` starts traversing the tile tree and update `requestTiles` (tiles of which content need
158
+ * to be fetched) and `selectedTiles` (tiles ready for rendering under the current viewport).
159
+ * `Tile3DLayer` will update rendering based on `selectedTiles`.
160
+ * `Tile3DLayer` also listens to `onTileLoad` callback and trigger another round of `update and then traversal`
161
+ * when new tiles are loaded.
162
+
163
+ * As I3S tileset have stored `tileHeader` file (metadata) and tile content files (geometry, texture, ...) separately.
164
+ * During each traversal, it issues `tilHeader` requests if that `tileHeader` is not yet fetched,
165
+ * after the tile header is fulfilled, it will resume the traversal starting from the tile just fetched (not root).
166
+
167
+ * Tile3DLayer
168
+ * |
169
+ * await load(tileset)
170
+ * |
171
+ * tileset.update()
172
+ * | async load tileHeader
173
+ * tileset.traverse() -------------------------- Queued
174
+ * | resume traversal after fetched |
175
+ * |----------------------------------------|
176
+ * |
177
+ * | async load tile content
178
+ * tilset.requestedTiles ----------------------------- RequestScheduler
179
+ * |
180
+ * tilset.selectedTiles (ready for rendering) |
181
+ * | Listen to |
182
+ * Tile3DLayer ----------- onTileLoad ----------------------|
183
+ * | | notify new tile is available
184
+ * updateLayers |
185
+ * tileset.update // trigger another round of update
186
+ */
187
+ export class Tileset3D {
181
188
  // props: Tileset3DProps;
182
189
  options: Props;
183
- loadOptions: {[key: string]: any};
190
+ loadOptions: LoaderOptions;
184
191
 
185
192
  type: string;
186
- tileset: {[key: string]: any};
193
+ tileset: TilesetJSON;
187
194
  loader: LoaderWithParser;
188
195
  url: string;
189
196
  basePath: string;
@@ -192,55 +199,71 @@ export default class Tileset3D {
192
199
  lodMetricType: string;
193
200
  lodMetricValue: number;
194
201
  refine: string;
195
- root: Tile3D | null;
196
- roots: {[key: string]: Tile3D};
197
- asset: {[key: string]: any};
202
+ root: Tile3D | null = null;
203
+ roots: Record<string, Tile3D> = {};
204
+ /** @todo any->unknown */
205
+ asset: Record<string, any> = {};
198
206
 
199
- description: string;
207
+ // Metadata for the entire tileset
208
+ description: string = '';
200
209
  properties: any;
201
- extras: any;
202
- attributions: any;
203
- credits: any;
210
+
211
+ extras: any = null;
212
+ attributions: any = {};
213
+ credits: any = {};
204
214
 
205
215
  stats: Stats;
206
216
 
207
- traverseCounter: number;
208
- geometricError: number;
209
- selectedTiles: Tile3D[];
217
+ /** flags that contain information about data types in nested tiles */
218
+ contentFormats = {draco: false, meshopt: false, dds: false, ktx2: false};
210
219
 
211
- cartographicCenter: Vector3 | null;
212
- cartesianCenter: Vector3 | null;
213
- zoom: number;
214
- boundingVolume: any;
220
+ // view props
221
+ cartographicCenter: Vector3 | null = null;
222
+ cartesianCenter: Vector3 | null = null;
223
+ zoom: number = 1;
224
+ boundingVolume: any = null;
225
+
226
+ /** Updated based on the camera position and direction */
227
+ dynamicScreenSpaceErrorComputedDensity: number = 0.0;
215
228
 
216
229
  // METRICS
217
- // The maximum amount of GPU memory (in MB) that may be used to cache tiles.
218
- // Tiles not in view are unloaded to enforce private
219
- // The total amount of GPU memory in bytes used by the tileset.
220
- gpuMemoryUsageInBytes: any;
221
- dynamicScreenSpaceErrorComputedDensity: any;
222
230
 
223
- // TRAVERSAL
224
- _traverser: TilesetTraverser;
225
- private _cache: TilesetCache;
226
- _requestScheduler: RequestScheduler;
231
+ /**
232
+ * The maximum amount of GPU memory (in MB) that may be used to cache tiles
233
+ * Tiles not in view are unloaded to enforce private
234
+ */
235
+ maximumMemoryUsage: number = 32;
227
236
 
228
- _frameNumber: number;
229
- private _queryParamsString: string;
230
- private _queryParams: any;
231
- private _extensionsUsed: any;
232
- private _tiles: {[id: string]: Tile3D};
237
+ /** The total amount of GPU memory in bytes used by the tileset. */
238
+ gpuMemoryUsageInBytes: number = 0;
233
239
 
234
- // counter for tracking tiles requests
235
- private _pendingCount: any;
240
+ /** Update tracker. increase in each update cycle. */
241
+ _frameNumber: number = 0;
242
+ private _queryParams: Record<string, string> = {};
243
+ private _extensionsUsed: string[] = [];
244
+ private _tiles: Record<string, Tile3D> = {};
236
245
 
237
- // HOLD TRAVERSAL RESULTS
238
- private lastUpdatedVieports: any[] | null;
239
- private _requestedTiles: any;
240
- private _emptyTiles: any;
241
- private frameStateData: any;
246
+ /** counter for tracking tiles requests */
247
+ private _pendingCount: number = 0;
242
248
 
243
- maximumMemoryUsage: number;
249
+ /** Hold traversal results */
250
+ selectedTiles: Tile3D[] = [];
251
+
252
+ // TRAVERSAL
253
+ traverseCounter: number = 0;
254
+ geometricError: number = 0;
255
+ private lastUpdatedVieports: Viewport[] | Viewport | null = null;
256
+ private _requestedTiles: Tile3D[] = [];
257
+ private _emptyTiles: Tile3D[] = [];
258
+ private frameStateData: any = {};
259
+
260
+ _traverser: TilesetTraverser;
261
+ _cache = new TilesetCache();
262
+ _requestScheduler: RequestScheduler;
263
+
264
+ // Promise tracking
265
+ private updatePromise: Promise<number> | null = null;
266
+ tilesetInitializationPromise: Promise<void>;
244
267
 
245
268
  /**
246
269
  * Create a new Tileset3D
@@ -248,83 +271,40 @@ export default class Tileset3D {
248
271
  * @param props
249
272
  */
250
273
  // eslint-disable-next-line max-statements
251
- constructor(json: any, options?: Tileset3DProps) {
252
- assert(json);
253
-
274
+ constructor(tileset: TilesetJSON, options?: Tileset3DProps) {
254
275
  // PUBLIC MEMBERS
255
276
  this.options = {...DEFAULT_PROPS, ...options};
256
277
  // raw data
257
- this.tileset = json;
258
- this.loader = json.loader;
278
+ this.tileset = tileset;
279
+ this.loader = tileset.loader;
259
280
  // could be 3d tiles, i3s
260
- this.type = json.type;
281
+ this.type = tileset.type;
261
282
  // The url to a tileset JSON file.
262
- this.url = json.url;
263
- this.basePath = json.basePath || path.dirname(this.url);
283
+ this.url = tileset.url;
284
+ this.basePath = tileset.basePath || path.dirname(this.url);
264
285
  this.modelMatrix = this.options.modelMatrix;
265
286
  this.ellipsoid = this.options.ellipsoid;
266
287
 
267
288
  // Geometric error when the tree is not rendered at all
268
- this.lodMetricType = json.lodMetricType;
269
- this.lodMetricValue = json.lodMetricValue;
270
- this.refine = json.root.refine;
289
+ this.lodMetricType = tileset.lodMetricType;
290
+ this.lodMetricValue = tileset.lodMetricValue;
291
+ this.refine = tileset.root.refine;
271
292
 
272
293
  this.loadOptions = this.options.loadOptions || {};
273
294
 
274
- this.root = null;
275
- this.roots = {};
276
- // view props
277
- this.cartographicCenter = null;
278
- this.cartesianCenter = null;
279
- this.zoom = 1;
280
- this.boundingVolume = null;
281
-
282
295
  // TRAVERSAL
283
- this.traverseCounter = 0;
284
- this.geometricError = 0;
285
296
  this._traverser = this._initializeTraverser();
286
- this._cache = new TilesetCache();
287
297
  this._requestScheduler = new RequestScheduler({
288
298
  throttleRequests: this.options.throttleRequests,
289
299
  maxRequests: this.options.maxRequests
290
300
  });
291
- // update tracker
292
- // increase in each update cycle
293
- this._frameNumber = 0;
294
-
295
- // counter for tracking tiles requests
296
- this._pendingCount = 0;
297
-
298
- // HOLD TRAVERSAL RESULTS
299
- this._tiles = {};
300
- this.selectedTiles = [];
301
- this._emptyTiles = [];
302
- this._requestedTiles = [];
303
- this.frameStateData = {};
304
- this.lastUpdatedVieports = null;
305
-
306
- this._queryParams = {};
307
- this._queryParamsString = '';
308
301
 
309
302
  // METRICS
310
- // The maximum amount of GPU memory (in MB) that may be used to cache tiles.
311
- // Tiles not in view are unloaded to enforce this.
312
- this.maximumMemoryUsage = this.options.maximumMemoryUsage || 32;
313
303
  // The total amount of GPU memory in bytes used by the tileset.
314
- this.gpuMemoryUsageInBytes = 0;
315
304
  this.stats = new Stats({id: this.url});
316
305
  this._initializeStats();
317
306
 
318
- // EXTRACTED FROM TILESET
319
- this._extensionsUsed = undefined;
320
- this.dynamicScreenSpaceErrorComputedDensity = 0.0; // Updated based on the camera position and direction
321
- // Metadata for the entire tileset
322
- this.extras = null;
323
- this.asset = {};
324
- this.credits = {};
325
- this.description = this.options.description || '';
326
-
327
- this._initializeTileSet(json);
307
+ this.tilesetInitializationPromise = this._initializeTileSet(tileset);
328
308
  }
329
309
 
330
310
  /** Release resources */
@@ -335,7 +315,7 @@ export default class Tileset3D {
335
315
  /** Is the tileset loaded (update needs to have been called at least once) */
336
316
  isLoaded(): boolean {
337
317
  // Check that `_frameNumber !== 0` which means that update was called at least once
338
- return this._pendingCount === 0 && this._frameNumber !== 0;
318
+ return this._pendingCount === 0 && this._frameNumber !== 0 && this._requestedTiles.length === 0;
339
319
  }
340
320
 
341
321
  get tiles(): object[] {
@@ -347,10 +327,7 @@ export default class Tileset3D {
347
327
  }
348
328
 
349
329
  get queryParams(): string {
350
- if (!this._queryParamsString) {
351
- this._queryParamsString = getQueryParamString(this._queryParams);
352
- }
353
- return this._queryParamsString;
330
+ return new URLSearchParams(this._queryParams).toString();
354
331
  }
355
332
 
356
333
  setProps(props: Tileset3DProps): void {
@@ -371,42 +348,79 @@ export default class Tileset3D {
371
348
  if (isDataUrl) {
372
349
  return tilePath;
373
350
  }
374
- return `${tilePath}${this.queryParams}`;
351
+ return `${tilePath}${tilePath.includes('?') ? '&' : '?'}${this.queryParams}`;
375
352
  }
376
353
 
377
354
  // TODO CESIUM specific
378
355
  hasExtension(extensionName: string): boolean {
379
- return Boolean(this._extensionsUsed && this._extensionsUsed.indexOf(extensionName) > -1);
356
+ return Boolean(this._extensionsUsed.indexOf(extensionName) > -1);
380
357
  }
381
358
 
382
359
  /**
383
360
  * Update visible tiles relying on a list of viewports
384
361
  * @param viewports - list of viewports
362
+ * @deprecated
363
+ */
364
+ update(viewports: Viewport[] | Viewport | null = null) {
365
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
366
+ this.tilesetInitializationPromise.then(() => {
367
+ if (!viewports && this.lastUpdatedVieports) {
368
+ viewports = this.lastUpdatedVieports;
369
+ } else {
370
+ this.lastUpdatedVieports = viewports;
371
+ }
372
+ if (viewports) {
373
+ this.doUpdate(viewports);
374
+ }
375
+ });
376
+ }
377
+
378
+ /**
379
+ * Update visible tiles relying on a list of viewports.
380
+ * Do it with debounce delay to prevent update spam
381
+ * @param viewports viewports
382
+ * @returns Promise of new frameNumber
383
+ */
384
+ async selectTiles(viewports: Viewport[] | Viewport | null = null): Promise<number> {
385
+ await this.tilesetInitializationPromise;
386
+ if (viewports) {
387
+ this.lastUpdatedVieports = viewports;
388
+ }
389
+ if (!this.updatePromise) {
390
+ this.updatePromise = new Promise<number>((resolve) => {
391
+ setTimeout(() => {
392
+ if (this.lastUpdatedVieports) {
393
+ this.doUpdate(this.lastUpdatedVieports);
394
+ }
395
+ resolve(this._frameNumber);
396
+ this.updatePromise = null;
397
+ }, this.options.debounceTime);
398
+ });
399
+ }
400
+ return this.updatePromise;
401
+ }
402
+
403
+ /**
404
+ * Update visible tiles relying on a list of viewports
405
+ * @param viewports viewports
385
406
  */
386
407
  // eslint-disable-next-line max-statements, complexity
387
- update(viewports: any[]): void {
408
+ private doUpdate(viewports: Viewport[] | Viewport): void {
388
409
  if ('loadTiles' in this.options && !this.options.loadTiles) {
389
410
  return;
390
411
  }
391
412
  if (this.traverseCounter > 0) {
392
413
  return;
393
414
  }
394
- if (!viewports && this.lastUpdatedVieports) {
395
- viewports = this.lastUpdatedVieports;
396
- } else {
397
- this.lastUpdatedVieports = viewports;
398
- }
399
- if (!(viewports instanceof Array)) {
400
- viewports = [viewports];
401
- }
415
+ const preparedViewports = viewports instanceof Array ? viewports : [viewports];
402
416
 
403
417
  this._cache.reset();
404
418
  this._frameNumber++;
405
- this.traverseCounter = viewports.length;
419
+ this.traverseCounter = preparedViewports.length;
406
420
  const viewportsToTraverse: string[] = [];
407
421
  // First loop to decrement traverseCounter
408
- for (const viewport of viewports) {
409
- const id = viewport.id as string;
422
+ for (const viewport of preparedViewports) {
423
+ const id = viewport.id;
410
424
  if (this._needTraverse(id)) {
411
425
  viewportsToTraverse.push(id);
412
426
  } else {
@@ -415,8 +429,8 @@ export default class Tileset3D {
415
429
  }
416
430
 
417
431
  // Second loop to traverse
418
- for (const viewport of viewports) {
419
- const id = viewport.id as string;
432
+ for (const viewport of preparedViewports) {
433
+ const id = viewport.id;
420
434
  if (!this.roots[id]) {
421
435
  this.roots[id] = this._initializeTileHeaders(this.tileset, null);
422
436
  }
@@ -424,7 +438,7 @@ export default class Tileset3D {
424
438
  if (!viewportsToTraverse.includes(id)) {
425
439
  continue; // eslint-disable-line no-continue
426
440
  }
427
- const frameState = getFrameState(viewport, this._frameNumber);
441
+ const frameState = getFrameState(viewport as GeospatialViewport, this._frameNumber);
428
442
  this._traverser.traverse(this.roots[id], frameState, this.options);
429
443
  }
430
444
  }
@@ -457,7 +471,16 @@ export default class Tileset3D {
457
471
  }
458
472
  const currentFrameStateData = this.frameStateData[id];
459
473
  const selectedTiles = Object.values(this._traverser.selectedTiles);
460
- currentFrameStateData.selectedTiles = selectedTiles;
474
+ const [filteredSelectedTiles, unselectedTiles] = limitSelectedTiles(
475
+ selectedTiles,
476
+ frameState,
477
+ this.options.maximumTilesSelected
478
+ );
479
+ currentFrameStateData.selectedTiles = filteredSelectedTiles;
480
+ for (const tile of unselectedTiles) {
481
+ tile.unselect();
482
+ }
483
+
461
484
  currentFrameStateData._requestedTiles = Object.values(this._traverser.requestedTiles);
462
485
  currentFrameStateData._emptyTiles = Object.values(this._traverser.emptyTiles);
463
486
 
@@ -495,7 +518,7 @@ export default class Tileset3D {
495
518
  this._updateStats();
496
519
  }
497
520
 
498
- _tilesChanged(oldSelectedTiles, selectedTiles) {
521
+ _tilesChanged(oldSelectedTiles: Tile3D[], selectedTiles: Tile3D[]): boolean {
499
522
  if (oldSelectedTiles.length !== selectedTiles.length) {
500
523
  return true;
501
524
  }
@@ -506,7 +529,7 @@ export default class Tileset3D {
506
529
  return changed;
507
530
  }
508
531
 
509
- _loadTiles() {
532
+ _loadTiles(): void {
510
533
  // Sort requests by priority before making any requests.
511
534
  // This makes it less likely this requests will be cancelled after being issued.
512
535
  // requestedTiles.sort((a, b) => a._priority - b._priority);
@@ -518,12 +541,12 @@ export default class Tileset3D {
518
541
  }
519
542
  }
520
543
 
521
- _unloadTiles() {
544
+ _unloadTiles(): void {
522
545
  // unload tiles from cache when hit maximumMemoryUsage
523
546
  this._cache.unloadTiles(this, (tileset, tile) => tileset._unloadTile(tile));
524
547
  }
525
548
 
526
- _updateStats() {
549
+ _updateStats(): void {
527
550
  let tilesRenderable = 0;
528
551
  let pointsRenderable = 0;
529
552
  for (const tile of this.selectedTiles) {
@@ -531,6 +554,9 @@ export default class Tileset3D {
531
554
  tilesRenderable++;
532
555
  if (tile.content.pointCount) {
533
556
  pointsRenderable += tile.content.pointCount;
557
+ } else {
558
+ // Calculate vertices for non point cloud tiles.
559
+ pointsRenderable += tile.content.vertexCount;
534
560
  }
535
561
  }
536
562
  }
@@ -540,37 +566,91 @@ export default class Tileset3D {
540
566
  this.stats.get(POINTS_COUNT).count = pointsRenderable;
541
567
  }
542
568
 
543
- _initializeTileSet(tilesetJson) {
569
+ async _initializeTileSet(tilesetJson: TilesetJSON): Promise<void> {
570
+ if (this.type === TILESET_TYPE.I3S) {
571
+ this.calculateViewPropsI3S();
572
+ tilesetJson.root = await tilesetJson.root;
573
+ }
544
574
  this.root = this._initializeTileHeaders(tilesetJson, null);
545
575
 
546
- // TODO CESIUM Specific
547
576
  if (this.type === TILESET_TYPE.TILES3D) {
548
- this._initializeCesiumTileset(tilesetJson);
577
+ this._initializeTiles3DTileset(tilesetJson);
578
+ this.calculateViewPropsTiles3D();
549
579
  }
550
580
 
551
581
  if (this.type === TILESET_TYPE.I3S) {
552
582
  this._initializeI3STileset();
553
583
  }
554
- // Calculate cartographicCenter & zoom props to help apps center view on tileset
555
- this._calculateViewProps();
556
584
  }
557
585
 
558
- // Called during initialize Tileset to initialize the tileset's cartographic center (longitude, latitude) and zoom.
559
- _calculateViewProps() {
586
+ /**
587
+ * Called during initialize Tileset to initialize the tileset's cartographic center (longitude, latitude) and zoom.
588
+ * These metrics help apps center view on tileset
589
+ * For I3S there is extent (<1.8 version) or fullExtent (>=1.8 version) to calculate view props
590
+ * @returns
591
+ */
592
+ private calculateViewPropsI3S(): void {
593
+ // for I3S 1.8 try to calculate with fullExtent
594
+ const fullExtent = this.tileset.fullExtent;
595
+ if (fullExtent) {
596
+ const {xmin, xmax, ymin, ymax, zmin, zmax} = fullExtent;
597
+ this.cartographicCenter = new Vector3(
598
+ xmin + (xmax - xmin) / 2,
599
+ ymin + (ymax - ymin) / 2,
600
+ zmin + (zmax - zmin) / 2
601
+ );
602
+ this.cartesianCenter = Ellipsoid.WGS84.cartographicToCartesian(
603
+ this.cartographicCenter,
604
+ new Vector3()
605
+ );
606
+ this.zoom = getZoomFromFullExtent(fullExtent, this.cartographicCenter, this.cartesianCenter);
607
+ return;
608
+ }
609
+ // for I3S 1.6-1.7 try to calculate with extent
610
+ const extent = this.tileset.store?.extent;
611
+ if (extent) {
612
+ const [xmin, ymin, xmax, ymax] = extent;
613
+ this.cartographicCenter = new Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, 0);
614
+ this.cartesianCenter = Ellipsoid.WGS84.cartographicToCartesian(
615
+ this.cartographicCenter,
616
+ new Vector3()
617
+ );
618
+ this.zoom = getZoomFromExtent(extent, this.cartographicCenter, this.cartesianCenter);
619
+ return;
620
+ }
621
+ // eslint-disable-next-line no-console
622
+ console.warn('Extent is not defined in the tileset header');
623
+ this.cartographicCenter = new Vector3();
624
+ this.zoom = 1;
625
+ return;
626
+ }
627
+
628
+ /**
629
+ * Called during initialize Tileset to initialize the tileset's cartographic center (longitude, latitude) and zoom.
630
+ * These metrics help apps center view on tileset.
631
+ * For 3DTiles the root tile data is used to calculate view props.
632
+ * @returns
633
+ */
634
+ private calculateViewPropsTiles3D() {
560
635
  const root = this.root as Tile3D;
561
- assert(root);
562
636
  const {center} = root.boundingVolume;
563
637
  // TODO - handle all cases
564
638
  if (!center) {
565
- // eslint-disable-next-line
639
+ // eslint-disable-next-line no-console
566
640
  console.warn('center was not pre-calculated for the root tile');
567
641
  this.cartographicCenter = new Vector3();
568
642
  this.zoom = 1;
569
643
  return;
570
644
  }
571
- this.cartographicCenter = Ellipsoid.WGS84.cartesianToCartographic(center, new Vector3());
645
+
646
+ // cartographic coordinates are undefined at the center of the ellipsoid
647
+ if (center[0] !== 0 || center[1] !== 0 || center[2] !== 0) {
648
+ this.cartographicCenter = Ellipsoid.WGS84.cartesianToCartographic(center, new Vector3());
649
+ } else {
650
+ this.cartographicCenter = new Vector3(0, 0, -Ellipsoid.WGS84.radii[0]);
651
+ }
572
652
  this.cartesianCenter = center;
573
- this.zoom = getZoomFromBoundingVolume(root.boundingVolume);
653
+ this.zoom = getZoomFromBoundingVolume(root.boundingVolume, this.cartographicCenter);
574
654
  }
575
655
 
576
656
  _initializeStats() {
@@ -582,13 +662,13 @@ export default class Tileset3D {
582
662
  this.stats.get(TILES_LOADED);
583
663
  this.stats.get(TILES_UNLOADED);
584
664
  this.stats.get(TILES_LOAD_FAILED);
585
- this.stats.get(POINTS_COUNT, 'memory');
665
+ this.stats.get(POINTS_COUNT);
586
666
  this.stats.get(TILES_GPU_MEMORY, 'memory');
587
667
  }
588
668
 
589
669
  // Installs the main tileset JSON file or a tileset JSON file referenced from a tile.
590
670
  // eslint-disable-next-line max-statements
591
- _initializeTileHeaders(tilesetJson, parentTileHeader) {
671
+ _initializeTileHeaders(tilesetJson: TilesetJSON, parentTileHeader?: any) {
592
672
  // A tileset JSON file referenced from a tile may exist in a different directory than the root tileset.
593
673
  // Get the basePath relative to the external tileset.
594
674
  const rootTile = new Tile3D(this, tilesetJson.root, parentTileHeader); // resource
@@ -600,7 +680,7 @@ export default class Tileset3D {
600
680
  rootTile.depth = parentTileHeader.depth + 1;
601
681
  }
602
682
 
603
- // Cesium 3d tiles knows the hierarchy beforehand
683
+ // 3DTiles knows the hierarchy beforehand
604
684
  if (this.type === TILESET_TYPE.TILES3D) {
605
685
  const stack: Tile3D[] = [];
606
686
  stack.push(rootTile);
@@ -611,6 +691,17 @@ export default class Tileset3D {
611
691
  const children = tile.header.children || [];
612
692
  for (const childHeader of children) {
613
693
  const childTile = new Tile3D(this, childHeader, tile);
694
+
695
+ // Special handling for Google
696
+ // A session key must be used for all tile requests
697
+ if (childTile.contentUrl?.includes('?session=')) {
698
+ const url = new URL(childTile.contentUrl);
699
+ const session = url.searchParams.get('session');
700
+ if (session) {
701
+ this._queryParams.session = session;
702
+ }
703
+ }
704
+
614
705
  tile.children.push(childTile);
615
706
  childTile.depth = tile.depth + 1;
616
707
  stack.push(childTile);
@@ -621,7 +712,7 @@ export default class Tileset3D {
621
712
  return rootTile;
622
713
  }
623
714
 
624
- _initializeTraverser() {
715
+ _initializeTraverser(): TilesetTraverser {
625
716
  let TraverserClass;
626
717
  const type = this.type;
627
718
  switch (type) {
@@ -629,7 +720,7 @@ export default class Tileset3D {
629
720
  TraverserClass = Tileset3DTraverser;
630
721
  break;
631
722
  case TILESET_TYPE.I3S:
632
- TraverserClass = I3SetTraverser;
723
+ TraverserClass = I3STilesetTraverser;
633
724
  break;
634
725
  default:
635
726
  TraverserClass = TilesetTraverser;
@@ -641,24 +732,24 @@ export default class Tileset3D {
641
732
  });
642
733
  }
643
734
 
644
- _destroyTileHeaders(parentTile) {
735
+ _destroyTileHeaders(parentTile: Tile3D): void {
645
736
  this._destroySubtree(parentTile);
646
737
  }
647
738
 
648
- async _loadTile(tile) {
739
+ async _loadTile(tile: Tile3D): Promise<void> {
649
740
  let loaded;
650
741
  try {
651
742
  this._onStartTileLoading();
652
743
  loaded = await tile.loadContent();
653
- } catch (error) {
654
- this._onTileLoadError(tile, error);
744
+ } catch (error: unknown) {
745
+ this._onTileLoadError(tile, error instanceof Error ? error : new Error('load failed'));
655
746
  } finally {
656
747
  this._onEndTileLoading();
657
748
  this._onTileLoad(tile, loaded);
658
749
  }
659
750
  }
660
751
 
661
- _onTileLoadError(tile, error) {
752
+ _onTileLoadError(tile: Tile3D, error: Error): void {
662
753
  this.stats.get(TILES_LOAD_FAILED).incrementCount();
663
754
 
664
755
  const message = error.message || error.toString();
@@ -668,20 +759,60 @@ export default class Tileset3D {
668
759
  this.options.onTileError(tile, message, url);
669
760
  }
670
761
 
671
- _onTileLoad(tile, loaded) {
762
+ _onTileLoad(tile: Tile3D, loaded: boolean): void {
672
763
  if (!loaded) {
673
764
  return;
674
765
  }
675
766
 
767
+ if (this.type === TILESET_TYPE.I3S) {
768
+ // We can't calculate tiles total in I3S in advance so we calculate it dynamically.
769
+ const nodesInNodePages = this.tileset?.nodePagesTile?.nodesInNodePages || 0;
770
+ this.stats.get(TILES_TOTAL).reset();
771
+ this.stats.get(TILES_TOTAL).addCount(nodesInNodePages);
772
+ }
773
+
676
774
  // add coordinateOrigin and modelMatrix to tile
677
775
  if (tile && tile.content) {
678
776
  calculateTransformProps(tile, tile.content);
679
777
  }
680
778
 
779
+ this.updateContentTypes(tile);
681
780
  this._addTileToCache(tile);
682
781
  this.options.onTileLoad(tile);
683
782
  }
684
783
 
784
+ /**
785
+ * Update information about data types in nested tiles
786
+ * @param tile instance of a nested Tile3D
787
+ */
788
+ private updateContentTypes(tile: Tile3D) {
789
+ if (this.type === TILESET_TYPE.I3S) {
790
+ if (tile.header.isDracoGeometry) {
791
+ this.contentFormats.draco = true;
792
+ }
793
+ switch (tile.header.textureFormat) {
794
+ case 'dds':
795
+ this.contentFormats.dds = true;
796
+ break;
797
+ case 'ktx2':
798
+ this.contentFormats.ktx2 = true;
799
+ break;
800
+ default:
801
+ }
802
+ } else if (this.type === TILESET_TYPE.TILES3D) {
803
+ const {extensionsRemoved = []} = tile.content?.gltf || {};
804
+ if (extensionsRemoved.includes('KHR_draco_mesh_compression')) {
805
+ this.contentFormats.draco = true;
806
+ }
807
+ if (extensionsRemoved.includes('EXT_meshopt_compression')) {
808
+ this.contentFormats.meshopt = true;
809
+ }
810
+ if (extensionsRemoved.includes('KHR_texture_basisu')) {
811
+ this.contentFormats.ktx2 = true;
812
+ }
813
+ }
814
+ }
815
+
685
816
  _onStartTileLoading() {
686
817
  this._pendingCount++;
687
818
  this.stats.get(TILES_LOADING).incrementCount();
@@ -692,7 +823,7 @@ export default class Tileset3D {
692
823
  this.stats.get(TILES_LOADING).decrementCount();
693
824
  }
694
825
 
695
- _addTileToCache(tile) {
826
+ _addTileToCache(tile: Tile3D) {
696
827
  this._cache.add(this, tile, (tileset) => tileset._updateCacheStats(tile));
697
828
  }
698
829
 
@@ -701,12 +832,12 @@ export default class Tileset3D {
701
832
  this.stats.get(TILES_IN_MEMORY).incrementCount();
702
833
 
703
834
  // Good enough? Just use the raw binary ArrayBuffer's byte length.
704
- this.gpuMemoryUsageInBytes += tile.content.byteLength || 0;
835
+ this.gpuMemoryUsageInBytes += tile.gpuMemoryUsageInBytes || 0;
705
836
  this.stats.get(TILES_GPU_MEMORY).count = this.gpuMemoryUsageInBytes;
706
837
  }
707
838
 
708
839
  _unloadTile(tile) {
709
- this.gpuMemoryUsageInBytes -= (tile.content && tile.content.byteLength) || 0;
840
+ this.gpuMemoryUsageInBytes -= tile.gpuMemoryUsageInBytes || 0;
710
841
 
711
842
  this.stats.get(TILES_IN_MEMORY).decrementCount();
712
843
  this.stats.get(TILES_UNLOADED).incrementCount();
@@ -759,13 +890,23 @@ export default class Tileset3D {
759
890
  tile.destroy();
760
891
  }
761
892
 
762
- _initializeCesiumTileset(tilesetJson) {
893
+ _initializeTiles3DTileset(tilesetJson) {
894
+ if (tilesetJson.queryString) {
895
+ const searchParams = new URLSearchParams(tilesetJson.queryString);
896
+ const queryParams = Object.fromEntries(searchParams.entries());
897
+ this._queryParams = {...this._queryParams, ...queryParams};
898
+ }
899
+
763
900
  this.asset = tilesetJson.asset;
764
901
  if (!this.asset) {
765
902
  throw new Error('Tileset must have an asset property.');
766
903
  }
767
- if (this.asset.version !== '0.0' && this.asset.version !== '1.0') {
768
- throw new Error('The tileset must be 3D Tiles version 0.0 or 1.0.');
904
+ if (
905
+ this.asset.version !== '0.0' &&
906
+ this.asset.version !== '1.0' &&
907
+ this.asset.version !== '1.1'
908
+ ) {
909
+ throw new Error('The tileset must be 3D Tiles version either 0.0 or 1.0 or 1.1.');
769
910
  }
770
911
 
771
912
  // Note: `asset.tilesetVersion` is version of the tileset itself (not the version of the 3D TILES standard)
@@ -783,29 +924,15 @@ export default class Tileset3D {
783
924
  // Gets the tileset's properties dictionary object, which contains metadata about per-feature properties.
784
925
  this.properties = tilesetJson.properties;
785
926
  this.geometricError = tilesetJson.geometricError;
786
- this._extensionsUsed = tilesetJson.extensionsUsed;
927
+ this._extensionsUsed = tilesetJson.extensionsUsed || [];
787
928
  // Returns the extras property at the top of the tileset JSON (application specific metadata).
788
929
  this.extras = tilesetJson.extras;
789
930
  }
790
931
 
791
932
  _initializeI3STileset() {
933
+ // @ts-expect-error
792
934
  if (this.loadOptions.i3s && 'token' in this.loadOptions.i3s) {
793
- this._queryParams.token = this.loadOptions.i3s.token;
935
+ this._queryParams.token = this.loadOptions.i3s.token as string;
794
936
  }
795
937
  }
796
938
  }
797
-
798
- function getQueryParamString(queryParams): string {
799
- const queryParamStrings: string[] = [];
800
- for (const key of Object.keys(queryParams)) {
801
- queryParamStrings.push(`${key}=${queryParams[key]}`);
802
- }
803
- switch (queryParamStrings.length) {
804
- case 0:
805
- return '';
806
- case 1:
807
- return `?${queryParamStrings[0]}`;
808
- default:
809
- return `?${queryParamStrings.join('&')}`;
810
- }
811
- }