@loaders.gl/tiles 4.0.0-alpha.4 → 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 (197) 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 +32 -0
  5. package/dist/constants.d.ts.map +1 -0
  6. package/dist/constants.js +30 -26
  7. package/dist/dist.min.js +8921 -0
  8. package/dist/es5/bundle.js +6 -0
  9. package/dist/es5/bundle.js.map +1 -0
  10. package/dist/es5/constants.js +44 -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 +565 -0
  37. package/dist/es5/tileset/tile-3d.js.map +1 -0
  38. package/dist/es5/tileset/tileset-3d.js +890 -0
  39. package/dist/es5/tileset/tileset-3d.js.map +1 -0
  40. package/dist/es5/tileset/tileset-cache.js +85 -0
  41. package/dist/es5/tileset/tileset-cache.js.map +1 -0
  42. package/dist/es5/tileset/tileset-traverser.js +328 -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 +32 -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 +445 -0
  81. package/dist/esm/tileset/tile-3d.js.map +1 -0
  82. package/dist/esm/tileset/tileset-3d.js +637 -0
  83. package/dist/esm/tileset/tileset-3d.js.map +1 -0
  84. package/dist/esm/tileset/tileset-cache.js +60 -0
  85. package/dist/esm/tileset/tileset-cache.js.map +1 -0
  86. package/dist/{tileset/traversers → esm/tileset}/tileset-traverser.js +20 -72
  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 +4 -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 +206 -0
  133. package/dist/tileset/tile-3d.d.ts.map +1 -0
  134. package/dist/tileset/tile-3d.js +593 -531
  135. package/dist/tileset/tileset-3d.d.ts +217 -0
  136. package/dist/tileset/tileset-3d.d.ts.map +1 -0
  137. package/dist/tileset/tileset-3d.js +707 -648
  138. package/dist/tileset/tileset-cache.d.ts +18 -0
  139. package/dist/tileset/tileset-cache.d.ts.map +1 -0
  140. package/dist/tileset/tileset-cache.js +70 -71
  141. package/dist/tileset/tileset-traverser.d.ts +46 -0
  142. package/dist/tileset/tileset-traverser.d.ts.map +1 -0
  143. package/dist/tileset/tileset-traverser.js +309 -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 +10 -10
  157. package/src/constants.ts +2 -0
  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/bounding-volume.ts +136 -0
  164. package/src/tileset/helpers/frame-state.ts +102 -18
  165. package/src/tileset/helpers/i3s-lod.ts +75 -96
  166. package/src/tileset/helpers/tiles-3d-lod.ts +2 -0
  167. package/src/tileset/helpers/transform-utils.ts +2 -0
  168. package/src/tileset/helpers/zoom.ts +84 -9
  169. package/src/tileset/tile-3d.ts +77 -20
  170. package/src/tileset/tileset-3d.ts +205 -43
  171. package/src/tileset/tileset-cache.ts +6 -2
  172. package/src/tileset/{traversers/tileset-traverser.ts → tileset-traverser.ts} +29 -17
  173. package/src/types.ts +36 -0
  174. package/src/utils/{doubly-linked-list-node.js → doubly-linked-list-node.ts} +7 -2
  175. package/src/utils/{doubly-linked-list.js → doubly-linked-list.ts} +5 -8
  176. package/src/utils/{managed-array.js → managed-array.ts} +5 -2
  177. package/dist/bundle.js.map +0 -1
  178. package/dist/constants.js.map +0 -1
  179. package/dist/index.js.map +0 -1
  180. package/dist/tileset/helpers/3d-tiles-options.js.map +0 -1
  181. package/dist/tileset/helpers/bounding-volume.js.map +0 -1
  182. package/dist/tileset/helpers/frame-state.js.map +0 -1
  183. package/dist/tileset/helpers/i3s-lod.js.map +0 -1
  184. package/dist/tileset/helpers/transform-utils.js.map +0 -1
  185. package/dist/tileset/helpers/zoom.js.map +0 -1
  186. package/dist/tileset/tile-3d.js.map +0 -1
  187. package/dist/tileset/tileset-3d.js.map +0 -1
  188. package/dist/tileset/tileset-cache.js.map +0 -1
  189. package/dist/tileset/traversers/i3s-tile-manager.js +0 -45
  190. package/dist/tileset/traversers/i3s-tile-manager.js.map +0 -1
  191. package/dist/tileset/traversers/i3s-tileset-traverser.js.map +0 -1
  192. package/dist/tileset/traversers/tileset-3d-traverser.js.map +0 -1
  193. package/dist/tileset/traversers/tileset-traverser.js.map +0 -1
  194. package/dist/utils/doubly-linked-list-node.js.map +0 -1
  195. package/dist/utils/doubly-linked-list.js.map +0 -1
  196. package/dist/utils/managed-array.js.map +0 -1
  197. package/src/tileset/traversers/i3s-tile-manager.ts +0 -39
