@deck.gl/geo-layers 8.7.9 → 8.8.0-alpha.2
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.
- package/dist/dist.dev.js +62897 -30614
- package/dist/es5/index.js +8 -0
- package/dist/es5/index.js.map +1 -1
- package/dist/es5/mvt-layer/mvt-layer.js +9 -12
- package/dist/es5/mvt-layer/mvt-layer.js.map +1 -1
- package/dist/es5/tile-3d-layer/tile-3d-layer.js +13 -10
- package/dist/es5/tile-3d-layer/tile-3d-layer.js.map +1 -1
- package/dist/es5/tile-layer/tile-2d-header.js +36 -17
- package/dist/es5/tile-layer/tile-2d-header.js.map +1 -1
- package/dist/es5/tile-layer/tile-2d-traversal.js +13 -3
- package/dist/es5/tile-layer/tile-2d-traversal.js.map +1 -1
- package/dist/es5/tile-layer/tile-layer.js +18 -12
- package/dist/es5/tile-layer/tile-layer.js.map +1 -1
- package/dist/es5/tile-layer/tileset-2d.js +122 -96
- package/dist/es5/tile-layer/tileset-2d.js.map +1 -1
- package/dist/es5/tile-layer/types.js +2 -0
- package/dist/es5/tile-layer/types.js.map +1 -0
- package/dist/es5/tile-layer/utils.js +27 -9
- package/dist/es5/tile-layer/utils.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/mvt-layer/mvt-layer.js +9 -13
- package/dist/esm/mvt-layer/mvt-layer.js.map +1 -1
- package/dist/esm/tile-3d-layer/tile-3d-layer.js +8 -7
- package/dist/esm/tile-3d-layer/tile-3d-layer.js.map +1 -1
- package/dist/esm/tile-layer/tile-2d-header.js +54 -18
- package/dist/esm/tile-layer/tile-2d-header.js.map +1 -1
- package/dist/esm/tile-layer/tile-2d-traversal.js +17 -3
- package/dist/esm/tile-layer/tile-2d-traversal.js.map +1 -1
- package/dist/esm/tile-layer/tile-layer.js +20 -13
- package/dist/esm/tile-layer/tile-layer.js.map +1 -1
- package/dist/esm/tile-layer/tileset-2d.js +88 -54
- package/dist/esm/tile-layer/tileset-2d.js.map +1 -1
- package/dist/esm/tile-layer/types.js +2 -0
- package/dist/esm/tile-layer/types.js.map +1 -0
- package/dist/esm/tile-layer/utils.js +26 -11
- package/dist/esm/tile-layer/utils.js.map +1 -1
- package/dist.min.js +2 -2
- package/package.json +11 -11
- package/src/index.ts +5 -0
- package/src/mvt-layer/mvt-layer.js +6 -6
- package/src/tile-3d-layer/tile-3d-layer.js +6 -5
- package/src/tile-layer/{tile-2d-header.js → tile-2d-header.ts} +50 -16
- package/src/tile-layer/{tile-2d-traversal.js → tile-2d-traversal.ts} +49 -12
- package/src/tile-layer/{tile-layer.js → tile-layer.ts} +147 -28
- package/src/tile-layer/{tileset-2d.js → tileset-2d.ts} +143 -66
- package/src/tile-layer/types.ts +17 -0
- package/src/tile-layer/{utils.js → utils.ts} +62 -25
|
@@ -2,6 +2,9 @@ import Tile2DHeader from './tile-2d-header';
|
|
|
2
2
|
import {getTileIndices, tileToBoundingBox} from './utils';
|
|
3
3
|
import {RequestScheduler} from '@loaders.gl/loader-utils';
|
|
4
4
|
import {Matrix4} from '@math.gl/core';
|
|
5
|
+
import {assert, Viewport} from '@deck.gl/core';
|
|
6
|
+
import {Bounds, TileIndex, ZRange} from './types';
|
|
7
|
+
import {TileLayerProps} from './tile-layer';
|
|
5
8
|
|
|
6
9
|
// bit masks
|
|
7
10
|
const TILE_STATE_VISITED = 1;
|
|
@@ -29,6 +32,13 @@ export const STRATEGY_NEVER = 'never';
|
|
|
29
32
|
export const STRATEGY_REPLACE = 'no-overlap';
|
|
30
33
|
export const STRATEGY_DEFAULT = 'best-available';
|
|
31
34
|
|
|
35
|
+
export type RefinementStrategyFunction = (tiles: Tile2DHeader[]) => void;
|
|
36
|
+
export type RefinementStrategy =
|
|
37
|
+
| typeof STRATEGY_NEVER
|
|
38
|
+
| typeof STRATEGY_REPLACE
|
|
39
|
+
| typeof STRATEGY_DEFAULT
|
|
40
|
+
| RefinementStrategyFunction;
|
|
41
|
+
|
|
32
42
|
const DEFAULT_CACHE_SCALE = 5;
|
|
33
43
|
|
|
34
44
|
const STRATEGIES = {
|
|
@@ -37,17 +47,52 @@ const STRATEGIES = {
|
|
|
37
47
|
[STRATEGY_NEVER]: () => {}
|
|
38
48
|
};
|
|
39
49
|
|
|
50
|
+
export type Tileset2DProps = Pick<
|
|
51
|
+
TileLayerProps,
|
|
52
|
+
| 'getTileData'
|
|
53
|
+
| 'tileSize'
|
|
54
|
+
| 'maxCacheSize'
|
|
55
|
+
| 'maxCacheByteSize'
|
|
56
|
+
| 'refinementStrategy'
|
|
57
|
+
| 'extent'
|
|
58
|
+
| 'maxZoom'
|
|
59
|
+
| 'minZoom'
|
|
60
|
+
| 'maxRequests'
|
|
61
|
+
| 'zoomOffset'
|
|
62
|
+
> & {
|
|
63
|
+
onTileLoad: (tile: Tile2DHeader) => void;
|
|
64
|
+
onTileUnload: (tile: Tile2DHeader) => void;
|
|
65
|
+
onTileError: (error: any, tile: Tile2DHeader) => void;
|
|
66
|
+
};
|
|
67
|
+
|
|
40
68
|
/**
|
|
41
|
-
* Manages loading and purging of
|
|
42
|
-
* and only
|
|
69
|
+
* Manages loading and purging of tile data. This class caches recently visited tiles
|
|
70
|
+
* and only creates new tiles if they are present.
|
|
43
71
|
*/
|
|
44
|
-
|
|
45
72
|
export default class Tileset2D {
|
|
73
|
+
private opts: Tileset2DProps;
|
|
74
|
+
private _requestScheduler: RequestScheduler;
|
|
75
|
+
private _cache: Map<string, Tile2DHeader>;
|
|
76
|
+
private _dirty: boolean;
|
|
77
|
+
private _tiles: Tile2DHeader[];
|
|
78
|
+
|
|
79
|
+
private _cacheByteSize: number;
|
|
80
|
+
private _viewport: Viewport | null;
|
|
81
|
+
private _selectedTiles: Tile2DHeader[] | null;
|
|
82
|
+
private _frameNumber: number;
|
|
83
|
+
private _modelMatrix: Matrix4;
|
|
84
|
+
private _modelMatrixInverse: Matrix4;
|
|
85
|
+
|
|
86
|
+
private _maxZoom?: number;
|
|
87
|
+
private _minZoom?: number;
|
|
88
|
+
|
|
89
|
+
private onTileLoad: (tile: Tile2DHeader) => void;
|
|
90
|
+
|
|
46
91
|
/**
|
|
47
92
|
* Takes in a function that returns tile data, a cache size, and a max and a min zoom level.
|
|
48
93
|
* Cache size defaults to 5 * number of tiles in the current viewport
|
|
49
94
|
*/
|
|
50
|
-
constructor(opts) {
|
|
95
|
+
constructor(opts: Tileset2DProps) {
|
|
51
96
|
this.opts = opts;
|
|
52
97
|
|
|
53
98
|
this.onTileLoad = tile => {
|
|
@@ -85,19 +130,19 @@ export default class Tileset2D {
|
|
|
85
130
|
return this._tiles;
|
|
86
131
|
}
|
|
87
132
|
|
|
88
|
-
get selectedTiles() {
|
|
133
|
+
get selectedTiles(): Tile2DHeader[] | null {
|
|
89
134
|
return this._selectedTiles;
|
|
90
135
|
}
|
|
91
136
|
|
|
92
|
-
get isLoaded() {
|
|
93
|
-
return this._selectedTiles.every(tile => tile.isLoaded);
|
|
137
|
+
get isLoaded(): boolean {
|
|
138
|
+
return this._selectedTiles !== null && this._selectedTiles.every(tile => tile.isLoaded);
|
|
94
139
|
}
|
|
95
140
|
|
|
96
|
-
get needsReload() {
|
|
97
|
-
return this._selectedTiles.some(tile => tile.needsReload);
|
|
141
|
+
get needsReload(): boolean {
|
|
142
|
+
return this._selectedTiles !== null && this._selectedTiles.some(tile => tile.needsReload);
|
|
98
143
|
}
|
|
99
144
|
|
|
100
|
-
setOptions(opts) {
|
|
145
|
+
setOptions(opts: Tileset2DProps): void {
|
|
101
146
|
Object.assign(this.opts, opts);
|
|
102
147
|
if (Number.isFinite(opts.maxZoom)) {
|
|
103
148
|
this._maxZoom = Math.floor(opts.maxZoom);
|
|
@@ -108,7 +153,7 @@ export default class Tileset2D {
|
|
|
108
153
|
}
|
|
109
154
|
|
|
110
155
|
// Clean up any outstanding tile requests.
|
|
111
|
-
finalize() {
|
|
156
|
+
finalize(): void {
|
|
112
157
|
for (const tile of this._cache.values()) {
|
|
113
158
|
if (tile.isLoading) {
|
|
114
159
|
tile.abort();
|
|
@@ -119,11 +164,11 @@ export default class Tileset2D {
|
|
|
119
164
|
this._selectedTiles = null;
|
|
120
165
|
}
|
|
121
166
|
|
|
122
|
-
reloadAll() {
|
|
123
|
-
for (const
|
|
124
|
-
const tile = this._cache.get(
|
|
125
|
-
if (!this._selectedTiles.includes(tile)) {
|
|
126
|
-
this._cache.delete(
|
|
167
|
+
reloadAll(): void {
|
|
168
|
+
for (const id of this._cache.keys()) {
|
|
169
|
+
const tile = this._cache.get(id) as Tile2DHeader;
|
|
170
|
+
if (!this._selectedTiles || !this._selectedTiles.includes(tile)) {
|
|
171
|
+
this._cache.delete(id);
|
|
127
172
|
} else {
|
|
128
173
|
tile.setNeedsReload();
|
|
129
174
|
}
|
|
@@ -132,14 +177,14 @@ export default class Tileset2D {
|
|
|
132
177
|
|
|
133
178
|
/**
|
|
134
179
|
* Update the cache with the given viewport and model matrix and triggers callback onUpdate.
|
|
135
|
-
* @param {*} viewport
|
|
136
|
-
* @param {*} onUpdate
|
|
137
|
-
* @param {*} modelMatrix
|
|
138
180
|
*/
|
|
139
|
-
update(
|
|
181
|
+
update(
|
|
182
|
+
viewport: Viewport,
|
|
183
|
+
{zRange, modelMatrix}: {zRange?: ZRange; modelMatrix?: Matrix4} = {}
|
|
184
|
+
): number {
|
|
140
185
|
const modelMatrixAsMatrix4 = new Matrix4(modelMatrix);
|
|
141
186
|
const isModelMatrixNew = !modelMatrixAsMatrix4.equals(this._modelMatrix);
|
|
142
|
-
if (!viewport.equals(this._viewport) || isModelMatrixNew) {
|
|
187
|
+
if (!this._viewport || !viewport.equals(this._viewport) || isModelMatrixNew) {
|
|
143
188
|
if (isModelMatrixNew) {
|
|
144
189
|
this._modelMatrixInverse = modelMatrixAsMatrix4.clone().invert();
|
|
145
190
|
this._modelMatrix = modelMatrixAsMatrix4;
|
|
@@ -161,9 +206,7 @@ export default class Tileset2D {
|
|
|
161
206
|
}
|
|
162
207
|
// Check for needed reloads explicitly even if the view/matrix has not changed.
|
|
163
208
|
} else if (this.needsReload) {
|
|
164
|
-
this._selectedTiles = this._selectedTiles
|
|
165
|
-
this._getTile({x: tile.x, y: tile.y, z: tile.z})
|
|
166
|
-
);
|
|
209
|
+
this._selectedTiles = this._selectedTiles!.map(tile => this._getTile(tile.index));
|
|
167
210
|
}
|
|
168
211
|
|
|
169
212
|
// Update tile states
|
|
@@ -184,8 +227,24 @@ export default class Tileset2D {
|
|
|
184
227
|
|
|
185
228
|
/* Public interface for subclassing */
|
|
186
229
|
|
|
187
|
-
|
|
188
|
-
getTileIndices({
|
|
230
|
+
/** Returns array of tile indices in the current viewport */
|
|
231
|
+
getTileIndices({
|
|
232
|
+
viewport,
|
|
233
|
+
maxZoom,
|
|
234
|
+
minZoom,
|
|
235
|
+
zRange,
|
|
236
|
+
modelMatrix,
|
|
237
|
+
modelMatrixInverse
|
|
238
|
+
}: {
|
|
239
|
+
viewport: Viewport;
|
|
240
|
+
maxZoom?: number;
|
|
241
|
+
minZoom?: number;
|
|
242
|
+
zRange: ZRange | undefined;
|
|
243
|
+
tileSize?: number;
|
|
244
|
+
modelMatrix?: Matrix4;
|
|
245
|
+
modelMatrixInverse?: Matrix4;
|
|
246
|
+
zoomOffset?: number;
|
|
247
|
+
}): TileIndex[] {
|
|
189
248
|
const {tileSize, extent, zoomOffset} = this.opts;
|
|
190
249
|
return getTileIndices({
|
|
191
250
|
viewport,
|
|
@@ -193,30 +252,41 @@ export default class Tileset2D {
|
|
|
193
252
|
minZoom,
|
|
194
253
|
zRange,
|
|
195
254
|
tileSize,
|
|
196
|
-
extent,
|
|
255
|
+
extent: extent as Bounds | undefined,
|
|
197
256
|
modelMatrix,
|
|
198
257
|
modelMatrixInverse,
|
|
199
258
|
zoomOffset
|
|
200
259
|
});
|
|
201
260
|
}
|
|
202
261
|
|
|
203
|
-
|
|
204
|
-
|
|
262
|
+
/** Returns unique string key for a tile index */
|
|
263
|
+
getTileId(index: TileIndex) {
|
|
264
|
+
return `${index.x}-${index.y}-${index.z}`;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Returns a zoom level for a tile index */
|
|
268
|
+
getTileZoom(index: TileIndex) {
|
|
269
|
+
return index.z;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Returns additional metadata to add to tile, bbox by default */
|
|
273
|
+
getTileMetadata(index: TileIndex) {
|
|
274
|
+
assert(this._viewport);
|
|
205
275
|
const {tileSize} = this.opts;
|
|
206
|
-
return {bbox: tileToBoundingBox(this._viewport, x, y, z, tileSize)};
|
|
276
|
+
return {bbox: tileToBoundingBox(this._viewport, index.x, index.y, index.z, tileSize)};
|
|
207
277
|
}
|
|
208
278
|
|
|
209
|
-
|
|
210
|
-
getParentIndex(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
return tileIndex;
|
|
279
|
+
/** Returns index of the parent tile */
|
|
280
|
+
getParentIndex(index: TileIndex) {
|
|
281
|
+
const x = Math.floor(index.x / 2);
|
|
282
|
+
const y = Math.floor(index.y / 2);
|
|
283
|
+
const z = index.z - 1;
|
|
284
|
+
return {x, y, z};
|
|
216
285
|
}
|
|
217
286
|
|
|
218
287
|
// Returns true if any tile's visibility changed
|
|
219
288
|
updateTileStates() {
|
|
289
|
+
assert(this._selectedTiles);
|
|
220
290
|
const refinementStrategy = this.opts.refinementStrategy || STRATEGY_DEFAULT;
|
|
221
291
|
|
|
222
292
|
const visibilities = new Array(this._cache.size);
|
|
@@ -251,10 +321,10 @@ export default class Tileset2D {
|
|
|
251
321
|
|
|
252
322
|
/* Private methods */
|
|
253
323
|
|
|
254
|
-
_pruneRequests() {
|
|
324
|
+
_pruneRequests(): void {
|
|
255
325
|
const {maxRequests} = this.opts;
|
|
256
326
|
|
|
257
|
-
const abortCandidates = [];
|
|
327
|
+
const abortCandidates: Tile2DHeader[] = [];
|
|
258
328
|
let ongoingRequestCount = 0;
|
|
259
329
|
for (const tile of this._cache.values()) {
|
|
260
330
|
// Keep track of all the ongoing requests
|
|
@@ -268,7 +338,7 @@ export default class Tileset2D {
|
|
|
268
338
|
|
|
269
339
|
while (maxRequests > 0 && ongoingRequestCount > maxRequests && abortCandidates.length > 0) {
|
|
270
340
|
// There are too many ongoing requests, so abort some that are unselected
|
|
271
|
-
const tile = abortCandidates.shift()
|
|
341
|
+
const tile = abortCandidates.shift()!;
|
|
272
342
|
tile.abort();
|
|
273
343
|
ongoingRequestCount--;
|
|
274
344
|
}
|
|
@@ -281,14 +351,16 @@ export default class Tileset2D {
|
|
|
281
351
|
// Reset states
|
|
282
352
|
for (const tile of _cache.values()) {
|
|
283
353
|
tile.parent = null;
|
|
284
|
-
tile.children
|
|
354
|
+
if (tile.children) {
|
|
355
|
+
tile.children.length = 0;
|
|
356
|
+
}
|
|
285
357
|
}
|
|
286
358
|
|
|
287
359
|
// Rebuild tree
|
|
288
360
|
for (const tile of _cache.values()) {
|
|
289
|
-
const parent = this._getNearestAncestor(tile
|
|
361
|
+
const parent = this._getNearestAncestor(tile);
|
|
290
362
|
tile.parent = parent;
|
|
291
|
-
if (parent) {
|
|
363
|
+
if (parent?.children) {
|
|
292
364
|
parent.children.push(tile);
|
|
293
365
|
}
|
|
294
366
|
}
|
|
@@ -299,6 +371,7 @@ export default class Tileset2D {
|
|
|
299
371
|
*/
|
|
300
372
|
/* eslint-disable complexity */
|
|
301
373
|
_resizeCache() {
|
|
374
|
+
assert(this.selectedTiles);
|
|
302
375
|
const {_cache, opts} = this;
|
|
303
376
|
|
|
304
377
|
const maxCacheSize =
|
|
@@ -309,11 +382,11 @@ export default class Tileset2D {
|
|
|
309
382
|
const overflown = _cache.size > maxCacheSize || this._cacheByteSize > maxCacheByteSize;
|
|
310
383
|
|
|
311
384
|
if (overflown) {
|
|
312
|
-
for (const [
|
|
385
|
+
for (const [id, tile] of _cache) {
|
|
313
386
|
if (!tile.isVisible) {
|
|
314
387
|
// delete tile
|
|
315
388
|
this._cacheByteSize -= opts.maxCacheByteSize ? tile.byteLength : 0;
|
|
316
|
-
_cache.delete(
|
|
389
|
+
_cache.delete(id);
|
|
317
390
|
this.opts.onTileUnload(tile);
|
|
318
391
|
}
|
|
319
392
|
if (_cache.size <= maxCacheSize && this._cacheByteSize <= maxCacheByteSize) {
|
|
@@ -324,30 +397,33 @@ export default class Tileset2D {
|
|
|
324
397
|
this._dirty = true;
|
|
325
398
|
}
|
|
326
399
|
if (this._dirty) {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
.sort((t1, t2) => t1.z - t2.z);
|
|
400
|
+
// sort by zoom level so that smaller tiles are displayed on top
|
|
401
|
+
this._tiles = Array.from(this._cache.values()).sort((t1, t2) => t1.zoom - t2.zoom);
|
|
330
402
|
|
|
331
403
|
this._dirty = false;
|
|
332
404
|
}
|
|
333
405
|
}
|
|
334
406
|
/* eslint-enable complexity */
|
|
335
407
|
|
|
336
|
-
_getTile(
|
|
337
|
-
|
|
338
|
-
|
|
408
|
+
_getTile(index: TileIndex, create: true): Tile2DHeader;
|
|
409
|
+
_getTile(index: TileIndex, create?: false): Tile2DHeader | undefined;
|
|
410
|
+
_getTile(index: TileIndex, create?: boolean): Tile2DHeader | undefined {
|
|
411
|
+
const id = this.getTileId(index);
|
|
412
|
+
let tile = this._cache.get(id);
|
|
339
413
|
let needsReload = false;
|
|
340
414
|
|
|
341
415
|
if (!tile && create) {
|
|
342
|
-
tile = new Tile2DHeader(
|
|
343
|
-
Object.assign(tile, this.getTileMetadata(tile));
|
|
416
|
+
tile = new Tile2DHeader(index);
|
|
417
|
+
Object.assign(tile, this.getTileMetadata(tile.index));
|
|
418
|
+
Object.assign(tile, {id, zoom: this.getTileZoom(tile.index)});
|
|
344
419
|
needsReload = true;
|
|
345
|
-
this._cache.set(
|
|
420
|
+
this._cache.set(id, tile);
|
|
346
421
|
this._dirty = true;
|
|
347
422
|
} else if (tile && tile.needsReload) {
|
|
348
423
|
needsReload = true;
|
|
349
424
|
}
|
|
350
|
-
if (needsReload) {
|
|
425
|
+
if (tile && needsReload) {
|
|
426
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
351
427
|
tile.loadData({
|
|
352
428
|
getData: this.opts.getTileData,
|
|
353
429
|
requestScheduler: this._requestScheduler,
|
|
@@ -359,11 +435,11 @@ export default class Tileset2D {
|
|
|
359
435
|
return tile;
|
|
360
436
|
}
|
|
361
437
|
|
|
362
|
-
_getNearestAncestor(
|
|
438
|
+
_getNearestAncestor(tile: Tile2DHeader): Tile2DHeader | null {
|
|
363
439
|
const {_minZoom = 0} = this;
|
|
364
|
-
let index = {x, y, z};
|
|
365
440
|
|
|
366
|
-
|
|
441
|
+
let index = tile.index;
|
|
442
|
+
while (this.getTileZoom(index) > _minZoom) {
|
|
367
443
|
index = this.getParentIndex(index);
|
|
368
444
|
const parent = this._getTile(index);
|
|
369
445
|
if (parent) {
|
|
@@ -380,7 +456,7 @@ export default class Tileset2D {
|
|
|
380
456
|
// For all the selected && pending tiles:
|
|
381
457
|
// - pick the closest ancestor as placeholder
|
|
382
458
|
// - if no ancestor is visible, pick the closest children as placeholder
|
|
383
|
-
function updateTileStateDefault(allTiles) {
|
|
459
|
+
function updateTileStateDefault(allTiles: Tile2DHeader[]) {
|
|
384
460
|
for (const tile of allTiles) {
|
|
385
461
|
tile.state = 0;
|
|
386
462
|
}
|
|
@@ -390,12 +466,12 @@ function updateTileStateDefault(allTiles) {
|
|
|
390
466
|
}
|
|
391
467
|
}
|
|
392
468
|
for (const tile of allTiles) {
|
|
393
|
-
tile.isVisible = Boolean(tile.state & TILE_STATE_VISIBLE);
|
|
469
|
+
tile.isVisible = Boolean(tile.state! & TILE_STATE_VISIBLE);
|
|
394
470
|
}
|
|
395
471
|
}
|
|
396
472
|
|
|
397
473
|
// Until a selected tile and all its selected siblings are loaded, use the closest ancestor as placeholder
|
|
398
|
-
function updateTileStateReplace(allTiles) {
|
|
474
|
+
function updateTileStateReplace(allTiles: Tile2DHeader[]) {
|
|
399
475
|
for (const tile of allTiles) {
|
|
400
476
|
tile.state = 0;
|
|
401
477
|
}
|
|
@@ -405,11 +481,11 @@ function updateTileStateReplace(allTiles) {
|
|
|
405
481
|
}
|
|
406
482
|
}
|
|
407
483
|
// Always process parents first
|
|
408
|
-
const sortedTiles = Array.from(allTiles).sort((t1, t2) => t1.
|
|
484
|
+
const sortedTiles = Array.from(allTiles).sort((t1, t2) => t1.zoom - t2.zoom);
|
|
409
485
|
for (const tile of sortedTiles) {
|
|
410
|
-
tile.isVisible = Boolean(tile.state & TILE_STATE_VISIBLE);
|
|
486
|
+
tile.isVisible = Boolean(tile.state! & TILE_STATE_VISIBLE);
|
|
411
487
|
|
|
412
|
-
if (tile.isVisible || tile.state & TILE_STATE_VISITED) {
|
|
488
|
+
if (tile.children && (tile.isVisible || tile.state! & TILE_STATE_VISITED)) {
|
|
413
489
|
// If the tile is rendered, or if the tile has been explicitly hidden, hide all of its children
|
|
414
490
|
for (const child of tile.children) {
|
|
415
491
|
child.state = TILE_STATE_VISITED;
|
|
@@ -421,10 +497,11 @@ function updateTileStateReplace(allTiles) {
|
|
|
421
497
|
}
|
|
422
498
|
|
|
423
499
|
// Walk up the tree until we find one ancestor that is loaded. Returns true if successful.
|
|
424
|
-
function getPlaceholderInAncestors(
|
|
500
|
+
function getPlaceholderInAncestors(startTile: Tile2DHeader) {
|
|
501
|
+
let tile: Tile2DHeader | null = startTile;
|
|
425
502
|
while (tile) {
|
|
426
503
|
if (tile.isLoaded || tile.content) {
|
|
427
|
-
tile.state |= TILE_STATE_VISIBLE;
|
|
504
|
+
tile.state! |= TILE_STATE_VISIBLE;
|
|
428
505
|
return true;
|
|
429
506
|
}
|
|
430
507
|
tile = tile.parent;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type ZRange = [minZ: number, maxZ: number];
|
|
2
|
+
|
|
3
|
+
export type Bounds = [minX: number, minY: number, maxX: number, maxY: number];
|
|
4
|
+
|
|
5
|
+
export type TileBoundingBox =
|
|
6
|
+
| {left: number; top: number; right: number; bottom: number}
|
|
7
|
+
| {west: number; north: number; east: number; south: number};
|
|
8
|
+
|
|
9
|
+
export type TileIndex = {x: number; y: number; z: number};
|
|
10
|
+
|
|
11
|
+
export type TileLoadProps = {
|
|
12
|
+
index: TileIndex;
|
|
13
|
+
id: string;
|
|
14
|
+
bbox: TileBoundingBox;
|
|
15
|
+
url?: string | null;
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
};
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import {Viewport} from '@deck.gl/core';
|
|
2
|
+
import {Matrix4} from '@math.gl/core';
|
|
3
|
+
import Tile2DHeader from './tile-2d-header';
|
|
1
4
|
import {getOSMTileIndices} from './tile-2d-traversal';
|
|
5
|
+
import {Bounds, TileBoundingBox, TileIndex, ZRange} from './types';
|
|
2
6
|
|
|
3
7
|
const TILE_SIZE = 512;
|
|
4
|
-
const DEFAULT_EXTENT = [-Infinity, -Infinity, Infinity, Infinity];
|
|
8
|
+
const DEFAULT_EXTENT: Bounds = [-Infinity, -Infinity, Infinity, Infinity];
|
|
5
9
|
|
|
6
10
|
export const urlType = {
|
|
7
11
|
type: 'url',
|
|
@@ -30,7 +34,7 @@ export const urlType = {
|
|
|
30
34
|
}
|
|
31
35
|
};
|
|
32
36
|
|
|
33
|
-
function transformBox(bbox, modelMatrix) {
|
|
37
|
+
function transformBox(bbox: Bounds, modelMatrix: Matrix4): Bounds {
|
|
34
38
|
const transformedCoords = [
|
|
35
39
|
// top-left
|
|
36
40
|
modelMatrix.transformAsPoint([bbox[0], bbox[1]]),
|
|
@@ -41,7 +45,7 @@ function transformBox(bbox, modelMatrix) {
|
|
|
41
45
|
// bottom-right
|
|
42
46
|
modelMatrix.transformAsPoint([bbox[2], bbox[3]])
|
|
43
47
|
];
|
|
44
|
-
const transformedBox = [
|
|
48
|
+
const transformedBox: Bounds = [
|
|
45
49
|
// Minimum x coord
|
|
46
50
|
Math.min(...transformedCoords.map(i => i[0])),
|
|
47
51
|
// Minimum y coord
|
|
@@ -54,27 +58,38 @@ function transformBox(bbox, modelMatrix) {
|
|
|
54
58
|
return transformedBox;
|
|
55
59
|
}
|
|
56
60
|
|
|
57
|
-
|
|
61
|
+
function stringHash(s: string): number {
|
|
62
|
+
return Math.abs(s.split('').reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getURLFromTemplate(template: string | string[], tile: Tile2DHeader): string | null {
|
|
58
66
|
if (!template || !template.length) {
|
|
59
67
|
return null;
|
|
60
68
|
}
|
|
69
|
+
const {index, id} = tile;
|
|
70
|
+
|
|
61
71
|
if (Array.isArray(template)) {
|
|
62
|
-
const
|
|
63
|
-
template = template[
|
|
72
|
+
const i = stringHash(id) % template.length;
|
|
73
|
+
template = template[i];
|
|
64
74
|
}
|
|
65
75
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
.replace(
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
let url = template;
|
|
77
|
+
for (const key of Object.keys(index)) {
|
|
78
|
+
const regex = new RegExp(`{${key}}`, 'g');
|
|
79
|
+
url = url.replace(regex, String(index[key]));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Back-compatible support for {-y}
|
|
83
|
+
if (Number.isInteger(index.y) && Number.isInteger(index.z)) {
|
|
84
|
+
url = url.replace(/\{-y\}/g, String(Math.pow(2, index.z) - index.y - 1));
|
|
85
|
+
}
|
|
86
|
+
return url;
|
|
72
87
|
}
|
|
73
88
|
|
|
74
89
|
/**
|
|
75
90
|
* gets the bounding box of a viewport
|
|
76
91
|
*/
|
|
77
|
-
function getBoundingBox(viewport, zRange, extent) {
|
|
92
|
+
function getBoundingBox(viewport: Viewport, zRange: number[] | null, extent: Bounds): Bounds {
|
|
78
93
|
let bounds;
|
|
79
94
|
if (zRange && zRange.length === 2) {
|
|
80
95
|
const [minZ, maxZ] = zRange;
|
|
@@ -107,22 +122,22 @@ function getBoundingBox(viewport, zRange, extent) {
|
|
|
107
122
|
];
|
|
108
123
|
}
|
|
109
124
|
|
|
110
|
-
function getIndexingCoords(bbox, scale, modelMatrixInverse) {
|
|
125
|
+
function getIndexingCoords(bbox: Bounds, scale: number, modelMatrixInverse?: Matrix4): Bounds {
|
|
111
126
|
if (modelMatrixInverse) {
|
|
112
127
|
const transformedTileIndex = transformBox(bbox, modelMatrixInverse).map(
|
|
113
128
|
i => (i * scale) / TILE_SIZE
|
|
114
129
|
);
|
|
115
|
-
return transformedTileIndex;
|
|
130
|
+
return transformedTileIndex as Bounds;
|
|
116
131
|
}
|
|
117
|
-
return bbox.map(i => (i * scale) / TILE_SIZE);
|
|
132
|
+
return bbox.map(i => (i * scale) / TILE_SIZE) as Bounds;
|
|
118
133
|
}
|
|
119
134
|
|
|
120
|
-
function getScale(z, tileSize) {
|
|
135
|
+
function getScale(z: number, tileSize: number): number {
|
|
121
136
|
return (Math.pow(2, z) * TILE_SIZE) / tileSize;
|
|
122
137
|
}
|
|
123
138
|
|
|
124
139
|
// https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Lon..2Flat._to_tile_numbers_2
|
|
125
|
-
export function osmTile2lngLat(x, y, z) {
|
|
140
|
+
export function osmTile2lngLat(x: number, y: number, z: number): [number, number] {
|
|
126
141
|
const scale = getScale(z, TILE_SIZE);
|
|
127
142
|
const lng = (x / scale) * 360 - 180;
|
|
128
143
|
const n = Math.PI - (2 * Math.PI * y) / scale;
|
|
@@ -130,11 +145,17 @@ export function osmTile2lngLat(x, y, z) {
|
|
|
130
145
|
return [lng, lat];
|
|
131
146
|
}
|
|
132
147
|
|
|
133
|
-
function tile2XY(x, y, z, tileSize) {
|
|
148
|
+
function tile2XY(x: number, y: number, z: number, tileSize: number): [number, number] {
|
|
134
149
|
const scale = getScale(z, tileSize);
|
|
135
150
|
return [(x / scale) * TILE_SIZE, (y / scale) * TILE_SIZE];
|
|
136
151
|
}
|
|
137
|
-
export function tileToBoundingBox(
|
|
152
|
+
export function tileToBoundingBox(
|
|
153
|
+
viewport: Viewport,
|
|
154
|
+
x: number,
|
|
155
|
+
y: number,
|
|
156
|
+
z: number,
|
|
157
|
+
tileSize: number = TILE_SIZE
|
|
158
|
+
): TileBoundingBox {
|
|
138
159
|
if (viewport.isGeospatial) {
|
|
139
160
|
const [west, north] = osmTile2lngLat(x, y, z);
|
|
140
161
|
const [east, south] = osmTile2lngLat(x + 1, y + 1, z);
|
|
@@ -145,11 +166,17 @@ export function tileToBoundingBox(viewport, x, y, z, tileSize = TILE_SIZE) {
|
|
|
145
166
|
return {left, top, right, bottom};
|
|
146
167
|
}
|
|
147
168
|
|
|
148
|
-
function getIdentityTileIndices(
|
|
169
|
+
function getIdentityTileIndices(
|
|
170
|
+
viewport: Viewport,
|
|
171
|
+
z: number,
|
|
172
|
+
tileSize: number,
|
|
173
|
+
extent: Bounds,
|
|
174
|
+
modelMatrixInverse?: Matrix4
|
|
175
|
+
) {
|
|
149
176
|
const bbox = getBoundingBox(viewport, null, extent);
|
|
150
177
|
const scale = getScale(z, tileSize);
|
|
151
178
|
const [minX, minY, maxX, maxY] = getIndexingCoords(bbox, scale, modelMatrixInverse);
|
|
152
|
-
const indices = [];
|
|
179
|
+
const indices: TileIndex[] = [];
|
|
153
180
|
|
|
154
181
|
/*
|
|
155
182
|
| TILE | TILE | TILE |
|
|
@@ -179,17 +206,27 @@ export function getTileIndices({
|
|
|
179
206
|
modelMatrix,
|
|
180
207
|
modelMatrixInverse,
|
|
181
208
|
zoomOffset = 0
|
|
209
|
+
}: {
|
|
210
|
+
viewport: Viewport;
|
|
211
|
+
maxZoom?: number;
|
|
212
|
+
minZoom?: number;
|
|
213
|
+
zRange: ZRange | undefined;
|
|
214
|
+
extent?: Bounds;
|
|
215
|
+
tileSize?: number;
|
|
216
|
+
modelMatrix?: Matrix4;
|
|
217
|
+
modelMatrixInverse?: Matrix4;
|
|
218
|
+
zoomOffset?: number;
|
|
182
219
|
}) {
|
|
183
220
|
let z = viewport.isGeospatial
|
|
184
221
|
? Math.round(viewport.zoom + Math.log2(TILE_SIZE / tileSize)) + zoomOffset
|
|
185
222
|
: Math.ceil(viewport.zoom) + zoomOffset;
|
|
186
|
-
if (Number.isFinite(minZoom) && z < minZoom) {
|
|
223
|
+
if (typeof minZoom === 'number' && Number.isFinite(minZoom) && z < minZoom) {
|
|
187
224
|
if (!extent) {
|
|
188
225
|
return [];
|
|
189
226
|
}
|
|
190
227
|
z = minZoom;
|
|
191
228
|
}
|
|
192
|
-
if (Number.isFinite(maxZoom) && z > maxZoom) {
|
|
229
|
+
if (typeof maxZoom === 'number' && Number.isFinite(maxZoom) && z > maxZoom) {
|
|
193
230
|
z = maxZoom;
|
|
194
231
|
}
|
|
195
232
|
let transformedExtent = extent;
|
|
@@ -210,6 +247,6 @@ export function getTileIndices({
|
|
|
210
247
|
/**
|
|
211
248
|
* Returns true if s is a valid URL template
|
|
212
249
|
*/
|
|
213
|
-
export function isURLTemplate(s) {
|
|
250
|
+
export function isURLTemplate(s: string): boolean {
|
|
214
251
|
return /(?=.*{z})(?=.*{x})(?=.*({y}|{-y}))/.test(s);
|
|
215
252
|
}
|