Skip to content

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

@kylebarron

Description

@kylebarron

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions