@developmentseed/deck.gl-raster 0.5.0-beta.1 → 0.5.0

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.
Files changed (64) hide show
  1. package/dist/gpu-modules/composite-bands.d.ts +85 -0
  2. package/dist/gpu-modules/composite-bands.d.ts.map +1 -0
  3. package/dist/gpu-modules/composite-bands.js +141 -0
  4. package/dist/gpu-modules/composite-bands.js.map +1 -0
  5. package/dist/gpu-modules/cutline-bbox.d.ts +70 -0
  6. package/dist/gpu-modules/cutline-bbox.d.ts.map +1 -0
  7. package/dist/gpu-modules/cutline-bbox.js +100 -0
  8. package/dist/gpu-modules/cutline-bbox.js.map +1 -0
  9. package/dist/gpu-modules/index.d.ts +6 -0
  10. package/dist/gpu-modules/index.d.ts.map +1 -1
  11. package/dist/gpu-modules/index.js +3 -0
  12. package/dist/gpu-modules/index.js.map +1 -1
  13. package/dist/gpu-modules/linear-rescale.d.ts +39 -0
  14. package/dist/gpu-modules/linear-rescale.d.ts.map +1 -0
  15. package/dist/gpu-modules/linear-rescale.js +40 -0
  16. package/dist/gpu-modules/linear-rescale.js.map +1 -0
  17. package/dist/gpu-modules/types.d.ts +10 -1
  18. package/dist/gpu-modules/types.d.ts.map +1 -1
  19. package/dist/index.d.ts +5 -9
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +4 -3
  22. package/dist/index.js.map +1 -1
  23. package/dist/layer-utils.d.ts +28 -0
  24. package/dist/layer-utils.d.ts.map +1 -0
  25. package/dist/layer-utils.js +56 -0
  26. package/dist/layer-utils.js.map +1 -0
  27. package/dist/mesh-layer/mesh-layer.d.ts.map +1 -1
  28. package/dist/mesh-layer/mesh-layer.js +10 -1
  29. package/dist/mesh-layer/mesh-layer.js.map +1 -1
  30. package/dist/multi-raster-tileset/index.d.ts +5 -0
  31. package/dist/multi-raster-tileset/index.d.ts.map +1 -0
  32. package/dist/multi-raster-tileset/index.js +3 -0
  33. package/dist/multi-raster-tileset/index.js.map +1 -0
  34. package/dist/multi-raster-tileset/multi-tileset-descriptor.d.ts +75 -0
  35. package/dist/multi-raster-tileset/multi-tileset-descriptor.d.ts.map +1 -0
  36. package/dist/multi-raster-tileset/multi-tileset-descriptor.js +97 -0
  37. package/dist/multi-raster-tileset/multi-tileset-descriptor.js.map +1 -0
  38. package/dist/multi-raster-tileset/secondary-tile-resolver.d.ts +129 -0
  39. package/dist/multi-raster-tileset/secondary-tile-resolver.d.ts.map +1 -0
  40. package/dist/multi-raster-tileset/secondary-tile-resolver.js +88 -0
  41. package/dist/multi-raster-tileset/secondary-tile-resolver.js.map +1 -0
  42. package/dist/raster-tileset/index.d.ts +4 -1
  43. package/dist/raster-tileset/index.d.ts.map +1 -1
  44. package/dist/raster-tileset/index.js +2 -1
  45. package/dist/raster-tileset/index.js.map +1 -1
  46. package/dist/raster-tileset/raster-tile-traversal.d.ts +29 -51
  47. package/dist/raster-tileset/raster-tile-traversal.d.ts.map +1 -1
  48. package/dist/raster-tileset/raster-tile-traversal.js +113 -171
  49. package/dist/raster-tileset/raster-tile-traversal.js.map +1 -1
  50. package/dist/raster-tileset/raster-tileset-2d.d.ts +8 -22
  51. package/dist/raster-tileset/raster-tileset-2d.d.ts.map +1 -1
  52. package/dist/raster-tileset/raster-tileset-2d.js +18 -81
  53. package/dist/raster-tileset/raster-tileset-2d.js.map +1 -1
  54. package/dist/raster-tileset/tile-matrix-set.d.ts +20 -0
  55. package/dist/raster-tileset/tile-matrix-set.d.ts.map +1 -0
  56. package/dist/raster-tileset/tile-matrix-set.js +121 -0
  57. package/dist/raster-tileset/tile-matrix-set.js.map +1 -0
  58. package/dist/raster-tileset/tileset-interface.d.ts +78 -0
  59. package/dist/raster-tileset/tileset-interface.d.ts.map +1 -0
  60. package/dist/raster-tileset/tileset-interface.js +2 -0
  61. package/dist/raster-tileset/tileset-interface.js.map +1 -0
  62. package/dist/raster-tileset/types.d.ts +10 -18
  63. package/dist/raster-tileset/types.d.ts.map +1 -1
  64. package/package.json +14 -14
