Skip to content

Commit a43d396

Browse files
authored
refactor(deck.gl-raster)!: Finish generalizing tile traversal (#394)
* Move morecantile to peer dep * Make RasterTileset2D fully generic * update cog-layer * fix tests
1 parent b33a1ae commit a43d396

File tree

9 files changed

+51
-81
lines changed

9 files changed

+51
-81
lines changed

packages/deck.gl-geotiff/src/cog-layer.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import type {
2020
} from "@developmentseed/deck.gl-raster";
2121
import {
2222
RasterLayer,
23-
TileMatrixSetTileset,
23+
RasterTileset2D,
24+
TileMatrixSetAdaptor,
2425
} from "@developmentseed/deck.gl-raster";
2526
import type { DecoderPool, GeoTIFF, Overview } from "@developmentseed/geotiff";
2627
import {
@@ -532,12 +533,15 @@ export class COGLayer<
532533
geotiff: GeoTIFF,
533534
): TileLayer {
534535
// Create a factory class that wraps COGTileset2D with the metadata
535-
class TileMatrixSetTilesetFactory extends TileMatrixSetTileset {
536+
class TileMatrixSetTilesetFactory extends RasterTileset2D {
536537
constructor(opts: Tileset2DProps) {
537-
super(opts, tms, {
538+
const descriptor = new TileMatrixSetAdaptor(tms, {
538539
projectTo4326: forwardTo4326,
539540
projectTo3857: forwardTo3857,
540541
});
542+
super(opts, descriptor, {
543+
projectTo4326: forwardTo4326,
544+
});
541545
}
542546
}
543547

packages/deck.gl-raster/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"vitest": "^4.0.18"
5353
},
5454
"peerDependencies": {
55+
"@developmentseed/morecantile": "workspace:^",
5556
"@deck.gl/core": "^9.2.7",
5657
"@deck.gl/geo-layers": "^9.2.7",
5758
"@deck.gl/layers": "^9.2.7",
@@ -61,7 +62,6 @@
6162
},
6263
"dependencies": {
6364
"@developmentseed/affine": "workspace:^",
64-
"@developmentseed/morecantile": "workspace:^",
6565
"@developmentseed/proj": "workspace:^",
6666
"@developmentseed/raster-reproject": "workspace:^",
6767
"@math.gl/core": "^4.1.0",

packages/deck.gl-raster/src/index.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ export type { RasterModule } from "./gpu-modules/types.js";
22
export type { RasterLayerProps, RenderTileResult } from "./raster-layer.js";
33
export { RasterLayer } from "./raster-layer.js";
44
export type { TileMetadata } from "./raster-tileset/index.js";
5-
export { TileMatrixSetTileset } from "./raster-tileset/index.js";
6-
7-
import { __TEST_EXPORTS as traversalTestExports } from "./raster-tileset/raster-tile-traversal.js";
8-
9-
export const __TEST_EXPORTS = { ...traversalTestExports };
5+
export {
6+
RasterTileset2D,
7+
TileMatrixSetAdaptor,
8+
} from "./raster-tileset/index.js";
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export type { TileMetadata } from "./raster-tileset-2d.js";
2-
export { TileMatrixSetTileset } from "./raster-tileset-2d.js";
2+
export { RasterTileset2D } from "./raster-tileset-2d.js";
3+
export { TileMatrixSetAdaptor } from "./tile-matrix-set.js";

packages/deck.gl-raster/src/raster-tileset/raster-tile-traversal.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -767,10 +767,3 @@ function bilerpPoint(
767767
p00[1] * w00 + p10[1] * w10 + p01[1] * w01 + p11[1] * w11,
768768
];
769769
}
770-
771-
/**
772-
* Exports only for use in testing
773-
*/
774-
export const __TEST_EXPORTS = {
775-
RasterTileNode,
776-
};

packages/deck.gl-raster/src/raster-tileset/raster-tileset-2d.ts

Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/**
2-
* TileMatrixSetTileset - Improved Implementation with Frustum Culling
2+
* RasterTileset2D - Generic tile traversal over a tile pyramid with Frustum
3+
* Culling
34
*
45
* This version properly implements frustum culling and bounding volume calculations
56
* following the pattern from deck.gl's OSM tile indexing.
@@ -11,12 +12,10 @@ import type {
1112
_Tileset2DProps as Tileset2DProps,
1213
} from "@deck.gl/geo-layers";
1314
import { _Tileset2D as Tileset2D } from "@deck.gl/geo-layers";
14-
import type { TileMatrixSet } from "@developmentseed/morecantile";
1515
import { transformBounds } from "@developmentseed/proj";
1616
import type { Matrix4 } from "@math.gl/core";
1717
import { getTileIndices } from "./raster-tile-traversal";
1818
import type { TilesetDescriptor } from "./tileset-interface";
19-
import { TileMatrixSetAdaptor } from "./tms-interface";
2019
import type {
2120
Bounds,
2221
Corners,
@@ -49,15 +48,11 @@ export type TileMetadata = {
4948

5049
/**
5150
* Tile width in pixels.
52-
*
53-
* Note this may differ between levels in some TileMatrixSets.
5451
*/
5552
tileWidth: number;
5653

5754
/**
5855
* Tile height in pixels.
59-
*
60-
* Note this may differ between levels in some TileMatrixSets.
6156
*/
6257
tileHeight: number;
6358
};
@@ -69,41 +64,27 @@ export type TileMetadata = {
6964
*
7065
* Handles tile lifecycle, caching, and viewport-based loading.
7166
*/
72-
export class TileMatrixSetTileset extends Tileset2D {
73-
private tms: TileMatrixSet;
67+
export class RasterTileset2D extends Tileset2D {
68+
private descriptor: TilesetDescriptor;
7469
private wgs84Bounds: Bounds;
7570
private projectTo4326: ProjectionFunction;
76-
private tilesetDescriptor: TilesetDescriptor;
7771

7872
constructor(
7973
opts: Tileset2DProps,
80-
tms: TileMatrixSet,
74+
descriptor: TilesetDescriptor,
8175
{
8276
projectTo4326,
83-
projectTo3857,
8477
}: {
8578
projectTo4326: ProjectionFunction;
86-
projectTo3857: ProjectionFunction;
8779
},
8880
) {
8981
super(opts);
90-
this.tms = tms;
82+
this.descriptor = descriptor;
9183
this.projectTo4326 = projectTo4326;
9284

93-
if (!tms.boundingBox) {
94-
throw new Error(
95-
"Bounding Box inference not yet implemented; should be provided on TileMatrixSet",
96-
);
97-
}
98-
99-
this.tilesetDescriptor = new TileMatrixSetAdaptor(tms, {
100-
projectTo3857,
101-
projectTo4326,
102-
});
103-
10485
this.wgs84Bounds = transformBounds(
10586
projectTo4326,
106-
...this.tilesetDescriptor.projectedBounds,
87+
...this.descriptor.projectedBounds,
10788
);
10889
}
10990

@@ -121,14 +102,14 @@ export class TileMatrixSetTileset extends Tileset2D {
121102
modelMatrix?: Matrix4;
122103
modelMatrixInverse?: Matrix4;
123104
}): TileIndex[] {
124-
const maxAvailableZ = this.tms.tileMatrices.length - 1;
105+
const maxAvailableZ = this.descriptor.levels.length - 1;
125106

126107
const maxZ =
127108
typeof opts.maxZoom === "number"
128109
? Math.min(opts.maxZoom, maxAvailableZ)
129110
: maxAvailableZ;
130111

131-
const tileIndices = getTileIndices(this.tilesetDescriptor, {
112+
const tileIndices = getTileIndices(this.descriptor, {
132113
viewport: opts.viewport,
133114
maxZ,
134115
zRange: opts.zRange ?? null,
@@ -148,21 +129,22 @@ export class TileMatrixSetTileset extends Tileset2D {
148129
return index;
149130
}
150131

151-
const currentOverview = this.tms.tileMatrices[index.z]!;
152-
const parentOverview = this.tms.tileMatrices[index.z - 1]!;
132+
const currentOverview = this.descriptor.levels[index.z]!;
133+
const parentOverview = this.descriptor.levels[index.z - 1]!;
153134

154135
// Decimation is the number of child tiles that fit across one parent tile.
155136
// Must use tile footprint (cellSize × tileWidth/Height), not cellSize alone,
156137
// because tileWidth can change between levels (e.g. the last Sentinel-2
157138
// overview doubles tileWidth while halving cellSize, giving a 1:1 spatial
158139
// mapping where decimation = 1).
159-
const parentFootprintX = parentOverview.cellSize * parentOverview.tileWidth;
140+
const parentFootprintX =
141+
parentOverview.metersPerPixel * parentOverview.tileWidth;
160142
const parentFootprintY =
161-
parentOverview.cellSize * parentOverview.tileHeight;
143+
parentOverview.metersPerPixel * parentOverview.tileHeight;
162144
const currentFootprintX =
163-
currentOverview.cellSize * currentOverview.tileWidth;
145+
currentOverview.metersPerPixel * currentOverview.tileWidth;
164146
const currentFootprintY =
165-
currentOverview.cellSize * currentOverview.tileHeight;
147+
currentOverview.metersPerPixel * currentOverview.tileHeight;
166148

167149
const decimationX = parentFootprintX / currentFootprintX;
168150
const decimationY = parentFootprintY / currentFootprintY;
@@ -180,7 +162,7 @@ export class TileMatrixSetTileset extends Tileset2D {
180162

181163
override getTileMetadata(index: TileIndex): TileMetadata {
182164
const { x, y, z } = index;
183-
const levelDescriptor = this.tilesetDescriptor.levels[z]!;
165+
const levelDescriptor = this.descriptor.levels[z]!;
184166
const { tileHeight, tileWidth } = levelDescriptor;
185167
const { topLeft, topRight, bottomLeft, bottomRight } =
186168
levelDescriptor.projectedTileCorners(x, y);

packages/deck.gl-raster/src/raster-tileset/tms-interface.ts renamed to packages/deck.gl-raster/src/raster-tileset/tile-matrix-set.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ class TileMatrixAdaptor implements TilesetLevel {
123123
}
124124
}
125125

126+
/**
127+
* An adapter interface to use a TileMatrixSet as a TilesetDescriptor for raster
128+
* tile traversal.
129+
*/
126130
export class TileMatrixSetAdaptor implements TilesetDescriptor {
127131
tms: TileMatrixSet;
128132
private _levels: TileMatrixAdaptor[];

packages/deck.gl-raster/src/raster-tileset/types.ts

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,10 @@ export type ProjectedBoundingBox = {
2525
bottom: number;
2626
};
2727

28-
export type TileBoundingBox = ProjectedBoundingBox | GeoBoundingBox;
29-
30-
export type TileLoadProps = {
31-
index: TileIndex;
32-
id: string;
33-
bbox: TileBoundingBox;
34-
url?: string | null;
35-
signal?: AbortSignal;
36-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
37-
userData?: Record<string, any>;
38-
zoom?: number;
39-
};
40-
28+
/** A 2D point represented as [x, y] */
4129
export type Point = [number, number];
4230

43-
type CRS = any;
44-
31+
/** A function that projects coordinates from one CRS to another */
4532
export type ProjectionFunction = (x: number, y: number) => Point;
4633

4734
/**
@@ -52,13 +39,6 @@ export type CornerBounds = {
5239
upperRight: Point;
5340
};
5441

55-
/**
56-
* Minimum bounding rectangle surrounding a 2D resource in the CRS indicated elsewhere
57-
*/
58-
export type TileMatrixSetBoundingBox = CornerBounds & {
59-
crs?: CRS;
60-
};
61-
6242
/**
6343
* Raster Tile Index
6444
*

packages/deck.gl-raster/tests/tileset-refinement.test.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import type { Viewport } from "@deck.gl/core";
99
import type { _Tileset2DProps as Tileset2DProps } from "@deck.gl/geo-layers";
1010
import type { TileMatrixSet } from "@developmentseed/morecantile";
1111
import { describe, expect, it } from "vitest";
12-
import { TileMatrixSetTileset } from "../src/raster-tileset/raster-tileset-2d.js";
12+
import { RasterTileset2D } from "../src/raster-tileset/raster-tileset-2d.js";
13+
import { TileMatrixSetAdaptor } from "../src/raster-tileset/tile-matrix-set.js";
1314
import type { TileIndex } from "../src/raster-tileset/types.js";
1415

1516
// ---------------------------------------------------------------------------
@@ -62,15 +63,15 @@ const identity = (x: number, y: number): [number, number] => [x, y];
6263
// "visible" at each simulated zoom level without needing a real Viewport.
6364
// ---------------------------------------------------------------------------
6465

65-
class ControlledTileset extends TileMatrixSetTileset {
66+
class ControlledTileset extends RasterTileset2D {
6667
private _forcedIndices: TileIndex[] = [];
6768

6869
setForcedIndices(indices: TileIndex[]) {
6970
this._forcedIndices = indices;
7071
}
7172

7273
override getTileIndices(
73-
_opts: Parameters<TileMatrixSetTileset["getTileIndices"]>[0],
74+
_opts: Parameters<RasterTileset2D["getTileIndices"]>[0],
7475
): TileIndex[] {
7576
return this._forcedIndices;
7677
}
@@ -101,8 +102,11 @@ function makeTileset(opts?: Partial<Tileset2DProps>): ControlledTileset {
101102
getTileData: () => new Promise(() => {}), // never resolves
102103
...opts,
103104
},
104-
MOCK_TMS,
105-
{ projectTo4326: identity, projectTo3857: identity },
105+
new TileMatrixSetAdaptor(MOCK_TMS, {
106+
projectTo4326: identity,
107+
projectTo3857: identity,
108+
}),
109+
{ projectTo4326: identity },
106110
);
107111
}
108112

@@ -274,8 +278,11 @@ describe("TileMatrixSetTileset – best-available refinement", () => {
274278

275279
const tileset = new ControlledTileset(
276280
{ getTileData: () => new Promise(() => {}) },
277-
sentinel2TMS as any,
278-
{ projectTo4326: identity, projectTo3857: identity },
281+
new TileMatrixSetAdaptor(sentinel2TMS, {
282+
projectTo4326: identity,
283+
projectTo3857: identity,
284+
}),
285+
{ projectTo4326: identity },
279286
);
280287

281288
// Every z=4 tile should map 1:1 to the z=3 tile at the same x,y.

0 commit comments

Comments
 (0)