@deck.gl-community/geo-layers 9.2.8 → 9.3.0-beta.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/global-grid-layer/global-grid-cluster-layer.d.ts.map +1 -1
- package/dist/global-grid-layer/global-grid-cluster-layer.js +1 -0
- package/dist/global-grid-layer/global-grid-cluster-layer.js.map +1 -1
- package/dist/index.cjs +1799 -7
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/shared-tile-2d-layer/deck-tile-traversal.d.ts +5 -0
- package/dist/shared-tile-2d-layer/deck-tile-traversal.d.ts.map +1 -0
- package/dist/shared-tile-2d-layer/deck-tile-traversal.js +173 -0
- package/dist/shared-tile-2d-layer/deck-tile-traversal.js.map +1 -0
- package/dist/shared-tile-2d-layer/deck-tileset-adapter.d.ts +26 -0
- package/dist/shared-tile-2d-layer/deck-tileset-adapter.d.ts.map +1 -0
- package/dist/shared-tile-2d-layer/deck-tileset-adapter.js +174 -0
- package/dist/shared-tile-2d-layer/deck-tileset-adapter.js.map +1 -0
- package/dist/shared-tile-2d-layer/index.d.ts +4 -0
- package/dist/shared-tile-2d-layer/index.d.ts.map +1 -0
- package/dist/shared-tile-2d-layer/index.js +6 -0
- package/dist/shared-tile-2d-layer/index.js.map +1 -0
- package/dist/shared-tile-2d-layer/shared-tile-2d-layer.d.ts +151 -0
- package/dist/shared-tile-2d-layer/shared-tile-2d-layer.d.ts.map +1 -0
- package/dist/shared-tile-2d-layer/shared-tile-2d-layer.js +422 -0
- package/dist/shared-tile-2d-layer/shared-tile-2d-layer.js.map +1 -0
- package/dist/shared-tile-2d-layer/shared-tile-2d-view.d.ts +57 -0
- package/dist/shared-tile-2d-layer/shared-tile-2d-view.d.ts.map +1 -0
- package/dist/shared-tile-2d-layer/shared-tile-2d-view.js +253 -0
- package/dist/shared-tile-2d-layer/shared-tile-2d-view.js.map +1 -0
- package/dist/shared-tile-2d-layer/url-template.d.ts +9 -0
- package/dist/shared-tile-2d-layer/url-template.d.ts.map +1 -0
- package/dist/shared-tile-2d-layer/url-template.js +25 -0
- package/dist/shared-tile-2d-layer/url-template.js.map +1 -0
- package/dist/tile-grid-layer/tile-grid-layer.d.ts +47 -0
- package/dist/tile-grid-layer/tile-grid-layer.d.ts.map +1 -0
- package/dist/tile-grid-layer/tile-grid-layer.js +94 -0
- package/dist/tile-grid-layer/tile-grid-layer.js.map +1 -0
- package/dist/tile-source-layer/tile-source-layer.d.ts +2 -2
- package/dist/tile-source-layer/tile-source-layer.d.ts.map +1 -1
- package/dist/tileset/adapter.d.ts +38 -0
- package/dist/tileset/adapter.d.ts.map +1 -0
- package/dist/tileset/adapter.js +5 -0
- package/dist/tileset/adapter.js.map +1 -0
- package/dist/tileset/index.cjs +721 -0
- package/dist/tileset/index.cjs.map +7 -0
- package/dist/tileset/index.d.ts +8 -0
- package/dist/tileset/index.d.ts.map +1 -0
- package/dist/tileset/index.js +7 -0
- package/dist/tileset/index.js.map +1 -0
- package/dist/tileset/tile-2d-header.d.ts +71 -0
- package/dist/tileset/tile-2d-header.d.ts.map +1 -0
- package/dist/tileset/tile-2d-header.js +168 -0
- package/dist/tileset/tile-2d-header.js.map +1 -0
- package/dist/tileset/tileset-2d.d.ts +210 -0
- package/dist/tileset/tileset-2d.d.ts.map +1 -0
- package/dist/tileset/tileset-2d.js +531 -0
- package/dist/tileset/tileset-2d.js.map +1 -0
- package/dist/tileset/types.d.ts +44 -0
- package/dist/tileset/types.d.ts.map +1 -0
- package/dist/tileset/types.js +5 -0
- package/dist/tileset/types.js.map +1 -0
- package/dist/utils/memoize.d.ts +2 -0
- package/dist/utils/memoize.d.ts.map +1 -0
- package/dist/utils/memoize.js +36 -0
- package/dist/utils/memoize.js.map +1 -0
- package/package.json +17 -11
- package/src/global-grid-layer/global-grid-cluster-layer.ts +1 -1
- package/src/index.ts +14 -0
- package/src/shared-tile-2d-layer/deck-tile-traversal.ts +247 -0
- package/src/shared-tile-2d-layer/deck-tileset-adapter.ts +262 -0
- package/src/shared-tile-2d-layer/index.ts +7 -0
- package/src/shared-tile-2d-layer/shared-tile-2d-layer.ts +681 -0
- package/src/shared-tile-2d-layer/shared-tile-2d-view.ts +339 -0
- package/src/shared-tile-2d-layer/url-template.ts +40 -0
- package/src/tile-grid-layer/tile-grid-layer.ts +158 -0
- package/src/tile-source-layer/tile-source-layer.ts +2 -2
- package/src/tileset/adapter.ts +47 -0
- package/src/tileset/index.ts +22 -0
- package/src/tileset/tile-2d-header.ts +210 -0
- package/src/tileset/tileset-2d.ts +705 -0
- package/src/tileset/types.ts +38 -0
- package/src/utils/memoize.ts +38 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
// deck.gl-community
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {type Viewport} from '@deck.gl/core';
|
|
6
|
+
import {Matrix4, equals, type NumericArray} from '@math.gl/core';
|
|
7
|
+
|
|
8
|
+
import {memoize} from '../utils/memoize';
|
|
9
|
+
import {
|
|
10
|
+
STRATEGY_DEFAULT,
|
|
11
|
+
STRATEGY_NEVER,
|
|
12
|
+
STRATEGY_REPLACE,
|
|
13
|
+
SharedTile2DHeader,
|
|
14
|
+
type SharedTileset2D
|
|
15
|
+
} from '../tileset/index';
|
|
16
|
+
import {getCullBounds, sharedTile2DDeckAdapter, transformBox} from './deck-tileset-adapter';
|
|
17
|
+
|
|
18
|
+
/** Bit flag marking a tile as visited during refinement traversal. */
|
|
19
|
+
const TILE_STATE_VISITED = 1;
|
|
20
|
+
/** Bit flag marking a tile as visible during refinement traversal. */
|
|
21
|
+
const TILE_STATE_VISIBLE = 2;
|
|
22
|
+
|
|
23
|
+
/** Built-in placeholder refinement handlers keyed by strategy name. */
|
|
24
|
+
const STRATEGIES = {
|
|
25
|
+
[STRATEGY_DEFAULT]: updateTileStateDefault,
|
|
26
|
+
[STRATEGY_REPLACE]: updateTileStateReplace,
|
|
27
|
+
[STRATEGY_NEVER]: () => {}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** View-specific visibility state cached per tile. */
|
|
31
|
+
type TileViewState = {
|
|
32
|
+
/** Whether the tile was selected directly for the current viewport. */
|
|
33
|
+
isSelected: boolean;
|
|
34
|
+
/** Whether the tile should render in this viewport after refinement. */
|
|
35
|
+
isVisible: boolean;
|
|
36
|
+
/** Bit field used by refinement helpers while traversing ancestors and children. */
|
|
37
|
+
state: number;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** Per-viewport traversal state for the deck-facing shared tile layer. */
|
|
41
|
+
export class SharedTile2DView<DataT = any> {
|
|
42
|
+
/** Unique consumer identifier used by the shared tileset cache. */
|
|
43
|
+
readonly id = Symbol('tile-2d-view');
|
|
44
|
+
|
|
45
|
+
/** Shared tileset queried by this view. */
|
|
46
|
+
private _tileset: SharedTileset2D<DataT, Viewport>;
|
|
47
|
+
/** Tiles selected during the latest traversal. */
|
|
48
|
+
private _selectedTiles: SharedTile2DHeader<DataT>[] | null = null;
|
|
49
|
+
/** Incremented whenever this view's visible tile set changes. */
|
|
50
|
+
private _frameNumber = 0;
|
|
51
|
+
/** Last viewport used to compute tile selection. */
|
|
52
|
+
private _viewport: Viewport | null = null;
|
|
53
|
+
/** Last z-range used during tile selection. */
|
|
54
|
+
private _zRange: [number, number] | null = null;
|
|
55
|
+
/** Last model matrix applied to tile selection. */
|
|
56
|
+
private _modelMatrix = new Matrix4();
|
|
57
|
+
/** Inverse of the current model matrix. */
|
|
58
|
+
private _modelMatrixInverse = new Matrix4();
|
|
59
|
+
/** Per-tile visibility and selection flags for this view. */
|
|
60
|
+
private _state = new Map<SharedTile2DHeader<DataT>, TileViewState>();
|
|
61
|
+
|
|
62
|
+
/** Creates a viewport-specific view of a shared tileset. */
|
|
63
|
+
constructor(tileset: SharedTileset2D<DataT, Viewport>) {
|
|
64
|
+
this._tileset = tileset;
|
|
65
|
+
if (!this._tileset.adapter) {
|
|
66
|
+
this._tileset.setOptions({adapter: sharedTile2DDeckAdapter});
|
|
67
|
+
}
|
|
68
|
+
this._tileset.attachConsumer(this.id);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Releases this view and detaches it from the shared tileset. */
|
|
72
|
+
finalize(): void {
|
|
73
|
+
this._tileset.detachConsumer(this.id);
|
|
74
|
+
this._selectedTiles = null;
|
|
75
|
+
this._state.clear();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Tiles selected for the last viewport update. */
|
|
79
|
+
get selectedTiles(): SharedTile2DHeader<DataT>[] | null {
|
|
80
|
+
return this._selectedTiles;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Indicates whether all selected tiles are fully loaded for this view. */
|
|
84
|
+
get isLoaded(): boolean {
|
|
85
|
+
return this._selectedTiles !== null && this._selectedTiles.every((tile) => tile.isLoaded);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Indicates whether any selected tile needs to be re-requested. */
|
|
89
|
+
get needsReload(): boolean {
|
|
90
|
+
return this._selectedTiles !== null && this._selectedTiles.some((tile) => tile.needsReload);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Updates tile selection and visibility for a viewport and returns the current frame number. */
|
|
94
|
+
update(
|
|
95
|
+
viewport: Viewport,
|
|
96
|
+
{
|
|
97
|
+
zRange,
|
|
98
|
+
modelMatrix
|
|
99
|
+
}: {
|
|
100
|
+
zRange: [number, number] | null;
|
|
101
|
+
modelMatrix: NumericArray | null;
|
|
102
|
+
} = {zRange: null, modelMatrix: null}
|
|
103
|
+
): number {
|
|
104
|
+
const modelMatrixAsMatrix4 = modelMatrix ? new Matrix4(modelMatrix) : new Matrix4();
|
|
105
|
+
const isModelMatrixNew = !modelMatrixAsMatrix4.equals(this._modelMatrix);
|
|
106
|
+
|
|
107
|
+
if (
|
|
108
|
+
!this._viewport ||
|
|
109
|
+
!viewport.equals(this._viewport) ||
|
|
110
|
+
!equals(this._zRange, zRange) ||
|
|
111
|
+
isModelMatrixNew
|
|
112
|
+
) {
|
|
113
|
+
if (isModelMatrixNew) {
|
|
114
|
+
this._modelMatrixInverse = modelMatrixAsMatrix4.clone().invert();
|
|
115
|
+
this._modelMatrix = modelMatrixAsMatrix4;
|
|
116
|
+
}
|
|
117
|
+
this._viewport = viewport;
|
|
118
|
+
this._zRange = zRange;
|
|
119
|
+
const tileIndices = this._tileset.getTileIndices({
|
|
120
|
+
viewState: viewport,
|
|
121
|
+
maxZoom: this._tileset.maxZoom,
|
|
122
|
+
minZoom: this._tileset.minZoom,
|
|
123
|
+
zRange,
|
|
124
|
+
modelMatrix: this._modelMatrix,
|
|
125
|
+
modelMatrixInverse: this._modelMatrixInverse
|
|
126
|
+
});
|
|
127
|
+
this._selectedTiles = tileIndices.map((index) => this._tileset.getTile(index, true));
|
|
128
|
+
this._tileset.prepareTiles();
|
|
129
|
+
} else if (this.needsReload) {
|
|
130
|
+
this._selectedTiles = (this._selectedTiles || []).map((tile) =>
|
|
131
|
+
this._tileset.getTile(tile.index, true)
|
|
132
|
+
);
|
|
133
|
+
this._tileset.prepareTiles();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const changed = this._updateTileStates();
|
|
137
|
+
this._tileset.updateConsumer(this.id, this._selectedTiles || [], this._getVisibleTiles());
|
|
138
|
+
|
|
139
|
+
if (changed) {
|
|
140
|
+
this._frameNumber++;
|
|
141
|
+
}
|
|
142
|
+
return this._frameNumber;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Tests whether a tile should render in the current viewport and culling rectangle. */
|
|
146
|
+
isTileVisible(
|
|
147
|
+
tile: SharedTile2DHeader<DataT>,
|
|
148
|
+
cullRect?: {x: number; y: number; width: number; height: number},
|
|
149
|
+
modelMatrix?: Matrix4 | null
|
|
150
|
+
): boolean {
|
|
151
|
+
const state = this._state.get(tile);
|
|
152
|
+
if (!state?.isVisible) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!cullRect || !this._viewport) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
const boundsArr = this._getCullBounds({
|
|
160
|
+
viewport: this._viewport,
|
|
161
|
+
z: this._zRange,
|
|
162
|
+
cullRect
|
|
163
|
+
});
|
|
164
|
+
return boundsArr.some((bounds) => this._tileOverlapsBounds(tile, bounds, modelMatrix));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Collects tiles currently marked visible for this view. */
|
|
168
|
+
private _getVisibleTiles(): SharedTile2DHeader<DataT>[] {
|
|
169
|
+
const result: SharedTile2DHeader<DataT>[] = [];
|
|
170
|
+
for (const tile of this._tileset.tiles) {
|
|
171
|
+
if (this._state.get(tile)?.isVisible) {
|
|
172
|
+
result.push(tile);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Memoized screen-space culling bounds helper. */
|
|
179
|
+
private _getCullBounds = memoize(getCullBounds);
|
|
180
|
+
|
|
181
|
+
/** Recomputes selected and placeholder-visible tiles for the current view. */
|
|
182
|
+
private _updateTileStates(): boolean {
|
|
183
|
+
const refinementStrategy = this._tileset.refinementStrategy || STRATEGY_DEFAULT;
|
|
184
|
+
const allTiles = this._tileset.tiles;
|
|
185
|
+
const previousVisibility = new Map<SharedTile2DHeader<DataT>, boolean>();
|
|
186
|
+
|
|
187
|
+
for (const tile of allTiles) {
|
|
188
|
+
const existing = this._state.get(tile);
|
|
189
|
+
previousVisibility.set(tile, existing?.isVisible || false);
|
|
190
|
+
this._state.set(tile, {isSelected: false, isVisible: false, state: 0});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
for (const tile of this._selectedTiles || []) {
|
|
194
|
+
const state = this._state.get(tile) || {isSelected: false, isVisible: false, state: 0};
|
|
195
|
+
state.isSelected = true;
|
|
196
|
+
state.isVisible = true;
|
|
197
|
+
this._state.set(tile, state);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (typeof refinementStrategy === 'function') {
|
|
201
|
+
refinementStrategy(allTiles);
|
|
202
|
+
} else {
|
|
203
|
+
STRATEGIES[refinementStrategy](allTiles, this._state);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
let changed = false;
|
|
207
|
+
for (const tile of allTiles) {
|
|
208
|
+
const state = this._state.get(tile);
|
|
209
|
+
if (state && state.isVisible !== previousVisibility.get(tile)) {
|
|
210
|
+
changed = true;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return changed;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Tests one tile against one culling bounds rectangle. */
|
|
217
|
+
private _tileOverlapsBounds(
|
|
218
|
+
tile: SharedTile2DHeader<DataT>,
|
|
219
|
+
[minX, minY, maxX, maxY]: [number, number, number, number],
|
|
220
|
+
modelMatrix?: Matrix4 | null
|
|
221
|
+
): boolean {
|
|
222
|
+
const bbox = this._getTileBoundingBox(tile, modelMatrix);
|
|
223
|
+
if ('west' in bbox) {
|
|
224
|
+
return bbox.west < maxX && bbox.east > minX && bbox.south < maxY && bbox.north > minY;
|
|
225
|
+
}
|
|
226
|
+
const y0 = Math.min(bbox.top, bbox.bottom);
|
|
227
|
+
const y1 = Math.max(bbox.top, bbox.bottom);
|
|
228
|
+
return bbox.left < maxX && bbox.right > minX && y0 < maxY && y1 > minY;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Applies model-matrix transforms to non-geospatial tile bounds when needed. */
|
|
232
|
+
private _getTileBoundingBox(tile: SharedTile2DHeader<DataT>, modelMatrix?: Matrix4 | null) {
|
|
233
|
+
const {bbox} = tile;
|
|
234
|
+
if ('west' in bbox || !modelMatrix || Matrix4.IDENTITY.equals(modelMatrix)) {
|
|
235
|
+
return bbox;
|
|
236
|
+
}
|
|
237
|
+
const [left, top, right, bottom] = transformBox(
|
|
238
|
+
[bbox.left, bbox.top, bbox.right, bbox.bottom],
|
|
239
|
+
modelMatrix
|
|
240
|
+
);
|
|
241
|
+
return {left, top, right, bottom};
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** Default refinement strategy that prefers best available loaded descendants. */
|
|
246
|
+
function updateTileStateDefault(
|
|
247
|
+
allTiles: SharedTile2DHeader[],
|
|
248
|
+
stateMap: Map<SharedTile2DHeader, TileViewState>
|
|
249
|
+
) {
|
|
250
|
+
for (const tile of allTiles) {
|
|
251
|
+
getTileState(stateMap, tile).state = 0;
|
|
252
|
+
}
|
|
253
|
+
for (const tile of allTiles) {
|
|
254
|
+
if (getTileState(stateMap, tile).isSelected && !getPlaceholderInAncestors(tile, stateMap)) {
|
|
255
|
+
getPlaceholderInChildren(tile, stateMap);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
for (const tile of allTiles) {
|
|
259
|
+
const state = getTileState(stateMap, tile);
|
|
260
|
+
state.isVisible = Boolean(state.state & TILE_STATE_VISIBLE);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Replacement refinement strategy that avoids visible overlap between ancestors and descendants. */
|
|
265
|
+
function updateTileStateReplace(
|
|
266
|
+
allTiles: SharedTile2DHeader[],
|
|
267
|
+
stateMap: Map<SharedTile2DHeader, TileViewState>
|
|
268
|
+
) {
|
|
269
|
+
for (const tile of allTiles) {
|
|
270
|
+
getTileState(stateMap, tile).state = 0;
|
|
271
|
+
}
|
|
272
|
+
for (const tile of allTiles) {
|
|
273
|
+
if (getTileState(stateMap, tile).isSelected) {
|
|
274
|
+
getPlaceholderInAncestors(tile, stateMap);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
const sortedTiles = Array.from(allTiles).sort((t1, t2) => t1.zoom - t2.zoom);
|
|
278
|
+
for (const tile of sortedTiles) {
|
|
279
|
+
const tileState = getTileState(stateMap, tile);
|
|
280
|
+
tileState.isVisible = Boolean(tileState.state & TILE_STATE_VISIBLE);
|
|
281
|
+
|
|
282
|
+
if (tile.children && (tileState.isVisible || tileState.state & TILE_STATE_VISITED)) {
|
|
283
|
+
for (const child of tile.children) {
|
|
284
|
+
getTileState(stateMap, child).state = TILE_STATE_VISITED;
|
|
285
|
+
}
|
|
286
|
+
} else if (tileState.isSelected) {
|
|
287
|
+
getPlaceholderInChildren(tile, stateMap);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** Searches upward for a loaded ancestor that can stand in for an unavailable selected tile. */
|
|
293
|
+
function getPlaceholderInAncestors(
|
|
294
|
+
startTile: SharedTile2DHeader,
|
|
295
|
+
stateMap: Map<SharedTile2DHeader, TileViewState>
|
|
296
|
+
): boolean {
|
|
297
|
+
let tile: SharedTile2DHeader | null = startTile;
|
|
298
|
+
while ((tile = tile.parent)) {
|
|
299
|
+
const state = getTileState(stateMap, tile);
|
|
300
|
+
state.state |= TILE_STATE_VISIBLE | TILE_STATE_VISITED;
|
|
301
|
+
if (tile.isLoaded || tile.content) {
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** Searches downward for loaded descendants that can stand in for an unavailable selected tile. */
|
|
309
|
+
function getPlaceholderInChildren(
|
|
310
|
+
tile: SharedTile2DHeader,
|
|
311
|
+
stateMap: Map<SharedTile2DHeader, TileViewState>
|
|
312
|
+
): void {
|
|
313
|
+
const state = getTileState(stateMap, tile);
|
|
314
|
+
state.state |= TILE_STATE_VISIBLE | TILE_STATE_VISITED;
|
|
315
|
+
|
|
316
|
+
if (!tile.children || !tile.children.length || tile.isLoaded || tile.content) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
for (const child of tile.children) {
|
|
321
|
+
const childState = getTileState(stateMap, child);
|
|
322
|
+
if (!(childState.state & TILE_STATE_VISITED)) {
|
|
323
|
+
getPlaceholderInChildren(child, stateMap);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** Returns view state for a tile, creating a default record when needed. */
|
|
329
|
+
function getTileState(
|
|
330
|
+
stateMap: Map<SharedTile2DHeader, TileViewState>,
|
|
331
|
+
tile: SharedTile2DHeader
|
|
332
|
+
): TileViewState {
|
|
333
|
+
let tileState = stateMap.get(tile);
|
|
334
|
+
if (!tileState) {
|
|
335
|
+
tileState = {isSelected: false, isVisible: false, state: 0};
|
|
336
|
+
stateMap.set(tile, tileState);
|
|
337
|
+
}
|
|
338
|
+
return tileState;
|
|
339
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// deck.gl-community
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import type {TileIndex} from '../tileset/types';
|
|
6
|
+
|
|
7
|
+
/** URL template accepted by {@link SharedTile2DLayer} when loading tiles directly by URL. */
|
|
8
|
+
export type URLTemplate = string | string[] | null;
|
|
9
|
+
|
|
10
|
+
function stringHash(s: string): number {
|
|
11
|
+
return Math.abs(s.split('').reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Expands one tile URL template for a given tile index. */
|
|
15
|
+
export function getURLFromTemplate(
|
|
16
|
+
template: URLTemplate,
|
|
17
|
+
tile: {
|
|
18
|
+
index: TileIndex;
|
|
19
|
+
id: string;
|
|
20
|
+
}
|
|
21
|
+
): string | null {
|
|
22
|
+
if (!template || !template.length) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const {index, id} = tile;
|
|
26
|
+
|
|
27
|
+
if (Array.isArray(template)) {
|
|
28
|
+
template = template[stringHash(id) % template.length];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let url = template;
|
|
32
|
+
for (const key of Object.keys(index)) {
|
|
33
|
+
url = url.replace(new RegExp(`{${key}}`, 'g'), String(index[key as keyof TileIndex]));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (Number.isInteger(index.y) && Number.isInteger(index.z)) {
|
|
37
|
+
url = url.replace(/\{-y\}/g, String(Math.pow(2, index.z) - index.y - 1));
|
|
38
|
+
}
|
|
39
|
+
return url;
|
|
40
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// deck.gl-community
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {CompositeLayer, type Color, type DefaultProps} from '@deck.gl/core';
|
|
6
|
+
import {PathLayer, TextLayer} from '@deck.gl/layers';
|
|
7
|
+
|
|
8
|
+
import type {SharedTile2DHeader} from '../tileset/index';
|
|
9
|
+
import type {TileBoundingBox} from '../tileset/types';
|
|
10
|
+
|
|
11
|
+
/** Label content or formatter used by {@link TileGridLayer}. */
|
|
12
|
+
type TileGridLabelAccessor = string | ((tile: SharedTile2DHeader<unknown>) => string);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Props for {@link TileGridLayer}, a helper overlay for visualizing tile loading
|
|
16
|
+
* and tile selection in tiled layers.
|
|
17
|
+
*/
|
|
18
|
+
export type TileGridLayerProps = {
|
|
19
|
+
/** Tile header whose bounds and index should be visualized. */
|
|
20
|
+
tile: SharedTile2DHeader<unknown>;
|
|
21
|
+
/** Whether to draw an outline around the tile bounds. Defaults to `true`. */
|
|
22
|
+
showBorder?: boolean;
|
|
23
|
+
/** Whether to render a label at the tile center. Defaults to `true`. */
|
|
24
|
+
showLabel?: boolean;
|
|
25
|
+
/** Static label text or formatter for per-tile label content. */
|
|
26
|
+
getLabel?: TileGridLabelAccessor;
|
|
27
|
+
/** Stroke color used for the tile border. */
|
|
28
|
+
borderColor?: Color;
|
|
29
|
+
/** Text color used for the tile label. */
|
|
30
|
+
labelColor?: Color;
|
|
31
|
+
/** Background color used behind the tile label text. */
|
|
32
|
+
labelBackgroundColor?: Color;
|
|
33
|
+
/** Minimum screen-space width of the tile border in pixels. */
|
|
34
|
+
borderWidthMinPixels?: number;
|
|
35
|
+
/** Screen-space font size of the tile label in pixels. */
|
|
36
|
+
labelSize?: number;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** Internal derived geometry used to render one tile grid overlay. */
|
|
40
|
+
type TileGridLayerItem = {
|
|
41
|
+
path: [number, number][];
|
|
42
|
+
center: [number, number];
|
|
43
|
+
label: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** Default prop values for {@link TileGridLayer}. */
|
|
47
|
+
const defaultProps: DefaultProps<TileGridLayerProps> = {
|
|
48
|
+
tile: {type: 'object', value: undefined!},
|
|
49
|
+
showBorder: true,
|
|
50
|
+
showLabel: true,
|
|
51
|
+
getLabel: {
|
|
52
|
+
type: 'function',
|
|
53
|
+
value: (tile: SharedTile2DHeader<unknown>) =>
|
|
54
|
+
`z${tile.index.z} x${tile.index.x} y${tile.index.y}`
|
|
55
|
+
},
|
|
56
|
+
borderColor: [255, 255, 255, 180],
|
|
57
|
+
labelColor: [255, 255, 255, 255],
|
|
58
|
+
labelBackgroundColor: [15, 23, 42, 210],
|
|
59
|
+
borderWidthMinPixels: 1,
|
|
60
|
+
labelSize: 12
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** Converts a tile bounding box into a closed outline path. */
|
|
64
|
+
function getTilePath(bbox: TileBoundingBox): [number, number][] {
|
|
65
|
+
if ('west' in bbox) {
|
|
66
|
+
return [
|
|
67
|
+
[bbox.west, bbox.north],
|
|
68
|
+
[bbox.west, bbox.south],
|
|
69
|
+
[bbox.east, bbox.south],
|
|
70
|
+
[bbox.east, bbox.north],
|
|
71
|
+
[bbox.west, bbox.north]
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return [
|
|
76
|
+
[bbox.left, bbox.top],
|
|
77
|
+
[bbox.left, bbox.bottom],
|
|
78
|
+
[bbox.right, bbox.bottom],
|
|
79
|
+
[bbox.right, bbox.top],
|
|
80
|
+
[bbox.left, bbox.top]
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Returns the center point of a tile bounding box. */
|
|
85
|
+
function getTileCenter(bbox: TileBoundingBox): [number, number] {
|
|
86
|
+
if ('west' in bbox) {
|
|
87
|
+
return [(bbox.west + bbox.east) / 2, (bbox.north + bbox.south) / 2];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return [(bbox.left + bbox.right) / 2, (bbox.top + bbox.bottom) / 2];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Helper layer that renders a tile outline and optional label for visualizing tile
|
|
95
|
+
* loading, coverage, and traversal.
|
|
96
|
+
*/
|
|
97
|
+
export class TileGridLayer extends CompositeLayer<TileGridLayerProps> {
|
|
98
|
+
static layerName = 'TileGridLayer';
|
|
99
|
+
static defaultProps: DefaultProps<TileGridLayerProps> = defaultProps;
|
|
100
|
+
|
|
101
|
+
/** Builds the tile border and label sublayers. */
|
|
102
|
+
renderLayers() {
|
|
103
|
+
const {
|
|
104
|
+
tile,
|
|
105
|
+
showBorder,
|
|
106
|
+
showLabel,
|
|
107
|
+
getLabel,
|
|
108
|
+
borderColor,
|
|
109
|
+
labelColor,
|
|
110
|
+
labelBackgroundColor,
|
|
111
|
+
borderWidthMinPixels,
|
|
112
|
+
labelSize
|
|
113
|
+
} = this.props;
|
|
114
|
+
|
|
115
|
+
const item: TileGridLayerItem = {
|
|
116
|
+
path: getTilePath(tile.bbox),
|
|
117
|
+
center: getTileCenter(tile.bbox),
|
|
118
|
+
label: typeof getLabel === 'function' ? getLabel(tile) : getLabel
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
return [
|
|
122
|
+
showBorder
|
|
123
|
+
? new PathLayer<TileGridLayerItem>(
|
|
124
|
+
this.getSubLayerProps({
|
|
125
|
+
id: 'border'
|
|
126
|
+
}),
|
|
127
|
+
{
|
|
128
|
+
data: [item],
|
|
129
|
+
getPath: (d) => d.path,
|
|
130
|
+
getColor: borderColor,
|
|
131
|
+
widthMinPixels: borderWidthMinPixels,
|
|
132
|
+
pickable: false
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
: null,
|
|
136
|
+
showLabel
|
|
137
|
+
? new TextLayer<TileGridLayerItem>(
|
|
138
|
+
this.getSubLayerProps({
|
|
139
|
+
id: 'label'
|
|
140
|
+
}),
|
|
141
|
+
{
|
|
142
|
+
data: [item],
|
|
143
|
+
getPosition: (d) => d.center,
|
|
144
|
+
getText: (d) => d.label,
|
|
145
|
+
getColor: labelColor,
|
|
146
|
+
getBackgroundColor: labelBackgroundColor,
|
|
147
|
+
getSize: labelSize,
|
|
148
|
+
sizeUnits: 'pixels',
|
|
149
|
+
background: true,
|
|
150
|
+
getTextAnchor: 'middle',
|
|
151
|
+
getAlignmentBaseline: 'center',
|
|
152
|
+
pickable: false
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
: null
|
|
156
|
+
];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -12,7 +12,7 @@ import type {TileSource} from '@loaders.gl/loader-utils';
|
|
|
12
12
|
const devicePixelRatio = (typeof window !== 'undefined' && window.devicePixelRatio) || 1;
|
|
13
13
|
|
|
14
14
|
export type TileSourceLayerProps = TileLayerProps & {
|
|
15
|
-
tileSource: TileSource
|
|
15
|
+
tileSource: TileSource;
|
|
16
16
|
showTileBorders?: boolean;
|
|
17
17
|
};
|
|
18
18
|
|
|
@@ -30,7 +30,7 @@ export class TileSourceLayer extends CompositeLayer<TileSourceLayerProps> {
|
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
state: {
|
|
33
|
-
tileSource: TileSource
|
|
33
|
+
tileSource: TileSource | null;
|
|
34
34
|
} = undefined!;
|
|
35
35
|
|
|
36
36
|
initializeState() {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// deck.gl-community
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import type {Matrix4} from '@math.gl/core';
|
|
6
|
+
import type {Bounds, TileBoundingBox, TileIndex, ZRange} from './types';
|
|
7
|
+
|
|
8
|
+
/** Traversal inputs required by a {@link SharedTileset2DAdapter}. */
|
|
9
|
+
export type SharedTileset2DTraversalContext<ViewStateT> = {
|
|
10
|
+
/** Consumer-defined view state used by the adapter. */
|
|
11
|
+
viewState: ViewStateT;
|
|
12
|
+
/** Tile size in pixels. */
|
|
13
|
+
tileSize: number;
|
|
14
|
+
/** Bounding box limiting tile generation. */
|
|
15
|
+
extent?: Bounds | null;
|
|
16
|
+
/** Minimum zoom level to request. */
|
|
17
|
+
minZoom?: number;
|
|
18
|
+
/** Maximum zoom level to request. */
|
|
19
|
+
maxZoom?: number;
|
|
20
|
+
/** Integer zoom offset applied during tile selection. */
|
|
21
|
+
zoomOffset?: number;
|
|
22
|
+
/** Elevation range used during geospatial tile selection. */
|
|
23
|
+
zRange?: ZRange | null;
|
|
24
|
+
/** Optional model matrix applied by the surrounding layer stack. */
|
|
25
|
+
modelMatrix?: Matrix4 | null;
|
|
26
|
+
/** Inverse of the current model matrix. */
|
|
27
|
+
modelMatrixInverse?: Matrix4 | null;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Minimal tile metadata inputs required by a {@link SharedTileset2DAdapter}. */
|
|
31
|
+
export type SharedTileset2DTileContext<ViewStateT> = {
|
|
32
|
+
/** Consumer-defined view state used by the adapter. */
|
|
33
|
+
viewState: ViewStateT;
|
|
34
|
+
/** Tile size in pixels. */
|
|
35
|
+
tileSize: number;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** Adapter used by {@link SharedTileset2D} to compute traversal and tile bounds without depending on a specific viewport implementation. */
|
|
39
|
+
export type SharedTileset2DAdapter<ViewStateT> = {
|
|
40
|
+
/** Returns tile indices that should be selected for one traversal context. */
|
|
41
|
+
getTileIndices: (context: SharedTileset2DTraversalContext<ViewStateT>) => TileIndex[];
|
|
42
|
+
/** Returns the structured bounding box for one tile index. */
|
|
43
|
+
getTileBoundingBox: (
|
|
44
|
+
context: SharedTileset2DTileContext<ViewStateT>,
|
|
45
|
+
index: TileIndex
|
|
46
|
+
) => TileBoundingBox;
|
|
47
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// deck.gl-community
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
export type {
|
|
6
|
+
TileLoadProps,
|
|
7
|
+
Bounds,
|
|
8
|
+
ZRange,
|
|
9
|
+
GeoBoundingBox,
|
|
10
|
+
NonGeoBoundingBox,
|
|
11
|
+
TileBoundingBox
|
|
12
|
+
} from './types';
|
|
13
|
+
export type {
|
|
14
|
+
SharedTileset2DAdapter,
|
|
15
|
+
SharedTileset2DTraversalContext,
|
|
16
|
+
SharedTileset2DTileContext
|
|
17
|
+
} from './adapter';
|
|
18
|
+
export type {RefinementStrategy, Tileset2DProps as SharedTileset2DBaseProps} from './tileset-2d';
|
|
19
|
+
export {STRATEGY_DEFAULT, STRATEGY_NEVER, STRATEGY_REPLACE} from './tileset-2d';
|
|
20
|
+
export type {SharedTileset2DProps} from './tileset-2d';
|
|
21
|
+
export {SharedTileset2D} from './tileset-2d';
|
|
22
|
+
export {SharedTile2DHeader} from './tile-2d-header';
|