@@ -1,6 +1,5 @@
1
1
  /**
2
- * This file implements tile traversal for generic 2D tilesets defined by
3
- * TileMatrixSet tile layouts.
2
+ * This file implements tile traversal for generic 2D tilesets.
4
3
  *
5
4
  * The main algorithm works as follows:
6
5
  *
@@ -8,26 +7,29 @@
8
7
  * necessarily the whole world)
9
8
  * 2. Test if each tile is visible using viewport frustum culling
10
9
  * 3. For visible tiles, compute distance-based LOD (Level of Detail)
11
- * 4. If LOD is insufficient, recursively subdivide into 4 child tiles
10
+ * 4. If LOD is insufficient, recursively subdivide into child tiles
12
11
  * 5. Select tiles at appropriate zoom levels based on distance from camera
13
12
  *
14
13
  * The result is a set of tiles at varying zoom levels that efficiently
15
14
  * cover the visible area with appropriate detail.
15
+ *
16
+ * The traversal is driven by a {@link TilesetDescriptor}, which abstracts over
17
+ * both OGC TileMatrixSet grids and Zarr multiscale pyramids.
16
18
  */
17
19
  import type { Viewport } from "@deck.gl/core";
18
- import type { TileMatrix, TileMatrixSet } from "@developmentseed/morecantile";
19
20
  import type { OrientedBoundingBox } from "@math.gl/culling";
20
21
  import { CullingVolume } from "@math.gl/culling";
21
- import type { Bounds, CornerBounds, ProjectionFunction, TileIndex, ZRange } from "./types.js";
22
+ import type { TilesetDescriptor, TilesetLevel } from "./tileset-interface.js";
23
+ import type { Bounds, TileIndex, ZRange } from "./types.js";
22
24
  /**
23
- * Raster Tile Node - represents a single tile in the TileMatrixSet structure
25
+ * Raster Tile Node - represents a single tile in a tileset pyramid.
24
26
  *
25
27
  * Akin to the upstream OSMNode class.
26
28
  *
27
29
  * This node class uses the following coordinate system:
28
30
  *
29
- * - x: tile column (0 to TileMatrix.matrixWidth, left to right)
30
- * - y: tile row (0 to TileMatrix.matrixHeight, top to bottom)
31
+ * - x: tile column (0 to TilesetLevel.matrixWidth, left to right)
32
+ * - y: tile row (0 to TilesetLevel.matrixHeight, top to bottom)
31
33
  * - z: overview level. This assumes ordering where: 0 = coarsest, higher = finer
32
34
  */
