Skip to content

Commit

Permalink
Add zarr support to exploration page
Browse files Browse the repository at this point in the history
  • Loading branch information
danielfdsilva committed Nov 17, 2023
1 parent b34e3b4 commit 56111aa
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 31 deletions.
1 change: 0 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ [email protected]

API_RASTER_ENDPOINT='https://staging-raster.delta-backend.com'
API_STAC_ENDPOINT='https://staging-stac.delta-backend.com'
API_XARRAY_ENDPOINT='https://dev-titiler-xarray.delta-backend.com/tilejson.json'

# If the app is being served in from a subfolder, the domain url must be set.
# For example, if the app is served from /mysite:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,15 @@ export function RasterTimeseries(props: RasterTimeseriesProps) {
minZoom,
points,
haveSourceParamsChanged,
generatorParams
generatorParams,
// generatorParams includes hidden and opacity
// hidden,
// opacity,
// generatorId, // - dependent on id
// sourceParams, // tracked by haveSourceParamsChanged
// Theme is stable
// theme.color?.base,
// theme.color?.primary
]
);

Expand Down
167 changes: 167 additions & 0 deletions app/scripts/components/common/map/style-generators/zarr-timeseries.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import { useEffect, useMemo, useState } from 'react';
import qs from 'qs';
import { RasterSource, RasterLayer } from 'mapbox-gl';

import { requestQuickCache } from '../utils';
import useMapStyle from '../hooks/use-map-style';
import useGeneratorParams from '../hooks/use-generator-params';
import { BaseGeneratorParams } from '../types';

import { ActionStatus, S_FAILED, S_LOADING, S_SUCCEEDED } from '$utils/status';

export interface ZarrTimeseriesProps extends BaseGeneratorParams {
id: string;
stacCol: string;
date?: Date;
sourceParams?: Record<string, any>;
stacApiEndpoint?: string;
tileApiEndpoint?: string;
zoomExtent?: number[];
onStatusChange?: (result: { status: ActionStatus; id: string }) => void;
}

export function ZarrTimeseries(props: ZarrTimeseriesProps) {
const {
id,
stacCol,
stacApiEndpoint,
tileApiEndpoint,
date,
sourceParams,
zoomExtent,
onStatusChange,
hidden,
opacity
} = props;

const { updateStyle } = useMapStyle();
const [assetUrl, setAssetUrl] = useState('');

const [minZoom] = zoomExtent ?? [0, 20];

const stacApiEndpointToUse = stacApiEndpoint ?? process.env.API_STAC_ENDPOINT;

const generatorId = `zarr-timeseries-${id}`;

//
// Get the asset url
//
useEffect(() => {
const controller = new AbortController();

async function load() {
try {
onStatusChange?.({ status: S_LOADING, id });
const data = await requestQuickCache({
url: `${stacApiEndpointToUse}/collections/${stacCol}`,
method: 'GET',
controller
});

setAssetUrl(data.assets.zarr.href);
onStatusChange?.({ status: S_SUCCEEDED, id });
} catch (error) {
if (!controller.signal.aborted) {
setAssetUrl('');
onStatusChange?.({ status: S_FAILED, id });
}
return;
}
}

load();

return () => {
controller.abort();
};
}, [id, stacCol, stacApiEndpointToUse, date, onStatusChange]);

//
// Generate Mapbox GL layers and sources for raster timeseries
//
const haveSourceParamsChanged = useMemo(
() => JSON.stringify(sourceParams),
[sourceParams]
);

const generatorParams = useGeneratorParams(props);

useEffect(
() => {
if (!tileApiEndpoint) return;

const tileParams = qs.stringify({
url: assetUrl,
time_slice: date,
...sourceParams
});

const zarrSource: RasterSource = {
type: 'raster',
url: `${tileApiEndpoint}?${tileParams}`
};

const rasterOpacity = typeof opacity === 'number' ? opacity / 100 : 1;

const zarrLayer: RasterLayer = {
id: id,
type: 'raster',
source: id,
paint: {
'raster-opacity': hidden ? 0 : rasterOpacity,
'raster-opacity-transition': {
duration: 320
}
},
minzoom: minZoom,
metadata: {
layerOrderPosition: 'raster'
}
};

const sources = {
[id]: zarrSource
};
const layers = [zarrLayer];

updateStyle({
generatorId,
sources,
layers,
params: generatorParams
});
},
// sourceParams not included, but using a stringified version of it to
// detect changes (haveSourceParamsChanged)
[
updateStyle,
id,
date,
assetUrl,
minZoom,
tileApiEndpoint,
haveSourceParamsChanged,
generatorParams
// generatorParams includes hidden and opacity
// hidden,
// opacity,
// generatorId, // - dependent on id
// sourceParams, // tracked by haveSourceParamsChanged
]
);

//
// Cleanup layers on unmount.
//
useEffect(() => {
return () => {
updateStyle({
generatorId,
sources: {},
layers: []
});
};
}, [updateStyle, generatorId]);

return null;
}
77 changes: 48 additions & 29 deletions app/scripts/components/exploration/components/map/layer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { resolveConfigFunctions } from '$components/common/map/utils';
import { RasterTimeseries } from '$components/common/map/style-generators/raster-timeseries';
import { VectorTimeseries } from '$components/common/map/style-generators/vector-timeseries';
import { ZarrTimeseries } from '$components/common/map/style-generators/zarr-timeseries';

