@developmentseed/deck.gl-raster 0.7.0-beta.1 → 0.8.0-beta.1
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/fp64.d.ts +18 -0
- package/dist/fp64.d.ts.map +1 -0
- package/dist/fp64.js +28 -0
- package/dist/fp64.js.map +1 -0
- package/dist/globe-grid-mesh.d.ts +30 -0
- package/dist/globe-grid-mesh.d.ts.map +1 -0
- package/dist/globe-grid-mesh.js +67 -0
- package/dist/globe-grid-mesh.js.map +1 -0
- package/dist/gpu-modules/cutline-bbox.d.ts +26 -40
- package/dist/gpu-modules/cutline-bbox.d.ts.map +1 -1
- package/dist/gpu-modules/cutline-bbox.js +24 -53
- package/dist/gpu-modules/cutline-bbox.js.map +1 -1
- package/dist/gpu-modules/index.d.ts +1 -1
- package/dist/gpu-modules/index.d.ts.map +1 -1
- package/dist/gpu-modules/index.js +1 -1
- package/dist/gpu-modules/index.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/layer-utils.d.ts +2 -2
- package/dist/layer-utils.d.ts.map +1 -1
- package/dist/layer-utils.js.map +1 -1
- package/dist/mesh-layer/mesh-layer-fragment.glsl.d.ts +1 -1
- package/dist/mesh-layer/mesh-layer-fragment.glsl.js +1 -1
- package/dist/mesh-layer/mesh-layer-vertex.glsl.d.ts +3 -0
- package/dist/mesh-layer/mesh-layer-vertex.glsl.d.ts.map +1 -0
- package/dist/mesh-layer/mesh-layer-vertex.glsl.js +90 -0
- package/dist/mesh-layer/mesh-layer-vertex.glsl.js.map +1 -0
- package/dist/mesh-layer/mesh-layer.d.ts +31 -5
- package/dist/mesh-layer/mesh-layer.d.ts.map +1 -1
- package/dist/mesh-layer/mesh-layer.js +67 -3
- package/dist/mesh-layer/mesh-layer.js.map +1 -1
- package/dist/multi-raster-tileset/index.d.ts +2 -2
- package/dist/multi-raster-tileset/index.d.ts.map +1 -1
- package/dist/multi-raster-tileset/index.js +1 -1
- package/dist/multi-raster-tileset/index.js.map +1 -1
- package/dist/multi-raster-tileset/multi-tileset-descriptor.d.ts +21 -21
- package/dist/multi-raster-tileset/multi-tileset-descriptor.d.ts.map +1 -1
- package/dist/multi-raster-tileset/multi-tileset-descriptor.js +11 -11
- package/dist/multi-raster-tileset/multi-tileset-descriptor.js.map +1 -1
- package/dist/multi-raster-tileset/secondary-tile-resolver.d.ts +4 -4
- package/dist/multi-raster-tileset/secondary-tile-resolver.d.ts.map +1 -1
- package/dist/multi-raster-tileset/secondary-tile-resolver.js +2 -2
- package/dist/multi-raster-tileset/secondary-tile-resolver.js.map +1 -1
- package/dist/raster-layer.d.ts +39 -4
- package/dist/raster-layer.d.ts.map +1 -1
- package/dist/raster-layer.js +51 -35
- package/dist/raster-layer.js.map +1 -1
- package/dist/raster-tile-layer/raster-tile-layer.d.ts +6 -6
- package/dist/raster-tile-layer/raster-tile-layer.d.ts.map +1 -1
- package/dist/raster-tile-layer/raster-tile-layer.js +40 -31
- package/dist/raster-tile-layer/raster-tile-layer.js.map +1 -1
- package/dist/raster-tileset/affine-tileset-level.d.ts +4 -4
- package/dist/raster-tileset/affine-tileset-level.d.ts.map +1 -1
- package/dist/raster-tileset/affine-tileset-level.js +2 -2
- package/dist/raster-tileset/affine-tileset.d.ts +3 -3
- package/dist/raster-tileset/affine-tileset.d.ts.map +1 -1
- package/dist/raster-tileset/affine-tileset.js +1 -1
- package/dist/raster-tileset/bounding-volume-cache.d.ts +11 -4
- package/dist/raster-tileset/bounding-volume-cache.d.ts.map +1 -1
- package/dist/raster-tileset/bounding-volume-cache.js +13 -4
- package/dist/raster-tileset/bounding-volume-cache.js.map +1 -1
- package/dist/raster-tileset/index.d.ts +3 -2
- package/dist/raster-tileset/index.d.ts.map +1 -1
- package/dist/raster-tileset/index.js +1 -0
- package/dist/raster-tileset/index.js.map +1 -1
- package/dist/raster-tileset/raster-tile-traversal.d.ts +68 -13
- package/dist/raster-tileset/raster-tile-traversal.d.ts.map +1 -1
- package/dist/raster-tileset/raster-tile-traversal.js +240 -35
- package/dist/raster-tileset/raster-tile-traversal.js.map +1 -1
- package/dist/raster-tileset/raster-tileset-2d.d.ts +64 -6
- package/dist/raster-tileset/raster-tileset-2d.d.ts.map +1 -1
- package/dist/raster-tileset/raster-tileset-2d.js +75 -2
- package/dist/raster-tileset/raster-tileset-2d.js.map +1 -1
- package/dist/raster-tileset/sort-by-distance.d.ts +41 -0
- package/dist/raster-tileset/sort-by-distance.d.ts.map +1 -0
- package/dist/raster-tileset/sort-by-distance.js +72 -0
- package/dist/raster-tileset/sort-by-distance.js.map +1 -0
- package/dist/raster-tileset/tile-matrix-set.d.ts +4 -4
- package/dist/raster-tileset/tile-matrix-set.d.ts.map +1 -1
- package/dist/raster-tileset/tile-matrix-set.js +1 -1
- package/dist/raster-tileset/tile-matrix-set.js.map +1 -1
- package/dist/raster-tileset/tileset-interface.d.ts +3 -3
- package/dist/raster-tileset/tileset-interface.d.ts.map +1 -1
- package/dist/raster-tileset/web-mercator-clamp.d.ts +29 -0
- package/dist/raster-tileset/web-mercator-clamp.d.ts.map +1 -0
- package/dist/raster-tileset/web-mercator-clamp.js +54 -0
- package/dist/raster-tileset/web-mercator-clamp.js.map +1 -0
- package/package.json +7 -7
- package/dist/raster-tile-layer/constants.d.ts +0 -11
- package/dist/raster-tile-layer/constants.d.ts.map +0 -1
- package/dist/raster-tile-layer/constants.js +0 -11
- package/dist/raster-tile-layer/constants.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/raster-tileset/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/raster-tileset/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,qCAAqC,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,YAAY,EACV,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,MAAM,EACN,YAAY,EACZ,OAAO,EACP,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { AffineTileset } from "./affine-tileset.js";
|
|
2
2
|
export { AffineTilesetLevel } from "./affine-tileset-level.js";
|
|
3
3
|
export { RasterTileset2D } from "./raster-tileset-2d.js";
|
|
4
|
+
export { sortItemsByDistanceFromViewportCenter } from "./sort-by-distance.js";
|
|
4
5
|
export { TileMatrixSetAdaptor } from "./tile-matrix-set.js";
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/raster-tileset/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/raster-tileset/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,qCAAqC,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -13,14 +13,13 @@
|
|
|
13
13
|
* The result is a set of tiles at varying zoom levels that efficiently
|
|
14
14
|
* cover the visible area with appropriate detail.
|
|
15
15
|
*
|
|
16
|
-
* The traversal is driven by a {@link
|
|
16
|
+
* The traversal is driven by a {@link RasterTilesetDescriptor}, which abstracts over
|
|
17
17
|
* both OGC TileMatrixSet grids and Zarr multiscale pyramids.
|
|
18
18
|
*/
|
|
19
19
|
import type { Viewport } from "@deck.gl/core";
|
|
20
|
-
import
|
|
21
|
-
import { CullingVolume } from "@math.gl/culling";
|
|
20
|
+
import { CullingVolume, OrientedBoundingBox } from "@math.gl/culling";
|
|
22
21
|
import { BoundingVolumeCache } from "./bounding-volume-cache.js";
|
|
23
|
-
import type {
|
|
22
|
+
import type { RasterTilesetDescriptor, RasterTilesetLevel } from "./tileset-interface.js";
|
|
24
23
|
import type { Bounds, TileIndex, ZRange } from "./types.js";
|
|
25
24
|
/**
|
|
26
25
|
* Raster Tile Node - represents a single tile in a tileset pyramid.
|
|
@@ -29,8 +28,8 @@ import type { Bounds, TileIndex, ZRange } from "./types.js";
|
|
|
29
28
|
*
|
|
30
29
|
* This node class uses the following coordinate system:
|
|
31
30
|
*
|
|
32
|
-
* - x: tile column (0 to
|
|
33
|
-
* - y: tile row (0 to
|
|
31
|
+
* - x: tile column (0 to RasterTilesetLevel.matrixWidth, left to right)
|
|
32
|
+
* - y: tile row (0 to RasterTilesetLevel.matrixHeight, top to bottom)
|
|
34
33
|
* - z: overview level. This assumes ordering where: 0 = coarsest, higher = finer
|
|
35
34
|
*/
|
|
36
35
|
export declare class RasterTileNode {
|
|
@@ -57,17 +56,17 @@ export declare class RasterTileNode {
|
|
|
57
56
|
/** A cache of the children of this node. */
|
|
58
57
|
private _children?;
|
|
59
58
|
constructor(x: number, y: number, z: number, { descriptor }: {
|
|
60
|
-
descriptor:
|
|
59
|
+
descriptor: RasterTilesetDescriptor;
|
|
61
60
|
});
|
|
62
61
|
/** Get the level info for this tile's z index. */
|
|
63
|
-
get level():
|
|
62
|
+
get level(): RasterTilesetLevel;
|
|
64
63
|
/** Get the children of this node.
|
|
65
64
|
*
|
|
66
65
|
* Find all tiles at level this.z + 1 whose spatial extent overlaps this tile.
|
|
67
66
|
*
|
|
68
67
|
* A tileset pyramid is not guaranteed to be a quadtree — it is a stack of
|
|
69
68
|
* independent grids. We find children by mapping the parent tile's CRS bounds
|
|
70
|
-
* into the child grid using {@link
|
|
69
|
+
* into the child grid using {@link RasterTilesetLevel.crsBoundsToTileRange}.
|
|
71
70
|
*/
|
|
72
71
|
get children(): RasterTileNode[] | null;
|
|
73
72
|
/**
|
|
@@ -106,6 +105,14 @@ export declare class RasterTileNode {
|
|
|
106
105
|
* comparison would. See `dev-docs/lod-and-pixel-matching.md` § (A).
|
|
107
106
|
*/
|
|
108
107
|
pixelRatio: number;
|
|
108
|
+
/**
|
|
109
|
+
* Number of world copies to shift this tile's bounding volume by along
|
|
110
|
+
* common-space X for frustum testing. Default `0` (primary world).
|
|
111
|
+
* Non-zero passes are additive — they may set `selected = true` but
|
|
112
|
+
* never override a previous `true` to `false`. See
|
|
113
|
+
* `dev-docs/world-copies.md`.
|
|
114
|
+
*/
|
|
115
|
+
worldOffset?: number;
|
|
109
116
|
/**
|
|
110
117
|
* Bounding-volume cache shared by every node in this traversal. Populated
|
|
111
118
|
* lazily as tiles are visited; reused across `getTileIndices` calls (so
|
|
@@ -138,8 +145,23 @@ export declare class RasterTileNode {
|
|
|
138
145
|
* volume depends only on `(z, x, y, zRange)` for a given descriptor, so on a
|
|
139
146
|
* cache hit it is returned without rerunning {@link computeBoundingVolume}'s
|
|
140
147
|
* proj4 reprojections + oriented-bounding-box fit.
|
|
148
|
+
*
|
|
149
|
+
* For non-zero `worldOffset`, returns a translated copy (center shifted by
|
|
150
|
+
* `worldOffset * TILE_SIZE` along common-space X) without polluting the
|
|
151
|
+
* cache — the cache always stores the offset-0 volume. See
|
|
152
|
+
* `dev-docs/world-copies.md`.
|
|
153
|
+
*
|
|
154
|
+
* @param zRange Elevation `[min, max]` in common-space units.
|
|
155
|
+
* @param project Projection function for Globe view, or `null`
|
|
156
|
+
* for Web Mercator common space.
|
|
157
|
+
* @param boundingVolumeCache Cache keyed by `z/x/y`. Stores the offset-0
|
|
158
|
+
* volume only.
|
|
159
|
+
* @param worldOffset Number of world copies to translate the result
|
|
160
|
+
* by along common-space X. `0` returns the
|
|
161
|
+
* cached offset-0 volume directly. Non-zero
|
|
162
|
+
* values return a fresh translated copy.
|
|
141
163
|
*/
|
|
142
|
-
getBoundingVolume(zRange: ZRange, project: ((xyz: number[]) => number[]) | null, boundingVolumeCache: BoundingVolumeCache): {
|
|
164
|
+
getBoundingVolume(zRange: ZRange, project: ((xyz: number[]) => number[]) | null, boundingVolumeCache: BoundingVolumeCache, worldOffset?: number): {
|
|
143
165
|
boundingVolume: OrientedBoundingBox;
|
|
144
166
|
commonSpaceBounds: Bounds;
|
|
145
167
|
};
|
|
@@ -157,7 +179,40 @@ export declare class RasterTileNode {
|
|
|
157
179
|
*
|
|
158
180
|
*/
|
|
159
181
|
private _getGenericBoundingVolume;
|
|
182
|
+
/**
|
|
183
|
+
* Globe-view bounding volume: reproject the tile's reference points to WGS84,
|
|
184
|
+
* project them onto the globe sphere (`project` = `viewport.projectPosition`)
|
|
185
|
+
* to build the oriented bounding box used for frustum culling, and separately
|
|
186
|
+
* compute a Web-Mercator-world AABB for the `bounds` pre-filter in
|
|
187
|
+
* {@link update} (which compares against `wgs84Bounds` in mercator world).
|
|
188
|
+
*
|
|
189
|
+
* NOTE: elevation is not modeled on globe yet — reference points are sampled
|
|
190
|
+
* at the surface (z = 0). Flat rasters only. See
|
|
191
|
+
* `dev-docs/specs/2026-05-21-globe-view-design.md`.
|
|
192
|
+
*/
|
|
193
|
+
private _getGlobeBoundingVolume;
|
|
160
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Rescale positions from EPSG:3857 into deck.gl's common space
|
|
197
|
+
*
|
|
198
|
+
* Similar to the upstream code here:
|
|
199
|
+
* https://github.com/visgl/deck.gl/blob/b0134f025148b52b91320d16768ab5d14a745328/modules/geo-layers/src/tileset-2d/tile-2d-traversal.ts#L172-L177
|
|
200
|
+
*/
|
|
201
|
+
export declare function rescaleEPSG3857ToCommonSpace([x, y]: [number, number]): [
|
|
202
|
+
number,
|
|
203
|
+
number
|
|
204
|
+
];
|
|
205
|
+
/**
|
|
206
|
+
* Inverse of {@link rescaleEPSG3857ToCommonSpace}: rescale a deck.gl
|
|
207
|
+
* common-space position back into EPSG:3857 meters.
|
|
208
|
+
*
|
|
209
|
+
* Common-space inputs are in-range by construction, so (unlike the forward
|
|
210
|
+
* direction) no latitude clamp is applied.
|
|
211
|
+
*/
|
|
212
|
+
export declare function rescaleCommonSpaceToEPSG3857([x, y]: [number, number]): [
|
|
213
|
+
number,
|
|
214
|
+
number
|
|
215
|
+
];
|
|
161
216
|
/**
|
|
162
217
|
* Build the list of root (z=0) `RasterTileNode`s for the traversal.
|
|
163
218
|
*
|
|
@@ -176,19 +231,19 @@ export declare class RasterTileNode {
|
|
|
176
231
|
* Exported for unit testing.
|
|
177
232
|
*/
|
|
178
233
|
export declare function createRootTiles(opts: {
|
|
179
|
-
descriptor:
|
|
234
|
+
descriptor: RasterTilesetDescriptor;
|
|
180
235
|
viewport: Pick<Viewport, "getBounds">;
|
|
181
236
|
datasetWgs84Bounds: Bounds;
|
|
182
237
|
}): RasterTileNode[];
|
|
183
238
|
/**
|
|
184
239
|
* Get tile indices visible in viewport.
|
|
185
240
|
*
|
|
186
|
-
* Uses frustum culling driven by a {@link
|
|
241
|
+
* Uses frustum culling driven by a {@link RasterTilesetDescriptor}, which abstracts
|
|
187
242
|
* over OGC TileMatrixSet grids and Zarr multiscale pyramids.
|
|
188
243
|
*
|
|
189
244
|
* Overview levels follow the descriptor ordering: index 0 = coarsest, higher = finer.
|
|
190
245
|
*/
|
|
191
|
-
export declare function getTileIndices(descriptor:
|
|
246
|
+
export declare function getTileIndices(descriptor: RasterTilesetDescriptor, opts: {
|
|
192
247
|
viewport: Viewport;
|
|
193
248
|
maxZ: number;
|
|
194
249
|
zRange: ZRange | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"raster-tile-traversal.d.ts","sourceRoot":"","sources":["../../src/raster-tileset/raster-tile-traversal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"raster-tile-traversal.d.ts","sourceRoot":"","sources":["../../src/raster-tileset/raster-tile-traversal.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,12 +13,13 @@
|
|
|
13
13
|
* The result is a set of tiles at varying zoom levels that efficiently
|
|
14
14
|
* cover the visible area with appropriate detail.
|
|
15
15
|
*
|
|
16
|
-
* The traversal is driven by a {@link
|
|
16
|
+
* The traversal is driven by a {@link RasterTilesetDescriptor}, which abstracts over
|
|
17
17
|
* both OGC TileMatrixSet grids and Zarr multiscale pyramids.
|
|
18
18
|
*/
|
|
19
|
-
import { _GlobeViewport
|
|
19
|
+
import { _GlobeViewport as GlobeViewport } from "@deck.gl/core";
|
|
20
20
|
import { transformBounds } from "@developmentseed/proj";
|
|
21
|
-
import {
|
|
21
|
+
import { Vector3 } from "@math.gl/core";
|
|
22
|
+
import { CullingVolume, makeOrientedBoundingBoxFromPoints, OrientedBoundingBox, Plane, } from "@math.gl/culling";
|
|
22
23
|
import { lngLatToWorld, worldToLngLat } from "@math.gl/web-mercator";
|
|
23
24
|
import { BoundingVolumeCache } from "./bounding-volume-cache.js";
|
|
24
25
|
/**
|
|
@@ -33,6 +34,12 @@ import { BoundingVolumeCache } from "./bounding-volume-cache.js";
|
|
|
33
34
|
* bottom-right.
|
|
34
35
|
*/
|
|
35
36
|
const TILE_SIZE = 512;
|
|
37
|
+
/**
|
|
38
|
+
* Maximum number of world copies to test on each side of the primary world
|
|
39
|
+
* during multi-world tile traversal. Matches upstream
|
|
40
|
+
* `@deck.gl/geo-layers/tile-2d-traversal.ts`.
|
|
41
|
+
*/
|
|
42
|
+
const MAX_MAPS = 3;
|
|
36
43
|
// Reference points used to sample tile boundaries for bounding volume
|
|
37
44
|
// calculation.
|
|
38
45
|
//
|
|
@@ -64,6 +71,18 @@ const REF_POINTS_9 = REF_POINTS_5.concat([
|
|
|
64
71
|
[1, 0.5], // right edge
|
|
65
72
|
[0.5, 1], // bottom edge
|
|
66
73
|
]);
|
|
74
|
+
// For the globe bounding volume: REF_POINTS_9 plus two more points on the
|
|
75
|
+
// horizontal centerline (11 points total). The sphere surface bulges most
|
|
76
|
+
// between samples along the widest span of a tile, so denser sampling there
|
|
77
|
+
// keeps the oriented bounding box from under-enclosing the tile (which would
|
|
78
|
+
// false-cull it). This matches upstream deck.gl's densest reference set, used
|
|
79
|
+
// there only for the coarsest (whole-world) zoom. We use it for every globe
|
|
80
|
+
// tile: a tile never spans more than the whole world, so 11 points always
|
|
81
|
+
// suffice, and per-tile cost is paid once thanks to the bounding-volume cache.
|
|
82
|
+
const REF_POINTS_11 = REF_POINTS_9.concat([
|
|
83
|
+
[0.25, 0.5],
|
|
84
|
+
[0.75, 0.5],
|
|
85
|
+
]);
|
|
67
86
|
/** semi-major axis of the WGS84 ellipsoid
|
|
68
87
|
*
|
|
69
88
|
* EPSG:3857 also uses the WGS84 datum, so this is used for conversions from
|
|
@@ -84,8 +103,8 @@ const MAX_WEB_MERCATOR_LAT = 85.05112877980659;
|
|
|
84
103
|
*
|
|
85
104
|
* This node class uses the following coordinate system:
|
|
86
105
|
*
|
|
87
|
-
* - x: tile column (0 to
|
|
88
|
-
* - y: tile row (0 to
|
|
106
|
+
* - x: tile column (0 to RasterTilesetLevel.matrixWidth, left to right)
|
|
107
|
+
* - y: tile row (0 to RasterTilesetLevel.matrixHeight, top to bottom)
|
|
89
108
|
* - z: overview level. This assumes ordering where: 0 = coarsest, higher = finer
|
|
90
109
|
*/
|
|
91
110
|
export class RasterTileNode {
|
|
@@ -127,7 +146,7 @@ export class RasterTileNode {
|
|
|
127
146
|
*
|
|
128
147
|
* A tileset pyramid is not guaranteed to be a quadtree — it is a stack of
|
|
129
148
|
* independent grids. We find children by mapping the parent tile's CRS bounds
|
|
130
|
-
* into the child grid using {@link
|
|
149
|
+
* into the child grid using {@link RasterTilesetLevel.crsBoundsToTileRange}.
|
|
131
150
|
*/
|
|
132
151
|
get children() {
|
|
133
152
|
if (!this._children) {
|
|
@@ -174,16 +193,29 @@ export class RasterTileNode {
|
|
|
174
193
|
* @returns true if this tile or any descendant is visible, false otherwise
|
|
175
194
|
*/
|
|
176
195
|
update(params) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
196
|
+
const { viewport, cullingVolume, elevationBounds, minZ, maxZ = this.descriptor.levels.length - 1, project, bounds, pixelRatio, worldOffset = 0, boundingVolumeCache, } = params;
|
|
197
|
+
// Reset per-frame state on the primary pass only. Non-zero worldOffset
|
|
198
|
+
// passes are additive — they can flip selected/childVisible from
|
|
199
|
+
// false → true but never the reverse. See dev-docs/world-copies.md.
|
|
200
|
+
if (worldOffset === 0) {
|
|
201
|
+
this.childVisible = false;
|
|
202
|
+
this.selected = false;
|
|
203
|
+
}
|
|
204
|
+
// Get bounding volume for this tile (translated for frustum culling at
|
|
205
|
+
// non-zero worldOffset). `commonSpaceBounds` is the Web-Mercator-world AABB
|
|
206
|
+
// used for the LOD latitude (a worldOffset only shifts X, so latitude is
|
|
207
|
+
// unaffected).
|
|
208
|
+
const { boundingVolume, commonSpaceBounds } = this.getBoundingVolume(elevationBounds, project, boundingVolumeCache, worldOffset);
|
|
183
209
|
// Step 1: Bounds checking
|
|
184
|
-
// If geographic bounds are specified, reject tiles outside those bounds
|
|
185
|
-
|
|
186
|
-
|
|
210
|
+
// If geographic bounds are specified, reject tiles outside those bounds.
|
|
211
|
+
// The dataset's `bounds` live in primary-world common space, and a tile
|
|
212
|
+
// at `(x, y, z)` represents the same data regardless of which world copy
|
|
213
|
+
// it's drawn in — so always compare against the offset-0 AABB.
|
|
214
|
+
if (bounds) {
|
|
215
|
+
const primaryWorldVolume = this.getBoundingVolume(elevationBounds, project, boundingVolumeCache, 0);
|
|
216
|
+
if (!this.insideBounds(bounds, primaryWorldVolume.commonSpaceBounds)) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
187
219
|
}
|
|
188
220
|
// Frustum culling
|
|
189
221
|
// Test if tile's bounding volume intersects the camera frustum
|
|
@@ -197,7 +229,7 @@ export class RasterTileNode {
|
|
|
197
229
|
// Only select this tile if no child is visible (prevents overlapping tiles)
|
|
198
230
|
// "When pitch is low, force selection at maxZ."
|
|
199
231
|
if (!this.childVisible && this.z >= minZ) {
|
|
200
|
-
const metersPerCSSPixel =
|
|
232
|
+
const metersPerCSSPixel = getMetersPerPixelAtCommonSpaceBounds(commonSpaceBounds, viewport.zoom);
|
|
201
233
|
const tileMetersPerPixel = this.level.metersPerPixel;
|
|
202
234
|
// On-screen size of one source pixel, measured in device pixels.
|
|
203
235
|
// ≤ 1 means the source can fully resolve the rendered framebuffer.
|
|
@@ -215,14 +247,22 @@ export class RasterTileNode {
|
|
|
215
247
|
// Note that if `this.children` is `null`, then there are no children
|
|
216
248
|
// available because we're already at the finest tile resolution available
|
|
217
249
|
if (children && children.length > 0) {
|
|
218
|
-
|
|
250
|
+
if (worldOffset === 0) {
|
|
251
|
+
this.selected = false;
|
|
252
|
+
}
|
|
219
253
|
let anyChildVisible = false;
|
|
220
254
|
for (const child of children) {
|
|
221
255
|
if (child.update(params)) {
|
|
222
256
|
anyChildVisible = true;
|
|
223
257
|
}
|
|
224
258
|
}
|
|
225
|
-
|
|
259
|
+
// Only set childVisible to true; never override a previous true to
|
|
260
|
+
// false on a subsequent pass. Offset-0 already starts with
|
|
261
|
+
// childVisible=false (reset above), so this preserves the
|
|
262
|
+
// "any pass that finds a visible child wins" semantics.
|
|
263
|
+
if (anyChildVisible) {
|
|
264
|
+
this.childVisible = true;
|
|
265
|
+
}
|
|
226
266
|
return anyChildVisible;
|
|
227
267
|
}
|
|
228
268
|
return true;
|
|
@@ -266,15 +306,41 @@ export class RasterTileNode {
|
|
|
266
306
|
* volume depends only on `(z, x, y, zRange)` for a given descriptor, so on a
|
|
267
307
|
* cache hit it is returned without rerunning {@link computeBoundingVolume}'s
|
|
268
308
|
* proj4 reprojections + oriented-bounding-box fit.
|
|
309
|
+
*
|
|
310
|
+
* For non-zero `worldOffset`, returns a translated copy (center shifted by
|
|
311
|
+
* `worldOffset * TILE_SIZE` along common-space X) without polluting the
|
|
312
|
+
* cache — the cache always stores the offset-0 volume. See
|
|
313
|
+
* `dev-docs/world-copies.md`.
|
|
314
|
+
*
|
|
315
|
+
* @param zRange Elevation `[min, max]` in common-space units.
|
|
316
|
+
* @param project Projection function for Globe view, or `null`
|
|
317
|
+
* for Web Mercator common space.
|
|
318
|
+
* @param boundingVolumeCache Cache keyed by `z/x/y`. Stores the offset-0
|
|
319
|
+
* volume only.
|
|
320
|
+
* @param worldOffset Number of world copies to translate the result
|
|
321
|
+
* by along common-space X. `0` returns the
|
|
322
|
+
* cached offset-0 volume directly. Non-zero
|
|
323
|
+
* values return a fresh translated copy.
|
|
269
324
|
*/
|
|
270
|
-
getBoundingVolume(zRange, project, boundingVolumeCache) {
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
325
|
+
getBoundingVolume(zRange, project, boundingVolumeCache, worldOffset = 0) {
|
|
326
|
+
const cacheHit = boundingVolumeCache.get(this.z, this.x, this.y);
|
|
327
|
+
// `base` is the tile's volume in the primary world (offset 0). The cache
|
|
328
|
+
// only ever stores the primary-world volume; it is returned as-is for
|
|
329
|
+
// worldOffset 0, or translated below for a non-zero offset.
|
|
330
|
+
let base;
|
|
331
|
+
if (cacheHit &&
|
|
332
|
+
cacheHit.zRange[0] === zRange[0] &&
|
|
333
|
+
cacheHit.zRange[1] === zRange[1]) {
|
|
334
|
+
base = cacheHit;
|
|
274
335
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
336
|
+
else {
|
|
337
|
+
base = this.computeBoundingVolume(zRange, project);
|
|
338
|
+
boundingVolumeCache.set(this.z, this.x, this.y, { zRange, ...base });
|
|
339
|
+
}
|
|
340
|
+
if (worldOffset === 0) {
|
|
341
|
+
return base;
|
|
342
|
+
}
|
|
343
|
+
return translateBoundingVolume(base, worldOffset * TILE_SIZE);
|
|
278
344
|
}
|
|
279
345
|
/**
|
|
280
346
|
* Compute (without caching) the 3D bounding volume for this tile in deck.gl's
|
|
@@ -284,12 +350,10 @@ export class RasterTileNode {
|
|
|
284
350
|
* tiling is already in EPSG:3857.
|
|
285
351
|
*/
|
|
286
352
|
computeBoundingVolume(zRange, project) {
|
|
287
|
-
// Case 1: Globe view
|
|
288
|
-
//
|
|
353
|
+
// Case 1: Globe view — reproject sample points to WGS84 and project them
|
|
354
|
+
// onto the globe sphere with the viewport's `project` function.
|
|
289
355
|
if (project) {
|
|
290
|
-
|
|
291
|
-
// Reproject positions to wgs84 instead, then pass them into `project`
|
|
292
|
-
// return makeOrientedBoundingBoxFromPoints(refPointPositions);
|
|
356
|
+
return this._getGlobeBoundingVolume(project);
|
|
293
357
|
}
|
|
294
358
|
// (Future) Case 2: Web Mercator input image, can directly compute AABB in
|
|
295
359
|
// common space
|
|
@@ -344,6 +408,47 @@ export class RasterTileNode {
|
|
|
344
408
|
commonSpaceBounds,
|
|
345
409
|
};
|
|
346
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* Globe-view bounding volume: reproject the tile's reference points to WGS84,
|
|
413
|
+
* project them onto the globe sphere (`project` = `viewport.projectPosition`)
|
|
414
|
+
* to build the oriented bounding box used for frustum culling, and separately
|
|
415
|
+
* compute a Web-Mercator-world AABB for the `bounds` pre-filter in
|
|
416
|
+
* {@link update} (which compares against `wgs84Bounds` in mercator world).
|
|
417
|
+
*
|
|
418
|
+
* NOTE: elevation is not modeled on globe yet — reference points are sampled
|
|
419
|
+
* at the surface (z = 0). Flat rasters only. See
|
|
420
|
+
* `dev-docs/specs/2026-05-21-globe-view-design.md`.
|
|
421
|
+
*/
|
|
422
|
+
_getGlobeBoundingVolume(project) {
|
|
423
|
+
const tileCorners = this.level.projectedTileCorners(this.x, this.y);
|
|
424
|
+
const refPointsWgs84 = sampleReferencePointsInWGS84(REF_POINTS_11, tileCorners, this.descriptor.projectTo4326);
|
|
425
|
+
const refPointPositions = [];
|
|
426
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
427
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
428
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
429
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
430
|
+
for (const [lng, lat] of refPointsWgs84) {
|
|
431
|
+
const projected = project([lng, lat, 0]);
|
|
432
|
+
refPointPositions.push([projected[0], projected[1], projected[2]]);
|
|
433
|
+
const [worldX, worldY] = lngLatToWorld([lng, lat]);
|
|
434
|
+
if (worldX < minX) {
|
|
435
|
+
minX = worldX;
|
|
436
|
+
}
|
|
437
|
+
if (worldY < minY) {
|
|
438
|
+
minY = worldY;
|
|
439
|
+
}
|
|
440
|
+
if (worldX > maxX) {
|
|
441
|
+
maxX = worldX;
|
|
442
|
+
}
|
|
443
|
+
if (worldY > maxY) {
|
|
444
|
+
maxY = worldY;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return {
|
|
448
|
+
boundingVolume: makeOrientedBoundingBoxFromPoints(refPointPositions),
|
|
449
|
+
commonSpaceBounds: [minX, minY, maxX, maxY],
|
|
450
|
+
};
|
|
451
|
+
}
|
|
347
452
|
}
|
|
348
453
|
/**
|
|
349
454
|
* Wrap a forward projection to EPSG:3857 so that it never returns NaN.
|
|
@@ -403,13 +508,31 @@ function sampleReferencePointsInEPSG3857(refPoints, tileCorners, projectTo3857,
|
|
|
403
508
|
}
|
|
404
509
|
return refPointPositions;
|
|
405
510
|
}
|
|
511
|
+
/**
|
|
512
|
+
* Sample the selected reference points in WGS84 lng/lat.
|
|
513
|
+
*
|
|
514
|
+
* Like {@link sampleReferencePointsInEPSG3857}, reference points are `[relX,
|
|
515
|
+
* relY]` fractions in `[0, 1]` bilinearly interpolated across the tile's four
|
|
516
|
+
* CRS corners, then reprojected to WGS84. Used by the GlobeView bounding-volume
|
|
517
|
+
* path, which projects lng/lat onto the sphere rather than rescaling 3857
|
|
518
|
+
* meters into common space.
|
|
519
|
+
*/
|
|
520
|
+
function sampleReferencePointsInWGS84(refPoints, tileCorners, projectTo4326) {
|
|
521
|
+
const { topLeft, topRight, bottomLeft, bottomRight } = tileCorners;
|
|
522
|
+
const refPointPositions = [];
|
|
523
|
+
for (const [relX, relY] of refPoints) {
|
|
524
|
+
const [geoX, geoY] = bilerpPoint(topLeft, topRight, bottomLeft, bottomRight, relX, relY);
|
|
525
|
+
refPointPositions.push(projectTo4326(geoX, geoY));
|
|
526
|
+
}
|
|
527
|
+
return refPointPositions;
|
|
528
|
+
}
|
|
406
529
|
/**
|
|
407
530
|
* Rescale positions from EPSG:3857 into deck.gl's common space
|
|
408
531
|
*
|
|
409
532
|
* Similar to the upstream code here:
|
|
410
533
|
* https://github.com/visgl/deck.gl/blob/b0134f025148b52b91320d16768ab5d14a745328/modules/geo-layers/src/tileset-2d/tile-2d-traversal.ts#L172-L177
|
|
411
534
|
*/
|
|
412
|
-
function rescaleEPSG3857ToCommonSpace([x, y]) {
|
|
535
|
+
export function rescaleEPSG3857ToCommonSpace([x, y]) {
|
|
413
536
|
// Clamp Y to Web Mercator bounds
|
|
414
537
|
const clampedY = Math.max(-EPSG_3857_HALF_CIRCUMFERENCE, Math.min(EPSG_3857_HALF_CIRCUMFERENCE, y));
|
|
415
538
|
return [
|
|
@@ -417,6 +540,19 @@ function rescaleEPSG3857ToCommonSpace([x, y]) {
|
|
|
417
540
|
(clampedY / EPSG_3857_CIRCUMFERENCE + 0.5) * TILE_SIZE,
|
|
418
541
|
];
|
|
419
542
|
}
|
|
543
|
+
/**
|
|
544
|
+
* Inverse of {@link rescaleEPSG3857ToCommonSpace}: rescale a deck.gl
|
|
545
|
+
* common-space position back into EPSG:3857 meters.
|
|
546
|
+
*
|
|
547
|
+
* Common-space inputs are in-range by construction, so (unlike the forward
|
|
548
|
+
* direction) no latitude clamp is applied.
|
|
549
|
+
*/
|
|
550
|
+
export function rescaleCommonSpaceToEPSG3857([x, y]) {
|
|
551
|
+
return [
|
|
552
|
+
(x / TILE_SIZE - 0.5) * EPSG_3857_CIRCUMFERENCE,
|
|
553
|
+
(y / TILE_SIZE - 0.5) * EPSG_3857_CIRCUMFERENCE,
|
|
554
|
+
];
|
|
555
|
+
}
|
|
420
556
|
/**
|
|
421
557
|
* Above this root-tile count, `createRootTiles` culls to the viewport
|
|
422
558
|
* before instantiation. Below it, every root tile is created and downstream
|
|
@@ -482,7 +618,7 @@ export function createRootTiles(opts) {
|
|
|
482
618
|
/**
|
|
483
619
|
* Get tile indices visible in viewport.
|
|
484
620
|
*
|
|
485
|
-
* Uses frustum culling driven by a {@link
|
|
621
|
+
* Uses frustum culling driven by a {@link RasterTilesetDescriptor}, which abstracts
|
|
486
622
|
* over OGC TileMatrixSet grids and Zarr multiscale pyramids.
|
|
487
623
|
*
|
|
488
624
|
* Overview levels follow the descriptor ordering: index 0 = coarsest, higher = finer.
|
|
@@ -497,7 +633,7 @@ export function getTileIndices(descriptor, opts) {
|
|
|
497
633
|
// so this frame can never evict an entry it will need again this frame.
|
|
498
634
|
boundingVolumeCache.sweep();
|
|
499
635
|
// Only define `project` function for Globe viewports, same as upstream
|
|
500
|
-
const project = viewport instanceof
|
|
636
|
+
const project = viewport instanceof GlobeViewport && viewport.resolution
|
|
501
637
|
? viewport.projectPosition
|
|
502
638
|
: null;
|
|
503
639
|
// Get the culling volume of the current camera
|
|
@@ -562,6 +698,24 @@ export function getTileIndices(descriptor, opts) {
|
|
|
562
698
|
for (const root of roots) {
|
|
563
699
|
root.update(traversalParams);
|
|
564
700
|
}
|
|
701
|
+
// World-copy passes: when the viewport spans multiple world copies (e.g.
|
|
702
|
+
// WebMercatorViewport with repeat: true panned across the antimeridian),
|
|
703
|
+
// re-run the traversal with the tile bounding volumes shifted by ±1, ±2…
|
|
704
|
+
// world copies along common-space X. A tile is selected if any pass selects
|
|
705
|
+
// it. See dev-docs/world-copies.md.
|
|
706
|
+
const subViewportCount = viewport.subViewports?.length ?? 0;
|
|
707
|
+
if (subViewportCount > 1) {
|
|
708
|
+
for (let offset = -1; offset >= -MAX_MAPS; offset--) {
|
|
709
|
+
if (!runOffsetPass(roots, traversalParams, offset)) {
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
for (let offset = 1; offset <= MAX_MAPS; offset++) {
|
|
714
|
+
if (!runOffsetPass(roots, traversalParams, offset)) {
|
|
715
|
+
break;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
565
719
|
// Collect selected tiles
|
|
566
720
|
const selectedNodes = [];
|
|
567
721
|
for (const root of roots) {
|
|
@@ -569,6 +723,23 @@ export function getTileIndices(descriptor, opts) {
|
|
|
569
723
|
}
|
|
570
724
|
return selectedNodes;
|
|
571
725
|
}
|
|
726
|
+
/**
|
|
727
|
+
* Run a non-zero world-offset traversal pass over each root.
|
|
728
|
+
*
|
|
729
|
+
* Returns `true` if any root tile was visible at this offset, signaling the
|
|
730
|
+
* caller to walk further from the primary world. Returns `false` when no
|
|
731
|
+
* tiles were visible — the offset has gone past the visible range and the
|
|
732
|
+
* caller stops walking that side.
|
|
733
|
+
*/
|
|
734
|
+
function runOffsetPass(roots, baseParams, worldOffset) {
|
|
735
|
+
let anyVisible = false;
|
|
736
|
+
for (const root of roots) {
|
|
737
|
+
if (root.update({ ...baseParams, worldOffset })) {
|
|
738
|
+
anyVisible = true;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return anyVisible;
|
|
742
|
+
}
|
|
572
743
|
/**
|
|
573
744
|
* Compute the meters per pixel at a given latitude and zoom level.
|
|
574
745
|
*
|
|
@@ -583,10 +754,44 @@ function getMetersPerPixel(latitude, zoom) {
|
|
|
583
754
|
return ((earthCircumference * Math.cos((latitude * Math.PI) / 180)) /
|
|
584
755
|
2 ** (zoom + 8));
|
|
585
756
|
}
|
|
586
|
-
function
|
|
587
|
-
const [
|
|
757
|
+
function getMetersPerPixelAtCommonSpaceBounds(commonSpaceBounds, zoom) {
|
|
758
|
+
const [minX, minY, maxX, maxY] = commonSpaceBounds;
|
|
759
|
+
// `commonSpaceBounds` is in Web Mercator world space ([0, 512]) in BOTH the
|
|
760
|
+
// mercator and globe paths (the globe path builds it via `lngLatToWorld`), so
|
|
761
|
+
// its center maps back to a real latitude. The 3D oriented-bounding-box
|
|
762
|
+
// center, by contrast, is in globe common space on a globe and would
|
|
763
|
+
// `worldToLngLat` to a garbage latitude (~-89°, near the Mercator
|
|
764
|
+
// singularity), making meters-per-pixel far too small so the LOD always
|
|
765
|
+
// recursed to the finest level.
|
|
766
|
+
const [, lat] = worldToLngLat([(minX + maxX) / 2, (minY + maxY) / 2]);
|
|
588
767
|
return getMetersPerPixel(lat, zoom);
|
|
589
768
|
}
|
|
769
|
+
/**
|
|
770
|
+
* Translate a tile's bounding volume by `dx` units along common-space X.
|
|
771
|
+
*
|
|
772
|
+
* Returns a fresh OBB and AABB; does not mutate the input. Used by the
|
|
773
|
+
* world-copy traversal to test the same tile at multiple shifted positions
|
|
774
|
+
* without recomputing the underlying geometry.
|
|
775
|
+
*/
|
|
776
|
+
function translateBoundingVolume(base, dx) {
|
|
777
|
+
const { boundingVolume, commonSpaceBounds } = base;
|
|
778
|
+
const center = boundingVolume.center;
|
|
779
|
+
const translatedCenter = new Vector3((center[0] ?? 0) + dx, center[1] ?? 0, center[2] ?? 0);
|
|
780
|
+
const translated = new OrientedBoundingBox(translatedCenter, boundingVolume.halfAxes);
|
|
781
|
+
// `update()`'s bounds check always re-reads the offset-0 `commonSpaceBounds`,
|
|
782
|
+
// so this translated AABB isn't consumed in production — it's kept for API
|
|
783
|
+
// symmetry with `boundingVolume` and is asserted directly by unit tests.
|
|
784
|
+
const translatedBounds = [
|
|
785
|
+
commonSpaceBounds[0] + dx,
|
|
786
|
+
commonSpaceBounds[1],
|
|
787
|
+
commonSpaceBounds[2] + dx,
|
|
788
|
+
commonSpaceBounds[3],
|
|
789
|
+
];
|
|
790
|
+
return {
|
|
791
|
+
boundingVolume: translated,
|
|
792
|
+
commonSpaceBounds: translatedBounds,
|
|
793
|
+
};
|
|
794
|
+
}
|
|
590
795
|
/**
|
|
591
796
|
* Compute the axis-aligned bounding box of a rotated tile rectangle.
|
|
592
797
|
*/
|