@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deck.gl-community/geo-layers",
|
|
3
|
-
"version": "9.2
|
|
3
|
+
"version": "9.3.0-beta.2",
|
|
4
4
|
"description": "Add-0n geospatial layers for deck.gl",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
"types": "./dist/index.d.ts",
|
|
23
23
|
"require": "./dist/index.cjs",
|
|
24
24
|
"import": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./tileset": {
|
|
27
|
+
"types": "./dist/tileset/index.d.ts",
|
|
28
|
+
"require": "./dist/tileset/index.cjs",
|
|
29
|
+
"import": "./dist/tileset/index.js"
|
|
25
30
|
}
|
|
26
31
|
},
|
|
27
32
|
"files": [
|
|
@@ -33,21 +38,22 @@
|
|
|
33
38
|
"test-watch": "vitest"
|
|
34
39
|
},
|
|
35
40
|
"dependencies": {
|
|
36
|
-
"@deck.gl/core": "~9.
|
|
37
|
-
"@deck.gl/geo-layers": "~9.
|
|
38
|
-
"@deck.gl/layers": "~9.
|
|
39
|
-
"@loaders.gl/loader-utils": "^4.
|
|
40
|
-
"@luma.gl/core": "~9.2
|
|
41
|
-
"@luma.gl/engine": "~9.2
|
|
42
|
-
"@luma.gl/shadertools": "~9.2
|
|
41
|
+
"@deck.gl/core": "~9.3.0",
|
|
42
|
+
"@deck.gl/geo-layers": "~9.3.0",
|
|
43
|
+
"@deck.gl/layers": "~9.3.0",
|
|
44
|
+
"@loaders.gl/loader-utils": "^4.4.1",
|
|
45
|
+
"@luma.gl/core": "~9.3.2",
|
|
46
|
+
"@luma.gl/engine": "~9.3.2",
|
|
47
|
+
"@luma.gl/shadertools": "~9.3.2",
|
|
43
48
|
"@math.gl/core": "^4.0.0",
|
|
49
|
+
"@probe.gl/stats": "^4.1.1",
|
|
44
50
|
"a5-js": "^0.5.0",
|
|
45
51
|
"h3-js": "^4.2.1"
|
|
46
52
|
},
|
|
47
53
|
"devDependencies": {
|
|
48
|
-
"@deck.gl/test-utils": "~9.
|
|
49
|
-
"@luma.gl/webgpu": "~9.2
|
|
54
|
+
"@deck.gl/test-utils": "~9.3.0",
|
|
55
|
+
"@luma.gl/webgpu": "~9.3.2",
|
|
50
56
|
"@probe.gl/test-utils": "^4.0.4"
|
|
51
57
|
},
|
|
52
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "82660b8a29be0958c81acd00669b3034375186ea"
|
|
53
59
|
}
|
|
@@ -34,7 +34,7 @@ export class GlobalGridClusterLayer<DataT = any, ExtraProps extends {} = {}> ext
|
|
|
34
34
|
globalGrid: {type: 'object', compare: true, value: undefined!}
|
|
35
35
|
} as const satisfies DefaultProps<GlobalGridClusterLayerProps>;
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
state = null as unknown as {
|
|
38
38
|
polygons: {polygon: number[][][]}[];
|
|
39
39
|
};
|
|
40
40
|
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
export type {TileSourceLayerProps} from './tile-source-layer/tile-source-layer';
|
|
6
6
|
export {TileSourceLayer} from './tile-source-layer/tile-source-layer';
|
|
7
|
+
export type {
|
|
8
|
+
SharedTile2DLayerProps,
|
|
9
|
+
SharedTile2DLayerPickingInfo
|
|
10
|
+
} from './shared-tile-2d-layer/index';
|
|
11
|
+
export {SharedTile2DLayer, sharedTile2DDeckAdapter} from './shared-tile-2d-layer/index';
|
|
12
|
+
export type {SharedTileset2DProps, SharedTileset2DBaseProps} from './tileset/index';
|
|
13
|
+
export type {
|
|
14
|
+
SharedTileset2DAdapter,
|
|
15
|
+
SharedTileset2DTraversalContext,
|
|
16
|
+
SharedTileset2DTileContext
|
|
17
|
+
} from './tileset/index';
|
|
18
|
+
export {SharedTileset2D, SharedTile2DHeader} from './tileset/index';
|
|
19
|
+
export type {TileGridLayerProps} from './tile-grid-layer/tile-grid-layer';
|
|
20
|
+
export {TileGridLayer} from './tile-grid-layer/tile-grid-layer';
|
|
7
21
|
|
|
8
22
|
export {GlobalGridLayer, type GlobalGridLayerProps} from './global-grid-layer/global-grid-layer';
|
|
9
23
|
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// deck.gl-community
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Viewport, WebMercatorViewport, _GlobeViewport} from '@deck.gl/core';
|
|
6
|
+
import {
|
|
7
|
+
CullingVolume,
|
|
8
|
+
Plane,
|
|
9
|
+
AxisAlignedBoundingBox,
|
|
10
|
+
makeOrientedBoundingBoxFromPoints
|
|
11
|
+
} from '@math.gl/culling';
|
|
12
|
+
import {lngLatToWorld} from '@math.gl/web-mercator';
|
|
13
|
+
import type {Bounds, TileIndex, ZRange} from '../tileset/types';
|
|
14
|
+
import {osmTile2lngLat} from './deck-tileset-adapter';
|
|
15
|
+
|
|
16
|
+
const TILE_SIZE = 512;
|
|
17
|
+
const MAX_MAPS = 3;
|
|
18
|
+
const REF_POINTS_5 = [
|
|
19
|
+
[0.5, 0.5],
|
|
20
|
+
[0, 0],
|
|
21
|
+
[0, 1],
|
|
22
|
+
[1, 0],
|
|
23
|
+
[1, 1]
|
|
24
|
+
];
|
|
25
|
+
const REF_POINTS_9 = REF_POINTS_5.concat([
|
|
26
|
+
[0, 0.5],
|
|
27
|
+
[0.5, 0],
|
|
28
|
+
[1, 0.5],
|
|
29
|
+
[0.5, 1]
|
|
30
|
+
]);
|
|
31
|
+
const REF_POINTS_11 = REF_POINTS_9.concat([
|
|
32
|
+
[0.25, 0.5],
|
|
33
|
+
[0.75, 0.5]
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
class OSMNode {
|
|
37
|
+
x: number;
|
|
38
|
+
y: number;
|
|
39
|
+
z: number;
|
|
40
|
+
|
|
41
|
+
private childVisible?: boolean;
|
|
42
|
+
private selected?: boolean;
|
|
43
|
+
private _children?: OSMNode[];
|
|
44
|
+
|
|
45
|
+
constructor(x: number, y: number, z: number) {
|
|
46
|
+
this.x = x;
|
|
47
|
+
this.y = y;
|
|
48
|
+
this.z = z;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get children(): OSMNode[] {
|
|
52
|
+
if (!this._children) {
|
|
53
|
+
const x = this.x * 2;
|
|
54
|
+
const y = this.y * 2;
|
|
55
|
+
const z = this.z + 1;
|
|
56
|
+
this._children = [
|
|
57
|
+
new OSMNode(x, y, z),
|
|
58
|
+
new OSMNode(x, y + 1, z),
|
|
59
|
+
new OSMNode(x + 1, y, z),
|
|
60
|
+
new OSMNode(x + 1, y + 1, z)
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
return this._children;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
update(params: {
|
|
67
|
+
viewport: Viewport;
|
|
68
|
+
project: ((xyz: number[]) => number[]) | null;
|
|
69
|
+
cullingVolume: CullingVolume;
|
|
70
|
+
elevationBounds: ZRange;
|
|
71
|
+
minZ: number;
|
|
72
|
+
maxZ: number;
|
|
73
|
+
bounds?: Bounds;
|
|
74
|
+
offset: number;
|
|
75
|
+
}): boolean {
|
|
76
|
+
const {viewport, cullingVolume, elevationBounds, minZ, maxZ, bounds, offset, project} = params;
|
|
77
|
+
const boundingVolume = this.getBoundingVolume(elevationBounds, offset, project);
|
|
78
|
+
|
|
79
|
+
if (bounds && !this.insideBounds(bounds)) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const isInside = cullingVolume.computeVisibility(boundingVolume);
|
|
84
|
+
if (isInside < 0) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!this.childVisible) {
|
|
89
|
+
let {z} = this;
|
|
90
|
+
if (z < maxZ && z >= minZ) {
|
|
91
|
+
const distance =
|
|
92
|
+
(boundingVolume.distanceTo(viewport.cameraPosition) * viewport.scale) / viewport.height;
|
|
93
|
+
z += Math.floor(Math.log2(distance));
|
|
94
|
+
}
|
|
95
|
+
if (z >= maxZ) {
|
|
96
|
+
this.selected = true;
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this.selected = false;
|
|
102
|
+
this.childVisible = true;
|
|
103
|
+
for (const child of this.children) {
|
|
104
|
+
child.update(params);
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
getSelected(result: OSMNode[] = []): OSMNode[] {
|
|
110
|
+
if (this.selected) {
|
|
111
|
+
result.push(this);
|
|
112
|
+
}
|
|
113
|
+
if (this._children) {
|
|
114
|
+
for (const node of this._children) {
|
|
115
|
+
node.getSelected(result);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
insideBounds([minX, minY, maxX, maxY]: Bounds): boolean {
|
|
122
|
+
const scale = Math.pow(2, this.z);
|
|
123
|
+
const extent = TILE_SIZE / scale;
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
this.x * extent < maxX &&
|
|
127
|
+
this.y * extent < maxY &&
|
|
128
|
+
(this.x + 1) * extent > minX &&
|
|
129
|
+
(this.y + 1) * extent > minY
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
getBoundingVolume(
|
|
134
|
+
zRange: ZRange,
|
|
135
|
+
worldOffset: number,
|
|
136
|
+
project: ((xyz: number[]) => number[]) | null
|
|
137
|
+
) {
|
|
138
|
+
if (project) {
|
|
139
|
+
const refPoints = this.z < 1 ? REF_POINTS_11 : this.z < 2 ? REF_POINTS_9 : REF_POINTS_5;
|
|
140
|
+
const refPointPositions: number[][] = [];
|
|
141
|
+
for (const p of refPoints) {
|
|
142
|
+
const lngLat: number[] = osmTile2lngLat(this.x + p[0], this.y + p[1], this.z);
|
|
143
|
+
lngLat[2] = zRange[0];
|
|
144
|
+
refPointPositions.push(project(lngLat));
|
|
145
|
+
|
|
146
|
+
if (zRange[0] !== zRange[1]) {
|
|
147
|
+
lngLat[2] = zRange[1];
|
|
148
|
+
refPointPositions.push(project(lngLat));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return makeOrientedBoundingBoxFromPoints(refPointPositions);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const scale = Math.pow(2, this.z);
|
|
156
|
+
const extent = TILE_SIZE / scale;
|
|
157
|
+
const originX = this.x * extent + worldOffset * TILE_SIZE;
|
|
158
|
+
const originY = TILE_SIZE - (this.y + 1) * extent;
|
|
159
|
+
|
|
160
|
+
return new AxisAlignedBoundingBox(
|
|
161
|
+
[originX, originY, zRange[0]],
|
|
162
|
+
[originX + extent, originY + extent, zRange[1]]
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** deck.gl-specific OSM tile traversal for geospatial viewports. */
|
|
168
|
+
export function getOSMTileIndices(
|
|
169
|
+
viewport: Viewport,
|
|
170
|
+
maxZ: number,
|
|
171
|
+
zRange: ZRange | null,
|
|
172
|
+
bounds?: Bounds
|
|
173
|
+
): TileIndex[] {
|
|
174
|
+
const project: ((xyz: number[]) => number[]) | null =
|
|
175
|
+
viewport instanceof _GlobeViewport && viewport.resolution
|
|
176
|
+
? (xyz) => viewport.projectPosition(xyz)
|
|
177
|
+
: null;
|
|
178
|
+
|
|
179
|
+
const planes: Plane[] = Object.values(viewport.getFrustumPlanes()).map(
|
|
180
|
+
({normal, distance}) => new Plane(normal.clone().negate(), distance)
|
|
181
|
+
);
|
|
182
|
+
const cullingVolume = new CullingVolume(planes);
|
|
183
|
+
|
|
184
|
+
const unitsPerMeter = viewport.distanceScales.unitsPerMeter[2];
|
|
185
|
+
const elevationMin = (zRange && zRange[0] * unitsPerMeter) || 0;
|
|
186
|
+
const elevationMax = (zRange && zRange[1] * unitsPerMeter) || 0;
|
|
187
|
+
const minZ = viewport instanceof WebMercatorViewport && viewport.pitch <= 60 ? maxZ : 0;
|
|
188
|
+
|
|
189
|
+
if (bounds) {
|
|
190
|
+
const [minLng, minLat, maxLng, maxLat] = bounds;
|
|
191
|
+
const topLeft = lngLatToWorld([minLng, maxLat]);
|
|
192
|
+
const bottomRight = lngLatToWorld([maxLng, minLat]);
|
|
193
|
+
bounds = [topLeft[0], TILE_SIZE - topLeft[1], bottomRight[0], TILE_SIZE - bottomRight[1]];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const root = new OSMNode(0, 0, 0);
|
|
197
|
+
const traversalParams = {
|
|
198
|
+
viewport,
|
|
199
|
+
project,
|
|
200
|
+
cullingVolume,
|
|
201
|
+
elevationBounds: [elevationMin, elevationMax] as ZRange,
|
|
202
|
+
minZ,
|
|
203
|
+
maxZ,
|
|
204
|
+
bounds,
|
|
205
|
+
offset: 0
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
root.update(traversalParams);
|
|
209
|
+
updateWrappedWorldCopies(root, traversalParams, viewport);
|
|
210
|
+
|
|
211
|
+
return root.getSelected();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function updateWrappedWorldCopies(
|
|
215
|
+
root: OSMNode,
|
|
216
|
+
traversalParams: Parameters<OSMNode['update']>[0],
|
|
217
|
+
viewport: Viewport
|
|
218
|
+
): void {
|
|
219
|
+
if (
|
|
220
|
+
!(viewport instanceof WebMercatorViewport) ||
|
|
221
|
+
!viewport.subViewports ||
|
|
222
|
+
viewport.subViewports.length <= 1
|
|
223
|
+
) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
sweepOffsets(root, traversalParams, -1, -MAX_MAPS);
|
|
228
|
+
sweepOffsets(root, traversalParams, 1, MAX_MAPS);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function sweepOffsets(
|
|
232
|
+
root: OSMNode,
|
|
233
|
+
traversalParams: Parameters<OSMNode['update']>[0],
|
|
234
|
+
startOffset: number,
|
|
235
|
+
limit: number
|
|
236
|
+
): void {
|
|
237
|
+
traversalParams.offset = startOffset;
|
|
238
|
+
while (root.update(traversalParams)) {
|
|
239
|
+
traversalParams.offset += Math.sign(startOffset);
|
|
240
|
+
if (
|
|
241
|
+
(startOffset < 0 && traversalParams.offset < limit) ||
|
|
242
|
+
(startOffset > 0 && traversalParams.offset > limit)
|
|
243
|
+
) {
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
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} from '@math.gl/core';
|
|
7
|
+
import type {Bounds, GeoBoundingBox, TileBoundingBox, TileIndex, ZRange} from '../tileset/types';
|
|
8
|
+
import type {
|
|
9
|
+
SharedTileset2DAdapter,
|
|
10
|
+
SharedTileset2DTileContext,
|
|
11
|
+
SharedTileset2DTraversalContext
|
|
12
|
+
} from '../tileset/adapter';
|
|
13
|
+
import {getOSMTileIndices} from './deck-tile-traversal';
|
|
14
|
+
|
|
15
|
+
const TILE_SIZE = 512;
|
|
16
|
+
const DEFAULT_EXTENT: Bounds = [-Infinity, -Infinity, Infinity, Infinity];
|
|
17
|
+
|
|
18
|
+
/** deck.gl viewport type used by the shared tile layer adapter. */
|
|
19
|
+
export type SharedTile2DDeckViewState = Viewport;
|
|
20
|
+
|
|
21
|
+
/** Applies a model transform to an axis-aligned bounding box. */
|
|
22
|
+
export function transformBox(bbox: Bounds, modelMatrix: Matrix4): Bounds {
|
|
23
|
+
const transformedCoords = [
|
|
24
|
+
modelMatrix.transformAsPoint([bbox[0], bbox[1]]),
|
|
25
|
+
modelMatrix.transformAsPoint([bbox[2], bbox[1]]),
|
|
26
|
+
modelMatrix.transformAsPoint([bbox[0], bbox[3]]),
|
|
27
|
+
modelMatrix.transformAsPoint([bbox[2], bbox[3]])
|
|
28
|
+
];
|
|
29
|
+
return [
|
|
30
|
+
Math.min(...transformedCoords.map((i) => i[0])),
|
|
31
|
+
Math.min(...transformedCoords.map((i) => i[1])),
|
|
32
|
+
Math.max(...transformedCoords.map((i) => i[0])),
|
|
33
|
+
Math.max(...transformedCoords.map((i) => i[1]))
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getBoundingBox(viewport: Viewport, zRange: number[] | null, extent: Bounds): Bounds {
|
|
38
|
+
let bounds;
|
|
39
|
+
if (zRange && zRange.length === 2) {
|
|
40
|
+
const [minZ, maxZ] = zRange;
|
|
41
|
+
const bounds0 = viewport.getBounds({z: minZ});
|
|
42
|
+
const bounds1 = viewport.getBounds({z: maxZ});
|
|
43
|
+
bounds = [
|
|
44
|
+
Math.min(bounds0[0], bounds1[0]),
|
|
45
|
+
Math.min(bounds0[1], bounds1[1]),
|
|
46
|
+
Math.max(bounds0[2], bounds1[2]),
|
|
47
|
+
Math.max(bounds0[3], bounds1[3])
|
|
48
|
+
];
|
|
49
|
+
} else {
|
|
50
|
+
bounds = viewport.getBounds();
|
|
51
|
+
}
|
|
52
|
+
if (!viewport.isGeospatial) {
|
|
53
|
+
return [
|
|
54
|
+
Math.max(Math.min(bounds[0], extent[2]), extent[0]),
|
|
55
|
+
Math.max(Math.min(bounds[1], extent[3]), extent[1]),
|
|
56
|
+
Math.min(Math.max(bounds[2], extent[0]), extent[2]),
|
|
57
|
+
Math.min(Math.max(bounds[3], extent[1]), extent[3])
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
return [
|
|
61
|
+
Math.max(bounds[0], extent[0]),
|
|
62
|
+
Math.max(bounds[1], extent[1]),
|
|
63
|
+
Math.min(bounds[2], extent[2]),
|
|
64
|
+
Math.min(bounds[3], extent[3])
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Computes cull bounds in projected/world coordinates for one deck viewport. */
|
|
69
|
+
export function getCullBounds({
|
|
70
|
+
viewport,
|
|
71
|
+
z,
|
|
72
|
+
cullRect
|
|
73
|
+
}: {
|
|
74
|
+
viewport: Viewport;
|
|
75
|
+
z: ZRange | number | null;
|
|
76
|
+
cullRect: {x: number; y: number; width: number; height: number};
|
|
77
|
+
}): [number, number, number, number][] {
|
|
78
|
+
const subViewports = viewport.subViewports || [viewport];
|
|
79
|
+
return subViewports.map((v) => getCullBoundsInViewport(v, z || 0, cullRect));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getCullBoundsInViewport(
|
|
83
|
+
viewport: Viewport,
|
|
84
|
+
z: ZRange | number,
|
|
85
|
+
cullRect: {x: number; y: number; width: number; height: number}
|
|
86
|
+
): [number, number, number, number] {
|
|
87
|
+
if (!Array.isArray(z)) {
|
|
88
|
+
const x = cullRect.x - viewport.x;
|
|
89
|
+
const y = cullRect.y - viewport.y;
|
|
90
|
+
const {width, height} = cullRect;
|
|
91
|
+
const unprojectOption = {targetZ: z};
|
|
92
|
+
|
|
93
|
+
const topLeft = viewport.unproject([x, y], unprojectOption);
|
|
94
|
+
const topRight = viewport.unproject([x + width, y], unprojectOption);
|
|
95
|
+
const bottomLeft = viewport.unproject([x, y + height], unprojectOption);
|
|
96
|
+
const bottomRight = viewport.unproject([x + width, y + height], unprojectOption);
|
|
97
|
+
|
|
98
|
+
return [
|
|
99
|
+
Math.min(topLeft[0], topRight[0], bottomLeft[0], bottomRight[0]),
|
|
100
|
+
Math.min(topLeft[1], topRight[1], bottomLeft[1], bottomRight[1]),
|
|
101
|
+
Math.max(topLeft[0], topRight[0], bottomLeft[0], bottomRight[0]),
|
|
102
|
+
Math.max(topLeft[1], topRight[1], bottomLeft[1], bottomRight[1])
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const bounds0 = getCullBoundsInViewport(viewport, z[0], cullRect);
|
|
107
|
+
const bounds1 = getCullBoundsInViewport(viewport, z[1], cullRect);
|
|
108
|
+
|
|
109
|
+
return [
|
|
110
|
+
Math.min(bounds0[0], bounds1[0]),
|
|
111
|
+
Math.min(bounds0[1], bounds1[1]),
|
|
112
|
+
Math.max(bounds0[2], bounds1[2]),
|
|
113
|
+
Math.max(bounds0[3], bounds1[3])
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function getIndexingCoords(
|
|
118
|
+
bbox: Bounds,
|
|
119
|
+
scale: number,
|
|
120
|
+
modelMatrixInverse?: Matrix4 | null
|
|
121
|
+
): Bounds {
|
|
122
|
+
if (modelMatrixInverse) {
|
|
123
|
+
return transformBox(bbox, modelMatrixInverse).map((i) => (i * scale) / TILE_SIZE) as Bounds;
|
|
124
|
+
}
|
|
125
|
+
return bbox.map((i) => (i * scale) / TILE_SIZE) as Bounds;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function getScale(z: number, tileSize: number): number {
|
|
129
|
+
return (Math.pow(2, z) * TILE_SIZE) / tileSize;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Converts an OSM tile coordinate to longitude/latitude. */
|
|
133
|
+
export function osmTile2lngLat(x: number, y: number, z: number): [number, number] {
|
|
134
|
+
const scale = getScale(z, TILE_SIZE);
|
|
135
|
+
const lng = (x / scale) * 360 - 180;
|
|
136
|
+
const n = Math.PI - (2 * Math.PI * y) / scale;
|
|
137
|
+
const lat = (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
|
|
138
|
+
return [lng, lat];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function tile2XY(x: number, y: number, z: number, tileSize: number): [number, number] {
|
|
142
|
+
const scale = getScale(z, tileSize);
|
|
143
|
+
return [(x / scale) * TILE_SIZE, (y / scale) * TILE_SIZE];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function tileToBoundingBox(
|
|
147
|
+
viewport: Viewport,
|
|
148
|
+
x: number,
|
|
149
|
+
y: number,
|
|
150
|
+
z: number,
|
|
151
|
+
tileSize: number = TILE_SIZE
|
|
152
|
+
): TileBoundingBox {
|
|
153
|
+
if (viewport.isGeospatial) {
|
|
154
|
+
const [west, north] = osmTile2lngLat(x, y, z);
|
|
155
|
+
const [east, south] = osmTile2lngLat(x + 1, y + 1, z);
|
|
156
|
+
return {west, north, east, south};
|
|
157
|
+
}
|
|
158
|
+
const [left, top] = tile2XY(x, y, z, tileSize);
|
|
159
|
+
const [right, bottom] = tile2XY(x + 1, y + 1, z, tileSize);
|
|
160
|
+
return {left, top, right, bottom};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function getIdentityTileIndices(
|
|
164
|
+
viewport: Viewport,
|
|
165
|
+
z: number,
|
|
166
|
+
tileSize: number,
|
|
167
|
+
extent: Bounds,
|
|
168
|
+
modelMatrixInverse?: Matrix4 | null
|
|
169
|
+
): TileIndex[] {
|
|
170
|
+
const bbox = getBoundingBox(viewport, null, extent);
|
|
171
|
+
const scale = getScale(z, tileSize);
|
|
172
|
+
const [minX, minY, maxX, maxY] = getIndexingCoords(bbox, scale, modelMatrixInverse);
|
|
173
|
+
const indices: TileIndex[] = [];
|
|
174
|
+
|
|
175
|
+
for (let x = Math.floor(minX); x < maxX; x++) {
|
|
176
|
+
for (let y = Math.floor(minY); y < maxY; y++) {
|
|
177
|
+
indices.push({x, y, z});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return indices;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function getTileZoomForViewport(viewport: Viewport, tileSize: number, zoomOffset: number): number {
|
|
184
|
+
return viewport.isGeospatial
|
|
185
|
+
? Math.round(viewport.zoom + Math.log2(TILE_SIZE / tileSize)) + zoomOffset
|
|
186
|
+
: Math.ceil(viewport.zoom) + zoomOffset;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function applyZoomBounds(
|
|
190
|
+
z: number,
|
|
191
|
+
minZoom: number | undefined,
|
|
192
|
+
maxZoom: number | undefined,
|
|
193
|
+
extent?: Bounds | null
|
|
194
|
+
): number | null {
|
|
195
|
+
if (typeof minZoom === 'number' && Number.isFinite(minZoom) && z < minZoom) {
|
|
196
|
+
if (!extent) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
z = minZoom;
|
|
200
|
+
}
|
|
201
|
+
if (typeof maxZoom === 'number' && Number.isFinite(maxZoom) && z > maxZoom) {
|
|
202
|
+
z = maxZoom;
|
|
203
|
+
}
|
|
204
|
+
return z;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function getDeckTileIndices(context: SharedTileset2DTraversalContext<Viewport>): TileIndex[] {
|
|
208
|
+
const {
|
|
209
|
+
viewState: viewport,
|
|
210
|
+
maxZoom,
|
|
211
|
+
minZoom,
|
|
212
|
+
zRange = null,
|
|
213
|
+
extent,
|
|
214
|
+
tileSize = TILE_SIZE,
|
|
215
|
+
modelMatrix,
|
|
216
|
+
modelMatrixInverse,
|
|
217
|
+
zoomOffset = 0
|
|
218
|
+
} = context;
|
|
219
|
+
let z = getTileZoomForViewport(viewport, tileSize, zoomOffset);
|
|
220
|
+
z = applyZoomBounds(z, minZoom, maxZoom, extent);
|
|
221
|
+
if (z === null) {
|
|
222
|
+
return [];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
let transformedExtent = extent || undefined;
|
|
226
|
+
if (modelMatrix && modelMatrixInverse && extent && !viewport.isGeospatial) {
|
|
227
|
+
transformedExtent = transformBox(extent, modelMatrix);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return viewport.isGeospatial
|
|
231
|
+
? getOSMTileIndices(viewport, z, zRange, extent || undefined)
|
|
232
|
+
: getIdentityTileIndices(
|
|
233
|
+
viewport,
|
|
234
|
+
z,
|
|
235
|
+
tileSize,
|
|
236
|
+
transformedExtent || DEFAULT_EXTENT,
|
|
237
|
+
modelMatrixInverse
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function getDeckTileBoundingBox(
|
|
242
|
+
context: SharedTileset2DTileContext<Viewport>,
|
|
243
|
+
index: TileIndex
|
|
244
|
+
): TileBoundingBox {
|
|
245
|
+
return tileToBoundingBox(context.viewState, index.x, index.y, index.z, context.tileSize);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Deck.gl adapter used by {@link SharedTile2DLayer} to drive a generic {@link SharedTileset2D}. */
|
|
249
|
+
export const sharedTile2DDeckAdapter: SharedTileset2DAdapter<Viewport> = {
|
|
250
|
+
getTileIndices: getDeckTileIndices,
|
|
251
|
+
getTileBoundingBox: getDeckTileBoundingBox
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
/** Type guard for geographic tile bounds. */
|
|
255
|
+
export function isGeoBoundingBox(v: any): v is GeoBoundingBox {
|
|
256
|
+
return (
|
|
257
|
+
Number.isFinite(v.west) &&
|
|
258
|
+
Number.isFinite(v.north) &&
|
|
259
|
+
Number.isFinite(v.east) &&
|
|
260
|
+
Number.isFinite(v.south)
|
|
261
|
+
);
|
|
262
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// deck.gl-community
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
export {SharedTile2DLayer} from './shared-tile-2d-layer';
|
|
6
|
+
export type {SharedTile2DLayerProps, SharedTile2DLayerPickingInfo} from './shared-tile-2d-layer';
|
|
7
|
+
export {sharedTile2DDeckAdapter} from './deck-tileset-adapter';
|