feat: Initial Globe view support#563
Merged
Merged
Conversation
Scopes a working globe-view prototype (globe tile-selection bounding volume, lng/lat-direct render path via a shader unification, cog-globe and zarr-globe examples over MapLibre globe, throwaway anti-faceting scaffold) while deliberately carving out spherical-reprojection correctness as a follow-up design. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the assert(false) globe stub in computeBoundingVolume with an oriented bounding box built from WGS84 reference points projected onto the globe sphere via viewport.projectPosition. Makes the bounding-volume cache key projection-aware so globe and mercator volumes don't collide. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kylebarron
commented
May 21, 2026
kylebarron
commented
May 21, 2026
kylebarron
commented
May 21, 2026
- Drop the projection-namespaced cache key; instead RasterTileset2D clears the BoundingVolumeCache when the viewport projection mode switches (globe<->mercator). Simpler z/x/y key; adds a clear() method. - Always use REF_POINTS_11 for the globe bounding volume. Our descriptor z is an internal overview index, not web-mercator zoom, so upstream's zoom-keyed point count doesn't transfer; a tile never spans more than the whole world, so 11 points always suffice and the cache makes the per-tile cost one-time. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kylebarron
commented
May 21, 2026
kylebarron
commented
May 21, 2026
Follow the repo convention of aliasing deck.gl's underscore-prefixed exports (cf. _Tileset2D as Tileset2D). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collapse SimpleMeshLayer's composeModelMatrix branch to one direct-projection path. MeshTextureLayer always draws one identity mesh at the origin, so projecting the mesh vertex directly is correct for both cartesian (Web Mercator) and lnglat (GlobeView). Fixes the globe path, which previously hit the meters-offset branch and ran project_size on lng/lat degrees. Behavior-preserving for mercator. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collapse SimpleMeshLayer's composeModelMatrix branch to one direct-projection path. MeshTextureLayer always draws one identity mesh at the origin, so projecting the mesh vertex directly is correct for both cartesian (Web Mercator) and lnglat (GlobeView). Fixes the globe path, which previously hit the meters-offset branch and ran project_size on lng/lat degrees. Behavior-preserving for mercator. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
037f178 to
0f3691b
Compare
In globe mode, build a uniform per-tile grid instead of the adaptive Delatin mesh, whose reprojection-error metric is blind to sphere curvature and facets EPSG:4326 sources at low zoom. Stopgap only; to be removed when sphere-aware reprojection lands. Globe is detected via viewport.resolution (null-safe so direct _generateMesh calls in tests fall back to the reprojector path). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Standalone example rendering an EPSG:4326 COG on MapLibre's globe projection (projection="globe") with deck.gl interleaved. Exercises the globe tile-selection, lng/lat render path, and uniform-grid scaffold end-to-end. fitBounds frames the data wherever the COG sits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
In globe mode the raster mesh is coplanar with MapLibre's basemap sphere and z-fights in the shared interleaved depth buffer. A depth bias does not help with maplibre's globe depth encoding; instead skip depth comparison (depthCompare: "always") and occlude the far hemisphere with back-face culling (cullMode: "back" for this grid's winding). Approach per visgl/deck.gl#9592. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror cog-basic's source list + selector (and debug controls) so every COG source can be tested on the globe. fitBounds reframes on each switch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The LOD meters-per-pixel read the latitude from the 3D oriented bounding box center, which on a globe is in globe common space (y far outside the Web Mercator world range), so worldToLngLat returned ~-89deg and cos(lat) made meters-per-pixel 10-270x too small. devicePixelsPerSource was then always >>1, so the traversal always recursed to the finest level -- loading the finest tiles across the whole globe when zoomed out. Drive the latitude from commonSpaceBounds instead, which is in Web Mercator world space in both the mercator and globe paths. Adds a regression test asserting LOD tracks zoom on a globe. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a "Globe view (prototype)" section: lnglat-direct rendering, the two-coordinate-space tile selection, the LOD latitude gotcha (+ limb foreshortening caveat), the depthCompare/cullMode z-fighting recipe, and the throwaway anti-faceting grid. Links the globe-view spec. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reconciles the globe-view work with #518 ("traverse tiles across world copies"), which refactored raster-tile-traversal.ts. The globe additions (globe bounding volume, REF_POINTS_11, lnglat LOD via getMetersPerPixelAtCommonSpaceBounds) layer onto main's worldOffset structure; update() now also destructures commonSpaceBounds for the LOD latitude. World-copy passes are gated on viewport.subViewports (>1), so they never run on a globe. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 22, 2026
kylebarron
commented
May 22, 2026
kylebarron
commented
May 22, 2026
Per review: the depthCompare/cullMode recipe for z-fighting against the basemap depends on the compositing context (MapLibre interleaved globe handedness vs a standalone _GlobeView), so it's an app decision, not a library one. RasterLayer no longer injects it; deck.gl forwards the `parameters` prop from COGLayer down to the mesh via getSubLayerProps, and the cog-globe example sets it. Also simplify the cog-globe README. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 26, 2026
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.
Screen.Recording.2026-05-22.at.12.02.20.PM.mov
What I am changing
Adds prototype globe-view support to deck.gl-raster.
RasterTileLayer-basedlayers (COG/Zarr) can now render on a 3D globe — deck.gl
_GlobeView, orMapLibre
projection: 'globe'with deck.gl interleaved — not just the flat WebMercator map. Includes a new
cog-globeexample.Deliberately scoped as a prototype: it renders real data end-to-end and
exercises the full pipeline, with the harder accuracy work called out and
deferred (see Limitations). Design + deferred work:
dev-docs/specs/2026-05-21-globe-view-design.md.How I did it
computeBoundingVolume(was
assert(false)): sample reference points (REF_POINTS_11), reproject toWGS84, and project onto the sphere via
viewport.projectPositionfor thefrustum-culling oriented bounding box; keep a Web-Mercator-world AABB for the
dataset pre-filter and the LOD latitude. The bounding-volume cache is rebuilt
when the viewport projection mode switches (globe↔mercator).
coordinateSystem: 'lnglat'(mesh vertices in WGS84 via
projectTo4326) — no manual common-space mapping.Collapsed
SimpleMeshLayer'scomposeModelMatrixbranch in theMeshTextureLayervertex shader to a single direct-projection path (the oldbranch projected lng/lat degrees as if they were meters).
globe sphere in the shared interleaved depth buffer. A depth bias doesn't help
with maplibre's globe depth encoding; instead
depthCompare: 'always'+back-face culling (
cullMode: 'back'for this grid's winding). Seevisgl/deck.gl#9592.
bounds, not the 3D OBB center (globe-common →
worldToLngLatreturns ~-89°near the Mercator singularity → meters/px far too small → the traversal loads
the finest tiles across the whole globe when zoomed out).
instead of the adaptive Delatin mesh, whose reprojection-error metric is blind
to sphere curvature (an EPSG:4326 source yields 2 triangles that chord through
the sphere).
main, reconciling with fix: traverse tiles across world copies (#517) #518 (world copies); world-copy passes aregated on
viewport.subViewports, so they don't run on a globe.dev-docs/coordinate-systems.md.How you can test it
Confirm: the basemap renders as a 3D globe, the COG drapes onto the sphere and
lines up with the basemap (source selector to try several COGs), no z-fighting,
back-of-globe tiles are occluded, and LOD tracks zoom (coarse when zoomed out,
finer when zoomed in).
Unit tests:
pnpm --filter @developmentseed/deck.gl-raster test— globebounding volume, cache rebuild on projection switch, and a globe LOD regression
test.
Limitations (deferred — tracked as follow-up issues)
error metric is blind to sphere curvature; the uniform-grid scaffold is a
stopgap.
ignores globe foreshortening, so tiles near the limb slightly over-fetch
(never under). A screen-space-error metric would be exact.
zarr-globeexample (Add zarr-globe example #568) — not included yet.ClipExtension(Remove CutlineBbox module in favor of upstream ClipExtension? #561).
Related Issues
Add zarr-globe example #568 (zarr-globe); cutline-on-globe depends on Remove CutlineBbox module in favor of upstream ClipExtension? #561 (ClipExtension)