Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,14 @@
"docs": "typedoc"
},
"dependencies": {
"@chunkd/middleware": "^11.3.0",
"@chunkd/source": "^11.4.0",
"@chunkd/source-http": "^11.4.0",
"@deck.gl/core": "^9.3.1",
"@deck.gl/layers": "^9.3.1",
"@deck.gl/mapbox": "^9.3.1",
"@developmentseed/deck.gl-geotiff": "^0.6.0",
"@developmentseed/geotiff": "^0.6.1",
"@developmentseed/proj": "^0.6.1",
"@devseed-ui/collecticons-chakra": "^4.0.0",
"@duckdb/duckdb-wasm": "^1.32.0",
Expand Down
23 changes: 15 additions & 8 deletions src/components/visualization.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { useGeoTIFF } from "@/hooks/stac";
import { useStore } from "@/store";
import type { StacAssets, StacItemCollection } from "@/types/stac";
import { loadGeoTIFF } from "@/utils/geotiff";
import { getCogHref, sanitizeBbox } from "@/utils/stac";
import {
Checkbox,
Expand Down Expand Up @@ -109,22 +111,26 @@ export default function Visualization({
const setLayer = useStore((store) => store.setLayer);
const setMaplibreLayer = useStore((store) => store.setMaplibreLayer);

const selectedCogHref = useMemo(() => {
if (!selected?.startsWith("asset:")) return undefined;
const asset = assets[selected.slice("asset:".length)];
return asset && getCogHref(asset);
}, [selected, assets]);

const { data: selectedCogGeotiff } = useGeoTIFF(selectedCogHref);

useEffect(() => {
if (!enabled || !selected) return;
if (selected.startsWith("items:")) return;

if (selected.startsWith("asset:")) {
const assetKey = selected.slice("asset:".length);
const asset = assets[assetKey];
if (!asset) return;
const cogHref = getCogHref(asset);
if (!cogHref) return;
if (!selectedCogGeotiff) return;
const layerId = "visualization";
setLayer(
layerId,
new COGLayer({
id: layerId,
geotiff: cogHref,
geotiff: selectedCogGeotiff,
})
);
return () => setLayer(layerId, undefined);
Expand Down Expand Up @@ -163,7 +169,7 @@ export default function Visualization({
}, [
enabled,
selected,
assets,
selectedCogGeotiff,
tilejsonLink,
wmtsLink,
setLayer,
Expand Down Expand Up @@ -265,8 +271,9 @@ function PageLayer({
new MosaicLayer({
id,
sources,
getSource: async (source) => source.assets.cog.href,
getSource: async (source) => loadGeoTIFF(source.assets.cog.href),
renderSource: (source, { data, signal }) => {
if (!data) return null;
const href = source.assets.cog.href;
return new COGLayer({
id: `cog-${href}`,
Expand Down
11 changes: 11 additions & 0 deletions src/hooks/stac.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useStore } from "@/store";
import { loadGeoTIFF } from "@/utils/geotiff";
import {
fetchStacGeoparquetItem,
fetchStacGeoparquetTable,
Expand Down Expand Up @@ -64,3 +65,13 @@ export function useStacGeoparquetItem({
fetchStacGeoparquetItem({ href, connection, hivePartitioning, id }),
});
}

export function useGeoTIFF(href: string | undefined) {
return useQuery({
queryKey: ["geotiff", href],
queryFn: async () => loadGeoTIFF(href!),
enabled: !!href,
staleTime: Infinity,
gcTime: Infinity,
});
}
31 changes: 31 additions & 0 deletions src/utils/geotiff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { SourceCache, SourceChunk } from "@chunkd/middleware";
import { SourceView } from "@chunkd/source";
import { SourceHttp } from "@chunkd/source-http";
import { GeoTIFF } from "@developmentseed/geotiff";

/**
* Load a `GeoTIFF` from a URL, priming the underlying HTTP source's metadata
* via a HEAD request before wrapping it in the chunked view.
*
* Mirrors `GeoTIFF.fromUrl`, but the upfront HEAD works around a
* `@chunkd/source-http` regression where `Content-Range` parsing on
* range-fetched responses can leave `metadata.size` unset, breaking COG reads
* (see https://github.com/blacha/chunkd/pull/1666 and
* https://github.com/developmentseed/stac-map/issues/459). `SourceHttp.fetch`
* will not overwrite `metadata` once populated, so the HEAD result wins.
*/
export async function loadGeoTIFF(
href: string,
options: { chunkSize?: number; cacheSize?: number } = {}
): Promise<GeoTIFF> {
const { chunkSize = 32 * 1024, cacheSize = 1024 * 1024 } = options;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI I'm currently doing brainstorming for how to overhaul this. This currently will split up all header requests into 32kb at a time, which is pretty bad. For a really big input COG, I'll see like 20 sequential small requests for the header.

In developmentseed/deck.gl-raster#509 I'm trying to switch to have a "multiplying read-ahead range". So if you don't read the entire header in the first 32kb, then read, say, 64kb, and then read, say, 128kb, and so on.

const source = new SourceHttp(href, {});
await source.head();
const chunk = new SourceChunk({ size: chunkSize });
const cache = new SourceCache({ size: cacheSize });
const view = new SourceView(source, [chunk, cache]);
return await GeoTIFF.open({
dataSource: source,
headerSource: view,
});
}
4 changes: 2 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -250,14 +250,14 @@
"@pandacss/is-valid-prop" "^1.4.2"
csstype "^3.2.3"

"@chunkd/middleware@^11.2.0":
"@chunkd/middleware@^11.2.0", "@chunkd/middleware@^11.3.0":
version "11.3.0"
resolved "https://registry.yarnpkg.com/@chunkd/middleware/-/middleware-11.3.0.tgz#4d79324a77e4b0c8437f4db3b34ead20aa08346a"
integrity sha512-9AzKHP4zX3DUawwJY4wOCCNSPJTmGQc6y1rABsLmoUQ6F5lN73/1u2FkQB0ihNIzzLYz2j86nwvdJSA6GArrtQ==
dependencies:
"@chunkd/source" "^11.4.0"

"@chunkd/source-http@^11.2.0":
"@chunkd/source-http@^11.2.0", "@chunkd/source-http@^11.4.0":
version "11.4.0"
resolved "https://registry.yarnpkg.com/@chunkd/source-http/-/source-http-11.4.0.tgz#29e9995ee363f44b430b220bfa70a20af0724d75"
integrity sha512-ZkekctSU+7m8SNWxvZNBJA4Uw+eOqhaQq9Sy6gYLMeoZCTWDleID21zjWSbh/4tuSoTN/jiA/U1N9uljWjEY/g==
Expand Down
Loading