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

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 (201) hide show
  1. package/dist/bundle.js +2 -2
  2. package/dist/constants.d.ts +6 -0
  3. package/dist/constants.d.ts.map +1 -1
  4. package/dist/constants.js +29 -26
  5. package/dist/dist.min.js +1994 -1035
  6. package/dist/es5/bundle.js +6 -0
  7. package/dist/es5/bundle.js.map +1 -0
  8. package/dist/es5/constants.js +44 -0
  9. package/dist/es5/constants.js.map +1 -0
  10. package/dist/es5/index.js +93 -0
  11. package/dist/es5/index.js.map +1 -0
  12. package/dist/es5/tileset/format-3d-tiles/tileset-3d-traverser.js +70 -0
  13. package/dist/es5/tileset/format-3d-tiles/tileset-3d-traverser.js.map +1 -0
  14. package/dist/es5/tileset/format-i3s/i3s-pending-tiles-register.js +45 -0
  15. package/dist/es5/tileset/format-i3s/i3s-pending-tiles-register.js.map +1 -0
  16. package/dist/es5/tileset/format-i3s/i3s-tile-manager.js +84 -0
  17. package/dist/es5/tileset/format-i3s/i3s-tile-manager.js.map +1 -0
  18. package/dist/es5/tileset/format-i3s/i3s-tileset-traverser.js +143 -0
  19. package/dist/es5/tileset/format-i3s/i3s-tileset-traverser.js.map +1 -0
  20. package/dist/es5/tileset/helpers/3d-tiles-options.js +12 -0
  21. package/dist/es5/tileset/helpers/3d-tiles-options.js.map +1 -0
  22. package/dist/es5/tileset/helpers/bounding-volume.js +176 -0
  23. package/dist/es5/tileset/helpers/bounding-volume.js.map +1 -0
  24. package/dist/es5/tileset/helpers/frame-state.js +129 -0
  25. package/dist/es5/tileset/helpers/frame-state.js.map +1 -0
  26. package/dist/es5/tileset/helpers/i3s-lod.js +60 -0
  27. package/dist/es5/tileset/helpers/i3s-lod.js.map +1 -0
  28. package/dist/es5/tileset/helpers/tiles-3d-lod.js +103 -0
  29. package/dist/es5/tileset/helpers/tiles-3d-lod.js.map +1 -0
  30. package/dist/es5/tileset/helpers/transform-utils.js +50 -0
  31. package/dist/es5/tileset/helpers/transform-utils.js.map +1 -0
  32. package/dist/es5/tileset/helpers/zoom.js +63 -0
  33. package/dist/es5/tileset/helpers/zoom.js.map +1 -0
  34. package/dist/es5/tileset/tile-3d.js +536 -0
  35. package/dist/es5/tileset/tile-3d.js.map +1 -0
  36. package/dist/es5/tileset/tileset-3d.js +855 -0
  37. package/dist/es5/tileset/tileset-3d.js.map +1 -0
  38. package/dist/es5/tileset/tileset-cache.js +82 -0
  39. package/dist/es5/tileset/tileset-cache.js.map +1 -0
  40. package/dist/es5/tileset/tileset-traverser.js +321 -0
  41. package/dist/es5/tileset/tileset-traverser.js.map +1 -0
  42. package/dist/es5/types.js +2 -0
  43. package/dist/es5/types.js.map +1 -0
  44. package/dist/es5/utils/doubly-linked-list-node.js +21 -0
  45. package/dist/es5/utils/doubly-linked-list-node.js.map +1 -0
  46. package/dist/es5/utils/doubly-linked-list.js +88 -0
  47. package/dist/es5/utils/doubly-linked-list.js.map +1 -0
  48. package/dist/es5/utils/managed-array.js +126 -0
  49. package/dist/es5/utils/managed-array.js.map +1 -0
  50. package/dist/esm/bundle.js +4 -0
  51. package/dist/esm/bundle.js.map +1 -0
  52. package/dist/esm/constants.js +32 -0
  53. package/dist/esm/constants.js.map +1 -0
  54. package/dist/esm/index.js +10 -0
  55. package/dist/esm/index.js.map +1 -0
  56. package/dist/{tileset/traversers → esm/tileset/format-3d-tiles}/tileset-3d-traverser.js +2 -13
  57. package/dist/esm/tileset/format-3d-tiles/tileset-3d-traverser.js.map +1 -0
  58. package/dist/esm/tileset/format-i3s/i3s-pending-tiles-register.js +26 -0
  59. package/dist/esm/tileset/format-i3s/i3s-pending-tiles-register.js.map +1 -0
  60. package/dist/esm/tileset/format-i3s/i3s-tile-manager.js +79 -0
  61. package/dist/esm/tileset/format-i3s/i3s-tile-manager.js.map +1 -0
  62. package/dist/{tileset/traversers → esm/tileset/format-i3s}/i3s-tileset-traverser.js +14 -26
  63. package/dist/esm/tileset/format-i3s/i3s-tileset-traverser.js.map +1 -0
  64. package/dist/esm/tileset/helpers/3d-tiles-options.js +6 -0
  65. package/dist/esm/tileset/helpers/3d-tiles-options.js.map +1 -0
  66. package/dist/esm/tileset/helpers/bounding-volume.js +155 -0
  67. package/dist/esm/tileset/helpers/bounding-volume.js.map +1 -0
  68. package/dist/esm/tileset/helpers/frame-state.js +109 -0
  69. package/dist/esm/tileset/helpers/frame-state.js.map +1 -0
  70. package/dist/esm/tileset/helpers/i3s-lod.js +53 -0
  71. package/dist/esm/tileset/helpers/i3s-lod.js.map +1 -0
  72. package/dist/esm/tileset/helpers/tiles-3d-lod.js +100 -0
  73. package/dist/{tileset → esm/tileset}/helpers/tiles-3d-lod.js.map +1 -1
  74. package/dist/esm/tileset/helpers/transform-utils.js +50 -0
  75. package/dist/esm/tileset/helpers/transform-utils.js.map +1 -0
  76. package/dist/esm/tileset/helpers/zoom.js +55 -0
  77. package/dist/esm/tileset/helpers/zoom.js.map +1 -0
  78. package/dist/esm/tileset/tile-3d.js +414 -0
  79. package/dist/esm/tileset/tile-3d.js.map +1 -0
  80. package/dist/esm/tileset/tileset-3d.js +606 -0
  81. package/dist/esm/tileset/tileset-3d.js.map +1 -0
  82. package/dist/esm/tileset/tileset-cache.js +57 -0
  83. package/dist/esm/tileset/tileset-cache.js.map +1 -0
  84. package/dist/{tileset/traversers → esm/tileset}/tileset-traverser.js +29 -88
  85. package/dist/esm/tileset/tileset-traverser.js.map +1 -0
  86. package/dist/esm/types.js +2 -0
  87. package/dist/esm/types.js.map +1 -0
  88. package/dist/esm/utils/doubly-linked-list-node.js +12 -0
  89. package/dist/esm/utils/doubly-linked-list-node.js.map +1 -0
  90. package/dist/esm/utils/doubly-linked-list.js +65 -0
  91. package/dist/esm/utils/doubly-linked-list.js.map +1 -0
  92. package/dist/esm/utils/managed-array.js +87 -0
  93. package/dist/esm/utils/managed-array.js.map +1 -0
  94. package/dist/index.d.ts +4 -4
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +26 -10
  97. package/dist/tileset/{traversers → format-3d-tiles}/tileset-3d-traverser.d.ts +2 -2
  98. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -0
  99. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.js +54 -0
  100. package/dist/tileset/format-i3s/i3s-pending-tiles-register.d.ts +27 -0
  101. package/dist/tileset/format-i3s/i3s-pending-tiles-register.d.ts.map +1 -0
  102. package/dist/tileset/format-i3s/i3s-pending-tiles-register.js +47 -0
  103. package/dist/tileset/format-i3s/i3s-tile-manager.d.ts +34 -0
  104. package/dist/tileset/format-i3s/i3s-tile-manager.d.ts.map +1 -0
  105. package/dist/tileset/format-i3s/i3s-tile-manager.js +80 -0
  106. package/dist/tileset/format-i3s/i3s-tileset-traverser.d.ts +25 -0
  107. package/dist/tileset/format-i3s/i3s-tileset-traverser.d.ts.map +1 -0
  108. package/dist/tileset/format-i3s/i3s-tileset-traverser.js +92 -0
  109. package/dist/tileset/helpers/3d-tiles-options.d.ts +3 -2
  110. package/dist/tileset/helpers/3d-tiles-options.d.ts.map +1 -1
  111. package/dist/tileset/helpers/3d-tiles-options.js +8 -5
  112. package/dist/tileset/helpers/bounding-volume.d.ts +10 -0
  113. package/dist/tileset/helpers/bounding-volume.d.ts.map +1 -1
  114. package/dist/tileset/helpers/bounding-volume.js +274 -69
  115. package/dist/tileset/helpers/frame-state.d.ts +16 -5
  116. package/dist/tileset/helpers/frame-state.d.ts.map +1 -1
  117. package/dist/tileset/helpers/frame-state.js +131 -49
  118. package/dist/tileset/helpers/i3s-lod.d.ts +1 -1
  119. package/dist/tileset/helpers/i3s-lod.d.ts.map +1 -1
  120. package/dist/tileset/helpers/i3s-lod.js +82 -64
  121. package/dist/tileset/helpers/tiles-3d-lod.d.ts.map +1 -1
  122. package/dist/tileset/helpers/tiles-3d-lod.js +112 -100
  123. package/dist/tileset/helpers/transform-utils.d.ts.map +1 -1
  124. package/dist/tileset/helpers/transform-utils.js +51 -56
  125. package/dist/tileset/helpers/zoom.d.ts +41 -2
  126. package/dist/tileset/helpers/zoom.d.ts.map +1 -1
  127. package/dist/tileset/helpers/zoom.js +83 -30
  128. package/dist/tileset/tile-3d.d.ts +82 -35
  129. package/dist/tileset/tile-3d.d.ts.map +1 -1
  130. package/dist/tileset/tile-3d.js +607 -534
  131. package/dist/tileset/tileset-3d.d.ts +140 -48
  132. package/dist/tileset/tileset-3d.d.ts.map +1 -1
  133. package/dist/tileset/tileset-3d.js +693 -652
  134. package/dist/tileset/tileset-cache.d.ts +6 -5
  135. package/dist/tileset/tileset-cache.d.ts.map +1 -1
  136. package/dist/tileset/tileset-cache.js +66 -73
  137. package/dist/tileset/tileset-traverser.d.ts +57 -0
  138. package/dist/tileset/tileset-traverser.d.ts.map +1 -0
  139. package/dist/tileset/tileset-traverser.js +300 -0
  140. package/dist/types.d.ts +34 -0
  141. package/dist/types.d.ts.map +1 -0
  142. package/dist/types.js +2 -0
  143. package/dist/utils/doubly-linked-list-node.d.ts +1 -2
  144. package/dist/utils/doubly-linked-list-node.d.ts.map +1 -1
  145. package/dist/utils/doubly-linked-list-node.js +17 -15
  146. package/dist/utils/doubly-linked-list.d.ts +2 -3
  147. package/dist/utils/doubly-linked-list.d.ts.map +1 -1
  148. package/dist/utils/doubly-linked-list.js +91 -75
  149. package/dist/utils/managed-array.d.ts +1 -1
  150. package/dist/utils/managed-array.d.ts.map +1 -1
  151. package/dist/utils/managed-array.js +144 -109
  152. package/package.json +11 -8
  153. package/src/constants.ts +20 -0
  154. package/src/index.ts +6 -4
  155. package/src/tileset/{traversers → format-3d-tiles}/tileset-3d-traverser.ts +4 -2
  156. package/src/tileset/format-i3s/i3s-pending-tiles-register.ts +44 -0
  157. package/src/tileset/format-i3s/i3s-tile-manager.ts +101 -0
  158. package/src/tileset/{traversers → format-i3s}/i3s-tileset-traverser.ts +25 -12
  159. package/src/tileset/helpers/3d-tiles-options.ts +3 -1
  160. package/src/tileset/helpers/bounding-volume.ts +136 -0
  161. package/src/tileset/helpers/frame-state.ts +102 -18
  162. package/src/tileset/helpers/i3s-lod.ts +24 -21
  163. package/src/tileset/helpers/tiles-3d-lod.ts +2 -0
  164. package/src/tileset/helpers/transform-utils.ts +2 -0
  165. package/src/tileset/helpers/zoom.ts +84 -9
  166. package/src/tileset/tile-3d.ts +137 -123
  167. package/src/tileset/tileset-3d.ts +378 -251
  168. package/src/tileset/tileset-cache.ts +24 -17
  169. package/src/tileset/{traversers/tileset-traverser.ts → tileset-traverser.ts} +71 -72
  170. package/src/types.ts +36 -0
  171. package/src/utils/doubly-linked-list-node.ts +3 -2
  172. package/src/utils/doubly-linked-list.ts +2 -3
  173. package/src/utils/managed-array.ts +1 -1
  174. package/dist/bundle.js.map +0 -1
  175. package/dist/constants.js.map +0 -1
  176. package/dist/index.js.map +0 -1
  177. package/dist/tileset/helpers/3d-tiles-options.js.map +0 -1
  178. package/dist/tileset/helpers/bounding-volume.js.map +0 -1
  179. package/dist/tileset/helpers/frame-state.js.map +0 -1
  180. package/dist/tileset/helpers/i3s-lod.js.map +0 -1
  181. package/dist/tileset/helpers/transform-utils.js.map +0 -1
  182. package/dist/tileset/helpers/zoom.js.map +0 -1
  183. package/dist/tileset/tile-3d.js.map +0 -1
  184. package/dist/tileset/tileset-3d.js.map +0 -1
  185. package/dist/tileset/tileset-cache.js.map +0 -1
  186. package/dist/tileset/traversers/i3s-tile-manager.d.ts +0 -8
  187. package/dist/tileset/traversers/i3s-tile-manager.d.ts.map +0 -1
  188. package/dist/tileset/traversers/i3s-tile-manager.js +0 -45
  189. package/dist/tileset/traversers/i3s-tile-manager.js.map +0 -1
  190. package/dist/tileset/traversers/i3s-tileset-traverser.d.ts +0 -18
  191. package/dist/tileset/traversers/i3s-tileset-traverser.d.ts.map +0 -1
  192. package/dist/tileset/traversers/i3s-tileset-traverser.js.map +0 -1
  193. package/dist/tileset/traversers/tileset-3d-traverser.d.ts.map +0 -1
  194. package/dist/tileset/traversers/tileset-3d-traverser.js.map +0 -1
  195. package/dist/tileset/traversers/tileset-traverser.d.ts +0 -52
  196. package/dist/tileset/traversers/tileset-traverser.d.ts.map +0 -1
  197. package/dist/tileset/traversers/tileset-traverser.js.map +0 -1
  198. package/dist/utils/doubly-linked-list-node.js.map +0 -1
  199. package/dist/utils/doubly-linked-list.js.map +0 -1
  200. package/dist/utils/managed-array.js.map +0 -1
  201. 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
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
  }
@@ -1,4 +1,6 @@
1
- export function get3dTilesOptions(tileset) {
1
+ import type {Tileset3D} from '../tileset-3d';
2
+
3
+ export function get3dTilesOptions(tileset: Tileset3D): {assetGltfUpAxis: 'X' | 'Y' | 'Z'} {
2
4
  return {
3
5
  assetGltfUpAxis: (tileset.asset && tileset.asset.gltfUpAxis) || 'Y'
4
6
  };
@@ -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';