diff --git a/examples/land-cover/package.json b/examples/land-cover/package.json index a852779..b956c47 100644 --- a/examples/land-cover/package.json +++ b/examples/land-cover/package.json @@ -14,6 +14,7 @@ "@deck.gl/layers": "^9.2.5", "@deck.gl/mapbox": "^9.2.5", "@deck.gl/mesh-layers": "^9.2.5", + "@deck.gl/widgets": "^9.2.5", "@developmentseed/deck.gl-geotiff": "workspace:^", "@developmentseed/deck.gl-raster": "workspace:^", "@luma.gl/core": "9.2.5", diff --git a/examples/land-cover/src/App.tsx b/examples/land-cover/src/App.tsx index f449e23..ae01449 100644 --- a/examples/land-cover/src/App.tsx +++ b/examples/land-cover/src/App.tsx @@ -6,6 +6,7 @@ import "maplibre-gl/dist/maplibre-gl.css"; import { useRef, useState } from "react"; import type { MapRef } from "react-map-gl/maplibre"; import { Map as MaplibreMap, useControl } from "react-map-gl/maplibre"; +import { DeckGLSplitView } from "./components/DeckGLSplitView"; import { InfoPanel } from "./components/InfoPanel"; import { UIOverlay } from "./components/UIOverlay"; @@ -34,6 +35,7 @@ export default function App() { const mapRef = useRef(null); const [debug, setDebug] = useState(false); const [debugOpacity, setDebugOpacity] = useState(0.25); + const [comparisonMode, setComparisonMode] = useState(false); const cog_layer = new COGLayer({ id: "cog-layer", @@ -61,26 +63,32 @@ export default function App() { return (
- - - + {comparisonMode ? ( + + ) : ( + + + + )}
diff --git a/examples/land-cover/src/components/DeckGLSplitView.tsx b/examples/land-cover/src/components/DeckGLSplitView.tsx new file mode 100644 index 0000000..635b9fa --- /dev/null +++ b/examples/land-cover/src/components/DeckGLSplitView.tsx @@ -0,0 +1,146 @@ +import { Deck, MapView } from "@deck.gl/core"; +import { _SplitterWidget as SplitterWidget } from "@deck.gl/widgets"; +import { COGLayer, proj } from "@developmentseed/deck.gl-geotiff"; +import { toProj4 } from "geotiff-geokeys-to-proj4"; +import { useEffect, useRef } from "react"; + +interface DeckGLSplitViewProps { + debug: boolean; + debugOpacity: number; +} + +async function geoKeysParser( + geoKeys: Record, +): Promise { + const projDefinition = toProj4(geoKeys as any); + + return { + def: projDefinition.proj4, + parsed: proj.parseCrs(projDefinition.proj4), + coordinatesUnits: projDefinition.coordinatesUnits as proj.SupportedCrsUnit, + }; +} + +const COG_URL_1985 = + "https://s3.us-east-1.amazonaws.com/ds-deck.gl-raster-public/cog/Annual_NLCD_LndCov_1985_CU_C1V1.tif"; +const COG_URL_2024 = + "https://s3.us-east-1.amazonaws.com/ds-deck.gl-raster-public/cog/Annual_NLCD_LndCov_2024_CU_C1V1.tif"; + +export function DeckGLSplitView({ debug, debugOpacity }: DeckGLSplitViewProps) { + const containerRef = useRef(null); + const deckRef = useRef(null); + + useEffect(() => { + if (!containerRef.current) return; + + const initialViewState = { + longitude: -98, + latitude: 38.5, + zoom: 3.5, + pitch: 0, + bearing: 0, + }; + + const cog_layer_1985 = new COGLayer({ + id: "cog-layer-1985", + geotiff: COG_URL_1985, + debug, + debugOpacity, + geoKeysParser, + operation: "draw", + }); + + const cog_layer_2024 = new COGLayer({ + id: "cog-layer-2024", + geotiff: COG_URL_2024, + debug, + debugOpacity, + geoKeysParser, + operation: "draw", + }); + + const deck = new Deck({ + parent: containerRef.current, + initialViewState: { + view1: initialViewState, + view2: initialViewState, + }, + controller: true, + views: [ + new MapView({ id: "left", x: 0, width: "50%", controller: true }), + new MapView({ id: "right", x: "50%", width: "50%", controller: true }), + ], + layers: [ + cog_layer_1985.clone({ operation: "draw" }), + cog_layer_2024.clone({ operation: "draw" }), + ], + layerFilter: ({ layer, viewport }) => { + if (viewport.id === "view1") { + return layer.id === "cog-layer-1985"; + } + if (viewport.id === "view2") { + return layer.id === "cog-layer-2024"; + } + return true; + }, + widgets: [ + new SplitterWidget({ + viewId1: "view1", + viewId2: "view2", + orientation: "vertical", + }), + ], + }); + + deckRef.current = deck; + + return () => { + deck.finalize(); + deckRef.current = null; + }; + }, []); + + // Update layers when debug settings change + useEffect(() => { + if (!deckRef.current) return; + + const cog_layer_1985 = new COGLayer({ + id: "cog-layer-1985", + geotiff: COG_URL_1985, + debug, + debugOpacity, + geoKeysParser, + operation: "draw", + }); + + const cog_layer_2024 = new COGLayer({ + id: "cog-layer-2024", + geotiff: COG_URL_2024, + debug, + debugOpacity, + geoKeysParser, + operation: "draw", + }); + + deckRef.current.setProps({ + layers: [ + cog_layer_1985.clone({ operation: "draw" }), + cog_layer_2024.clone({ operation: "draw" }), + ], + }); + }, [debug, debugOpacity]); + + return ( +
+ ); +} diff --git a/examples/land-cover/src/components/InfoPanel.tsx b/examples/land-cover/src/components/InfoPanel.tsx index 6fd5267..82f7386 100644 --- a/examples/land-cover/src/components/InfoPanel.tsx +++ b/examples/land-cover/src/components/InfoPanel.tsx @@ -4,8 +4,10 @@ import { Legend } from "./Legend"; interface InfoPanelProps { debug: boolean; debugOpacity: number; + comparisonMode: boolean; onDebugChange: (checked: boolean) => void; onDebugOpacityChange: (opacity: number) => void; + onComparisonModeChange: (checked: boolean) => void; } const helpIconTooltip = ` @@ -17,8 +19,10 @@ Triangles depict the GPU-based reprojection. Instead of per-pixel reprojection, export function InfoPanel({ debug, debugOpacity, + comparisonMode, onDebugChange, onDebugOpacityChange, + onComparisonModeChange, }: InfoPanelProps) { return (
+
+ +
+
=18'} @@ -1809,6 +1818,9 @@ packages: potpack@2.1.0: resolution: {integrity: sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==} + preact@10.28.2: + resolution: {integrity: sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA==} + process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -2538,6 +2550,12 @@ snapshots: transitivePeerDependencies: - '@loaders.gl/core' + '@deck.gl/widgets@9.2.5(@deck.gl/core@9.2.5)(@luma.gl/core@9.2.5)': + dependencies: + '@deck.gl/core': 9.2.5 + '@luma.gl/core': 9.2.5 + preact: 10.28.2 + '@esbuild/aix-ppc64@0.27.2': optional: true @@ -3814,6 +3832,8 @@ snapshots: potpack@2.1.0: {} + preact@10.28.2: {} + process-nextick-args@2.0.1: {} proj4@2.20.2: