@deck.gl/geo-layers 8.7.10 → 8.8.0-alpha.4
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 +62999 -30700
- package/dist/es5/geo-cell-layer/GeoCellLayer.js +3 -3
- package/dist/es5/geo-cell-layer/GeoCellLayer.js.map +1 -1
- package/dist/es5/index.js +8 -0
- package/dist/es5/index.js.map +1 -1
- package/dist/es5/mvt-layer/mvt-layer.js +19 -19
- package/dist/es5/mvt-layer/mvt-layer.js.map +1 -1
- package/dist/es5/quadkey-layer/quadkey-layer.js +4 -2
- package/dist/es5/quadkey-layer/quadkey-layer.js.map +1 -1
- package/dist/es5/quadkey-layer/quadkey-utils.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 +38 -32
- 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/geo-cell-layer/GeoCellLayer.js +6 -3
- package/dist/esm/geo-cell-layer/GeoCellLayer.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 +17 -17
- package/dist/esm/mvt-layer/mvt-layer.js.map +1 -1
- package/dist/esm/quadkey-layer/quadkey-layer.js +5 -2
- package/dist/esm/quadkey-layer/quadkey-layer.js.map +1 -1
- package/dist/esm/quadkey-layer/quadkey-utils.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 +23 -17
- 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/geo-cell-layer/{GeoCellLayer.js → GeoCellLayer.ts} +14 -10
- package/src/index.ts +6 -0
- package/src/mvt-layer/mvt-layer.js +11 -10
- package/src/quadkey-layer/quadkey-layer.ts +39 -0
- package/src/quadkey-layer/{quadkey-utils.js → quadkey-utils.ts} +2 -2
- 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} +153 -31
- 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} +67 -25
- package/src/quadkey-layer/quadkey-layer.js +0 -22
|
@@ -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,10 @@
|
|
|
1
|
+
import {Viewport} from '@deck.gl/core';
|
|
2
|
+
import {Matrix4} from '@math.gl/core';
|
|
1
3
|
import {getOSMTileIndices} from './tile-2d-traversal';
|
|
4
|
+
import {Bounds, TileBoundingBox, TileIndex, ZRange} from './types';
|
|
2
5
|
|
|
3
6
|
const TILE_SIZE = 512;
|
|
4
|
-
const DEFAULT_EXTENT = [-Infinity, -Infinity, Infinity, Infinity];
|
|
7
|
+
const DEFAULT_EXTENT: Bounds = [-Infinity, -Infinity, Infinity, Infinity];
|
|
5
8
|
|
|
6
9
|
export const urlType = {
|
|
7
10
|
type: 'url',
|
|
@@ -30,7 +33,7 @@ export const urlType = {
|
|
|
30
33
|
}
|
|
31
34
|
};
|
|
32
35
|
|
|
33
|
-
function transformBox(bbox, modelMatrix) {
|
|
36
|
+
function transformBox(bbox: Bounds, modelMatrix: Matrix4): Bounds {
|
|
34
37
|
const transformedCoords = [
|
|
35
38
|
// top-left
|
|
36
39
|
modelMatrix.transformAsPoint([bbox[0], bbox[1]]),
|
|
@@ -41,7 +44,7 @@ function transformBox(bbox, modelMatrix) {
|
|
|
41
44
|
// bottom-right
|
|
42
45
|
modelMatrix.transformAsPoint([bbox[2], bbox[3]])
|
|
43
46
|
];
|
|
44
|
-
const transformedBox = [
|
|
47
|
+
const transformedBox: Bounds = [
|
|
45
48
|
// Minimum x coord
|
|
46
49
|
Math.min(...transformedCoords.map(i => i[0])),
|
|
47
50
|
// Minimum y coord
|
|
@@ -54,27 +57,44 @@ function transformBox(bbox, modelMatrix) {
|
|
|
54
57
|
return transformedBox;
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
function stringHash(s: string): number {
|
|
61
|
+
return Math.abs(s.split('').reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function getURLFromTemplate(
|
|
65
|
+
template: string | string[],
|
|
66
|
+
tile: {
|
|
67
|
+
index: TileIndex;
|
|
68
|
+
id: string;
|
|
69
|
+
}
|
|
70
|
+
): string | null {
|
|
58
71
|
if (!template || !template.length) {
|
|
59
72
|
return null;
|
|
60
73
|
}
|
|
74
|
+
const {index, id} = tile;
|
|
75
|
+
|
|
61
76
|
if (Array.isArray(template)) {
|
|
62
|
-
const
|
|
63
|
-
template = template[
|
|
77
|
+
const i = stringHash(id) % template.length;
|
|
78
|
+
template = template[i];
|
|
64
79
|
}
|
|
65
80
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
.replace(
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
let url = template;
|
|
82
|
+
for (const key of Object.keys(index)) {
|
|
83
|
+
const regex = new RegExp(`{${key}}`, 'g');
|
|
84
|
+
url = url.replace(regex, String(index[key]));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Back-compatible support for {-y}
|
|
88
|
+
if (Number.isInteger(index.y) && Number.isInteger(index.z)) {
|
|
89
|
+
url = url.replace(/\{-y\}/g, String(Math.pow(2, index.z) - index.y - 1));
|
|
90
|
+
}
|
|
91
|
+
return url;
|
|
72
92
|
}
|
|
73
93
|
|
|
74
94
|
/**
|
|
75
95
|
* gets the bounding box of a viewport
|
|
76
96
|
*/
|
|
77
|
-
function getBoundingBox(viewport, zRange, extent) {
|
|
97
|
+
function getBoundingBox(viewport: Viewport, zRange: number[] | null, extent: Bounds): Bounds {
|
|
78
98
|
let bounds;
|
|
79
99
|
if (zRange && zRange.length === 2) {
|
|
80
100
|
const [minZ, maxZ] = zRange;
|
|
@@ -107,22 +127,22 @@ function getBoundingBox(viewport, zRange, extent) {
|
|
|
107
127
|
];
|
|
108
128
|
}
|
|
109
129
|
|
|
110
|
-
function getIndexingCoords(bbox, scale, modelMatrixInverse) {
|
|
130
|
+
function getIndexingCoords(bbox: Bounds, scale: number, modelMatrixInverse?: Matrix4): Bounds {
|
|
111
131
|
if (modelMatrixInverse) {
|
|
112
132
|
const transformedTileIndex = transformBox(bbox, modelMatrixInverse).map(
|
|
113
133
|
i => (i * scale) / TILE_SIZE
|
|
114
134
|
);
|
|
115
|
-
return transformedTileIndex;
|
|
135
|
+
return transformedTileIndex as Bounds;
|
|
116
136
|
}
|
|
117
|
-
return bbox.map(i => (i * scale) / TILE_SIZE);
|
|
137
|
+
return bbox.map(i => (i * scale) / TILE_SIZE) as Bounds;
|
|
118
138
|
}
|
|
119
139
|
|
|
120
|
-
function getScale(z, tileSize) {
|
|
140
|
+
function getScale(z: number, tileSize: number): number {
|
|
121
141
|
return (Math.pow(2, z) * TILE_SIZE) / tileSize;
|
|
122
142
|
}
|
|
123
143
|
|
|
124
144
|
// https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Lon..2Flat._to_tile_numbers_2
|
|
125
|
-
export function osmTile2lngLat(x, y, z) {
|
|
145
|
+
export function osmTile2lngLat(x: number, y: number, z: number): [number, number] {
|
|
126
146
|
const scale = getScale(z, TILE_SIZE);
|
|
127
147
|
const lng = (x / scale) * 360 - 180;
|
|
128
148
|
const n = Math.PI - (2 * Math.PI * y) / scale;
|
|
@@ -130,11 +150,17 @@ export function osmTile2lngLat(x, y, z) {
|
|
|
130
150
|
return [lng, lat];
|
|
131
151
|
}
|
|
132
152
|
|
|
133
|
-
function tile2XY(x, y, z, tileSize) {
|
|
153
|
+
function tile2XY(x: number, y: number, z: number, tileSize: number): [number, number] {
|
|
134
154
|
const scale = getScale(z, tileSize);
|
|
135
155
|
return [(x / scale) * TILE_SIZE, (y / scale) * TILE_SIZE];
|
|
136
156
|
}
|
|
137
|
-
export function tileToBoundingBox(
|
|
157
|
+
export function tileToBoundingBox(
|
|
158
|
+
viewport: Viewport,
|
|
159
|
+
x: number,
|
|
160
|
+
y: number,
|
|
161
|
+
z: number,
|
|
162
|
+
tileSize: number = TILE_SIZE
|
|
163
|
+
): TileBoundingBox {
|
|
138
164
|
if (viewport.isGeospatial) {
|
|
139
165
|
const [west, north] = osmTile2lngLat(x, y, z);
|
|
140
166
|
const [east, south] = osmTile2lngLat(x + 1, y + 1, z);
|
|
@@ -145,11 +171,17 @@ export function tileToBoundingBox(viewport, x, y, z, tileSize = TILE_SIZE) {
|
|
|
145
171
|
return {left, top, right, bottom};
|
|
146
172
|
}
|
|
147
173
|
|
|
148
|
-
function getIdentityTileIndices(
|
|
174
|
+
function getIdentityTileIndices(
|
|
175
|
+
viewport: Viewport,
|
|
176
|
+
z: number,
|
|
177
|
+
tileSize: number,
|
|
178
|
+
extent: Bounds,
|
|
179
|
+
modelMatrixInverse?: Matrix4
|
|
180
|
+
) {
|
|
149
181
|
const bbox = getBoundingBox(viewport, null, extent);
|
|
150
182
|
const scale = getScale(z, tileSize);
|
|
151
183
|
const [minX, minY, maxX, maxY] = getIndexingCoords(bbox, scale, modelMatrixInverse);
|
|
152
|
-
const indices = [];
|
|
184
|
+
const indices: TileIndex[] = [];
|
|
153
185
|
|
|
154
186
|
/*
|
|
155
187
|
| TILE | TILE | TILE |
|
|
@@ -179,17 +211,27 @@ export function getTileIndices({
|
|
|
179
211
|
modelMatrix,
|
|
180
212
|
modelMatrixInverse,
|
|
181
213
|
zoomOffset = 0
|
|
214
|
+
}: {
|
|
215
|
+
viewport: Viewport;
|
|
216
|
+
maxZoom?: number;
|
|
217
|
+
minZoom?: number;
|
|
218
|
+
zRange: ZRange | undefined;
|
|
219
|
+
extent?: Bounds;
|
|
220
|
+
tileSize?: number;
|
|
221
|
+
modelMatrix?: Matrix4;
|
|
222
|
+
modelMatrixInverse?: Matrix4;
|
|
223
|
+
zoomOffset?: number;
|
|
182
224
|
}) {
|
|
183
225
|
let z = viewport.isGeospatial
|
|
184
226
|
? Math.round(viewport.zoom + Math.log2(TILE_SIZE / tileSize)) + zoomOffset
|
|
185
227
|
: Math.ceil(viewport.zoom) + zoomOffset;
|
|
186
|
-
if (Number.isFinite(minZoom) && z < minZoom) {
|
|
228
|
+
if (typeof minZoom === 'number' && Number.isFinite(minZoom) && z < minZoom) {
|
|
187
229
|
if (!extent) {
|
|
188
230
|
return [];
|
|
189
231
|
}
|
|
190
232
|
z = minZoom;
|
|
191
233
|
}
|
|
192
|
-
if (Number.isFinite(maxZoom) && z > maxZoom) {
|
|
234
|
+
if (typeof maxZoom === 'number' && Number.isFinite(maxZoom) && z > maxZoom) {
|
|
193
235
|
z = maxZoom;
|
|
194
236
|
}
|
|
195
237
|
let transformedExtent = extent;
|
|
@@ -210,6 +252,6 @@ export function getTileIndices({
|
|
|
210
252
|
/**
|
|
211
253
|
* Returns true if s is a valid URL template
|
|
212
254
|
*/
|
|
213
|
-
export function isURLTemplate(s) {
|
|
255
|
+
export function isURLTemplate(s: string): boolean {
|
|
214
256
|
return /(?=.*{z})(?=.*{x})(?=.*({y}|{-y}))/.test(s);
|
|
215
257
|
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import GeoCellLayer from '../geo-cell-layer/GeoCellLayer';
|
|
2
|
-
import {getQuadkeyPolygon} from './quadkey-utils';
|
|
3
|
-
|
|
4
|
-
const defaultProps = {
|
|
5
|
-
getQuadkey: {type: 'accessor', value: d => d.quadkey}
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
export default class QuadkeyLayer extends GeoCellLayer {
|
|
9
|
-
indexToBounds() {
|
|
10
|
-
const {data, getQuadkey} = this.props;
|
|
11
|
-
|
|
12
|
-
return {
|
|
13
|
-
data,
|
|
14
|
-
_normalize: false,
|
|
15
|
-
positionFormat: 'XY',
|
|
16
|
-
getPolygon: (x, objectInfo) => getQuadkeyPolygon(getQuadkey(x, objectInfo))
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
QuadkeyLayer.layerName = 'QuadkeyLayer';
|
|
22
|
-
QuadkeyLayer.defaultProps = defaultProps;
|