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

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/README.md +110 -1
  2. package/dist/dist.dev.js +4359 -1950
  3. package/dist/dist.min.js +1 -1
  4. package/dist/image-set.d.ts +94 -0
  5. package/dist/image-set.d.ts.map +1 -0
  6. package/dist/image-set.js +243 -0
  7. package/dist/image-set.js.map +1 -0
  8. package/dist/index.cjs +4178 -1892
  9. package/dist/index.cjs.map +4 -4
  10. package/dist/index.d.ts +27 -9
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +17 -8
  13. package/dist/index.js.map +1 -1
  14. package/dist/point-cloud/point-cloud-tile.d.ts +47 -0
  15. package/dist/point-cloud/point-cloud-tile.d.ts.map +1 -0
  16. package/dist/point-cloud/point-cloud-tile.js +67 -0
  17. package/dist/point-cloud/point-cloud-tile.js.map +1 -0
  18. package/dist/point-cloud/point-cloud-tileset.d.ts +131 -0
  19. package/dist/point-cloud/point-cloud-tileset.d.ts.map +1 -0
  20. package/dist/point-cloud/point-cloud-tileset.js +507 -0
  21. package/dist/point-cloud/point-cloud-tileset.js.map +1 -0
  22. package/dist/point-cloud/types.d.ts +64 -0
  23. package/dist/point-cloud/types.d.ts.map +1 -0
  24. package/dist/point-cloud/types.js +2 -0
  25. package/dist/point-cloud/types.js.map +1 -0
  26. package/dist/raster-set.d.ts +107 -0
  27. package/dist/raster-set.d.ts.map +1 -0
  28. package/dist/raster-set.js +268 -0
  29. package/dist/raster-set.js.map +1 -0
  30. package/dist/tileset-2d/adapter.d.ts +38 -0
  31. package/dist/tileset-2d/adapter.d.ts.map +1 -0
  32. package/dist/tileset-2d/adapter.js +5 -0
  33. package/dist/tileset-2d/adapter.js.map +1 -0
  34. package/dist/tileset-2d/index.d.ts +7 -0
  35. package/dist/tileset-2d/index.d.ts.map +1 -0
  36. package/dist/tileset-2d/index.js +6 -0
  37. package/dist/tileset-2d/index.js.map +1 -0
  38. package/dist/tileset-2d/tile-2d-header.d.ts +68 -0
  39. package/dist/tileset-2d/tile-2d-header.d.ts.map +1 -0
  40. package/dist/tileset-2d/tile-2d-header.js +181 -0
  41. package/dist/tileset-2d/tile-2d-header.js.map +1 -0
  42. package/dist/tileset-2d/tileset-2d.d.ts +201 -0
  43. package/dist/tileset-2d/tileset-2d.d.ts.map +1 -0
  44. package/dist/tileset-2d/tileset-2d.js +532 -0
  45. package/dist/tileset-2d/tileset-2d.js.map +1 -0
  46. package/dist/tileset-2d/types.d.ts +44 -0
  47. package/dist/tileset-2d/types.d.ts.map +1 -0
  48. package/dist/tileset-2d/types.js +5 -0
  49. package/dist/tileset-2d/types.js.map +1 -0
  50. package/dist/tileset-2d.d.ts +2 -0
  51. package/dist/tileset-2d.d.ts.map +1 -0
  52. package/dist/tileset-2d.js +5 -0
  53. package/dist/tileset-2d.js.map +1 -0
  54. package/dist/tileset-3d/common/indexed-archive-tileset-source.d.ts +60 -0
  55. package/dist/tileset-3d/common/indexed-archive-tileset-source.d.ts.map +1 -0
  56. package/dist/tileset-3d/common/indexed-archive-tileset-source.js +125 -0
  57. package/dist/tileset-3d/common/indexed-archive-tileset-source.js.map +1 -0
  58. package/dist/{tileset → tileset-3d/common}/tile-3d.d.ts +36 -27
  59. package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -0
  60. package/dist/{tileset → tileset-3d/common}/tile-3d.js +53 -62
  61. package/dist/tileset-3d/common/tile-3d.js.map +1 -0
  62. package/dist/tileset-3d/common/tileset-3d.d.ts +177 -0
  63. package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -0
  64. package/dist/tileset-3d/common/tileset-3d.js +566 -0
  65. package/dist/tileset-3d/common/tileset-3d.js.map +1 -0
  66. package/dist/tileset-3d/common/tileset-cache.d.ts.map +1 -0
  67. package/dist/{tileset → tileset-3d/common}/tileset-cache.js +1 -1
  68. package/dist/tileset-3d/common/tileset-cache.js.map +1 -0
  69. package/dist/tileset-3d/common/tileset-source.d.ts +186 -0
  70. package/dist/tileset-3d/common/tileset-source.d.ts.map +1 -0
  71. package/dist/tileset-3d/common/tileset-source.js +17 -0
  72. package/dist/tileset-3d/common/tileset-source.js.map +1 -0
  73. package/dist/{tileset → tileset-3d/common}/tileset-traverser.d.ts +2 -2
  74. package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -0
  75. package/dist/{tileset → tileset-3d/common}/tileset-traverser.js +2 -2
  76. package/dist/tileset-3d/common/tileset-traverser.js.map +1 -0
  77. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +100 -0
  78. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts.map +1 -0
  79. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js +312 -0
  80. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -0
  81. package/dist/{tileset → tileset-3d}/format-3d-tiles/tileset-3d-traverser.d.ts +1 -1
  82. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -0
  83. package/dist/{tileset → tileset-3d}/format-3d-tiles/tileset-3d-traverser.js +1 -1
  84. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js.map +1 -0
  85. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.d.ts.map +1 -0
  86. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js.map +1 -0
  87. package/dist/tileset-3d/format-i3s/i3s-source.d.ts +99 -0
  88. package/dist/tileset-3d/format-i3s/i3s-source.d.ts.map +1 -0
  89. package/dist/tileset-3d/format-i3s/i3s-source.js +299 -0
  90. package/dist/tileset-3d/format-i3s/i3s-source.js.map +1 -0
  91. package/dist/tileset-3d/format-i3s/i3s-tile-manager.d.ts.map +1 -0
  92. package/dist/{tileset → tileset-3d}/format-i3s/i3s-tile-manager.js +2 -2
  93. package/dist/tileset-3d/format-i3s/i3s-tile-manager.js.map +1 -0
  94. package/dist/{tileset → tileset-3d}/format-i3s/i3s-tileset-traverser.d.ts +2 -2
  95. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.d.ts.map +1 -0
  96. package/dist/{tileset → tileset-3d}/format-i3s/i3s-tileset-traverser.js +14 -23
  97. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js.map +1 -0
  98. package/dist/{tileset → tileset-3d}/helpers/3d-tiles-options.d.ts +1 -1
  99. package/dist/tileset-3d/helpers/3d-tiles-options.d.ts.map +1 -0
  100. package/dist/tileset-3d/helpers/3d-tiles-options.js.map +1 -0
  101. package/dist/tileset-3d/helpers/bounding-volume.d.ts.map +1 -0
  102. package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -0
  103. package/dist/{tileset → tileset-3d}/helpers/frame-state.d.ts +1 -1
  104. package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -0
  105. package/dist/tileset-3d/helpers/frame-state.js.map +1 -0
  106. package/dist/{tileset → tileset-3d}/helpers/i3s-lod.d.ts +1 -1
  107. package/dist/tileset-3d/helpers/i3s-lod.d.ts.map +1 -0
  108. package/dist/{tileset → tileset-3d}/helpers/i3s-lod.js +1 -1
  109. package/dist/tileset-3d/helpers/i3s-lod.js.map +1 -0
  110. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts +20 -0
  111. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts.map +1 -0
  112. package/dist/{tileset → tileset-3d}/helpers/tiles-3d-lod.js +43 -4
  113. package/dist/tileset-3d/helpers/tiles-3d-lod.js.map +1 -0
  114. package/dist/{tileset → tileset-3d}/helpers/transform-utils.d.ts +1 -1
  115. package/dist/tileset-3d/helpers/transform-utils.d.ts.map +1 -0
  116. package/dist/tileset-3d/helpers/transform-utils.js.map +1 -0
  117. package/dist/tileset-3d/helpers/zoom.d.ts.map +1 -0
  118. package/dist/tileset-3d/helpers/zoom.js.map +1 -0
  119. package/dist/types.d.ts +7 -0
  120. package/dist/types.d.ts.map +1 -1
  121. package/package.json +7 -6
  122. package/src/image-set.ts +328 -0
  123. package/src/index.ts +80 -9
  124. package/src/point-cloud/point-cloud-tile.ts +76 -0
  125. package/src/point-cloud/point-cloud-tileset.ts +709 -0
  126. package/src/point-cloud/types.ts +78 -0
  127. package/src/raster-set.ts +374 -0
  128. package/src/tileset-2d/adapter.ts +47 -0
  129. package/src/tileset-2d/index.ts +33 -0
  130. package/src/tileset-2d/tile-2d-header.ts +224 -0
  131. package/src/tileset-2d/tileset-2d.ts +716 -0
  132. package/src/tileset-2d/types.ts +39 -0
  133. package/src/tileset-2d.ts +5 -0
  134. package/src/tileset-3d/common/indexed-archive-tileset-source.ts +205 -0
  135. package/src/{tileset → tileset-3d/common}/tile-3d.ts +64 -73
  136. package/src/tileset-3d/common/tileset-3d.ts +736 -0
  137. package/src/{tileset → tileset-3d/common}/tileset-cache.ts +1 -1
  138. package/src/tileset-3d/common/tileset-source.ts +234 -0
  139. package/src/{tileset → tileset-3d/common}/tileset-traverser.ts +3 -3
  140. package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +379 -0
  141. package/src/{tileset → tileset-3d}/format-3d-tiles/tileset-3d-traverser.ts +1 -1
  142. package/src/tileset-3d/format-i3s/i3s-source.ts +366 -0
  143. package/src/{tileset → tileset-3d}/format-i3s/i3s-tile-manager.ts +2 -2
  144. package/src/{tileset → tileset-3d}/format-i3s/i3s-tileset-traverser.ts +15 -24
  145. package/src/{tileset → tileset-3d}/helpers/3d-tiles-options.ts +1 -1
  146. package/src/{tileset → tileset-3d}/helpers/frame-state.ts +1 -1
  147. package/src/{tileset → tileset-3d}/helpers/i3s-lod.ts +2 -2
  148. package/src/{tileset → tileset-3d}/helpers/tiles-3d-lod.ts +46 -4
  149. package/src/{tileset → tileset-3d}/helpers/transform-utils.ts +1 -1
  150. package/src/types.ts +7 -0
  151. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.d.ts.map +0 -1
  152. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.js.map +0 -1
  153. package/dist/tileset/format-i3s/i3s-pending-tiles-register.d.ts.map +0 -1
  154. package/dist/tileset/format-i3s/i3s-pending-tiles-register.js.map +0 -1
  155. package/dist/tileset/format-i3s/i3s-tile-manager.d.ts.map +0 -1
  156. package/dist/tileset/format-i3s/i3s-tile-manager.js.map +0 -1
  157. package/dist/tileset/format-i3s/i3s-tileset-traverser.d.ts.map +0 -1
  158. package/dist/tileset/format-i3s/i3s-tileset-traverser.js.map +0 -1
  159. package/dist/tileset/helpers/3d-tiles-options.d.ts.map +0 -1
  160. package/dist/tileset/helpers/3d-tiles-options.js.map +0 -1
  161. package/dist/tileset/helpers/bounding-volume.d.ts.map +0 -1
  162. package/dist/tileset/helpers/bounding-volume.js.map +0 -1
  163. package/dist/tileset/helpers/frame-state.d.ts.map +0 -1
  164. package/dist/tileset/helpers/frame-state.js.map +0 -1
  165. package/dist/tileset/helpers/i3s-lod.d.ts.map +0 -1
  166. package/dist/tileset/helpers/i3s-lod.js.map +0 -1
  167. package/dist/tileset/helpers/tiles-3d-lod.d.ts +0 -8
  168. package/dist/tileset/helpers/tiles-3d-lod.d.ts.map +0 -1
  169. package/dist/tileset/helpers/tiles-3d-lod.js.map +0 -1
  170. package/dist/tileset/helpers/transform-utils.d.ts.map +0 -1
  171. package/dist/tileset/helpers/transform-utils.js.map +0 -1
  172. package/dist/tileset/helpers/zoom.d.ts.map +0 -1
  173. package/dist/tileset/helpers/zoom.js.map +0 -1
  174. package/dist/tileset/tile-3d.d.ts.map +0 -1
  175. package/dist/tileset/tile-3d.js.map +0 -1
  176. package/dist/tileset/tileset-3d.d.ts +0 -284
  177. package/dist/tileset/tileset-3d.d.ts.map +0 -1
  178. package/dist/tileset/tileset-3d.js +0 -814
  179. package/dist/tileset/tileset-3d.js.map +0 -1
  180. package/dist/tileset/tileset-cache.d.ts.map +0 -1
  181. package/dist/tileset/tileset-cache.js.map +0 -1
  182. package/dist/tileset/tileset-traverser.d.ts.map +0 -1
  183. package/dist/tileset/tileset-traverser.js.map +0 -1
  184. package/src/tileset/tileset-3d.ts +0 -1037
  185. /package/dist/{tileset → tileset-3d/common}/tileset-cache.d.ts +0 -0
  186. /package/dist/{tileset → tileset-3d}/format-i3s/i3s-pending-tiles-register.d.ts +0 -0
  187. /package/dist/{tileset → tileset-3d}/format-i3s/i3s-pending-tiles-register.js +0 -0
  188. /package/dist/{tileset → tileset-3d}/format-i3s/i3s-tile-manager.d.ts +0 -0
  189. /package/dist/{tileset → tileset-3d}/helpers/3d-tiles-options.js +0 -0
  190. /package/dist/{tileset → tileset-3d}/helpers/bounding-volume.d.ts +0 -0
  191. /package/dist/{tileset → tileset-3d}/helpers/bounding-volume.js +0 -0
  192. /package/dist/{tileset → tileset-3d}/helpers/frame-state.js +0 -0
  193. /package/dist/{tileset → tileset-3d}/helpers/transform-utils.js +0 -0
  194. /package/dist/{tileset → tileset-3d}/helpers/zoom.d.ts +0 -0
  195. /package/dist/{tileset → tileset-3d}/helpers/zoom.js +0 -0
  196. /package/src/{tileset → tileset-3d}/format-i3s/i3s-pending-tiles-register.ts +0 -0
  197. /package/src/{tileset → tileset-3d}/helpers/bounding-volume.ts +0 -0
  198. /package/src/{tileset → tileset-3d}/helpers/zoom.ts +0 -0