33
35
  export declare class RasterTileNode {
@@ -37,7 +39,7 @@ export declare class RasterTileNode {
37
39
  y: number;
38
40
  /** Zoom index assumed to be (higher = finer detail) */
39
41
  z: number;
40
- private metadata;
42
+ private descriptor;
41
43
  /**
42
44
  * Flag indicating whether any descendant of this tile is visible.
43
45
  *
@@ -53,35 +55,32 @@ export declare class RasterTileNode {
53
55
  private selected?;
54
56
  /** A cache of the children of this node. */
55
57
  private _children?;
56
- private projectTo3857;
57
- private projectTo4326;
58
58
  /**
59
59
  * A cached bounding volume for this tile, used for frustum culling
60
60
  *
61
61
  * This stores the result of `getBoundingVolume`.
62
62
  */
63
63
  private _boundingVolume?;
64
- constructor(x: number, y: number, z: number, { metadata, projectTo3857, projectTo4326, }: {
65
- metadata: TileMatrixSet;
66
- projectTo3857: ProjectionFunction;
67
- projectTo4326: ProjectionFunction;
64
+ constructor(x: number, y: number, z: number, { descriptor }: {
65
+ descriptor: TilesetDescriptor;
68
66
  });
69
- /** Get overview info for this tile's z level */
70
- get tileMatrix(): TileMatrix;
67
+ /** Get the level info for this tile's z index. */
68
+ get level(): TilesetLevel;
71
69
  /** Get the children of this node.
72
70
  *
73
71
  * Find all tiles at level this.z + 1 whose spatial extent overlaps this tile.
74
72
  *
75
- * A TileMatrixSet is not a quadtree, but rather a stack of independent grids. We can't cleanly find child tiles by decimation directly.
76
- *
73
+ * A tileset pyramid is not guaranteed to be a quadtree it is a stack of
74
+ * independent grids. We find children by mapping the parent tile's CRS bounds
75
+ * into the child grid using {@link TilesetLevel.crsBoundsToTileRange}.
77
76
  */
78
77
  get children(): RasterTileNode[] | null;
79
78
  /**
80
79
  * Recursively traverse the tile pyramid to determine if this tile (or its
81
80
  * descendants) should be rendered.
82
81
  *
83
- * I.e. Given this tile node, should I render this tile, or should I recurse
84
- * into its children?”
82
+ * I.e. "Given this tile node, should I render this tile, or should I recurse
83
+ * into its children?"
85
84
  *
86
85
  * The algorithm performs:
87
86
  * 1. Visibility culling - reject tiles outside the view frustum
@@ -99,9 +98,9 @@ export declare class RasterTileNode {
99
98
  project: ((xyz: number[]) => number[]) | null;
100
99
  cullingVolume: CullingVolume;
101
100
  elevationBounds: ZRange;
102
- /** Minimum (coarsest) COG overview level */
101
+ /** Minimum (coarsest) overview level */
103
102
  minZ: number;
104
- /** Maximum (finest) COG overview level */
103
+ /** Maximum (finest) overview level */
105
104
  maxZ?: number;
106
105
  /** Optional geographic bounds filter */
107
106
  bounds?: Bounds;
@@ -111,7 +110,7 @@ export declare class RasterTileNode {
111
110
  * Recursively traverses the entire tree and gathers tiles where selected=true.
112
111
  *
113
112
  * @param result - Accumulator array for selected tiles
114
- * @returns Array of selected OSMNode tiles
113
+ * @returns Array of selected RasterTileNode tiles
115
114
  */
116
115
  getSelected(result?: RasterTileNode[]): RasterTileNode[];
117
116
  /**
@@ -141,38 +140,17 @@ export declare class RasterTileNode {
141
140
  private _getGenericBoundingVolume;
142
141
  }
143
142
  /**
144
- * Compute the projected tile bounds in the tile matrix's CRS.
143
+ * Get tile indices visible in viewport.
145
144
  *
146
- * Because it's a linear transformation from the tile index to projected bounds,
147
- * we don't need to sample this for each of the reference points. We only need
148
- * the corners.
145
+ * Uses frustum culling driven by a {@link TilesetDescriptor}, which abstracts
146
+ * over OGC TileMatrixSet grids and Zarr multiscale pyramids.
149
147
  *
150
- * @return The bounding box as [minX, minY, maxX, maxY] in projected CRS.
148
+ * Overview levels follow the descriptor ordering: index 0 = coarsest, higher = finer.
151
149
  */
152
- declare function computeProjectedTileBounds(tileMatrix: TileMatrix, { x, y, }: {
153
- x: number;
154
- y: number;
155
- }): [number, number, number, number];
156
- /**
157
- * Get tile indices visible in viewport
158
- * Uses frustum culling similar to OSM implementation
159
- *
160
- * Overviews follow TileMatrixSet ordering: index 0 = coarsest, higher = finer
161
- */
162
- export declare function getTileIndices(metadata: TileMatrixSet, opts: {
150
+ export declare function getTileIndices(descriptor: TilesetDescriptor, opts: {
163
151
  viewport: Viewport;
164
152
  maxZ: number;
165
153
  zRange: ZRange | null;
166
- projectTo3857: ProjectionFunction;
167
- projectTo4326: ProjectionFunction;
168
- wgs84Bounds: CornerBounds;
154
+ wgs84Bounds: Bounds;
169
155
  }): TileIndex[];
170
- /**
171
- * Exports only for use in testing
172
- */
173
- export declare const __TEST_EXPORTS: {
174
- computeProjectedTileBounds: typeof computeProjectedTileBounds;
175
- RasterTileNode: typeof RasterTileNode;
176
- };
177
- export {};
178
156
  //# sourceMappingURL=raster-tile-traversal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"raster-tile-traversal.d.ts","sourceRoot":"","sources":["../../src/raster-tileset/raster-tile-traversal.ts"],"names":[],"mappings":"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"}
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;AAE9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EACL,aAAa,EAGd,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,KAAK,EACV,MAAM,EAIN,SAAS,EACT,MAAM,EACP,MAAM,YAAY,CAAC;AAiEpB;;;;;;;;;;GAUG;AACH,qBAAa,cAAc;IACzB,yBAAyB;IACzB,CAAC,EAAE,MAAM,CAAC;IAEV,0BAA0B;IAC1B,CAAC,EAAE,MAAM,CAAC;IAEV,uDAAuD;IACvD,CAAC,EAAE,MAAM,CAAC;IAEV,OAAO,CAAC,UAAU,CAAoB;IAEtC;;;;;OAKG;IACH,OAAO,CAAC,YAAY,CAAC,CAAU;IAE/B;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,CAAU;IAE3B,4CAA4C;IAC5C,OAAO,CAAC,SAAS,CAAC,CAA0B;IAE5C;;;;OAIG;IACH,OAAO,CAAC,eAAe,CAAC,CAItB;gBAGA,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,EAAE,UAAU,EAAE,EAAE;QAAE,UAAU,EAAE,iBAAiB,CAAA;KAAE;IAQnD,kDAAkD;IAClD,IAAI,KAAK,IAAI,YAAY,CAExB;IAED;;;;;;;OAOG;IACH,IAAI,QAAQ,IAAI,cAAc,EAAE,GAAG,IAAI,CA+BtC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CAAC,MAAM,EAAE;QACb,QAAQ,EAAE,QAAQ,CAAC;QAEnB,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;QAE9C,aAAa,EAAE,aAAa,CAAC;QAE7B,eAAe,EAAE,MAAM,CAAC;QACxB,wCAAwC;QACxC,IAAI,EAAE,MAAM,CAAC;QACb,sCAAsC;QACtC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,wCAAwC;QACxC,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO;IAiFX;;;;;;OAMG;IACH,WAAW,CAAC,MAAM,GAAE,cAAc,EAAO,GAAG,cAAc,EAAE;IAY5D;;;;;;OAMG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,OAAO;IAUhE;;;;;;OAMG;IACH,iBAAiB,CACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC,GAAG,IAAI,GAC5C;QAAE,cAAc,EAAE,mBAAmB,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE;IAgCrE;;;;OAIG;IACH,OAAO,CAAC,yBAAyB;CAiDlC;AA4GD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,iBAAiB,EAC7B,IAAI,EAAE;IACJ,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB,GACA,SAAS,EAAE,CA4Fb"}
@@ -1,6 +1,5 @@
1
1
  /**
2
- * This file implements tile traversal for generic 2D tilesets defined by
3
- * TileMatrixSet tile layouts.
2
+ * This file implements tile traversal for generic 2D tilesets.
4
3
  *
5
4
  * The main algorithm works as follows:
6
5
  *
@@ -8,14 +7,16 @@
8
7
  * necessarily the whole world)
9
8
  * 2. Test if each tile is visible using viewport frustum culling
10
9
  * 3. For visible tiles, compute distance-based LOD (Level of Detail)
11
- * 4. If LOD is insufficient, recursively subdivide into 4 child tiles
10
+ * 4. If LOD is insufficient, recursively subdivide into child tiles
12
11
  * 5. Select tiles at appropriate zoom levels based on distance from camera
13
12
  *
14
13
  * The result is a set of tiles at varying zoom levels that efficiently
15
14
  * cover the visible area with appropriate detail.
15
+ *
16
+ * The traversal is driven by a {@link TilesetDescriptor}, which abstracts over
17
+ * both OGC TileMatrixSet grids and Zarr multiscale pyramids.
16
18
  */
17
19
  import { _GlobeViewport, assert } from "@deck.gl/core";
18
- import { xy_bounds } from "@developmentseed/morecantile";
19
20
  import { CullingVolume, makeOrientedBoundingBoxFromPoints, Plane, } from "@math.gl/culling";
20
21
  import { lngLatToWorld, worldToLngLat } from "@math.gl/web-mercator";
21
22
  /**
@@ -74,18 +75,15 @@ const EPSG_3857_CIRCUMFERENCE = 2 * Math.PI * WGS84_ELLIPSOID_A;
74
75
  const EPSG_3857_HALF_CIRCUMFERENCE = EPSG_3857_CIRCUMFERENCE / 2;
75
76
  // Maximum latitude representable in Web Mercator (EPSG:3857), in degrees.
76
77
  const MAX_WEB_MERCATOR_LAT = 85.05112877980659;
77
- // 0.28 mm per pixel
78
- // https://docs.ogc.org/is/17-083r4/17-083r4.html#toc15
79
- const SCREEN_PIXEL_SIZE = 0.00028;
80
78
  /**
81
- * Raster Tile Node - represents a single tile in the TileMatrixSet structure
79
+ * Raster Tile Node - represents a single tile in a tileset pyramid.
82
80
  *
83
81
  * Akin to the upstream OSMNode class.
84
82
  *
85
83
  * This node class uses the following coordinate system:
86
84
  *
87
- * - x: tile column (0 to TileMatrix.matrixWidth, left to right)
88
- * - y: tile row (0 to TileMatrix.matrixHeight, top to bottom)
85
+ * - x: tile column (0 to TilesetLevel.matrixWidth, left to right)
86
+ * - y: tile row (0 to TilesetLevel.matrixHeight, top to bottom)
89
87
  * - z: overview level. This assumes ordering where: 0 = coarsest, higher = finer
90
88
  */
91
89
  export class RasterTileNode {
@@ -95,7 +93,7 @@ export class RasterTileNode {
95
93
  y;
96
94
  /** Zoom index assumed to be (higher = finer detail) */
97
95
  z;
98
- metadata;
96
+ descriptor;
99
97
  /**
100
98
  * Flag indicating whether any descendant of this tile is visible.
101
99
  *
@@ -111,61 +109,50 @@ export class RasterTileNode {
111
109
  selected;
112
110
  /** A cache of the children of this node. */
113
111
  _children;
114
- projectTo3857;
115
- projectTo4326;
116
112
  /**
117
113
  * A cached bounding volume for this tile, used for frustum culling
118
114
  *
119
115
  * This stores the result of `getBoundingVolume`.
120
116
  */
121
117
  _boundingVolume;
122
- constructor(x, y, z, { metadata, projectTo3857, projectTo4326, }) {
118
+ constructor(x, y, z, { descriptor }) {
123
119
  this.x = x;
124
120
  this.y = y;
125
121
  this.z = z;
126
- this.metadata = metadata;
127
- this.projectTo3857 = projectTo3857;
128
- this.projectTo4326 = projectTo4326;
122
+ this.descriptor = descriptor;
129
123
  }
130
- /** Get overview info for this tile's z level */
131
- get tileMatrix() {
132
- return this.metadata.tileMatrices[this.z];
124
+ /** Get the level info for this tile's z index. */
125
+ get level() {
126
+ return this.descriptor.levels[this.z];
133
127
  }
134
128
  /** Get the children of this node.
135
129
  *
136
130
  * Find all tiles at level this.z + 1 whose spatial extent overlaps this tile.
137
131
  *
138
- * A TileMatrixSet is not a quadtree, but rather a stack of independent grids. We can't cleanly find child tiles by decimation directly.
139
- *
132
+ * A tileset pyramid is not guaranteed to be a quadtree it is a stack of
133
+ * independent grids. We find children by mapping the parent tile's CRS bounds
134
+ * into the child grid using {@link TilesetLevel.crsBoundsToTileRange}.
140
135
  */
141
136
  get children() {
142
137
  if (!this._children) {
143
- const maxZ = this.metadata.tileMatrices.length - 1;
138
+ const maxZ = this.descriptor.levels.length - 1;
144
139
  if (this.z >= maxZ) {
145
140
  // Already at finest resolution, no children
146
141
  this._children = null;
147
142
  return null;
148
143
  }
149
- // In TileMatrixSet ordering: refine to z + 1 (finer detail)
150
- const parentMatrix = this.tileMatrix;
151
144
  const childZ = this.z + 1;
152
- const childMatrix = this.metadata.tileMatrices[childZ];
153
- // Compute this tile's bounds in TMS' CRS
154
- const parentBounds = computeProjectedTileBounds(parentMatrix, {
155
- x: this.x,
156
- y: this.y,
157
- });
145
+ const childLevel = this.descriptor.levels[childZ];
146
+ // Compute this tile's bounds in the source CRS
147
+ const parentCorners = this.level.projectedTileCorners(this.x, this.y);
148
+ const parentBounds = cornersToBounds(parentCorners);
158
149
  // Find overlapping child index range
159
- const { minCol, maxCol, minRow, maxRow } = getOverlappingChildRange(parentBounds, childMatrix);
150
+ const { minCol, maxCol, minRow, maxRow } = childLevel.crsBoundsToTileRange(...parentBounds);
160
151
  const children = [];
161
- const { metadata, projectTo3857, projectTo4326 } = this;
152
+ const { descriptor } = this;
162
153
  for (let y = minRow; y <= maxRow; y++) {
163
154
  for (let x = minCol; x <= maxCol; x++) {
164
- children.push(new RasterTileNode(x, y, childZ, {
165
- metadata,
166
- projectTo3857,
167
- projectTo4326,
168
- }));
155
+ children.push(new RasterTileNode(x, y, childZ, { descriptor }));
169
156
  }
170
157
  }
171
158
  this._children = children.length > 0 ? children : null;
@@ -176,8 +163,8 @@ export class RasterTileNode {
176
163
  * Recursively traverse the tile pyramid to determine if this tile (or its
177
164
  * descendants) should be rendered.
178
165
  *
179
- * I.e. Given this tile node, should I render this tile, or should I recurse
180
- * into its children?”
166
+ * I.e. "Given this tile node, should I render this tile, or should I recurse
167
+ * into its children?"
181
168
  *
182
169
  * The algorithm performs:
183
170
  * 1. Visibility culling - reject tiles outside the view frustum
@@ -194,7 +181,7 @@ export class RasterTileNode {
194
181
  // Reset state
195
182
  this.childVisible = false;
196
183
  this.selected = false;
197
- const { viewport, cullingVolume, elevationBounds, minZ, maxZ = this.metadata.tileMatrices.length - 1, project, bounds, } = params;
184
+ const { viewport, cullingVolume, elevationBounds, minZ, maxZ = this.descriptor.levels.length - 1, project, bounds, } = params;
198
185
  // Get bounding volume for this tile
199
186
  const { boundingVolume, commonSpaceBounds } = this.getBoundingVolume(elevationBounds, project);
200
187
  // Step 1: Bounds checking
@@ -212,29 +199,14 @@ export class RasterTileNode {
212
199
  const children = this.children;
213
200
  // LOD (Level of Detail) selection (only if allowed at this level)
214
201
  // Only select this tile if no child is visible (prevents overlapping tiles)
215
- // When pitch is low, force selection at maxZ.”
202
+ // "When pitch is low, force selection at maxZ."
216
203
  if (!this.childVisible && this.z >= minZ) {
217
204
  const metersPerScreenPixel = getMetersPerPixelAtBoundingVolume(boundingVolume, viewport.zoom);
218
- // console.log("metersPerScreenPixel", metersPerScreenPixel);
219
- const tileMetersPerPixel = this.tileMatrix.scaleDenominator * SCREEN_PIXEL_SIZE;
220
- // console.log("tileMetersPerPixel", tileMetersPerPixel);
221
- // const screenScaleDenominator = metersPerScreenPixel / SCREEN_PIXEL_SIZE;
222
- // console.log("screenScaleDenominator", screenScaleDenominator);
223
- // TODO: in the future we could try adding a bias
224
- // const LOD_BIAS = 0.75;
225
- // this.tileMatrix.scaleDenominator <= screenScaleDenominator * LOD_BIAS
226
- // console.log(
227
- // "this.tileMatrix.scaleDenominator",
228
- // this.tileMatrix.scaleDenominator,
229
- // );
230
- // console.log(
231
- // "tileMetersPerPixel <= metersPerScreenPixel",
232
- // tileMetersPerPixel <= metersPerScreenPixel,
233
- // );
205
+ const tileMetersPerPixel = this.level.metersPerPixel;
234
206
  if (tileMetersPerPixel <= metersPerScreenPixel ||
235
207
  this.z >= maxZ ||
236
208
  (children === null && this.z >= minZ)) {
237
- // Select this tile when its scale is at least as detailed as the screen.”
209
+ // "Select this tile when its scale is at least as detailed as the screen."
238
210
  this.selected = true;
239
211
  return true;
240
212
  }
@@ -261,7 +233,7 @@ export class RasterTileNode {
261
233
  * Recursively traverses the entire tree and gathers tiles where selected=true.
262
234
  *
263
235
  * @param result - Accumulator array for selected tiles
264
- * @returns Array of selected OSMNode tiles
236
+ * @returns Array of selected RasterTileNode tiles
265
237
  */
266
238
  getSelected(result = []) {
267
239
  if (this.selected) {
@@ -325,13 +297,9 @@ export class RasterTileNode {
325
297
  *
326
298
  */
327
299
  _getGenericBoundingVolume(zRange) {
328
- const tileMatrix = this.tileMatrix;
329
300
  const [minZ, maxZ] = zRange;
330
- const tileCrsBounds = computeProjectedTileBounds(tileMatrix, {
331
- x: this.x,
332
- y: this.y,
333
- });
334
- const refPointsEPSG3857 = sampleReferencePointsInEPSG3857(REF_POINTS_9, tileCrsBounds, this.projectTo3857, this.projectTo4326);
301
+ const tileCorners = this.level.projectedTileCorners(this.x, this.y);
302
+ const refPointsEPSG3857 = sampleReferencePointsInEPSG3857(REF_POINTS_9, tileCorners, this.descriptor.projectTo3857, this.descriptor.projectTo4326);
335
303
  const commonSpacePositions = refPointsEPSG3857.map((xy) => rescaleEPSG3857ToCommonSpace(xy));
336
304
  const refPointPositions = [];
337
305
  for (const p of commonSpacePositions) {
@@ -364,24 +332,6 @@ export class RasterTileNode {
364
332
  };
365
333
  }
366
334
  }
367
- /**
368
- * Compute the projected tile bounds in the tile matrix's CRS.
369
- *
370
- * Because it's a linear transformation from the tile index to projected bounds,
371
- * we don't need to sample this for each of the reference points. We only need
372
- * the corners.
373
- *
374
- * @return The bounding box as [minX, minY, maxX, maxY] in projected CRS.
375
- */
376
- function computeProjectedTileBounds(tileMatrix, { x, y, }) {
377
- const bounds = xy_bounds(tileMatrix, { x, y });
378
- return [
379
- bounds.lowerLeft[0],
380
- bounds.lowerLeft[1],
381
- bounds.upperRight[0],
382
- bounds.upperRight[1],
383
- ];
384
- }
385
335
  /**
386
336
  * Wrap a forward projection to EPSG:3857 so that it never returns NaN.
387
337
  *
@@ -417,22 +367,25 @@ function makeClampedForwardTo3857(projectTo3857, projectTo4326) {
417
367
  };
418
368
  }
419
369
  /**
420
- * Sample the selected reference points in EPSG:3857
370
+ * Sample the selected reference points in EPSG:3857.
371
+ *
372
+ * Reference points are given as `[relX, relY]` fractions in `[0, 1]` and are
373
+ * bilinearly interpolated across the tile's four CRS corners. For axis-aligned
374
+ * tiles this is equivalent to the old AABB lerp; for rotated tiles it correctly
375
+ * samples the actual quadrilateral rather than its bounding box.
421
376
  *
422
- * Note that EPSG:3857 is **not** the same as deck.gl's common space! deck.gl's
423
- * common space is the size of `TILE_SIZE` (512) units, while EPSG:3857 uses
424
- * meters.
377
+ * Note that EPSG:3857 is **not** the same as deck.gl's common space deck.gl's
378
+ * common space is 512 units wide, while EPSG:3857 uses meters.
425
379
  *
426
- * @param refPoints selected reference points. Each coordinate should be in [0-1]
427
- * @param tileBounds the bounds of the tile in **tile CRS** [minX, minY, maxX, maxY]
380
+ * @param refPoints Reference points as `[relX, relY]` fractions in `[0, 1]`.
381
+ * @param tileCorners The four CRS corners of the tile.
428
382
  */
429
- function sampleReferencePointsInEPSG3857(refPoints, tileBounds, projectTo3857, projectTo4326) {
430
- const [minX, minY, maxX, maxY] = tileBounds;
383
+ function sampleReferencePointsInEPSG3857(refPoints, tileCorners, projectTo3857, projectTo4326) {
384
+ const { topLeft, topRight, bottomLeft, bottomRight } = tileCorners;
431
385
  const clampedProjectTo3857 = makeClampedForwardTo3857(projectTo3857, projectTo4326);
432
386
  const refPointPositions = [];
433
387
  for (const [relX, relY] of refPoints) {
434
- const geoX = minX + relX * (maxX - minX);
435
- const geoY = minY + relY * (maxY - minY);
388
+ const [geoX, geoY] = bilerpPoint(topLeft, topRight, bottomLeft, bottomRight, relX, relY);
436
389
  refPointPositions.push(clampedProjectTo3857(geoX, geoY));
437
390
  }
438
391
  return refPointPositions;
@@ -442,10 +395,6 @@ function sampleReferencePointsInEPSG3857(refPoints, tileBounds, projectTo3857, p
442
395
  *
443
396
  * Similar to the upstream code here:
444
397
  * https://github.com/visgl/deck.gl/blob/b0134f025148b52b91320d16768ab5d14a745328/modules/geo-layers/src/tileset-2d/tile-2d-traversal.ts#L172-L177
445
- *
446
- * @param {number[]} xy [xy description]
447
- *
448
- * @return {number} [return description]
449
398
  */
450
399
  function rescaleEPSG3857ToCommonSpace([x, y]) {
451
400
  // Clamp Y to Web Mercator bounds
@@ -456,63 +405,14 @@ function rescaleEPSG3857ToCommonSpace([x, y]) {
456
405
  ];
457
406
  }
458
407
  /**
459
- * Compute the range of tile indices in a child TileMatrix that spatially
460
- * overlap a parent tile.
461
- *
462
- * TileMatrixSets are not guaranteed to form a strict quadtree: successive
463
- * TileMatrix levels may differ by non-integer refinement ratios and may not
464
- * align perfectly in tile space. As a result, parent/child relationships
465
- * cannot be inferred from zoom level or resolution alone.
466
- *
467
- * This function determines parent→child relationships by:
468
- * 1. Treating each TileMatrix as an independent, axis-aligned grid in CRS space
469
- * 2. Mapping the parent tile's CRS bounding box into the child grid
470
- * 3. Returning the inclusive range of child tile indices whose spatial extent
471
- * intersects the parent tile
472
- *
473
- * The returned indices are clamped to the valid extents of the child matrix
474
- * (`[0, matrixWidth)` and `[0, matrixHeight)`).
475
- *
476
- * Assumptions:
477
- * - The TileMatrix grid is axis-aligned in CRS space
478
- * - `cornerOfOrigin` is `"topLeft"`
479
- * - Tiles are rectangular and uniformly sized within a TileMatrix
408
+ * Get tile indices visible in viewport.
480
409
  *
481
- * @param parentBounds Bounding box of the parent tile in CRS coordinates
482
- * as `[minX, minY, maxX, maxY]`
483
- * @param childMatrix The TileMatrix definition for the child zoom level
410
+ * Uses frustum culling driven by a {@link TilesetDescriptor}, which abstracts
411
+ * over OGC TileMatrixSet grids and Zarr multiscale pyramids.
484
412
  *
485
- * @returns An object containing inclusive index ranges:
486
- * `{ minCol, maxCol, minRow, maxRow }`, identifying all child tiles
487
- * that spatially overlap the parent tile
413
+ * Overview levels follow the descriptor ordering: index 0 = coarsest, higher = finer.
488
414
  */
489
- function getOverlappingChildRange(parentBounds, childMatrix) {
490
- const [pMinX, pMinY, pMaxX, pMaxY] = parentBounds;
491
- const { tileWidth, tileHeight, cellSize, matrixWidth, matrixHeight, pointOfOrigin, } = childMatrix;
492
- const childTileWidthCRS = tileWidth * cellSize;
493
- const childTileHeightCRS = tileHeight * cellSize;
494
- // Note: we assume top left origin
495
- const originX = pointOfOrigin[0];
496
- const originY = pointOfOrigin[1];
497
- // Convert CRS bounds → tile indices
498
- let minCol = Math.floor((pMinX - originX) / childTileWidthCRS);
499
- let maxCol = Math.floor((pMaxX - originX) / childTileWidthCRS);
500
- let minRow = Math.floor((originY - pMaxY) / childTileHeightCRS);
501
- let maxRow = Math.floor((originY - pMinY) / childTileHeightCRS);
502
- // Clamp to matrix bounds
503
- minCol = Math.max(0, Math.min(matrixWidth - 1, minCol));
504
- maxCol = Math.max(0, Math.min(matrixWidth - 1, maxCol));
505
- minRow = Math.max(0, Math.min(matrixHeight - 1, minRow));
506
- maxRow = Math.max(0, Math.min(matrixHeight - 1, maxRow));
507
- return { minCol, maxCol, minRow, maxRow };
508
- }
509
- /**
510
- * Get tile indices visible in viewport
511
- * Uses frustum culling similar to OSM implementation
512
- *
513
- * Overviews follow TileMatrixSet ordering: index 0 = coarsest, higher = finer
514
- */
515
- export function getTileIndices(metadata, opts) {
415
+ export function getTileIndices(descriptor, opts) {
516
416
  const { viewport, maxZ, zRange, wgs84Bounds } = opts;
517
417
  // Only define `project` function for Globe viewports, same as upstream
518
418
  const project = viewport instanceof _GlobeViewport && viewport.resolution
@@ -551,9 +451,7 @@ export function getTileIndices(metadata, opts) {
551
451
  // In our case we want LOD to be evaluated at **all** levels, so we set the
552
452
  // minZ to 0
553
453
  const minZ = 0;
554
- const { lowerLeft, upperRight } = wgs84Bounds;
555
- const [minLng, minLat] = lowerLeft;
556
- const [maxLng, maxLat] = upperRight;
454
+ const [minLng, minLat, maxLng, maxLat] = wgs84Bounds;
557
455
  const bottomLeft = lngLatToWorld([minLng, minLat]);
558
456
  const topRight = lngLatToWorld([maxLng, maxLat]);
559
457
  const bounds = [
@@ -562,19 +460,15 @@ export function getTileIndices(metadata, opts) {
562
460
  topRight[0],
563
461
  topRight[1],
564
462
  ];
565
- // Start from coarsest overview
566
- const rootMatrix = metadata.tileMatrices[0];
567
- // Create root tiles at coarsest level
463
+ // Start from coarsest level
464
+ const rootLevel = descriptor.levels[0];
465
+ // Create root tiles at coarsest level.
568
466
  // In contrary to OSM tiling, we might have more than one tile at the
569
- // coarsest level (z=0)
467
+ // coarsest level (z=0).
570
468
  const roots = [];
571
- for (let y = 0; y < rootMatrix.matrixHeight; y++) {
572
- for (let x = 0; x < rootMatrix.matrixWidth; x++) {
573
- roots.push(new RasterTileNode(x, y, 0, {
574
- metadata,
575
- projectTo3857: opts.projectTo3857,
576
- projectTo4326: opts.projectTo4326,
577
- }));
469
+ for (let y = 0; y < rootLevel.matrixHeight; y++) {
470
+ for (let x = 0; x < rootLevel.matrixWidth; x++) {
471
+ roots.push(new RasterTileNode(x, y, 0, { descriptor }));
578
472
  }
579
473
  }
580
474
  // Traverse and update visibility
@@ -616,10 +510,58 @@ function getMetersPerPixelAtBoundingVolume(boundingVolume, zoom) {
616
510
  return getMetersPerPixel(lat, zoom);
617
511
  }
618
512
  /**
619
- * Exports only for use in testing
513
+ * Compute the axis-aligned bounding box of a rotated tile rectangle.
514
+ */
515
+ function cornersToBounds({ topLeft, topRight, bottomLeft, bottomRight, }) {
516
+ const xs = [topLeft[0], topRight[0], bottomLeft[0], bottomRight[0]];
517
+ const ys = [topLeft[1], topRight[1], bottomLeft[1], bottomRight[1]];
518
+ return [Math.min(...xs), Math.min(...ys), Math.max(...xs), Math.max(...ys)];
519
+ }
520
+ /**
521
+ * Bilinearly interpolate a 2D point over a unit square.
522
+ *
523
+ * Given four corner points of a quadrilateral, this evaluates the bilinear
524
+ * interpolation at normalized coordinates `(x, y)` ∈ [0, 1]². The mapping is:
525
+ *
526
+ * p(x, y) =
527
+ * p00 * (1 - x) * (1 - y) +
528
+ * p10 * x * (1 - y) +
529
+ * p01 * (1 - x) * y +
530
+ * p11 * x * y
531
+ *
532
+ * where:
533
+ * - `p00` corresponds to (x=0, y=0) (top-left)
534
+ * - `p10` corresponds to (x=1, y=0) (top-right)
535
+ * - `p01` corresponds to (x=0, y=1) (bottom-left)
536
+ * - `p11` corresponds to (x=1, y=1) (bottom-right)
537
+ *
538
+ * This performs interpolation in Euclidean space (component-wise on x/y),
539
+ * producing a bilinear mapping from the unit square to the quadrilateral
540
+ * defined by the four input points.
541
+ *
542
+ * @param p00 - Point at (0, 0), typically top-left.
543
+ * @param p10 - Point at (1, 0), typically top-right.
544
+ * @param p01 - Point at (0, 1), typically bottom-left.
545
+ * @param p11 - Point at (1, 1), typically bottom-right.
546
+ * @param x - Normalized horizontal coordinate in [0, 1].
547
+ * @param y - Normalized vertical coordinate in [0, 1].
548
+ * @returns Interpolated 2D point `[x, y]`.
549
+ *
550
+ * @remarks
551
+ * - Reduces to linear interpolation along edges when `x = 0/1` or `y = 0/1`.
552
+ * - Produces an affine mapping only if the four points form a parallelogram;
553
+ * otherwise the interior mapping is bilinear (not affine).
554
+ * - No CRS or geodesic behavior is implied; inputs are treated as Cartesian
555
+ * coordinates.
620
556
  */
621
- export const __TEST_EXPORTS = {
622
- computeProjectedTileBounds,
623
- RasterTileNode,
624
- };
557
+ function bilerpPoint(p00, p10, p01, p11, x, y) {
558
+ const w00 = (1 - x) * (1 - y);
559
+ const w10 = x * (1 - y);
560
+ const w01 = (1 - x) * y;
561
+ const w11 = x * y;
562
+ return [
563
+ p00[0] * w00 + p10[0] * w10 + p01[0] * w01 + p11[0] * w11,
564
+ p00[1] * w00 + p10[1] * w10 + p01[1] * w01 + p11[1] * w11,
565
+ ];
566
+ }
625
567
  //# sourceMappingURL=raster-tile-traversal.js.map