feat: Web Mercator axis-aligned cutline support#424
Merged
kylebarron merged 19 commits intomainfrom Apr 15, 2026
Merged
Conversation
Discards fragments outside a WGS84 axis-aligned bbox for rendering USGS historical topographic maps and other rasters with a "map collar". Mercator-only, opt-in via a custom renderTile callback on COGLayer. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Discards fragments outside a WGS84 axis-aligned bbox. Intended for rendering rasters with a "map collar" (e.g. USGS historical topos) where the valid data area is described as a lat/lng bbox but the raw pixels include surrounding metadata. Mercator-only; caller must enforce WebMercatorViewport. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DECKGL_FILTER_COLOR is generated as a separate hook function whose body is assembled before the main FS source. Top-level FS varyings like position_commonspace are therefore out of scope inside it, producing "undeclared identifier" at compile time. Injecting at fs:#main-start places the discard test inside main() where the varying is visible. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Expose inferRenderPipeline and TextureDataT so users can wrap the default COGLayer pipeline with additional modules (e.g. CutlineBbox) without reimplementing tile fetching. Also update the spec to document the fs:#main-start hook choice. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Renders the Emigrant Gap, CA 1:62,500 USGS historical topo (1955) with an optional CutlineBbox module that discards the map collar. Wires the module into a custom renderTile callback on COGLayer by wrapping the inferred default pipeline. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Explains the distinction between hook-function injections (like fs:DECKGL_FILTER_COLOR) and source injections (fs:#main-start, fs:#main-end, fs:#decl), including why top-level FS varyings are visible in the latter but not the former. Captured after debugging an "undeclared identifier" error on position_commonspace while implementing CutlineBbox. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The earlier approach read position_commonspace from fs:#main-start and compared against a uniform computed via lngLatToWorld. That worked at low zoom but broke visibly at higher zoom: deck.gl applies a viewport-dependent precision translation to common-space positions to maintain f32 precision, so the FS varying and the CPU-computed uniform ended up in different coordinate frames and the test evaluated every fragment as "outside", discarding the whole tile. This commit sidesteps common space entirely. The VS captures `positions.xy` (raw 3857 meters, per RasterLayer's mesh construction) into a module-owned varying `v_cutlineBboxMercator`, and the FS compares it against a uniform also in raw 3857 meters computed via a hand-rolled spherical mercator forward. Both sides of the comparison live in the same absolute coordinate frame at every zoom level. Known precision ceiling: at |10M| mercator values, float32 gives ~1.2m quantization, which becomes visible as "wiggle" at the bbox edges around z=17-18. Acceptable for the USGS historical topo use case (typical viewing z=11-17). A future fix for sharper edges at z=18+ would require POSITION64LOW mesh attributes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The usgs-topo-cutline example originally needed inferRenderPipeline to wrap the default pipeline with an extra module, but it now uses an explicit two-module pipeline (CreateTexture + CutlineBbox) with its own minimal getTileData. No remaining consumer of the export, so we drop it rather than ship unused public API. This reverts the export added in a65e000. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Match the naip-mosaic example pattern: a minimal inline getTileData that decodes a 3-band uint8 RGB COG tile and uploads it as rgba8unorm, plus a two-module renderTile that chains CreateTexture and CutlineBbox. No dependency on inferRenderPipeline — the example reads as a direct demonstration of how the two modules compose. Also adds a zoom display in the info panel to help eyeball the float32 precision ceiling around z=17-18. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rewrite the spec's "coordinate space for the test" section to describe the raw-EPSG:3857-meters approach and explain why the earlier common-space attempt failed at high zoom. Add a float32 precision ceiling note covering the z=17-18 quantization behavior. Update the gpu-modules injection-hooks guide to use the final VS-injection + fs:#main-start pattern as the worked example, with the full narrative of the two-step diagnosis (undeclared identifier → common-space precision drift → raw mercator varying). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Plans under dev-docs/plans/ are one-shot TDD scaffolds that mirror what ends up in the spec, tests, and git log — and they rot as implementation evolves away from them. Specs in dev-docs/specs/ capture design decisions worth versioning; plans do not. Removes both existing plans and gitignores the directory. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…or helper Previously CutlineBbox accepted a WGS84 bbox and did the WGS84 → 3857 projection inside getUniforms. That ran on every frame per visible tile because luma.gl's ShaderInputs.setProps calls getUniforms unconditionally — the cost was negligible in absolute terms but conceptually wasteful work on the hot render path. Split into: - CutlineBbox: now accepts the bbox in EPSG:3857 meters directly. Its getUniforms is a pure pass-through, zero math in the render loop. Matches the pattern the rest of this repo uses (FilterNoDataVal, MaskTexture, LinearRescale, CompositeBands, CreateTexture) where getUniforms maps props 1:1 to uniforms without CPU-side transformations. - lngLatToMercator(lng, lat): a new exported point-level helper. Takes a WGS84 point and returns [x, y] in 3857 meters. Callers invoke it once at bbox definition time and pack two corners into the bbox vec4 themselves. Validates the Web Mercator latitude limit per point; bbox packing concerns (antimeridian, axis alignment) are left to the caller. The example composes lngLatToMercator twice per USGS quad via a local `topoBbox` wrapper. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Change list
Full image:
Map collar stripped:
Closes #421