@@ -0,0 +1,736 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) vis.gl contributors
3
+
4
+ // This file is derived from the Cesium code base under Apache 2 license
5
+ // See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md
6
+
7
+ import {Matrix4, Vector3} from '@math.gl/core';
8
+ import {Ellipsoid} from '@math.gl/geospatial';
9
+ import {Stats} from '@probe.gl/stats';
10
+ import {RequestScheduler, LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils';
11
+ import {TilesetCache} from './tileset-cache';
12
+ import {calculateTransformProps} from '../helpers/transform-utils';
13
+ import {FrameState, getFrameState, limitSelectedTiles} from '../helpers/frame-state';
14
+
15
+ import type {GeospatialViewport, Viewport} from '../../types';
16
+ import {Tile3D} from './tile-3d';
17
+ import {TILESET_TYPE} from '../../constants';
18
+
19
+ import {TilesetTraverser} from './tileset-traverser';
20
+ import {
21
+ isTileset3DSource,
22
+ TileContentLoadResult,
23
+ TilesetContentFormats,
24
+ TilesetJSON,
25
+ Tileset3DSource,
26
+ TilesetSourceMetadata
27
+ } from './tileset-source';
28
+
29
+ export type {TilesetJSON} from './tileset-source';
30
+
31
+ export type Tileset3DProps = {
32
+ throttleRequests?: boolean;
33
+ maxRequests?: number;
34
+ loadOptions?: LoaderOptions;
35
+ loadTiles?: boolean;
36
+ basePath?: string;
37
+ maximumMemoryUsage?: number;
38
+ memoryCacheOverflow?: number;
39
+ maximumTilesSelected?: number;
40
+ debounceTime?: number;
41
+
42
+ description?: string;
43
+ attributions?: string[];
44
+
45
+ ellipsoid?: object;
46
+ modelMatrix?: Matrix4;
47
+
48
+ maximumScreenSpaceError?: number;
49
+ memoryAdjustedScreenSpaceError?: boolean;
50
+ viewportTraversersMap?: any;
51
+ updateTransforms?: boolean;
52
+ viewDistanceScale?: number;
53
+
54
+ onTileLoad?: (tile: Tile3D) => any;
55
+ onTileUnload?: (tile: Tile3D) => any;
56
+ onTileError?: (tile: Tile3D, message: string, url: string) => any;
57
+ /** Called when a format-specific source operation fails. */
58
+ onSourceError?: (error: Error, source: Tileset3DSource, tile?: Tile3D | null) => any;
59
+ /** Called when tileset initialization fails before traversal can begin. */
60
+ onTilesetError?: (error: Error, tileset: Tileset3D) => any;
61
+ contentLoader?: (tile: Tile3D) => Promise<void>;
62
+ onTraversalComplete?: (selectedTiles: Tile3D[]) => Tile3D[];
63
+ onUpdate?: () => void;
64
+ };
65
+
66
+ type Props = {
67
+ description: string;
68
+ ellipsoid: object;
69
+ modelMatrix: Matrix4;
70
+ throttleRequests: boolean;
71
+ maxRequests: number;
72
+ maximumMemoryUsage: number;
73
+ memoryCacheOverflow: number;
74
+ maximumTilesSelected: number;
75
+ debounceTime: number;
76
+ onTileLoad: (tile: Tile3D) => void;
77
+ onTileUnload: (tile: Tile3D) => void;
78
+ onTileError: (tile: Tile3D, message: string, url: string) => void;
79
+ onSourceError: (error: Error, source: Tileset3DSource, tile?: Tile3D | null) => void;
80
+ onTilesetError: (error: Error, tileset: Tileset3D) => void;
81
+ onTraversalComplete: (selectedTiles: Tile3D[]) => Tile3D[];
82
+ onUpdate: () => void;
83
+ maximumScreenSpaceError: number;
84
+ memoryAdjustedScreenSpaceError: boolean;
85
+ viewportTraversersMap: Record<string, any> | null;
86
+ attributions: string[];
87
+ loadTiles: boolean;
88
+ loadOptions: LoaderOptions;
89
+ updateTransforms: boolean;
90
+ viewDistanceScale: number;
91
+ basePath: string;
92
+ contentLoader?: (tile: Tile3D) => Promise<void>;
93
+ i3s: Record<string, any>;
94
+ };
95
+
96
+ const DEFAULT_PROPS: Props = {
97
+ description: '',
98
+ ellipsoid: Ellipsoid.WGS84,
99
+ modelMatrix: new Matrix4(),
100
+ throttleRequests: true,
101
+ maxRequests: 64,
102
+ maximumMemoryUsage: 32,
103
+ memoryCacheOverflow: 1,
104
+ maximumTilesSelected: 0,
105
+ debounceTime: 0,
106
+ onTileLoad: () => {},
107
+ onTileUnload: () => {},
108
+ onTileError: () => {},
109
+ onSourceError: () => {},
110
+ onTilesetError: () => {},
111
+ onTraversalComplete: (selectedTiles: Tile3D[]) => selectedTiles,
112
+ onUpdate: () => {},
113
+ contentLoader: undefined,
114
+ viewDistanceScale: 1.0,
115
+ maximumScreenSpaceError: 8,
116
+ memoryAdjustedScreenSpaceError: false,
117
+ loadTiles: true,
118
+ updateTransforms: true,
119
+ viewportTraversersMap: null,
120
+ loadOptions: {fetch: {}},
121
+ attributions: [],
122
+ basePath: '',
123
+ i3s: {}
124
+ };
125
+
126
+ const TILES_TOTAL = 'Tiles In Tileset(s)';
127
+ const TILES_IN_MEMORY = 'Tiles In Memory';
128
+ const TILES_IN_VIEW = 'Tiles In View';
129
+ const TILES_RENDERABLE = 'Tiles To Render';
130
+ const TILES_LOADED = 'Tiles Loaded';
131
+ const TILES_LOADING = 'Tiles Loading';
132
+ const TILES_UNLOADED = 'Tiles Unloaded';
133
+ const TILES_LOAD_FAILED = 'Failed Tile Loads';
134
+ const POINTS_COUNT = 'Points/Vertices';
135
+ const TILES_GPU_MEMORY = 'Tile Memory Use';
136
+ const MAXIMUM_SSE = 'Maximum Screen Space Error';
137
+
138
+ /**
139
+ * Shared runtime for tile traversal, selection, cache management, and request scheduling.
140
+ *
141
+ * Format-specific loading behavior is delegated to a {@link Tileset3DSource} implementation.
142
+ */
143
+ export class Tileset3D {
144
+ options: Props;
145
+ loadOptions: LoaderOptions;
146
+ /** Source implementation responsible for format-specific initialization and loading. */
147
+ source: Tileset3DSource;
148
+
149
+ type: TILESET_TYPE;
150
+ tileset: TilesetJSON | null;
151
+ loader: LoaderWithParser;
152
+ url: string;
153
+ basePath: string;
154
+ modelMatrix: Matrix4;
155
+ ellipsoid: any;
156
+ lodMetricType: string;
157
+ lodMetricValue: number;
158
+ refine: string;
159
+ root: Tile3D | null = null;
160
+ roots: Record<string, Tile3D> = {};
161
+ asset: Record<string, any> = {};
162
+
163
+ description = '';
164
+ properties: any;
165
+ extras: any = null;
166
+ attributions: any = {};
167
+ credits: any = {};
168
+
169
+ stats: Stats;
170
+ contentFormats: TilesetContentFormats;
171
+
172
+ cartographicCenter: Vector3 | null = null;
173
+ cartesianCenter: Vector3 | null = null;
174
+ zoom = 1;
175
+ boundingVolume: any = null;
176
+
177
+ dynamicScreenSpaceErrorComputedDensity = 0.0;
178
+
179
+ maximumMemoryUsage = 32;
180
+ gpuMemoryUsageInBytes = 0;
181
+ memoryAdjustedScreenSpaceError = 0.0;
182
+
183
+ private _cacheBytes = 0;
184
+ private _cacheOverflowBytes = 0;
185
+
186
+ _frameNumber = 0;
187
+ private _tiles: Record<string, Tile3D> = {};
188
+ private _pendingCount = 0;
189
+
190
+ selectedTiles: Tile3D[] = [];
191
+
192
+ traverseCounter = 0;
193
+ geometricError = 0;
194
+ private lastUpdatedVieports: Viewport[] | Viewport | null = null;
195
+ private _requestedTiles: Tile3D[] = [];
196
+ private _emptyTiles: Tile3D[] = [];
197
+ private frameStateData: any = {};
198
+
199
+ _traverser: TilesetTraverser;
200
+ _cache = new TilesetCache();
201
+ _requestScheduler: RequestScheduler;
202
+
203
+ private _heldTiles: Set<string> = new Set();
204
+ private updatePromise: Promise<number> | null = null;
205
+ tilesetInitializationPromise: Promise<void>;
206
+
207
+ /**
208
+ * Creates a tileset runtime from an explicit source implementation.
209
+ * @param source Source-backed tileset implementation.
210
+ * @param options Traversal and runtime options.
211
+ */
212
+ constructor(source: Tileset3DSource, options?: Tileset3DProps) {
213
+ this.options = {...DEFAULT_PROPS, ...options};
214
+ this.loadOptions = this.options.loadOptions || {};
215
+
216
+ if (!isTileset3DSource(source)) {
217
+ throw new Error('Tileset3D requires a Tileset3DSource instance');
218
+ }
219
+ this.source = source;
220
+
221
+ this.tileset = null;
222
+ this.loader = this.source.loader;
223
+ this.type = this.source.type;
224
+ this.url = this.source.url;
225
+ this.basePath = this.source.basePath;
226
+ this.modelMatrix = this.options.modelMatrix;
227
+ this.ellipsoid = this.options.ellipsoid;
228
+ this.lodMetricType = '';
229
+ this.lodMetricValue = 0;
230
+ this.refine = '';
231
+ this.contentFormats = this.source.contentFormats;
232
+
233
+ this._traverser = this._initializeTraverser();
234
+ this._requestScheduler = new RequestScheduler({
235
+ throttleRequests: this.options.throttleRequests,
236
+ maxRequests: this.options.maxRequests
237
+ });
238
+
239
+ this.memoryAdjustedScreenSpaceError = this.options.maximumScreenSpaceError;
240
+ this._cacheBytes = this.options.maximumMemoryUsage * 1024 * 1024;
241
+ this._cacheOverflowBytes = this.options.memoryCacheOverflow * 1024 * 1024;
242
+
243
+ this.stats = new Stats({id: this.url});
244
+ this._initializeStats();
245
+
246
+ this.tilesetInitializationPromise = this._initializeTileSet();
247
+ }
248
+
249
+ destroy(): void {
250
+ this._destroy();
251
+ }
252
+
253
+ isLoaded(): boolean {
254
+ return this._pendingCount === 0 && this._frameNumber !== 0 && this._requestedTiles.length === 0;
255
+ }
256
+
257
+ get tiles(): object[] {
258
+ return Object.values(this._tiles);
259
+ }
260
+
261
+ get frameNumber(): number {
262
+ return this._frameNumber;
263
+ }
264
+
265
+ get queryParams(): string {
266
+ const rootUrl = this.source.getTileUrl(this.url);
267
+ const queryIndex = rootUrl.indexOf('?');
268
+ return queryIndex >= 0 ? rootUrl.slice(queryIndex + 1) : '';
269
+ }
270
+
271
+ setProps(props: Tileset3DProps): void {
272
+ this.options = {...this.options, ...props};
273
+ }
274
+
275
+ getTileUrl(tilePath: string): string {
276
+ return this.source.getTileUrl(tilePath);
277
+ }
278
+
279
+ hasExtension(extensionName: string): boolean {
280
+ return Boolean(this.source.hasExtension?.(extensionName));
281
+ }
282
+
283
+ update(viewports: Viewport[] | Viewport | null = null): void {
284
+ this.tilesetInitializationPromise.then(() => {
285
+ if (!viewports && this.lastUpdatedVieports) {
286
+ viewports = this.lastUpdatedVieports;
287
+ } else {
288
+ this.lastUpdatedVieports = viewports;
289
+ }
290
+ if (viewports) {
291
+ this.doUpdate(viewports);
292
+ }
293
+ });
294
+ }
295
+
296
+ async selectTiles(viewports: Viewport[] | Viewport | null = null): Promise<number> {
297
+ await this.tilesetInitializationPromise;
298
+ if (viewports) {
299
+ this.lastUpdatedVieports = viewports;
300
+ }
301
+ if (!this.updatePromise) {
302
+ this.updatePromise = new Promise<number>(resolve => {
303
+ setTimeout(() => {
304
+ if (this.lastUpdatedVieports) {
305
+ this.doUpdate(this.lastUpdatedVieports);
306
+ }
307
+ resolve(this._frameNumber);
308
+ this.updatePromise = null;
309
+ }, this.options.debounceTime);
310
+ });
311
+ }
312
+ return this.updatePromise;
313
+ }
314
+
315
+ adjustScreenSpaceError(): void {
316
+ if (this.gpuMemoryUsageInBytes < this._cacheBytes) {
317
+ this.memoryAdjustedScreenSpaceError = Math.max(
318
+ this.memoryAdjustedScreenSpaceError / 1.02,
319
+ this.options.maximumScreenSpaceError
320
+ );
321
+ } else if (this.gpuMemoryUsageInBytes > this._cacheBytes + this._cacheOverflowBytes) {
322
+ this.memoryAdjustedScreenSpaceError *= 1.02;
323
+ }
324
+ }
325
+
326
+ private doUpdate(viewports: Viewport[] | Viewport): void {
327
+ if ('loadTiles' in this.options && !this.options.loadTiles) {
328
+ return;
329
+ }
330
+ if (this.traverseCounter > 0) {
331
+ return;
332
+ }
333
+ const preparedViewports = viewports instanceof Array ? viewports : [viewports];
334
+
335
+ this._cache.reset();
336
+ this._frameNumber++;
337
+ this.traverseCounter = preparedViewports.length;
338
+ const viewportsToTraverse: string[] = [];
339
+ for (const viewport of preparedViewports) {
340
+ const id = viewport.id;
341
+ if (this._needTraverse(id)) {
342
+ viewportsToTraverse.push(id);
343
+ } else {
344
+ this.traverseCounter--;
345
+ }
346
+ }
347
+
348
+ for (const viewport of preparedViewports) {
349
+ const id = viewport.id;
350
+ if (!this.roots[id]) {
351
+ this.roots[id] = this._initializeTileHeaders(this.tileset, null);
352
+ }
353
+
354
+ if (!viewportsToTraverse.includes(id)) {
355
+ continue;
356
+ }
357
+ const frameState = getFrameState(viewport as GeospatialViewport, this._frameNumber);
358
+ this._traverser.traverse(this.roots[id], frameState, this.options);
359
+ }
360
+ }
361
+
362
+ _needTraverse(viewportId: string): boolean {
363
+ let traverserId = viewportId;
364
+ if (this.options.viewportTraversersMap) {
365
+ traverserId = this.options.viewportTraversersMap[viewportId];
366
+ }
367
+ return traverserId === viewportId;
368
+ }
369
+
370
+ _onTraversalEnd(frameState: FrameState): void {
371
+ const id = frameState.viewport.id;
372
+ if (!this.frameStateData[id]) {
373
+ this.frameStateData[id] = {selectedTiles: [], _requestedTiles: [], _emptyTiles: []};
374
+ }
375
+ const currentFrameStateData = this.frameStateData[id];
376
+ const selectedTiles = Object.values(this._traverser.selectedTiles);
377
+ const [filteredSelectedTiles, unselectedTiles] = limitSelectedTiles(
378
+ selectedTiles,
379
+ frameState,
380
+ this.options.maximumTilesSelected
381
+ );
382
+ currentFrameStateData.selectedTiles = filteredSelectedTiles;
383
+ for (const tile of unselectedTiles) {
384
+ tile.unselect();
385
+ }
386
+
387
+ currentFrameStateData._requestedTiles = Object.values(this._traverser.requestedTiles);
388
+ currentFrameStateData._emptyTiles = Object.values(this._traverser.emptyTiles);
389
+
390
+ this.traverseCounter--;
391
+ if (this.traverseCounter > 0) {
392
+ return;
393
+ }
394
+
395
+ this._updateTiles();
396
+ }
397
+
398
+ _updateTiles(): void {
399
+ const previousSelectedTiles = this.selectedTiles;
400
+ this.selectedTiles = [];
401
+ this._requestedTiles = [];
402
+ this._emptyTiles = [];
403
+
404
+ for (const frameStateKey in this.frameStateData) {
405
+ const frameStateDataValue = this.frameStateData[frameStateKey];
406
+ this.selectedTiles = this.selectedTiles.concat(frameStateDataValue.selectedTiles);
407
+ this._requestedTiles = this._requestedTiles.concat(frameStateDataValue._requestedTiles);
408
+ this._emptyTiles = this._emptyTiles.concat(frameStateDataValue._emptyTiles);
409
+ }
410
+
411
+ this.selectedTiles = this.options.onTraversalComplete(this.selectedTiles);
412
+
413
+ const selectedIds = new Set(this.selectedTiles.map(tile => tile.id));
414
+ const hasUndrawnTiles = this.selectedTiles.some(tile => !tile.tileDrawn);
415
+
416
+ let heldBackCount = 0;
417
+ if (hasUndrawnTiles) {
418
+ for (const tileId of selectedIds) {
419
+ this._heldTiles.add(tileId);
420
+ }
421
+ for (const tileId of this._heldTiles) {
422
+ if (selectedIds.has(tileId)) continue;
423
+
424
+ const tile = this._tiles[tileId];
425
+ if (tile && tile.contentAvailable) {
426
+ tile._selectedFrame = this._frameNumber;
427
+ this.selectedTiles.push(tile);
428
+ heldBackCount++;
429
+ } else {
430
+ this._heldTiles.delete(tileId);
431
+ }
432
+ }
433
+ } else {
434
+ this._heldTiles = selectedIds;
435
+ }
436
+
437
+ if (heldBackCount > 0) {
438
+ setTimeout(() => {
439
+ this.selectTiles();
440
+ }, 0);
441
+ }
442
+
443
+ for (const tile of this.selectedTiles) {
444
+ this._tiles[tile.id] = tile;
445
+ }
446
+
447
+ this._loadTiles();
448
+ this._unloadTiles();
449
+ this._updateStats();
450
+
451
+ if (this._tilesChanged(previousSelectedTiles, this.selectedTiles)) {
452
+ this.options.onUpdate();
453
+ }
454
+ }
455
+
456
+ _tilesChanged(oldSelectedTiles: Tile3D[], selectedTiles: Tile3D[]): boolean {
457
+ if (oldSelectedTiles.length !== selectedTiles.length) {
458
+ return true;
459
+ }
460
+ const oldSet = new Set(oldSelectedTiles.map(tile => tile.id));
461
+ const newSet = new Set(selectedTiles.map(tile => tile.id));
462
+ let changed = oldSelectedTiles.some(tile => !newSet.has(tile.id));
463
+ changed = changed || selectedTiles.some(tile => !oldSet.has(tile.id));
464
+ return changed;
465
+ }
466
+
467
+ _loadTiles(): void {
468
+ this._requestedTiles.sort((tileA, tileB) => tileA._priority - tileB._priority);
469
+ for (const tile of this._requestedTiles) {
470
+ if (tile.contentUnloaded) {
471
+ this._loadTile(tile);
472
+ }
473
+ }
474
+ }
475
+
476
+ _unloadTiles(): void {
477
+ this._cache.unloadTiles(this, (tileset, tile) => tileset._unloadTile(tile));
478
+ }
479
+
480
+ _updateStats(): void {
481
+ let tilesRenderable = 0;
482
+ let pointsRenderable = 0;
483
+ for (const tile of this.selectedTiles) {
484
+ if (tile.contentAvailable && tile.content) {
485
+ tilesRenderable++;
486
+ if (tile.content.pointCount) {
487
+ pointsRenderable += tile.content.pointCount;
488
+ } else {
489
+ pointsRenderable += tile.content.vertexCount;
490
+ }
491
+ }
492
+ }
493
+
494
+ this.stats.get(TILES_IN_VIEW).count = this.selectedTiles.length;
495
+ this.stats.get(TILES_RENDERABLE).count = tilesRenderable;
496
+ this.stats.get(POINTS_COUNT).count = pointsRenderable;
497
+ this.stats.get(MAXIMUM_SSE).count = this.memoryAdjustedScreenSpaceError;
498
+ }
499
+
500
+ async _initializeTileSet(): Promise<void> {
501
+ try {
502
+ const initializePromise = this.source.initialize();
503
+ const initialMetadata = this._getSourceMetadata();
504
+ if (initialMetadata) {
505
+ this._syncSourceState(initialMetadata);
506
+ }
507
+ if (
508
+ initialMetadata?.tileset?.root &&
509
+ typeof initialMetadata.tileset.root.then !== 'function'
510
+ ) {
511
+ this.root = this._initializeTileHeaders(initialMetadata.tileset, null);
512
+ this._applyViewState();
513
+ }
514
+
515
+ await initializePromise;
516
+ const metadata = this.source.getMetadata();
517
+ this._syncSourceState(metadata);
518
+ if (!this.root) {
519
+ this.root = this._initializeTileHeaders(metadata.tileset, null);
520
+ }
521
+ this._applyViewState();
522
+ } catch (error: unknown) {
523
+ const sourceError = error instanceof Error ? error : new Error(String(error));
524
+ this._onSourceError(sourceError, this.root);
525
+ this._onTilesetError(sourceError);
526
+ throw sourceError;
527
+ }
528
+ }
529
+
530
+ _initializeStats(): void {
531
+ this.stats.get(TILES_TOTAL);
532
+ this.stats.get(TILES_LOADING);
533
+ this.stats.get(TILES_IN_MEMORY);
534
+ this.stats.get(TILES_IN_VIEW);
535
+ this.stats.get(TILES_RENDERABLE);
536
+ this.stats.get(TILES_LOADED);
537
+ this.stats.get(TILES_UNLOADED);
538
+ this.stats.get(TILES_LOAD_FAILED);
539
+ this.stats.get(POINTS_COUNT);
540
+ this.stats.get(TILES_GPU_MEMORY, 'memory');
541
+ this.stats.get(MAXIMUM_SSE);
542
+ }
543
+
544
+ _initializeTileHeaders(tilesetJson: TilesetJSON, parentTileHeader?: Tile3D | null): Tile3D {
545
+ return this.source.initializeTileHeaders(this, tilesetJson, parentTileHeader);
546
+ }
547
+
548
+ _initializeTraverser(): TilesetTraverser {
549
+ return this.source.createTraverser({
550
+ basePath: this.basePath,
551
+ onTraversalEnd: this._onTraversalEnd.bind(this)
552
+ });
553
+ }
554
+
555
+ _destroyTileHeaders(parentTile: Tile3D): void {
556
+ this._destroySubtree(parentTile);
557
+ }
558
+
559
+ async _loadTile(tile: Tile3D): Promise<void> {
560
+ let loadResult: TileContentLoadResult | null = null;
561
+ try {
562
+ this._onStartTileLoading();
563
+ loadResult = await tile.loadContent();
564
+ } catch (error: unknown) {
565
+ const tileError = error instanceof Error ? error : new Error('load failed');
566
+ this._onSourceError(tileError, tile);
567
+ this._onTileLoadError(tile, tileError);
568
+ } finally {
569
+ this._onEndTileLoading();
570
+ this._onTileLoad(tile, loadResult);
571
+ }
572
+ }
573
+
574
+ _onTileLoadError(tile: Tile3D, error: Error): void {
575
+ this.stats.get(TILES_LOAD_FAILED).incrementCount();
576
+
577
+ const message = error.message || error.toString();
578
+ const url = tile.url;
579
+ console.error(`A 3D tile failed to load: ${tile.url} ${message}`);
580
+ this.options.onTileError(tile, message, url);
581
+ }
582
+
583
+ _onSourceError(error: Error, tile: Tile3D | null = null): void {
584
+ this.options.onSourceError(error, this.source, tile);
585
+ }
586
+
587
+ _onTilesetError(error: Error): void {
588
+ this.options.onTilesetError(error, this);
589
+ }
590
+
591
+ _onTileLoad(tile: Tile3D, loadResult: TileContentLoadResult | null): void {
592
+ if (!loadResult?.loaded) {
593
+ return;
594
+ }
595
+
596
+ if (tile.content) {
597
+ calculateTransformProps(tile, tile.content);
598
+ }
599
+
600
+ this.source.onTileLoaded?.(this, tile, loadResult);
601
+ this._syncSourceState(this.source.getMetadata());
602
+
603
+ const totalTiles = this.source.getTilesTotalCount?.();
604
+ if (totalTiles) {
605
+ this.stats.get(TILES_TOTAL).reset();
606
+ this.stats.get(TILES_TOTAL).addCount(totalTiles);
607
+ }
608
+
609
+ this._addTileToCache(tile);
610
+ this.options.onTileLoad(tile);
611
+ }
612
+
613
+ _onStartTileLoading(): void {
614
+ this._pendingCount++;
615
+ this.stats.get(TILES_LOADING).incrementCount();
616
+ }
617
+
618
+ _onEndTileLoading(): void {
619
+ this._pendingCount--;
620
+ this.stats.get(TILES_LOADING).decrementCount();
621
+ }
622
+
623
+ _addTileToCache(tile: Tile3D): void {
624
+ this._cache.add(this, tile, tileset => tileset._updateCacheStats(tile));
625
+ }
626
+
627
+ _updateCacheStats(tile: Tile3D): void {
628
+ this.stats.get(TILES_LOADED).incrementCount();
629
+ this.stats.get(TILES_IN_MEMORY).incrementCount();
630
+
631
+ this.gpuMemoryUsageInBytes += tile.gpuMemoryUsageInBytes || 0;
632
+ this.stats.get(TILES_GPU_MEMORY).count = this.gpuMemoryUsageInBytes;
633
+
634
+ if (this.options.memoryAdjustedScreenSpaceError) {
635
+ this.adjustScreenSpaceError();
636
+ }
637
+ }
638
+
639
+ _unloadTile(tile: Tile3D): void {
640
+ this.gpuMemoryUsageInBytes -= tile.gpuMemoryUsageInBytes || 0;
641
+
642
+ this.stats.get(TILES_IN_MEMORY).decrementCount();
643
+ this.stats.get(TILES_UNLOADED).incrementCount();
644
+ this.stats.get(TILES_GPU_MEMORY).count = this.gpuMemoryUsageInBytes;
645
+
646
+ this.options.onTileUnload(tile);
647
+ tile.unloadContent();
648
+ }
649
+
650
+ _destroy(): void {
651
+ const stack: Tile3D[] = [];
652
+
653
+ if (this.root) {
654
+ stack.push(this.root);
655
+ }
656
+
657
+ while (stack.length > 0) {
658
+ const tile = stack.pop() as Tile3D;
659
+
660
+ for (const child of tile.children) {
661
+ stack.push(child);
662
+ }
663
+
664
+ this._destroyTile(tile);
665
+ }
666
+ this.root = null;
667
+ }
668
+
669
+ _destroySubtree(tile: Tile3D): void {
670
+ const rootTile = tile;
671
+ const stack: Tile3D[] = [rootTile];
672
+ while (stack.length > 0) {
673
+ tile = stack.pop() as Tile3D;
674
+ for (const child of tile.children) {
675
+ stack.push(child);
676
+ }
677
+ if (tile !== rootTile) {
678
+ this._destroyTile(tile);
679
+ }
680
+ }
681
+ rootTile.children = [];
682
+ }
683
+
684
+ _destroyTile(tile: Tile3D): void {
685
+ this._cache.unloadTile(this, tile);
686
+ this._unloadTile(tile);
687
+ tile.destroy();
688
+ }
689
+
690
+ private _syncSourceState(metadata: TilesetSourceMetadata): void {
691
+ this.tileset = metadata.tileset;
692
+ this.loader = metadata.loader;
693
+ this.type = metadata.type;
694
+ this.url = metadata.url;
695
+ this.basePath = metadata.basePath;
696
+ this.lodMetricType = metadata.lodMetricType;
697
+ this.lodMetricValue = metadata.lodMetricValue;
698
+ this.refine = metadata.refine;
699
+ this.contentFormats = this.source.contentFormats;
700
+ this.asset = this.source.asset || this.asset;
701
+ this.properties = this.source.properties ?? this.properties;
702
+ this.extras = this.source.extras ?? this.extras;
703
+ if (this.options.attributions?.length) {
704
+ this.credits = {attributions: this.options.attributions};
705
+ } else {
706
+ this.credits = this.source.credits || this.credits;
707
+ }
708
+ }
709
+
710
+ private _applyViewState(): void {
711
+ const viewState = this.source.getViewState(this.root);
712
+ this.asset = viewState.asset || this.source.asset || {};
713
+ this.properties = viewState.properties ?? this.source.properties;
714
+ this.extras = viewState.extras ?? this.source.extras ?? null;
715
+ this.credits = viewState.credits ||
716
+ this.source.credits ||
717
+ this.credits || {attributions: this.options.attributions || []};
718
+ this.description = viewState.description || this.options.description || '';
719
+ this.boundingVolume = viewState.boundingVolume || this.root?.boundingVolume || null;
720
+ this.cartographicCenter = viewState.cartographicCenter || new Vector3();
721
+ this.cartesianCenter = viewState.cartesianCenter || null;
722
+ this.zoom = viewState.zoom || 1;
723
+ this.geometricError = this.tileset?.geometricError || 0;
724
+ }
725
+
726
+ /**
727
+ * Returns normalized source metadata when initialization has already populated it.
728
+ */
729
+ private _getSourceMetadata(): TilesetSourceMetadata | null {
730
+ try {
731
+ return this.source.getMetadata();
732
+ } catch {
733
+ return null;
734
+ }
735
+ }
736
+ }