From db570fdb35c4ea2d3030403a3cabfdc1c8b9fc3c Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Mon, 6 Apr 2026 15:26:20 -0400 Subject: [PATCH 01/15] init example --- examples/zarr-sentinel2-tci/README.md | 21 ++++++++++++++ examples/zarr-sentinel2-tci/index.html | 22 +++++++++++++++ examples/zarr-sentinel2-tci/package.json | 32 ++++++++++++++++++++++ examples/zarr-sentinel2-tci/tsconfig.json | 24 ++++++++++++++++ examples/zarr-sentinel2-tci/vite.config.ts | 11 ++++++++ 5 files changed, 110 insertions(+) create mode 100644 examples/zarr-sentinel2-tci/README.md create mode 100644 examples/zarr-sentinel2-tci/index.html create mode 100644 examples/zarr-sentinel2-tci/package.json create mode 100644 examples/zarr-sentinel2-tci/tsconfig.json create mode 100644 examples/zarr-sentinel2-tci/vite.config.ts diff --git a/examples/zarr-sentinel2-tci/README.md b/examples/zarr-sentinel2-tci/README.md new file mode 100644 index 00000000..43134b13 --- /dev/null +++ b/examples/zarr-sentinel2-tci/README.md @@ -0,0 +1,21 @@ +# GeoZarr Sentinel-2 True Color Image + +## Setup + +1. Install dependencies from the repository root: + ```bash + pnpm install + ``` + +2. Build the packages: + ```bash + pnpm build + ``` + +3. Run the development server: + ```bash + cd examples/cog-basic + pnpm dev + ``` + +4. Open your browser to http://localhost:3000 diff --git a/examples/zarr-sentinel2-tci/index.html b/examples/zarr-sentinel2-tci/index.html new file mode 100644 index 00000000..d9119b05 --- /dev/null +++ b/examples/zarr-sentinel2-tci/index.html @@ -0,0 +1,22 @@ + + + + + + COGLayer Example + + + +
+ + + diff --git a/examples/zarr-sentinel2-tci/package.json b/examples/zarr-sentinel2-tci/package.json new file mode 100644 index 00000000..e2d80906 --- /dev/null +++ b/examples/zarr-sentinel2-tci/package.json @@ -0,0 +1,32 @@ +{ + "name": "deck.gl-zarr-sentinel2-tci", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "publish": "pnpm build && gh-pages -d dist -b gh-pages -e examples/zarr-sentinel2-tci" + }, + "dependencies": { + "@deck.gl/core": "^9.2.10", + "@deck.gl/geo-layers": "^9.2.10", + "@deck.gl/layers": "^9.2.10", + "@deck.gl/mapbox": "^9.2.10", + "@deck.gl/mesh-layers": "^9.2.10", + "@developmentseed/deck.gl-zarr": "workspace:^", + "@luma.gl/core": "9.2.6", + "maplibre-gl": "^5.19.0", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-map-gl": "^8.1.0", + "zarrita": "^0.6.1" + }, + "devDependencies": { + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.4", + "gh-pages": "^6.3.0", + "vite": "^7.3.1" + } +} diff --git a/examples/zarr-sentinel2-tci/tsconfig.json b/examples/zarr-sentinel2-tci/tsconfig.json new file mode 100644 index 00000000..f0a23505 --- /dev/null +++ b/examples/zarr-sentinel2-tci/tsconfig.json @@ -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"] +} diff --git a/examples/zarr-sentinel2-tci/vite.config.ts b/examples/zarr-sentinel2-tci/vite.config.ts new file mode 100644 index 00000000..7632979b --- /dev/null +++ b/examples/zarr-sentinel2-tci/vite.config.ts @@ -0,0 +1,11 @@ +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [react()], + base: "/deck.gl-raster/examples/zarr-sentinel2-tci/", + worker: { format: "es" }, + server: { + port: 3000, + }, +}); From c4ad576826135a1d64f371b12d24203ed7e8b9b0 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Mon, 6 Apr 2026 15:26:28 -0400 Subject: [PATCH 02/15] add main --- examples/zarr-sentinel2-tci/src/main.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 examples/zarr-sentinel2-tci/src/main.tsx diff --git a/examples/zarr-sentinel2-tci/src/main.tsx b/examples/zarr-sentinel2-tci/src/main.tsx new file mode 100644 index 00000000..f8fc6f51 --- /dev/null +++ b/examples/zarr-sentinel2-tci/src/main.tsx @@ -0,0 +1,9 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import App from "./App"; + +createRoot(document.getElementById("root")!).render( + + + , +); From 3aa27ded3fc2d6a66ae359a279dc4cb8f94e93e1 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Tue, 7 Apr 2026 14:03:51 -0400 Subject: [PATCH 03/15] init example --- examples/zarr-sentinel2-tci/src/App.tsx | 184 ++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 examples/zarr-sentinel2-tci/src/App.tsx diff --git a/examples/zarr-sentinel2-tci/src/App.tsx b/examples/zarr-sentinel2-tci/src/App.tsx new file mode 100644 index 00000000..2087f268 --- /dev/null +++ b/examples/zarr-sentinel2-tci/src/App.tsx @@ -0,0 +1,184 @@ +import type { DeckProps } from "@deck.gl/core"; +import { MapboxOverlay } from "@deck.gl/mapbox"; +import { COGLayer } from "@developmentseed/deck.gl-geotiff"; +import "maplibre-gl/dist/maplibre-gl.css"; +import type { ReactNode } from "react"; +import { useRef, useState } from "react"; +import type { MapRef } from "react-map-gl/maplibre"; +import { Map as MaplibreMap, useControl } from "react-map-gl/maplibre"; + +function DeckGLOverlay(props: DeckProps) { + const overlay = useControl(() => new MapboxOverlay(props)); + overlay.setProps(props); + return null; +} + +const COG_OPTIONS: { title: string; url: string; attribution?: ReactNode }[] = [ + { + title: "Sentinel-2 TCI", + url: "http://localhost:8080/TCI.zarr/zarr.json", + }, +]; + +export default function App() { + const mapRef = useRef(null); + const [debug, setDebug] = useState(false); + const [debugOpacity, setDebugOpacity] = useState(0.25); + const [selectedIndex, setSelectedIndex] = useState(0); + + const cog_layer = new COGLayer({ + id: "cog-layer", + geotiff: COG_OPTIONS[selectedIndex].url, + debug, + debugOpacity, + onGeoTIFFLoad: (tiff, options) => { + (window as any).tiff = tiff; + const { west, south, east, north } = options.geographicBounds; + mapRef.current?.fitBounds( + [ + [west, south], + [east, north], + ], + { + padding: 40, + duration: 1000, + }, + ); + }, + beforeId: "boundary_country_outline", + }); + + return ( +
+ + + + + {/* UI Overlay Container */} +
+
+

+ COGLayer Example +

+ + {/*

+ Displaying RGB imagery from New Zealand (NZTM2000 projection) +

*/} + + {/* Attribution */} + {COG_OPTIONS[selectedIndex].attribution && ( +

+ {COG_OPTIONS[selectedIndex].attribution} +

+ )} + + {/* Debug Controls */} +
+ + + {debug && ( +
+ +
+ )} +
+
+
+
+ ); +} From 2243e2373892f2de649aa19e9f10e706e985a8cf Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Tue, 7 Apr 2026 14:04:10 -0400 Subject: [PATCH 04/15] no need to cast --- packages/geozarr/src/parse.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/geozarr/src/parse.ts b/packages/geozarr/src/parse.ts index 24bc0454..dc1efdfe 100644 --- a/packages/geozarr/src/parse.ts +++ b/packages/geozarr/src/parse.ts @@ -36,7 +36,7 @@ export function parseGeoZarrMetadata(attrs: unknown): GeoZarrMetadata { } else if ("proj:wkt2" in geoProj) { crs.wkt2 = geoProj["proj:wkt2"]; } else if ("proj:projjson" in geoProj) { - crs.projjson = geoProj["proj:projjson"] as Record; + crs.projjson = geoProj["proj:projjson"]; } // --- Axes --- From bdbb87d07d887f1645351dc319cd889e4ba38088 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Tue, 7 Apr 2026 14:23:19 -0400 Subject: [PATCH 05/15] most basic app rendering --- examples/zarr-sentinel2-tci/src/App.tsx | 93 ++--- packages/deck.gl-zarr/src/index.ts | 5 +- packages/deck.gl-zarr/src/zarr-layer.ts | 524 ++++++++++++++++++++++++ 3 files changed, 551 insertions(+), 71 deletions(-) create mode 100644 packages/deck.gl-zarr/src/zarr-layer.ts diff --git a/examples/zarr-sentinel2-tci/src/App.tsx b/examples/zarr-sentinel2-tci/src/App.tsx index 2087f268..d2d961d3 100644 --- a/examples/zarr-sentinel2-tci/src/App.tsx +++ b/examples/zarr-sentinel2-tci/src/App.tsx @@ -1,51 +1,38 @@ -import type { DeckProps } from "@deck.gl/core"; +import type { MapboxOverlayProps } from "@deck.gl/mapbox"; import { MapboxOverlay } from "@deck.gl/mapbox"; -import { COGLayer } from "@developmentseed/deck.gl-geotiff"; +import { ZarrLayer } from "@developmentseed/deck.gl-zarr"; import "maplibre-gl/dist/maplibre-gl.css"; -import type { ReactNode } from "react"; import { useRef, useState } from "react"; import type { MapRef } from "react-map-gl/maplibre"; import { Map as MaplibreMap, useControl } from "react-map-gl/maplibre"; -function DeckGLOverlay(props: DeckProps) { +function DeckGLOverlay(props: MapboxOverlayProps) { const overlay = useControl(() => new MapboxOverlay(props)); overlay.setProps(props); return null; } -const COG_OPTIONS: { title: string; url: string; attribution?: ReactNode }[] = [ - { - title: "Sentinel-2 TCI", - url: "http://localhost:8080/TCI.zarr/zarr.json", - }, -]; +const ZARR_URL = "http://localhost:8080/TCI.zarr"; export default function App() { const mapRef = useRef(null); const [debug, setDebug] = useState(false); const [debugOpacity, setDebugOpacity] = useState(0.25); - const [selectedIndex, setSelectedIndex] = useState(0); - const cog_layer = new COGLayer({ - id: "cog-layer", - geotiff: COG_OPTIONS[selectedIndex].url, + const zarrLayer = new ZarrLayer({ + id: "zarr-layer", + source: ZARR_URL, debug, debugOpacity, - onGeoTIFFLoad: (tiff, options) => { - (window as any).tiff = tiff; - const { west, south, east, north } = options.geographicBounds; - mapRef.current?.fitBounds( - [ - [west, south], - [east, north], - ], - { - padding: 40, - duration: 1000, - }, - ); + onZarrLoad: () => { + // Bounds are [300000, 3990240, 409800, 4100040] in EPSG:32612 (UTM zone 12N) + // Center approx: lon=-111.5, lat=36.2 (Utah/Arizona area) + mapRef.current?.flyTo({ + center: [-111.5, 36.2], + zoom: 8, + duration: 1000, + }); }, - beforeId: "boundary_country_outline", }); return ( @@ -53,18 +40,15 @@ export default function App() { - + - {/* UI Overlay Container */}

- COGLayer Example + ZarrLayer — Sentinel-2 TCI

- - {/*

- Displaying RGB imagery from New Zealand (NZTM2000 projection) -

*/} - - {/* Attribution */} - {COG_OPTIONS[selectedIndex].attribution && ( -

- {COG_OPTIONS[selectedIndex].attribution} -

- )} +

+ GeoZarr multiscale, EPSG:32612 +

- {/* Debug Controls */}