developmentseed / developmentseed/deck.gl-raster

`COGLayer`: add a `bounds` prop to skip tile fetches outside a valid-data bbox?

Open
#425 0 comments 0 reactions 0 assignees View on GitHub
cog
Dominant language
TypeScript
Stars
228
Forks
29
Avg merge
12h 27m
Merged PRs (30d)
4

Description

## Motivation

#424 added a `CutlineBbox` module, which discards fragments outside a user-supplied bounding bbox.

But the tiles that fall *entirely* outside that bbox still get fetched, decoded, uploaded to the GPU as textures, and then discarded at every fragment. That's wasted work at every layer of the pipeline: network, CPU decode, GPU upload, and GPU memory.

In the context of, say, USGS historical maps: for a single USGS 7.5′ quad the fraction of "entirely-collar" tiles is modest (back-of-envelope ~15–25% of image-bbox tiles, depending on scale and how the quad sits relative to the tile grid).

But... the more you're zoomed in, and thus likely to have tiles entirely outside the extent, the less likely you are to have multiple images mosaicked together at the same time

----

Claude-written proposal:

## Proposal

Add an optional `bounds?: [west, south, east, north]` prop to `COGLayer` (WGS84 degrees). When set, tiles whose geographic footprint does not intersect the bounds are filtered out *before* `getTileData` is called, so we never fetch or upload them.

This composes with `CutlineBbox` rather than replacing it:

- `bounds` filters at tile granularity — skips tiles entirely outside.
- `CutlineBbox` filters at fragment granularity — handles the tiles that straddle the bbox edge.

Together they cover the whole collar without duplication.

## Implementation sketch

`COGLayer` already runs its tile iteration through `RasterTileset2D` + `TileMatrixSetAdaptor` with forward/inverse projection fns set up during `_parseGeoTIFF` (`cog-layer.ts` `_renderSubLayers` / `renderTileLayer`). deck.gl's underlying `TileLayer` has an `extent: [minX, minY, maxX, maxY]` prop for exactly this purpose.

Concrete steps:

1. Add `bounds?: [number, number, number, number]` to `COGLayerProps` (WGS84 degrees).
2. In `renderTileLayer`, if `bounds` is set, project the user's WGS84 corners through the already-cached `inverseFrom3857` (mercator) or `inverseFrom4326` (globe) to get the source-CRS bbox.
3. Intersect each tile's source-pixel bbox against the projected bounds at tile-traversal time, or pass a derived `extent` down to the inner `TileLayer`.
4. For non-rectangular source projections (Polyconic, LCC, TM), a WGS84 rectangle projects to a curved quad. Err on the side of *keeping* tiles when the intersection is ambiguous — a false positive just means `CutlineBbox` handles the per-fragment discard, which is correct and cheap. A false negative would be a missing tile, which is visible.

Rough budget: ~30–50 lines of new code in `cog-layer.ts` plus a test covering "tiles outside bounds are not fetched".

## Relationship to `MosaicLayer`

`MosaicLayer` already uses a flatbush spatial index to decide which sources cover which tiles. If each `COGLayer` inside a mosaic respects its own `bounds`, the mosaic automatically benefits — no changes to `MosaicLayer` itself. The per-source bounds become the natural bridge between USGS CSV metadata (`westbc`/`southbc`/`eastbc`/`northbc`) and what the layer fetches.

## Non-goals

- **Arbitrary polygon bounds.** Axis-aligned WGS84 rectangle is enough for USGS quads and most raster-with-collar use cases. Polygon support is a separate feature.
- **Replacing `CutlineBbox`.** The shader-side discard is still needed for tiles that cross the bbox edge.
- **Fetch-level optimization for already-loaded tiles.** This only affects *which* tiles get fetched, not what happens to tiles already in the cache.

## Why not do it now

No user-visible performance complaint yet, and the current `CutlineBbox` shader-level discard is correct at every zoom. Worth doing when:

- A multi-quad USGS mosaic demo lands and network/GPU pressure becomes visible.
- Someone reports "why is my map fetching tiles it's not going to draw."
- `COGLayer`'s tile iteration is being touched for another reason.

## References

- `packages/deck.gl-raster/src/gpu-modules/cutline-bbox.ts` — the shader-side discard this would complement
- `packages/deck.gl-geotiff/src/cog-layer.ts` `_renderSubLayers` / `renderTileLayer` — where plumbing would live
- `examples/usgs-topo-cutline/` — example where this optimization would show up first
- deck.gl `TileLayer` [`extent` prop](https://deck.gl/docs/api-reference/geo-layers/tile-layer#extent)

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.