Conversation
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…lify render-gray; fix stale-index crash in MosaicTileset2D
…r ramp Drop WhiteIsZero (color inversion, not needed for this example) and the texture-format-inference indirection (hardcode the known r16unorm format). Swap in BlackIsZero, which still does the required single-band -> RGB broadcast so the image doesn't render red-tinted, but leaves values un-inverted: dark = low, bright = high.
This branch has not been deployed
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.
This PR aims to test the open antimeridian PR and make it functional. I am using tiled Digital Earth Pacific data that is in PDC (EPSG:3832) which passes the vertical (not slanted) cut condition. One column of tiled COGs span the antimeridian.
An earlier revision of this PR included a heuristic that checked which side of common-space x=256 a reprojected point was on — it failed for a piece spanning more than ~180° of the earth. That heuristic has been replaced with a per-point correction that derives its reference from each point's own position (reusing the same GeoJSON west>east idiom already used for cut detection) rather than a fixed midpoint, so it's width-independent: the only remaining limit is a tile's total width staying under 360°, which isn't a new limitation — data ≥360° wide is self-overlapping and has no correct rendering regardless. See the antimeridian design doc's "Seam handling" section for the full derivation, including the two narrower fixes tried and superseded along the way.
The above linked PR sets up a good approach. This PR continues this split-in-two mesh designand also aims to follow https://www.gadom.ski/antimeridian/latest/ where relevant.
This PR has significant overlap with my earlier PR #639, and should replace it.
This PR contains 2 examples, a simple COGLayer spanning the antimeridian, and a MosaicLayer/MultiCOGLayer example (supporting both requires seperate testing).
Technical details:
RasterTileLayer._renderAntimeridianTile emits two RasterLayers (west/east), each seeded via triangulateRectangle from its own UV sub-domain, sharing one texture — exactly the "split in the sublayer factory, everything else keeps its single-mesh contract" design.
Cut detection matches the MVP's stated scope. antimeridianCut/edgeUCut — vertical-only, rejects slanted (U_EPSILON tolerance), falls back to full mesh otherwise. Matches the code's own docstring; the design doc's broader "vertical and slanted" scope claim was already aspirational in docs(specs): add antimeridian seam-handling detail #576, not something this branch touched either way.
All 3 of kylebarron's review comments on antimeridian-cut.ts are addressed:
Test plan items present: cut-location unit tests (antimeridian-cut.test.ts), piece-reprojection unit tests (raster-tileset-2d-antimeridian.test.ts), and the cog-basic dev-only antimeridian.tif fixture entry from docs(specs): add antimeridian seam-handling detail #576 is still intact and untouched.
One real discrepancy — the design doc (
2026-05-27-antimeridian-crossing-tile-design.md) is now stale on seam handling:projectPosition— each point's own fractional position along the tile derives its own expected value (reusingantimeridianCut's already-located seam and total width), snapped to the nearest representative of that expectation. Not piece-specific, not a native-CRS sign check, and — unlike an earlier revision of this same fix — not a fixed midpoint test either, which turned out to have its own width limit (full derivation in the design doc's "Seam handling" section).dev-docs/specs/2026-05-27-antimeridian-crossing-tile-design.md's "Seam handling" section now describes the shipped mechanism (both superseded mechanisms kept, one struck through, for history), and its "Traversal" bullet, "Edge cases", and "Test plan" sections are updated to match. Also added a new "Locating and selecting a crossing tile in the traversal" section documenting the fix described below, anddev-docs/world-copies.mddocuments why the world-copy offset cap (MAX_MAPS) didn't need to scale with piece width.MosaicLayer/MultiCOGLayer support:
normalizeSourceBbox(mosaic-layer.ts) unwraps a GeoJSON-flipped source bbox (RFC 7946 §5.2:minX > maxXmarks a crossing) onto a continuous frame before it's indexed into Flatbush, so the spatial index, the priority-queue distance calculation, andMosaicTileset2D's own viewport search all agree on one bbox per source.MultiCOGLayer's debug overlay (_renderDebugLayers/pieceBoxWgs84) splits into matching west/east boxes for a crossing tile —MultiCOGLayer extends RasterTileLayer, so the actual mesh-splitting/reprojection path is shared automatically; only the debug-outline drawing (which works in plain lng/lat, not common-space) needed its own antimeridian handling.This PR also covers visualising just one side of the AM-split COG/Mosaic. An earlier version of this PR would not show the right/east side of an am-split texture, if the left/west side was not in view. Root cause:
raster-tile-traversal.ts's frustum-culling traversal had zero antimeridian awareness, three compounding defects:_getGenericBoundingVolumesampled 9 reference points per tile with no antimeridian correction — for a crossing tile some land on each edge of common space, producing a bounding volume wide enough to be found only by accident (when the seam itself was in view). Fixed by extracting the wrap-correction already used inbuildPieceReprojectioninto a sharedunwrapCommonSpaceXhelper (antimeridian-cut.ts) and applying it before fitting the box. (This helper's own signature and internals were revised again in a later pass — see the "one symmetric per-point test" bullet above and the design doc's "Seam handling" section — but the traversal integration point described here didn't change.)insideBoundspre-filter ingetTileIndices— built from the same lossy min/maxwgs84Bounds— only touched the now-tight per-tile box at the seam instead of overlapping it, rejecting the tile at every zoom. Fixed by recomputing the dataset's own bounds from its real corner longitudes when the dataset itself crosses, instead of patching the already-mixed-up min/max.buildPieceReprojectionbuilt one shared reprojection for both pieces, always anchoring both near common-spacex≈512— correct when both pieces are in view together, wrong once only the east piece is in view (its mesh stayed a full world away from the camera, selected but never drawn on screen). Fixed by mirroring the correction per piece so each renders at its own natural position; deck.gl's existing repeat-rendering (subViewports) already draws whatever world copies the camera needs.subViewports.length > 1tosubViewports != nullin both traversal files — the former only tests whether the viewport itself currently straddles a seam, not whether a tile's (or, forMosaicTileset2D's Flatbush-based, lng/lat bbox search inmosaic-tileset-2d.ts, a source's) own position needs a shifted pass to be found once zoomed in tight to one side alone.Test PR:
I have made a frankenstein monster of my 3 PRs to test, and they work well together for my needs (a combo of MosaicLayer, MultiCOGLayer that spans the antimeridian and needs nearest sampling). https://github.com/willjnz/deck.gl-raster/tree/willjnz/frankenstein. This combines:
linear#640