-
Notifications
You must be signed in to change notification settings - Fork 13
feat: Globe view, antimeridian, and polar projection support #266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
842fe4b
Add baseline tests for tile traversal code
espg f9f360a
Implement globe view support for tile traversal
espg 7b909c5
Add antimeridian crossing support to RasterReprojector
espg 78da2f3
Add polar projection support to RasterReprojector
espg 6b18b45
Add globe-view example demonstrating COGLayer in GlobeView
espg 86f79b2
addressing test/lint failures, lockfile update
espg 2958358
fixing global viewer to have context map
espg 8e3fdbf
switching to calculated zoom instead of hard coded zoom level 3
espg 4bbfbf0
fix: update pnpm-lock.yaml for globe-view @luma.gl/webgl dep
espg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>COGLayer Globe View Example</title> | ||
| <style> | ||
| body { | ||
| margin: 0; | ||
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; | ||
| } | ||
| #root { | ||
| width: 100vw; | ||
| height: 100vh; | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <div id="root"></div> | ||
| <script type="module" src="/src/main.tsx"></script> | ||
| </body> | ||
| </html> |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| { | ||
| "name": "deck.gl-cog-globe-example", | ||
| "private": true, | ||
| "type": "module", | ||
| "scripts": { | ||
| "dev": "vite", | ||
| "build": "vite build", | ||
| "preview": "vite preview" | ||
| }, | ||
| "dependencies": { | ||
| "@deck.gl/core": "^9.2.7", | ||
| "@deck.gl/geo-layers": "^9.2.7", | ||
| "@deck.gl/layers": "^9.2.7", | ||
| "@deck.gl/mesh-layers": "^9.2.7", | ||
| "@deck.gl/react": "^9.2.7", | ||
| "@developmentseed/geotiff": "workspace:^", | ||
| "@developmentseed/deck.gl-geotiff": "workspace:^", | ||
| "@developmentseed/deck.gl-raster": "workspace:^", | ||
| "@luma.gl/core": "9.2.6", | ||
| "@luma.gl/shadertools": "9.2.6", | ||
| "@luma.gl/webgl": "9.2.6", | ||
| "proj4": "^2.20.2", | ||
| "react": "^19.2.4", | ||
| "react-dom": "^19.2.4" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/react": "^19.2.10", | ||
| "@types/react-dom": "^19.2.3", | ||
| "@vitejs/plugin-react": "^5.1.2", | ||
| "vite": "^7.3.1" | ||
| } | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,181 @@ | ||
| import { _GlobeView as GlobeView } from "@deck.gl/core"; | ||
| import { GeoJsonLayer, SolidPolygonLayer } from "@deck.gl/layers"; | ||
| import { DeckGL } from "@deck.gl/react"; | ||
| import { COGLayer } from "@developmentseed/deck.gl-geotiff"; | ||
| import { luma } from "@luma.gl/core"; | ||
| import { webgl2Adapter } from "@luma.gl/webgl"; | ||
| import { useCallback, useState } from "react"; | ||
|
|
||
| // Register WebGL adapter — required when DeckGL creates its own context | ||
| // (unlike MapboxOverlay which reuses MaplibreGL's existing context) | ||
| luma.registerAdapters([webgl2Adapter]); | ||
|
|
||
| // New Zealand imagery (NZTM2000 projection) | ||
| const COG_URL = | ||
| "https://nz-imagery.s3-ap-southeast-2.amazonaws.com/new-zealand/new-zealand_2024-2025_10m/rgb/2193/CC11.tiff"; | ||
|
|
||
| // Antarctic sea ice (polar stereographic) | ||
| // const COG_URL = | ||
| // "https://data.source.coop/ausantarctic/ghrsst-mur-v2/2020/12/12/20201212090000-JPL-L4_GHRSST-SSTfnd-MUR-GLOB-v02.0-fv04.1_sea_ice_fraction.tif"; | ||
|
|
||
| export default function App() { | ||
| const [debug, setDebug] = useState(false); | ||
| const [debugOpacity, setDebugOpacity] = useState(0.25); | ||
|
|
||
| // Use initialViewState (uncontrolled) so deck.gl manages view state internally. | ||
| // Updating the object reference triggers deck.gl to transition to the new view. | ||
| const [initialViewState, setInitialViewState] = useState({ | ||
| longitude: 0, | ||
| latitude: 0, | ||
| zoom: 1, | ||
| }); | ||
|
|
||
| const onGeoTIFFLoad = useCallback( | ||
| ( | ||
| _tiff: unknown, | ||
| options: { | ||
| geographicBounds: { | ||
| west: number; | ||
| south: number; | ||
| east: number; | ||
| north: number; | ||
| }; | ||
| }, | ||
| ) => { | ||
| const { west, south, east, north } = options.geographicBounds; | ||
| const lonSpan = east - west; | ||
| const latSpan = north - south; | ||
| const maxSpan = Math.max(lonSpan, latSpan); | ||
| // At zoom N, ~360/2^N degrees are visible; subtract 1 for padding | ||
| const zoom = Math.log2(360 / maxSpan) - 1; | ||
| setInitialViewState({ | ||
| longitude: (west + east) / 2, | ||
| latitude: (south + north) / 2, | ||
| zoom, | ||
| }); | ||
| }, | ||
| [], | ||
| ); | ||
|
|
||
| const layers = [ | ||
| // Dark background sphere | ||
| new SolidPolygonLayer({ | ||
| id: "background", | ||
| data: [ | ||
| [ | ||
| [-180, 90], | ||
| [0, 90], | ||
| [180, 90], | ||
| [180, -90], | ||
| [0, -90], | ||
| [-180, -90], | ||
| ], | ||
| ], | ||
| getPolygon: (d) => d, | ||
| stroked: false, | ||
| filled: true, | ||
| getFillColor: [10, 20, 40], | ||
| }), | ||
| // Land masses basemap (Natural Earth via deck.gl CDN) | ||
| new GeoJsonLayer({ | ||
| id: "basemap", | ||
| data: "https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_admin_0_scale_rank.geojson", | ||
| stroked: true, | ||
| filled: true, | ||
| lineWidthMinPixels: 1, | ||
| getLineColor: [40, 60, 90], | ||
| getFillColor: [25, 40, 70], | ||
| }), | ||
| new COGLayer({ | ||
| id: "cog-layer", | ||
| geotiff: COG_URL, | ||
| debug, | ||
| debugOpacity, | ||
| onGeoTIFFLoad, | ||
| }), | ||
| ]; | ||
|
|
||
| return ( | ||
| <div style={{ position: "relative", width: "100%", height: "100%" }}> | ||
| <DeckGL | ||
| views={new GlobeView()} | ||
| initialViewState={initialViewState} | ||
| controller={true} | ||
| layers={layers} | ||
| /> | ||
|
|
||
| {/* UI Controls */} | ||
| <div | ||
| style={{ | ||
| position: "absolute", | ||
| top: "20px", | ||
| left: "20px", | ||
| background: "white", | ||
| padding: "16px", | ||
| borderRadius: "8px", | ||
| boxShadow: "0 2px 8px rgba(0,0,0,0.1)", | ||
| maxWidth: "300px", | ||
| zIndex: 1000, | ||
| }} | ||
| > | ||
| <h3 style={{ margin: "0 0 8px 0", fontSize: "16px" }}> | ||
| COGLayer Globe View | ||
| </h3> | ||
| <p style={{ margin: "0 0 12px 0", fontSize: "14px", color: "#666" }}> | ||
| Displaying COG imagery on a 3D globe | ||
| </p> | ||
|
|
||
| <div | ||
| style={{ | ||
| padding: "12px 0", | ||
| borderTop: "1px solid #eee", | ||
| marginTop: "12px", | ||
| }} | ||
| > | ||
| <label | ||
| style={{ | ||
| display: "flex", | ||
| alignItems: "center", | ||
| gap: "8px", | ||
| fontSize: "14px", | ||
| cursor: "pointer", | ||
| marginBottom: "12px", | ||
| }} | ||
| > | ||
| <input | ||
| type="checkbox" | ||
| checked={debug} | ||
| onChange={(e) => setDebug(e.target.checked)} | ||
| style={{ cursor: "pointer" }} | ||
| /> | ||
| <span>Show Debug Mesh</span> | ||
| </label> | ||
|
|
||
| {debug && ( | ||
| <div style={{ marginTop: "8px" }}> | ||
| <label | ||
| style={{ | ||
| display: "block", | ||
| fontSize: "12px", | ||
| color: "#666", | ||
| marginBottom: "4px", | ||
| }} | ||
| > | ||
| Debug Opacity: {debugOpacity.toFixed(2)} | ||
| <input | ||
| type="range" | ||
| min="0" | ||
| max="1" | ||
| step="0.01" | ||
| value={debugOpacity} | ||
| onChange={(e) => setDebugOpacity(parseFloat(e.target.value))} | ||
| style={{ width: "100%", cursor: "pointer" }} | ||
| /> | ||
| </label> | ||
| </div> | ||
| )} | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { StrictMode } from "react"; | ||
| import { createRoot } from "react-dom/client"; | ||
| import App from "./App"; | ||
|
|
||
| createRoot(document.getElementById("root")!).render( | ||
| <StrictMode> | ||
| <App /> | ||
| </StrictMode>, | ||
| ); |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| { | ||
| "compilerOptions": { | ||
| "target": "ES2020", | ||
| "useDefineForClassFields": true, | ||
| "lib": ["ES2020", "DOM", "DOM.Iterable"], | ||
| "module": "ESNext", | ||
| "skipLibCheck": true, | ||
|
|
||
| /* Bundler mode */ | ||
| "moduleResolution": "bundler", | ||
| "allowImportingTsExtensions": true, | ||
| "isolatedModules": true, | ||
| "moduleDetection": "force", | ||
| "noEmit": true, | ||
| "jsx": "react-jsx", | ||
|
|
||
| /* Linting */ | ||
| "strict": true, | ||
| "noUnusedLocals": true, | ||
| "noUnusedParameters": true, | ||
| "noFallthroughCasesInSwitch": true | ||
| }, | ||
| "include": ["src"] | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import react from "@vitejs/plugin-react"; | ||
| import { defineConfig } from "vite"; | ||
|
|
||
| export default defineConfig({ | ||
| plugins: [react()], | ||
| base: "/deck.gl-raster/examples/globe-view/", | ||
| server: { | ||
| port: 3001, | ||
| }, | ||
| }); |
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've never seen this before; this isn't required for a standard DeckGL app
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, thought this was needed to get a proper context map background (i.e., coastlines, country borders)-- looks like another change in the same commit fixed that issue and we can excise this.