@loaders.gl/tiles 4.0.0-alpha.5 → 4.0.0-alpha.6

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 (196) hide show
  1. package/dist/bundle.js +2 -2
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/constants.js +30 -26
  4. package/dist/dist.min.js +1572 -855
  5. package/dist/es5/bundle.js +6 -0
  6. package/dist/es5/bundle.js.map +1 -0
  7. package/dist/es5/constants.js +44 -0
  8. package/dist/es5/constants.js.map +1 -0
  9. package/dist/es5/index.js +93 -0
  10. package/dist/es5/index.js.map +1 -0
  11. package/dist/es5/tileset/format-3d-tiles/tileset-3d-traverser.js +70 -0
  12. package/dist/es5/tileset/format-3d-tiles/tileset-3d-traverser.js.map +1 -0
  13. package/dist/es5/tileset/format-i3s/i3s-pending-tiles-register.js +45 -0
  14. package/dist/es5/tileset/format-i3s/i3s-pending-tiles-register.js.map +1 -0
  15. package/dist/es5/tileset/format-i3s/i3s-tile-manager.js +84 -0
  16. package/dist/es5/tileset/format-i3s/i3s-tile-manager.js.map +1 -0
  17. package/dist/es5/tileset/format-i3s/i3s-tileset-traverser.js +143 -0
  18. package/dist/es5/tileset/format-i3s/i3s-tileset-traverser.js.map +1 -0
  19. package/dist/es5/tileset/helpers/3d-tiles-options.js +12 -0
  20. package/dist/es5/tileset/helpers/3d-tiles-options.js.map +1 -0
  21. package/dist/es5/tileset/helpers/bounding-volume.js +176 -0
  22. package/dist/es5/tileset/helpers/bounding-volume.js.map +1 -0
  23. package/dist/es5/tileset/helpers/frame-state.js +129 -0
  24. package/dist/es5/tileset/helpers/frame-state.js.map +1 -0
  25. package/dist/es5/tileset/helpers/i3s-lod.js +60 -0
  26. package/dist/es5/tileset/helpers/i3s-lod.js.map +1 -0
  27. package/dist/es5/tileset/helpers/tiles-3d-lod.js +103 -0
  28. package/dist/es5/tileset/helpers/tiles-3d-lod.js.map +1 -0
  29. package/dist/es5/tileset/helpers/transform-utils.js +50 -0
  30. package/dist/es5/tileset/helpers/transform-utils.js.map +1 -0
  31. package/dist/es5/tileset/helpers/zoom.js +63 -0
  32. package/dist/es5/tileset/helpers/zoom.js.map +1 -0
  33. package/dist/es5/tileset/tile-3d.js +565 -0
  34. package/dist/es5/tileset/tile-3d.js.map +1 -0
  35. package/dist/es5/tileset/tileset-3d.js +890 -0
  36. package/dist/es5/tileset/tileset-3d.js.map +1 -0
  37. package/dist/es5/tileset/tileset-cache.js +85 -0
  38. package/dist/es5/tileset/tileset-cache.js.map +1 -0
  39. package/dist/es5/tileset/tileset-traverser.js +328 -0
  40. package/dist/es5/tileset/tileset-traverser.js.map +1 -0
  41. package/dist/es5/types.js +2 -0
  42. package/dist/es5/types.js.map +1 -0
  43. package/dist/es5/utils/doubly-linked-list-node.js +21 -0
  44. package/dist/es5/utils/doubly-linked-list-node.js.map +1 -0
  45. package/dist/es5/utils/doubly-linked-list.js +88 -0
  46. package/dist/es5/utils/doubly-linked-list.js.map +1 -0
  47. package/dist/es5/utils/managed-array.js +126 -0
  48. package/dist/es5/utils/managed-array.js.map +1 -0
  49. package/dist/esm/bundle.js +4 -0
  50. package/dist/esm/bundle.js.map +1 -0
  51. package/dist/esm/constants.js +32 -0
  52. package/dist/esm/constants.js.map +1 -0
  53. package/dist/esm/index.js +10 -0
  54. package/dist/esm/index.js.map +1 -0
  55. package/dist/{tileset/traversers → esm/tileset/format-3d-tiles}/tileset-3d-traverser.js +2 -13
  56. package/dist/esm/tileset/format-3d-tiles/tileset-3d-traverser.js.map +1 -0
  57. package/dist/esm/tileset/format-i3s/i3s-pending-tiles-register.js +26 -0
  58. package/dist/esm/tileset/format-i3s/i3s-pending-tiles-register.js.map +1 -0
  59. package/dist/esm/tileset/format-i3s/i3s-tile-manager.js +79 -0
  60. package/dist/esm/tileset/format-i3s/i3s-tile-manager.js.map +1 -0
  61. package/dist/{tileset/traversers → esm/tileset/format-i3s}/i3s-tileset-traverser.js +14 -26
  62. package/dist/esm/tileset/format-i3s/i3s-tileset-traverser.js.map +1 -0
  63. package/dist/esm/tileset/helpers/3d-tiles-options.js +6 -0
  64. package/dist/esm/tileset/helpers/3d-tiles-options.js.map +1 -0
  65. package/dist/esm/tileset/helpers/bounding-volume.js +155 -0
  66. package/dist/esm/tileset/helpers/bounding-volume.js.map +1 -0
  67. package/dist/esm/tileset/helpers/frame-state.js +109 -0
  68. package/dist/esm/tileset/helpers/frame-state.js.map +1 -0
  69. package/dist/esm/tileset/helpers/i3s-lod.js +53 -0
  70. package/dist/esm/tileset/helpers/i3s-lod.js.map +1 -0
  71. package/dist/esm/tileset/helpers/tiles-3d-lod.js +100 -0
  72. package/dist/{tileset → esm/tileset}/helpers/tiles-3d-lod.js.map +1 -1
  73. package/dist/esm/tileset/helpers/transform-utils.js +50 -0
  74. package/dist/esm/tileset/helpers/transform-utils.js.map +1 -0
  75. package/dist/esm/tileset/helpers/zoom.js +55 -0
  76. package/dist/esm/tileset/helpers/zoom.js.map +1 -0
  77. package/dist/esm/tileset/tile-3d.js +445 -0
  78. package/dist/esm/tileset/tile-3d.js.map +1 -0
  79. package/dist/esm/tileset/tileset-3d.js +637 -0
  80. package/dist/esm/tileset/tileset-3d.js.map +1 -0
  81. package/dist/esm/tileset/tileset-cache.js +60 -0
  82. package/dist/esm/tileset/tileset-cache.js.map +1 -0
  83. package/dist/{tileset/traversers → esm/tileset}/tileset-traverser.js +20 -72
  84. package/dist/esm/tileset/tileset-traverser.js.map +1 -0
  85. package/dist/esm/types.js +2 -0
  86. package/dist/esm/types.js.map +1 -0
  87. package/dist/esm/utils/doubly-linked-list-node.js +12 -0
  88. package/dist/esm/utils/doubly-linked-list-node.js.map +1 -0
  89. package/dist/esm/utils/doubly-linked-list.js +65 -0
  90. package/dist/esm/utils/doubly-linked-list.js.map +1 -0
  91. package/dist/esm/utils/managed-array.js +87 -0
  92. package/dist/esm/utils/managed-array.js.map +1 -0
  93. package/dist/index.d.ts +4 -4
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +26 -10
  96. package/dist/tileset/{traversers → format-3d-tiles}/tileset-3d-traverser.d.ts +2 -2
  97. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -0
  98. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.js +54 -0
  99. package/dist/tileset/format-i3s/i3s-pending-tiles-register.d.ts +27 -0
  100. package/dist/tileset/format-i3s/i3s-pending-tiles-register.d.ts.map +1 -0
  101. package/dist/tileset/format-i3s/i3s-pending-tiles-register.js +47 -0
  102. package/dist/tileset/format-i3s/i3s-tile-manager.d.ts +34 -0
  103. package/dist/tileset/format-i3s/i3s-tile-manager.d.ts.map +1 -0
  104. package/dist/tileset/format-i3s/i3s-tile-manager.js +80 -0
  105. package/dist/tileset/format-i3s/i3s-tileset-traverser.d.ts +25 -0
  106. package/dist/tileset/format-i3s/i3s-tileset-traverser.d.ts.map +1 -0
  107. package/dist/tileset/format-i3s/i3s-tileset-traverser.js +92 -0
  108. package/dist/tileset/helpers/3d-tiles-options.js +8 -5
  109. package/dist/tileset/helpers/bounding-volume.d.ts +10 -0
  110. package/dist/tileset/helpers/bounding-volume.d.ts.map +1 -1
  111. package/dist/tileset/helpers/bounding-volume.js +274 -69
  112. package/dist/tileset/helpers/frame-state.d.ts +16 -5
  113. package/dist/tileset/helpers/frame-state.d.ts.map +1 -1
  114. package/dist/tileset/helpers/frame-state.js +131 -49
  115. package/dist/tileset/helpers/i3s-lod.d.ts +1 -1
  116. package/dist/tileset/helpers/i3s-lod.d.ts.map +1 -1
  117. package/dist/tileset/helpers/i3s-lod.js +82 -64
  118. package/dist/tileset/helpers/tiles-3d-lod.d.ts.map +1 -1
  119. package/dist/tileset/helpers/tiles-3d-lod.js +112 -100
  120. package/dist/tileset/helpers/transform-utils.d.ts.map +1 -1
  121. package/dist/tileset/helpers/transform-utils.js +51 -56
  122. package/dist/tileset/helpers/zoom.d.ts +41 -2
  123. package/dist/tileset/helpers/zoom.d.ts.map +1 -1
  124. package/dist/tileset/helpers/zoom.js +83 -30
  125. package/dist/tileset/tile-3d.d.ts +45 -14
  126. package/dist/tileset/tile-3d.d.ts.map +1 -1
  127. package/dist/tileset/tile-3d.js +593 -534
  128. package/dist/tileset/tileset-3d.d.ts +54 -9
  129. package/dist/tileset/tileset-3d.d.ts.map +1 -1
  130. package/dist/tileset/tileset-3d.js +707 -648
  131. package/dist/tileset/tileset-cache.d.ts +1 -1
  132. package/dist/tileset/tileset-cache.d.ts.map +1 -1
  133. package/dist/tileset/tileset-cache.js +70 -71
  134. package/dist/tileset/{traversers/tileset-traverser.d.ts → tileset-traverser.d.ts} +11 -17
  135. package/dist/tileset/tileset-traverser.d.ts.map +1 -0
  136. package/dist/tileset/tileset-traverser.js +309 -0
  137. package/dist/types.d.ts +34 -0
  138. package/dist/types.d.ts.map +1 -0
  139. package/dist/types.js +2 -0
  140. package/dist/utils/doubly-linked-list-node.d.ts +1 -2
  141. package/dist/utils/doubly-linked-list-node.d.ts.map +1 -1
  142. package/dist/utils/doubly-linked-list-node.js +17 -15
  143. package/dist/utils/doubly-linked-list.d.ts +2 -3
  144. package/dist/utils/doubly-linked-list.d.ts.map +1 -1
  145. package/dist/utils/doubly-linked-list.js +91 -75
  146. package/dist/utils/managed-array.d.ts +1 -1
  147. package/dist/utils/managed-array.d.ts.map +1 -1
  148. package/dist/utils/managed-array.js +144 -109
  149. package/package.json +8 -8
  150. package/src/constants.ts +2 -0
  151. package/src/index.ts +6 -4
  152. package/src/tileset/{traversers → format-3d-tiles}/tileset-3d-traverser.ts +4 -2
  153. package/src/tileset/format-i3s/i3s-pending-tiles-register.ts +44 -0
  154. package/src/tileset/format-i3s/i3s-tile-manager.ts +101 -0
  155. package/src/tileset/{traversers → format-i3s}/i3s-tileset-traverser.ts +25 -12
  156. package/src/tileset/helpers/bounding-volume.ts +136 -0
  157. package/src/tileset/helpers/frame-state.ts +102 -18
  158. package/src/tileset/helpers/i3s-lod.ts +24 -21
  159. package/src/tileset/helpers/tiles-3d-lod.ts +2 -0
  160. package/src/tileset/helpers/transform-utils.ts +2 -0
  161. package/src/tileset/helpers/zoom.ts +84 -9
  162. package/src/tileset/tile-3d.ts +73 -18
  163. package/src/tileset/tileset-3d.ts +205 -43
  164. package/src/tileset/tileset-cache.ts +4 -2
  165. package/src/tileset/{traversers/tileset-traverser.ts → tileset-traverser.ts} +29 -17
  166. package/src/types.ts +36 -0
  167. package/src/utils/doubly-linked-list-node.ts +3 -2
  168. package/src/utils/doubly-linked-list.ts +2 -3
  169. package/src/utils/managed-array.ts +1 -1
  170. package/dist/bundle.js.map +0 -1
  171. package/dist/constants.js.map +0 -1
  172. package/dist/index.js.map +0 -1
  173. package/dist/tileset/helpers/3d-tiles-options.js.map +0 -1
  174. package/dist/tileset/helpers/bounding-volume.js.map +0 -1
  175. package/dist/tileset/helpers/frame-state.js.map +0 -1
  176. package/dist/tileset/helpers/i3s-lod.js.map +0 -1
  177. package/dist/tileset/helpers/transform-utils.js.map +0 -1
  178. package/dist/tileset/helpers/zoom.js.map +0 -1
  179. package/dist/tileset/tile-3d.js.map +0 -1
  180. package/dist/tileset/tileset-3d.js.map +0 -1
  181. package/dist/tileset/tileset-cache.js.map +0 -1
  182. package/dist/tileset/traversers/i3s-tile-manager.d.ts +0 -8
  183. package/dist/tileset/traversers/i3s-tile-manager.d.ts.map +0 -1
  184. package/dist/tileset/traversers/i3s-tile-manager.js +0 -45
  185. package/dist/tileset/traversers/i3s-tile-manager.js.map +0 -1
  186. package/dist/tileset/traversers/i3s-tileset-traverser.d.ts +0 -18
  187. package/dist/tileset/traversers/i3s-tileset-traverser.d.ts.map +0 -1
  188. package/dist/tileset/traversers/i3s-tileset-traverser.js.map +0 -1
  189. package/dist/tileset/traversers/tileset-3d-traverser.d.ts.map +0 -1
  190. package/dist/tileset/traversers/tileset-3d-traverser.js.map +0 -1
  191. package/dist/tileset/traversers/tileset-traverser.d.ts.map +0 -1
  192. package/dist/tileset/traversers/tileset-traverser.js.map +0 -1
  193. package/dist/utils/doubly-linked-list-node.js.map +0 -1
  194. package/dist/utils/doubly-linked-list.js.map +0 -1
  195. package/dist/utils/managed-array.js.map +0 -1
  196. package/src/tileset/traversers/i3s-tile-manager.ts +0 -39
@@ -1,11 +1,12 @@
1
1
  import {load} from '@loaders.gl/core';
2
- import TilesetTraverser from './tileset-traverser';
2
+ import {TilesetTraverser} from '../tileset-traverser';
3
3
 
4
4
  import {getLodStatus} from '../helpers/i3s-lod';
5
- import TileHeader from '../tile-3d';
6
- import I3STileManager from './i3s-tile-manager';
5
+ import {Tile3D} from '../tile-3d';
6
+ import {I3STileManager} from './i3s-tile-manager';
7
+ import {FrameState} from '../helpers/frame-state';
7
8
 
8
- export default class I3STilesetTraverser extends TilesetTraverser {
9
+ export class I3STilesetTraverser extends TilesetTraverser {
9
10
  private _tileManager: I3STileManager;
10
11
 
11
12
  constructor(options) {
@@ -13,12 +14,21 @@ export default class I3STilesetTraverser extends TilesetTraverser {
13
14
  this._tileManager = new I3STileManager();
14
15
  }
15
16
 
16
- shouldRefine(tile, frameState) {
17
+ /**
18
+ * Check if there are no penging tile header requests,
19
+ * that means the traversal is finished and we can call
20
+ * following-up callbacks.
21
+ */
22
+ traversalFinished(frameState: FrameState): boolean {
23
+ return !this._tileManager.hasPendingTiles(frameState.viewport.id, this._frameNumber || 0);
24
+ }
25
+
26
+ shouldRefine(tile, frameState: FrameState) {
17
27
  tile._lodJudge = getLodStatus(tile, frameState);
18
28
  return tile._lodJudge === 'DIG';
19
29
  }
20
30
 
21
- updateChildTiles(tile, frameState): boolean {
31
+ updateChildTiles(tile, frameState: FrameState): boolean {
22
32
  const children = tile.header.children || [];
23
33
  // children which are already fetched and constructed as Tile3D instances
24
34
  const childTiles = tile.children;
@@ -62,8 +72,7 @@ export default class I3STilesetTraverser extends TilesetTraverser {
62
72
  ...tileset.loadOptions,
63
73
  i3s: {
64
74
  ...tileset.loadOptions.i3s,
65
- isTileHeader: true,
66
- loadContent: false
75
+ isTileHeader: true
67
76
  }
68
77
  };
69
78
 
@@ -71,22 +80,26 @@ export default class I3STilesetTraverser extends TilesetTraverser {
71
80
  }
72
81
 
73
82
  /**
74
- * The callback to init TileHeader instance after loading the tile JSON
83
+ * The callback to init Tile3D instance after loading the tile JSON
75
84
  * @param {Object} header - the tile JSON from a dataset
76
- * @param {TileHeader} tile - the parent TileHeader instance
85
+ * @param {Tile3D} tile - the parent Tile3D instance
77
86
  * @param {string} extendedId - optional ID to separate copies of a tile for different viewports.
78
87
  * const extendedId = `${tile.id}-${frameState.viewport.id}`;
79
88
  * @return {void}
80
89
  */
81
90
  _onTileLoad(header, tile, extendedId) {
82
91
  // after child tile is fetched
83
- const childTile = new TileHeader(tile.tileset, header, tile, extendedId);
92
+ const childTile = new Tile3D(tile.tileset, header, tile, extendedId);
84
93
  tile.children.push(childTile);
85
94
  const frameState = this._tileManager.find(childTile.id).frameState;
86
95
  this.updateTile(childTile, frameState);
87
96
 
88
97
  // after tile fetched, resume traversal if still in current update/traversal frame
89
- if (this._frameNumber === frameState.frameNumber) {
98
+ if (
99
+ this._frameNumber === frameState.frameNumber &&
100
+ (this.traversalFinished(frameState) ||
101
+ new Date().getTime() - this.lastUpdate > this.updateDebounceTime)
102
+ ) {
90
103
  this.executeTraversal(childTile, frameState);
91
104
  }
92
105
  }
@@ -14,6 +14,7 @@ function defined(x) {
14
14
  }
15
15
 
16
16
  // const scratchMatrix = new Matrix3();
17
+ const scratchPoint = new Vector3();
17
18
  const scratchScale = new Vector3();
18
19
  const scratchNorthWest = new Vector3();
19
20
  const scratchSouthEast = new Vector3();
@@ -68,6 +69,43 @@ export function createBoundingVolume(boundingVolumeHeader, transform, result) {
68
69
  throw new Error('3D Tile: boundingVolume must contain a sphere, region, or box');
69
70
  }
70
71
 
72
+ /** [min, max] each in [longitude, latitude, altitude] */
73
+ export type CartographicBounds = [min: number[], max: number[]];
74
+
75
+ /**
76
+ * Calculate the cartographic bounding box the tile's bounding volume.
77
+ * @param {Object} boundingVolumeHeader The tile's bounding volume header.
78
+ * @param {BoundingVolume} boundingVolume The bounding volume.
79
+ * @returns {CartographicBounds}
80
+ */
81
+ export function getCartographicBounds(
82
+ boundingVolumeHeader,
83
+ boundingVolume: OrientedBoundingBox | BoundingSphere
84
+ ): CartographicBounds {
85
+ // boundingVolume schema:
86
+ // https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/specification/schema/boundingVolume.schema.json
87
+ if (boundingVolumeHeader.box) {
88
+ return orientedBoundingBoxToCartographicBounds(boundingVolume as OrientedBoundingBox);
89
+ }
90
+ if (boundingVolumeHeader.region) {
91
+ // [west, south, east, north, minimum height, maximum height]
92
+ // Latitudes and longitudes are in the WGS 84 datum as defined in EPSG 4979 and are in radians.
93
+ // Heights are in meters above (or below) the WGS 84 ellipsoid.
94
+ const [west, south, east, north, minHeight, maxHeight] = boundingVolumeHeader.region;
95
+
96
+ return [
97
+ [degrees(west), degrees(south), minHeight],
98
+ [degrees(east), degrees(north), maxHeight]
99
+ ];
100
+ }
101
+
102
+ if (boundingVolumeHeader.sphere) {
103
+ return boundingSphereToCartographicBounds(boundingVolume as BoundingSphere);
104
+ }
105
+
106
+ throw new Error('Unkown boundingVolume type');
107
+ }
108
+
71
109
  function createBox(box, transform, result) {
72
110
  // https://math.gl/modules/culling/docs/api-reference/oriented-bounding-box
73
111
  // 1. A half-axes based representation.
@@ -197,3 +235,101 @@ function createSphere(sphere, transform, result?) {
197
235
 
198
236
  return new BoundingSphere(center, radius);
199
237
  }
238
+
239
+ /**
240
+ * Convert a bounding volume defined by OrientedBoundingBox to cartographic bounds
241
+ * @returns {CartographicBounds}
242
+ */
243
+ function orientedBoundingBoxToCartographicBounds(
244
+ boundingVolume: OrientedBoundingBox
245
+ ): CartographicBounds {
246
+ const result = emptyCartographicBounds();
247
+
248
+ const {halfAxes} = boundingVolume as OrientedBoundingBox;
249
+ const xAxis = new Vector3(halfAxes.getColumn(0));
250
+ const yAxis = new Vector3(halfAxes.getColumn(1));
251
+ const zAxis = new Vector3(halfAxes.getColumn(2));
252
+
253
+ // Test all 8 corners of the box
254
+ for (let x = 0; x < 2; x++) {
255
+ for (let y = 0; y < 2; y++) {
256
+ for (let z = 0; z < 2; z++) {
257
+ scratchPoint.copy(boundingVolume.center);
258
+ scratchPoint.add(xAxis);
259
+ scratchPoint.add(yAxis);
260
+ scratchPoint.add(zAxis);
261
+
262
+ addToCartographicBounds(result, scratchPoint);
263
+ zAxis.negate();
264
+ }
265
+ yAxis.negate();
266
+ }
267
+ xAxis.negate();
268
+ }
269
+ return result;
270
+ }
271
+
272
+ /**
273
+ * Convert a bounding volume defined by BoundingSphere to cartographic bounds
274
+ * @returns {CartographicBounds}
275
+ */
276
+ function boundingSphereToCartographicBounds(boundingVolume: BoundingSphere): CartographicBounds {
277
+ const result = emptyCartographicBounds();
278
+
279
+ const {center, radius} = boundingVolume as BoundingSphere;
280
+ const point = Ellipsoid.WGS84.scaleToGeodeticSurface(center, scratchPoint);
281
+
282
+ let zAxis: Vector3;
283
+ if (point) {
284
+ zAxis = Ellipsoid.WGS84.geodeticSurfaceNormal(point) as Vector3;
285
+ } else {
286
+ zAxis = new Vector3(0, 0, 1);
287
+ }
288
+ let xAxis = new Vector3(zAxis[2], -zAxis[1], 0);
289
+ if (xAxis.len() > 0) {
290
+ xAxis.normalize();
291
+ } else {
292
+ xAxis = new Vector3(0, 1, 0);
293
+ }
294
+ const yAxis = xAxis.clone().cross(zAxis);
295
+
296
+ // Test 6 end points of the 3 axes
297
+ for (const axis of [xAxis, yAxis, zAxis]) {
298
+ scratchScale.copy(axis).scale(radius);
299
+ for (let dir = 0; dir < 2; dir++) {
300
+ scratchPoint.copy(center);
301
+ scratchPoint.add(scratchScale);
302
+ addToCartographicBounds(result, scratchPoint);
303
+ // Flip the axis
304
+ scratchScale.negate();
305
+ }
306
+ }
307
+ return result;
308
+ }
309
+
310
+ /**
311
+ * Create a new cartographic bounds that contains no points
312
+ * @returns {CartographicBounds}
313
+ */
314
+ function emptyCartographicBounds(): CartographicBounds {
315
+ return [
316
+ [Infinity, Infinity, Infinity],
317
+ [-Infinity, -Infinity, -Infinity]
318
+ ];
319
+ }
320
+
321
+ /**
322
+ * Add a point to the target cartographic bounds
323
+ * @param {CartographicBounds} target
324
+ * @param {Vector3} cartesian coordinates of the point to add
325
+ */
326
+ function addToCartographicBounds(target: CartographicBounds, cartesian: Readonly<Vector3>) {
327
+ Ellipsoid.WGS84.cartesianToCartographic(cartesian, scratchPoint);
328
+ target[0][0] = Math.min(target[0][0], scratchPoint[0]);
329
+ target[0][1] = Math.min(target[0][1], scratchPoint[1]);
330
+ target[0][2] = Math.min(target[0][2], scratchPoint[2]);
331
+
332
+ target[1][0] = Math.max(target[1][0], scratchPoint[0]);
333
+ target[1][1] = Math.max(target[1][1], scratchPoint[1]);
334
+ target[1][2] = Math.max(target[1][2], scratchPoint[2]);
335
+ }
@@ -1,6 +1,8 @@
1
+ import {Tile3D} from '@loaders.gl/tiles';
1
2
  import {Vector3} from '@math.gl/core';
2
3
  import {CullingVolume, Plane} from '@math.gl/culling';
3
4
  import {Ellipsoid} from '@math.gl/geospatial';
5
+ import {GeospatialViewport, Viewport} from '../../types';
4
6
 
5
7
  export type FrameState = {
6
8
  camera: {
@@ -8,7 +10,8 @@ export type FrameState = {
8
10
  direction: number[];
9
11
  up: number[];
10
12
  };
11
- viewport: {[key: string]: any};
13
+ viewport: GeospatialViewport;
14
+ topDownViewport: GeospatialViewport; // Use it to calculate projected radius for a tile
12
15
  height: number;
13
16
  cullingVolume: CullingVolume;
14
17
  frameNumber: number; // TODO: This can be the same between updates, what number is unique for between updates?
@@ -28,18 +31,15 @@ const cullingVolume = new CullingVolume([
28
31
 
29
32
  // Extracts a frame state appropriate for tile culling from a deck.gl viewport
30
33
  // TODO - this could likely be generalized and merged back into deck.gl for other culling scenarios
31
- export function getFrameState(viewport, frameNumber: number): FrameState {
34
+ export function getFrameState(viewport: GeospatialViewport, frameNumber: number): FrameState {
35
+ // Traverse and and request. Update _selectedTiles so that we know what to render.
32
36
  // Traverse and and request. Update _selectedTiles so that we know what to render.
33
37
  const {cameraDirection, cameraUp, height} = viewport;
34
38
  const {metersPerUnit} = viewport.distanceScales;
35
39
 
36
- const viewportCenterCartographic = viewport.unprojectPosition(viewport.center);
37
40
  // TODO - Ellipsoid.eastNorthUpToFixedFrame() breaks on raw array, create a Vector.
38
41
  // TODO - Ellipsoid.eastNorthUpToFixedFrame() takes a cartesian, is that intuitive?
39
- const viewportCenterCartesian = Ellipsoid.WGS84.cartographicToCartesian(
40
- viewportCenterCartographic,
41
- new Vector3()
42
- );
42
+ const viewportCenterCartesian = worldToCartesian(viewport, viewport.center);
43
43
  const enuToFixedTransform = Ellipsoid.WGS84.eastNorthUpToFixedFrame(viewportCenterCartesian);
44
44
 
45
45
  const cameraPositionCartographic = viewport.unprojectPosition(viewport.cameraPosition);
@@ -58,7 +58,20 @@ export function getFrameState(viewport, frameNumber: number): FrameState {
58
58
  enuToFixedTransform.transformAsVector(new Vector3(cameraUp).scale(metersPerUnit))
59
59
  ).normalize();
60
60
 
61
- commonSpacePlanesToWGS84(viewport, viewportCenterCartesian);
61
+ commonSpacePlanesToWGS84(viewport);
62
+
63
+ const ViewportClass = viewport.constructor;
64
+ const {longitude, latitude, width, bearing, zoom} = viewport;
65
+ // @ts-ignore
66
+ const topDownViewport = new ViewportClass({
67
+ longitude,
68
+ latitude,
69
+ height,
70
+ width,
71
+ bearing,
72
+ zoom,
73
+ pitch: 0
74
+ });
62
75
 
63
76
  // TODO: make a file/class for frameState and document what needs to be attached to this so that traversal can function
64
77
  return {
@@ -68,6 +81,7 @@ export function getFrameState(viewport, frameNumber: number): FrameState {
68
81
  up: cameraUpCartesian
69
82
  },
70
83
  viewport,
84
+ topDownViewport,
71
85
  height,
72
86
  cullingVolume,
73
87
  frameNumber, // TODO: This can be the same between updates, what number is unique for between updates?
@@ -75,25 +89,95 @@ export function getFrameState(viewport, frameNumber: number): FrameState {
75
89
  };
76
90
  }
77
91
 
78
- function commonSpacePlanesToWGS84(viewport, viewportCenterCartesian) {
92
+ /**
93
+ * Limit `tiles` array length with `maximumTilesSelected` number.
94
+ * The criteria for this filtering is distance of a tile center
95
+ * to the `frameState.viewport`'s longitude and latitude
96
+ * @param tiles - tiles array to filter
97
+ * @param frameState - frameState to calculate distances
98
+ * @param maximumTilesSelected - maximal amount of tiles in the output array
99
+ * @returns new tiles array
100
+ */
101
+ export function limitSelectedTiles(
102
+ tiles: Tile3D[],
103
+ frameState: FrameState,
104
+ maximumTilesSelected: number
105
+ ): [Tile3D[], Tile3D[]] {
106
+ if (maximumTilesSelected === 0 || tiles.length <= maximumTilesSelected) {
107
+ return [tiles, []];
108
+ }
109
+ // Accumulate distances in couples array: [tileIndex: number, distanceToViewport: number]
110
+ const tuples: [number, number][] = [];
111
+ const {longitude: viewportLongitude, latitude: viewportLatitude} = frameState.viewport;
112
+ for (const [index, tile] of tiles.entries()) {
113
+ const [longitude, latitude] = tile.header.mbs;
114
+ const deltaLon = Math.abs(viewportLongitude - longitude);
115
+ const deltaLat = Math.abs(viewportLatitude - latitude);
116
+ const distance = Math.sqrt(deltaLat * deltaLat + deltaLon * deltaLon);
117
+ tuples.push([index, distance]);
118
+ }
119
+ const tuplesSorted = tuples.sort((a, b) => a[1] - b[1]);
120
+ const selectedTiles: Tile3D[] = [];
121
+ for (let i = 0; i < maximumTilesSelected; i++) {
122
+ selectedTiles.push(tiles[tuplesSorted[i][0]]);
123
+ }
124
+ const unselectedTiles: Tile3D[] = [];
125
+ for (let i = maximumTilesSelected; i < tuplesSorted.length; i++) {
126
+ unselectedTiles.push(tiles[tuplesSorted[i][0]]);
127
+ }
128
+
129
+ return [selectedTiles, unselectedTiles];
130
+ }
131
+
132
+ function commonSpacePlanesToWGS84(viewport) {
79
133
  // Extract frustum planes based on current view.
80
134
  const frustumPlanes = viewport.getFrustumPlanes();
135
+
136
+ // Get the near/far plane centers
137
+ const nearCenterCommon = closestPointOnPlane(frustumPlanes.near, viewport.cameraPosition);
138
+ const nearCenterCartesian = worldToCartesian(viewport, nearCenterCommon);
139
+ const cameraCartesian = worldToCartesian(viewport, viewport.cameraPosition, scratchPosition);
140
+
81
141
  let i = 0;
142
+ cullingVolume.planes[i++].fromPointNormal(
143
+ nearCenterCartesian,
144
+ scratchVector.copy(nearCenterCartesian).subtract(cameraCartesian)
145
+ );
146
+
82
147
  for (const dir in frustumPlanes) {
148
+ if (dir === 'near') {
149
+ continue; // eslint-disable-line no-continue
150
+ }
83
151
  const plane = frustumPlanes[dir];
84
- const distanceToCenter = plane.normal.dot(viewport.center);
85
- scratchPosition
86
- .copy(plane.normal)
87
- .scale(plane.distance - distanceToCenter)
88
- .add(viewport.center);
89
- const cartographicPos = viewport.unprojectPosition(scratchPosition);
90
-
91
- const cartesianPos = Ellipsoid.WGS84.cartographicToCartesian(cartographicPos, new Vector3());
152
+ const posCommon = closestPointOnPlane(plane, nearCenterCommon, scratchPosition);
153
+ const cartesianPos = worldToCartesian(viewport, posCommon, scratchPosition);
92
154
 
93
155
  cullingVolume.planes[i++].fromPointNormal(
94
156
  cartesianPos,
95
157
  // Want the normal to point into the frustum since that's what culling expects
96
- scratchVector.copy(viewportCenterCartesian).subtract(cartesianPos)
158
+ scratchVector.copy(nearCenterCartesian).subtract(cartesianPos)
97
159
  );
98
160
  }
99
161
  }
162
+
163
+ function closestPointOnPlane(
164
+ plane: {distance: number; normal: Vector3},
165
+ refPoint: [number, number, number] | Vector3,
166
+ out: Vector3 = new Vector3()
167
+ ): Vector3 {
168
+ const distanceToRef = plane.normal.dot(refPoint);
169
+ out
170
+ .copy(plane.normal)
171
+ .scale(plane.distance - distanceToRef)
172
+ .add(refPoint);
173
+ return out;
174
+ }
175
+
176
+ function worldToCartesian(
177
+ viewport: Viewport,
178
+ point: number[] | Vector3,
179
+ out: Vector3 = new Vector3()
180
+ ): Vector3 {
181
+ const cartographicPos = viewport.unprojectPosition(point);
182
+ return Ellipsoid.WGS84.cartographicToCartesian(cartographicPos, out);
183
+ }
@@ -1,8 +1,18 @@
1
+ // loaders.gl, MIT license
2
+
1
3
  import {Matrix4, Vector3} from '@math.gl/core';
2
4
  import {Ellipsoid} from '@math.gl/geospatial';
3
- import Tile3D from '../tile-3d';
5
+ import {Tile3D} from '../tile-3d';
4
6
  import {FrameState} from './frame-state';
5
7
 
8
+ const cameraPositionCartesian = new Vector3();
9
+ const toEye = new Vector3();
10
+ const cameraPositionEnu = new Vector3();
11
+ const extraVertexEnu = new Vector3();
12
+ const projectedOriginVector = new Vector3();
13
+ const enuToCartesianMatrix = new Matrix4();
14
+ const cartesianToEnuMatrix = new Matrix4();
15
+
6
16
  /**
7
17
  * For the maxScreenThreshold error metric, maxError means that you should replace the node with it's children
8
18
  as soon as the nodes bounding sphere has a screen radius larger than maxError pixels.
@@ -36,45 +46,35 @@ export function getLodStatus(tile: Tile3D, frameState: FrameState): 'DIG' | 'OUT
36
46
  */
37
47
  // eslint-disable-next-line max-statements
38
48
  export function getProjectedRadius(tile: Tile3D, frameState: FrameState): number {
39
- const originalViewport = frameState.viewport;
40
- const ViewportClass = originalViewport.constructor;
41
- const {longitude, latitude, height, width, bearing, zoom} = originalViewport;
42
- // @ts-ignore
43
- const viewport = new ViewportClass({longitude, latitude, height, width, bearing, zoom, pitch: 0});
49
+ const {topDownViewport: viewport} = frameState;
44
50
  const mbsLat = tile.header.mbs[1];
45
51
  const mbsLon = tile.header.mbs[0];
46
52
  const mbsZ = tile.header.mbs[2];
47
53
  const mbsR = tile.header.mbs[3];
48
54
  const mbsCenterCartesian = [...tile.boundingVolume.center];
49
55
  const cameraPositionCartographic = viewport.unprojectPosition(viewport.cameraPosition);
50
- const cameraPositionCartesian = Ellipsoid.WGS84.cartographicToCartesian(
51
- cameraPositionCartographic,
52
- new Vector3()
53
- );
56
+ Ellipsoid.WGS84.cartographicToCartesian(cameraPositionCartographic, cameraPositionCartesian);
54
57
 
55
58
  // ---------------------------
56
59
  // Calculate mbs border vertex
57
60
  // ---------------------------
58
- const toEye = new Vector3(cameraPositionCartesian).subtract(mbsCenterCartesian).normalize();
61
+ toEye.copy(cameraPositionCartesian).subtract(mbsCenterCartesian).normalize();
59
62
  // Add extra vector to form plane
60
- const enuToCartesianMatrix = new Matrix4();
61
63
  Ellipsoid.WGS84.eastNorthUpToFixedFrame(mbsCenterCartesian, enuToCartesianMatrix);
62
- const cartesianToEnuMatrix = new Matrix4(enuToCartesianMatrix).invert();
63
- const cameraPositionEnu = new Vector3(cameraPositionCartesian).transform(cartesianToEnuMatrix);
64
+ cartesianToEnuMatrix.copy(enuToCartesianMatrix).invert();
65
+ cameraPositionEnu.copy(cameraPositionCartesian).transform(cartesianToEnuMatrix);
64
66
  // Mean Proportionals in Right Triangles - Altitude rule
65
67
  // https://mathbitsnotebook.com/Geometry/RightTriangles/RTmeanRight.html
66
68
  const projection = Math.sqrt(
67
69
  cameraPositionEnu[0] * cameraPositionEnu[0] + cameraPositionEnu[1] * cameraPositionEnu[1]
68
70
  );
69
71
  const extraZ = (projection * projection) / cameraPositionEnu[2];
70
- const extraVertexEnu = new Vector3([cameraPositionEnu[0], cameraPositionEnu[1], extraZ]);
72
+ extraVertexEnu.copy([cameraPositionEnu[0], cameraPositionEnu[1], extraZ]);
71
73
  const extraVertexCartesian = extraVertexEnu.transform(enuToCartesianMatrix);
72
- const extraVectorCartesian = new Vector3(extraVertexCartesian)
73
- .subtract(mbsCenterCartesian)
74
- .normalize();
74
+ const extraVectorCartesian = extraVertexCartesian.subtract(mbsCenterCartesian).normalize();
75
75
  // We need radius vector orthogonal to toEye vector
76
76
  const radiusVector = toEye.cross(extraVectorCartesian).normalize().scale(mbsR);
77
- const sphereMbsBorderVertexCartesian = new Vector3(mbsCenterCartesian).add(radiusVector);
77
+ const sphereMbsBorderVertexCartesian = radiusVector.add(mbsCenterCartesian);
78
78
  const sphereMbsBorderVertexCartographic = Ellipsoid.WGS84.cartesianToCartographic(
79
79
  sphereMbsBorderVertexCartesian
80
80
  );
@@ -82,8 +82,11 @@ export function getProjectedRadius(tile: Tile3D, frameState: FrameState): number
82
82
 
83
83
  // Project center vertex and border vertex and calculate projected radius of MBS
84
84
  const projectedOrigin = viewport.project([mbsLon, mbsLat, mbsZ]);
85
- const projectedMbsBorderVertex = viewport.project(sphereMbsBorderVertexCartographic);
86
- const projectedRadius = new Vector3(projectedOrigin)
85
+ const projectedMbsBorderVertex = viewport.project(
86
+ sphereMbsBorderVertexCartographic as [number, number, number]
87
+ );
88
+ const projectedRadius = projectedOriginVector
89
+ .copy(projectedOrigin)
87
90
  .subtract(projectedMbsBorderVertex)
88
91
  .magnitude();
89
92
  return projectedRadius;
@@ -1,3 +1,5 @@
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
 
@@ -1,3 +1,5 @@
1
+ // loaders.gl, MIT license
2
+
1
3
  import {Ellipsoid} from '@math.gl/geospatial';
2
4
  import {Matrix4, Vector3} from '@math.gl/core';
3
5
  import {assert} from '@loaders.gl/loader-utils';
@@ -1,4 +1,9 @@
1
+ // loaders.gl, MIT license
2
+
1
3
  import {Vector3} from '@math.gl/core';
4
+ import {BoundingSphere, OrientedBoundingBox} from '@math.gl/culling';
5
+ import {Ellipsoid} from '@math.gl/geospatial';
6
+ import {BoundingRectangle} from '../../types';
2
7
 
3
8
  const WGS84_RADIUS_X = 6378137.0;
4
9
  const WGS84_RADIUS_Y = 6378137.0;
@@ -8,22 +13,29 @@ const scratchVector = new Vector3();
8
13
 
9
14
  /**
10
15
  * Calculate appropriate zoom value for a particular boundingVolume
11
- * @param {BoundingSphere, OrientedBoundingBox} boundingVolume - the instance of bounding volume
16
+ * @param boundingVolume - the instance of bounding volume
17
+ * @param cartorgraphicCenter - cartographic center of the bounding volume
12
18
  * @returns {number} - zoom value
13
19
  */
14
- export function getZoomFromBoundingVolume(boundingVolume) {
15
- const {halfAxes, radius, width, height} = boundingVolume;
16
-
17
- if (halfAxes) {
20
+ export function getZoomFromBoundingVolume(
21
+ boundingVolume: BoundingSphere | OrientedBoundingBox | BoundingRectangle,
22
+ cartorgraphicCenter: Vector3
23
+ ) {
24
+ if (boundingVolume instanceof OrientedBoundingBox) {
18
25
  // OrientedBoundingBox
26
+ const {halfAxes} = boundingVolume;
19
27
  const obbSize = getObbSize(halfAxes);
20
28
  // Use WGS84_RADIUS_Z to allign with BoundingSphere algorithm
21
- return Math.log2(WGS84_RADIUS_Z / obbSize);
22
- } else if (radius) {
29
+ // Add the tile elevation value for correct zooming to elevated tiles
30
+ return Math.log2(WGS84_RADIUS_Z / (obbSize + cartorgraphicCenter[2]));
31
+ } else if (boundingVolume instanceof BoundingSphere) {
23
32
  // BoundingSphere
24
- return Math.log2(WGS84_RADIUS_Z / radius);
25
- } else if (height && width) {
33
+ const {radius} = boundingVolume;
34
+ // Add the tile elevation value for correct zooming to elevated tiles
35
+ return Math.log2(WGS84_RADIUS_Z / (radius + cartorgraphicCenter[2]));
36
+ } else if (boundingVolume.width && boundingVolume.height) {
26
37
  // BoundingRectangle
38
+ const {width, height} = boundingVolume;
27
39
  const zoomX = Math.log2(WGS84_RADIUS_X / width);
28
40
  const zoomY = Math.log2(WGS84_RADIUS_Y / height);
29
41
 
@@ -33,6 +45,69 @@ export function getZoomFromBoundingVolume(boundingVolume) {
33
45
  return 1;
34
46
  }
35
47
 
48
+ /**
49
+ * Calculate initial zoom for the tileset from 3D `fullExtent` defined in
50
+ * the tileset metadata
51
+ * @param fullExtent - 3D extent of the tileset
52
+ * @param fullExtent.xmin - minimal longitude in decimal degrees
53
+ * @param fullExtent.xmax - maximal longitude in decimal degrees
54
+ * @param fullExtent.ymin - minimal latitude in decimal degrees
55
+ * @param fullExtent.ymax - maximal latitude in decimal degrees
56
+ * @param fullExtent.zmin - minimal elevation in meters
57
+ * @param fullExtent.zmax - maximal elevation in meters
58
+ * @param cartorgraphicCenter - tileset center in cartographic coordinate system
59
+ * @param cartesianCenter - tileset center in cartesian coordinate system
60
+ * @returns - initial zoom for the tileset
61
+ */
62
+ export function getZoomFromFullExtent(
63
+ fullExtent: {
64
+ xmin: number;
65
+ xmax: number;
66
+ ymin: number;
67
+ ymax: number;
68
+ zmin: number;
69
+ zmax: number;
70
+ },
71
+ cartorgraphicCenter: Vector3,
72
+ cartesianCenter: Vector3
73
+ ) {
74
+ const extentVertex = Ellipsoid.WGS84.cartographicToCartesian(
75
+ [fullExtent.xmax, fullExtent.ymax, fullExtent.zmax],
76
+ new Vector3()
77
+ );
78
+ const extentSize = Math.sqrt(
79
+ Math.pow(extentVertex[0] - cartesianCenter[0], 2) +
80
+ Math.pow(extentVertex[1] - cartesianCenter[1], 2) +
81
+ Math.pow(extentVertex[2] - cartesianCenter[2], 2)
82
+ );
83
+ return Math.log2(WGS84_RADIUS_Z / (extentSize + cartorgraphicCenter[2]));
84
+ }
85
+
86
+ /**
87
+ * Calculate initial zoom for the tileset from 2D `extent` defined in
88
+ * the tileset metadata
89
+ * @param extent - 2D extent of the tileset. It is array of 4 elements [xmin, ymin, xmax, ymax]
90
+ * @param extent[0] - minimal longitude in decimal degrees
91
+ * @param extent[1] - minimal latitude in decimal degrees
92
+ * @param extent[2] - maximal longitude in decimal degrees
93
+ * @param extent[3] - maximal latitude in decimal degrees
94
+ * @param cartorgraphicCenter - tileset center in cartographic coordinate system
95
+ * @param cartesianCenter - tileset center in cartesian coordinate system
96
+ * @returns - initial zoom for the tileset
97
+ */
98
+ export function getZoomFromExtent(
99
+ extent: [number, number, number, number],
100
+ cartorgraphicCenter: Vector3,
101
+ cartesianCenter: Vector3
102
+ ) {
103
+ const [xmin, ymin, xmax, ymax] = extent;
104
+ return getZoomFromFullExtent(
105
+ {xmin, xmax, ymin, ymax, zmin: 0, zmax: 0},
106
+ cartorgraphicCenter,
107
+ cartesianCenter
108
+ );
109
+ }
110
+
36
111
  function getObbSize(halfAxes) {
37
112
  halfAxes.getColumn(0, scratchVector);
38
113
  const axeY = halfAxes.getColumn(1);