@@ -0,0 +1,101 @@
1
+ import {FrameState} from '../helpers/frame-state';
2
+ import {I3SPendingTilesRegister} from './i3s-pending-tiles-register';
3
+
4
+ const STATUS = {
5
+ REQUESTED: 'REQUESTED',
6
+ COMPLETED: 'COMPLETED',
7
+ ERROR: 'ERROR'
8
+ };
9
+
10
+ // A helper class to manage tile metadata fetching
11
+ export class I3STileManager {
12
+ private _statusMap: object;
13
+ private pendingTilesRegister = new I3SPendingTilesRegister();
14
+
15
+ constructor() {
16
+ this._statusMap = {};
17
+ }
18
+
19
+ /**
20
+ * Add request to map
21
+ * @param request - node metadata request
22
+ * @param key - unique key
23
+ * @param callback - callback after request completed
24
+ * @param frameState - frameState data
25
+ */
26
+ add(request, key, callback, frameState: FrameState) {
27
+ if (!this._statusMap[key]) {
28
+ const {
29
+ frameNumber,
30
+ viewport: {id}
31
+ } = frameState;
32
+ this._statusMap[key] = {request, callback, key, frameState, status: STATUS.REQUESTED};
33
+ // Register pending request for the frameNumber
34
+ this.pendingTilesRegister.register(id, frameNumber);
35
+ request()
36
+ .then((data) => {
37
+ this._statusMap[key].status = STATUS.COMPLETED;
38
+ const {
39
+ frameNumber: actualFrameNumber,
40
+ viewport: {id}
41
+ } = this._statusMap[key].frameState;
42
+ // Deregister pending request for the frameNumber
43
+ this.pendingTilesRegister.deregister(id, actualFrameNumber);
44
+ this._statusMap[key].callback(data, frameState);
45
+ })
46
+ .catch((error) => {
47
+ this._statusMap[key].status = STATUS.ERROR;
48
+ const {
49
+ frameNumber: actualFrameNumber,
50
+ viewport: {id}
51
+ } = this._statusMap[key].frameState;
52
+ // Deregister pending request for the frameNumber
53
+ this.pendingTilesRegister.deregister(id, actualFrameNumber);
54
+ callback(error);
55
+ });
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Update request if it is still actual for the new frameState
61
+ * @param key - unique key
62
+ * @param frameState - frameState data
63
+ */
64
+ update(key, frameState: FrameState) {
65
+ if (this._statusMap[key]) {
66
+ // Deregister pending request for the old frameNumber
67
+ const {
68
+ frameNumber,
69
+ viewport: {id}
70
+ } = this._statusMap[key].frameState;
71
+ this.pendingTilesRegister.deregister(id, frameNumber);
72
+
73
+ // Register pending request for the new frameNumber
74
+ const {
75
+ frameNumber: newFrameNumber,
76
+ viewport: {id: newViewportId}
77
+ } = frameState;
78
+ this.pendingTilesRegister.register(newViewportId, newFrameNumber);
79
+ this._statusMap[key].frameState = frameState;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Find request in the map
85
+ * @param key - unique key
86
+ * @returns
87
+ */
88
+ find(key) {
89
+ return this._statusMap[key];
90
+ }
91
+
92
+ /**
93
+ * Check it there are pending tile headers for the particular frameNumber
94
+ * @param viewportId
95
+ * @param frameNumber
96
+ * @returns
97
+ */
98
+ hasPendingTiles(viewportId: string, frameNumber: number): boolean {
99
+ return !this.pendingTilesRegister.isZero(viewportId, frameNumber);
100
+ }
101
+ }
@@ -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
- import {lodJudge} from '../helpers/i3s-lod';
5
- import TileHeader from '../tile-3d';
6
- import I3STileManager from './i3s-tile-manager';
4
+ import {getLodStatus} from '../helpers/i3s-lod';
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,13 +14,21 @@ export default class I3STilesetTraverser extends TilesetTraverser {
13
14
  this._tileManager = new I3STileManager();
14
15
  }
15
16
 
16
- shouldRefine(tile, frameState) {
17
- // TODO refactor loaJudge
18
- tile._lodJudge = lodJudge(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) {
27
+ tile._lodJudge = getLodStatus(tile, frameState);
19
28
  return tile._lodJudge === 'DIG';
20
29
  }
21
30
 
22
- updateChildTiles(tile, frameState): boolean {
31
+ updateChildTiles(tile, frameState: FrameState): boolean {
23
32
  const children = tile.header.children || [];
24
33
  // children which are already fetched and constructed as Tile3D instances
25
34
  const childTiles = tile.children;
@@ -63,8 +72,7 @@ export default class I3STilesetTraverser extends TilesetTraverser {
63
72
  ...tileset.loadOptions,
64
73
  i3s: {
65
74
  ...tileset.loadOptions.i3s,
66
- isTileHeader: true,
67
- loadContent: false
75
+ isTileHeader: true
68
76
  }
69
77
  };
70
78
 
@@ -72,22 +80,26 @@ export default class I3STilesetTraverser extends TilesetTraverser {
72
80
  }
73
81
 
74
82
  /**
75
- * The callback to init TileHeader instance after loading the tile JSON
83
+ * The callback to init Tile3D instance after loading the tile JSON
76
84
  * @param {Object} header - the tile JSON from a dataset
77
- * @param {TileHeader} tile - the parent TileHeader instance
85
+ * @param {Tile3D} tile - the parent Tile3D instance
78
86
  * @param {string} extendedId - optional ID to separate copies of a tile for different viewports.
79
87
  * const extendedId = `${tile.id}-${frameState.viewport.id}`;
80
88
  * @return {void}
81
89
  */
82
90
  _onTileLoad(header, tile, extendedId) {
83
91
  // after child tile is fetched
84
- const childTile = new TileHeader(tile.tileset, header, tile, extendedId);
92
+ const childTile = new Tile3D(tile.tileset, header, tile, extendedId);
85
93
  tile.children.push(childTile);
86
94
  const frameState = this._tileManager.find(childTile.id).frameState;
87
95
  this.updateTile(childTile, frameState);
88
96
 
89
97
  // after tile fetched, resume traversal if still in current update/traversal frame
90
- 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
+ ) {
91
103
  this.executeTraversal(childTile, frameState);
92
104
  }
93
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.longitude, viewport.latitude, 0];
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
+ }