interface LayerProps {
id: string;
Expand Down Expand Up @@ -46,34 +47,52 @@ export function Layer(props: LayerProps) {
return resolveConfigFunctions(dataset.data, bag);
}, [dataset, relevantDate]);

if (dataset.data.type === 'vector') {
return (
<VectorTimeseries
id={layerId}
stacCol={dataset.data.stacCol}
stacApiEndpoint={dataset.data.stacApiEndpoint}
date={relevantDate}
zoomExtent={params.zoomExtent}
sourceParams={params.sourceParams}
generatorOrder={order}
hidden={!isVisible}
opacity={opacity}
/>
);
} else {
return (
<RasterTimeseries
id={layerId}
stacCol={dataset.data.stacCol}
stacApiEndpoint={dataset.data.stacApiEndpoint}
tileApiEndpoint={dataset.data.tileApiEndpoint}
date={relevantDate}
zoomExtent={params.zoomExtent}
sourceParams={params.sourceParams}
generatorOrder={order}
hidden={!isVisible}
opacity={opacity}
/>
);
switch (dataset.data.type) {
case 'vector':
return (
<VectorTimeseries
id={layerId}
stacCol={dataset.data.stacCol}
stacApiEndpoint={dataset.data.stacApiEndpoint}
date={relevantDate}
zoomExtent={params.zoomExtent}
sourceParams={params.sourceParams}
generatorOrder={order}
hidden={!isVisible}
opacity={opacity}
/>
);
case 'zarr':
return (
<ZarrTimeseries
id={layerId}
stacCol={dataset.data.stacCol}
stacApiEndpoint={dataset.data.stacApiEndpoint}
tileApiEndpoint={dataset.data.tileApiEndpoint}
date={relevantDate}
zoomExtent={params.zoomExtent}
sourceParams={params.sourceParams}
generatorOrder={order}
hidden={!isVisible}
opacity={opacity}
/>
);
case 'raster':
return (
<RasterTimeseries
id={layerId}
stacCol={dataset.data.stacCol}
stacApiEndpoint={dataset.data.stacApiEndpoint}
tileApiEndpoint={dataset.data.tileApiEndpoint}
date={relevantDate}
zoomExtent={params.zoomExtent}
sourceParams={params.sourceParams}
generatorOrder={order}
hidden={!isVisible}
opacity={opacity}
/>
);
default:
throw new Error(`No layer generator for type: ${dataset.data.type}`);
}
}
29 changes: 29 additions & 0 deletions mock/datasets/sandbox.data.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,35 @@ taxonomy:
- Experimental
- Untested
layers:
- id: combined_CMIP6_daily_GISS-E2-1-G_tas_kerchunk_DEMO
stacCol: combined_CMIP6_daily_GISS-E2-1-G_tas_kerchunk_DEMO
name: CMIP6 Daily GISS-E2-1-G Near-Surface Air Temperature (demo subset)
type: zarr
tileApiEndpoint: https://dev-titiler-xarray.delta-backend.com/tilejson.json
description: "Historical (1950-2014) daily-mean near-surface (usually, 2 meter) air temperature in Kelvin."
zoomExtent:
- 0
- 20
sourceParams:
reference: "true"
resampling_method: bilinear
variable: tas
colormap_name: coolwarm
rescale: 232,312
maxzoom: 12
legend:
unit:
label: K
type: gradient
min: 232
max: 312
stops:
- '#3b4cc0'
- '#7b9ff9'
- '#c0d4f5'
- '#f2cbb7'
- '#ee8468'
- '#b40426'
- id: blue-tarp-planetscope
stacCol: blue-tarp-planetscope
name: Blue tarp test
Expand Down

0 comments on commit 56111aa

Please sign in to comment.