Skip to content
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

Titiler-cmr layer #1001

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from 14 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
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react';

import { BaseGeneratorParams } from '../types';
import { ZarrPaintLayer } from './zarr-timeseries';
import { useCMR } from './hooks';
import { useCMRSTAC } from './hooks';
import { ActionStatus } from '$utils/status';
import { RasterPaintLayer } from '$components/common/mapbox/layers/raster-paint-layer';

interface AssetUrlReplacement {
from: string;
Expand All @@ -30,9 +30,10 @@ export function CMRTimeseries(props:CMRTimeseriesProps) {
date,
assetUrlReplacements,
onStatusChange,
sourceParams
} = props;

const stacApiEndpointToUse = stacApiEndpoint?? process.env.API_STAC_ENDPOINT;
const assetUrl = useCMR({ id, stacCol, stacApiEndpointToUse, date, assetUrlReplacements, stacApiEndpoint, onStatusChange });
return <ZarrPaintLayer {...props} assetUrl={assetUrl} />;
const tileParams = useCMRSTAC({ id, stacCol, stacApiEndpointToUse, date, assetUrlReplacements, stacApiEndpoint, onStatusChange, sourceParams });
return <RasterPaintLayer {...props} tileParams={tileParams} />;
}
101 changes: 88 additions & 13 deletions app/scripts/components/common/map/style-generators/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface ZarrResponseData {
zarr: {
href: string
}
}
},
}
interface CMRResponseData {
features: {
Expand All @@ -23,8 +23,13 @@ interface CMRResponseData {
}[]
}

export function useZarr({ id, stacCol, stacApiEndpointToUse, date, onStatusChange }){
const [assetUrl, setAssetUrl] = useState('');
interface STACforCMRResponseData {
collection_concept_id: string;
renders: Record<string, any>;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder 🤔 if the backend team has json schemas we could generate into typescript interfaces to import, interested.. I'll follow up with them on this


export function useZarr({ id, stacCol, stacApiEndpointToUse, date, onStatusChange, sourceParams }){
const [tileParams, setTileParams] = useState({});

useEffect(() => {
const controller = new AbortController();
Expand All @@ -38,11 +43,19 @@ export function useZarr({ id, stacCol, stacApiEndpointToUse, date, onStatusChang
controller
});

setAssetUrl(data.assets.zarr.href);
const tileParams = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

🔧 : This matches the local state variable's name, can we change this so it isn't confusing? Or i'd just set state directly with the object like so...

        if (data.assets.zarr.href) {
          setTileParams({
             url: data.assets.zarr.href,
             time_slice: date,
             ...sourceParams
           });
        }

url: data.assets.zarr.href,
time_slice: date,
...sourceParams
};
if (data.assets.zarr.href) {
setTileParams(tileParams);
}

onStatusChange?.({ status: S_SUCCEEDED, id });
} catch (error) {
if (!controller.signal.aborted) {
setAssetUrl('');
setTileParams({});
onStatusChange?.({ status: S_FAILED, id });
}
return;
Expand All @@ -54,15 +67,15 @@ export function useZarr({ id, stacCol, stacApiEndpointToUse, date, onStatusChang
return () => {
controller.abort();
};
}, [id, stacCol, stacApiEndpointToUse, date, onStatusChange]);
}, [id, stacCol, stacApiEndpointToUse, date, onStatusChange, sourceParams]);

return assetUrl;
return tileParams;
}



export function useCMR({ id, stacCol, stacApiEndpointToUse, date, assetUrlReplacements, stacApiEndpoint, onStatusChange }){
const [assetUrl, setAssetUrl] = useState('');
export function useCMRSTAC({ id, stacCol, stacApiEndpointToUse, date, assetUrlReplacements, stacApiEndpoint, onStatusChange, sourceParams }){
const [tileParams, setTileParams] = useState({});

const replaceInAssetUrl = (url: string, replacement: AssetUrlReplacement) => {
const {from, to } = replacement;
Expand Down Expand Up @@ -90,11 +103,73 @@ export function useCMR({ id, stacCol, stacApiEndpointToUse, date, assetUrlReplac
});

const assetUrl = replaceInAssetUrl(data.features[0].assets.data.href, assetUrlReplacements);
setAssetUrl(assetUrl);
setTileParams({
url: assetUrl,
time_slice: date,
...sourceParams
});
onStatusChange?.({ status: S_SUCCEEDED, id });
} catch (error) {
if (!controller.signal.aborted) {
setTileParams({});
onStatusChange?.({ status: S_FAILED, id });
}
return;
}
}

load();

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

return tileParams;

}


export function useTitilerCMR({ id, stacCol, stacApiEndpointToUse, date, stacApiEndpoint, onStatusChange, sourceParams }){
const [tileParams, setTileParams] = useState({});

useEffect(() => {
const controller = new AbortController();

async function load() {
try {
onStatusChange?.({ status: S_LOADING, id });

const data: STACforCMRResponseData = await requestQuickCache({
url: `${stacApiEndpointToUse}/collections/${stacCol}`,
method: 'GET',
controller
});

let tileParams = {
concept_id: data.collection_concept_id,
datetime: date,
...sourceParams
Copy link
Collaborator

Choose a reason for hiding this comment

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

🔧 : This matches the naming of local state variable, can we change the name so it isn't confusing.

};

// pick out the variable from the sourceParams and use it to get the renders params
// see all ZarrReader Options: https://github.com/developmentseed/titiler-cmr/blob/develop/titiler/cmr/factory.py#L433-L452
const variable = sourceParams?.variable || null;
if (variable != null) {
tileParams.variable = variable;
if (data.renders[variable]) {
// what's in sourceParams will override what's in the renders object
tileParams = { ...data.renders[variable], ...tileParams };
}
}
// if it's a COG collection we would want to use the bands parameter
// see all Rasterio Reader Options: https://github.com/developmentseed/titiler-cmr/blob/develop/titiler/cmr/factory.py#L454-L498

setTileParams(tileParams);
onStatusChange?.({ status: S_SUCCEEDED, id });
} catch (error) {
if (!controller.signal.aborted) {
setAssetUrl('');
setTileParams({});
onStatusChange?.({ status: S_FAILED, id });
}
return;
Expand All @@ -106,8 +181,8 @@ export function useCMR({ id, stacCol, stacApiEndpointToUse, date, assetUrlReplac
return () => {
controller.abort();
};
}, [id, stacCol, stacApiEndpointToUse, date, assetUrlReplacements, stacApiEndpoint, onStatusChange]);
}, [id, stacCol, stacApiEndpointToUse, date, stacApiEndpoint, onStatusChange, sourceParams]);

return assetUrl;
return tileParams;

}
128 changes: 5 additions & 123 deletions app/scripts/components/common/map/style-generators/zarr-timeseries.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import React, { useEffect, useMemo } from 'react';
import qs from 'qs';
import { RasterSource, RasterLayer } from 'mapbox-gl';

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

import { useZarr } from './hooks';
import { ActionStatus } from '$utils/status';
import { RasterPaintLayer } from '$components/common/mapbox/layers/raster-paint-layer';

export interface ZarrTimeseriesProps extends BaseGeneratorParams {
id: string;
Expand All @@ -20,131 +16,17 @@ export interface ZarrTimeseriesProps extends BaseGeneratorParams {
onStatusChange?: (result: { status: ActionStatus; id: string }) => void;
}

interface ZarrPaintLayerProps extends BaseGeneratorParams {
id: string;
date?: Date;
sourceParams?: Record<string, any>;
tileApiEndpoint?: string;
zoomExtent?: number[];
assetUrl: string;
}

export function ZarrPaintLayer(props: ZarrPaintLayerProps) {
const {
id,
tileApiEndpoint,
date,
sourceParams,
zoomExtent,
assetUrl,
hidden,
opacity
} = props;

const { updateStyle } = useMapStyle();
const [minZoom] = zoomExtent ?? [0, 20];
const generatorId = `zarr-timeseries-${id}`;

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

const generatorParams = useGeneratorParams(props);

useEffect(
() => {
if (!assetUrl) 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;
}

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

const stacApiEndpointToUse = stacApiEndpoint?? process.env.API_STAC_ENDPOINT;
const assetUrl = useZarr({id, stacCol, stacApiEndpointToUse, date, onStatusChange});
return <ZarrPaintLayer {...props} assetUrl={assetUrl} />;
const tileParams = useZarr({id, stacCol, stacApiEndpointToUse, date, onStatusChange, sourceParams});
return <RasterPaintLayer {...props} tileParams={tileParams} />;
}
2 changes: 1 addition & 1 deletion app/scripts/components/common/mapbox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ function MapboxMapComponent(
// Include access to raw data.
const bag = { ...resolverBag, raw: baseLayer.data };
const data = resolveConfigFunctions(baseLayer.data, bag);

return [data, getLayerComponent(!!data.timeseries, data.type)];
}, [baseLayer, resolverBag]);

Expand Down
34 changes: 7 additions & 27 deletions app/scripts/components/common/mapbox/layers/cmr-timeseries.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,22 @@
import React from 'react';
import { Map as MapboxMap } from 'mapbox-gl';

import { ZarrPaintLayer } from './zarr-timeseries';
import { ActionStatus } from '$utils/status';
import { RasterPaintLayer } from './raster-paint-layer';

import { useCMR } from '$components/common/map/style-generators/hooks';
import { MapLayerRasterTimeseriesProps } from './raster-timeseries';
import { useCMRSTAC } from '$components/common/map/style-generators/hooks';

interface AssetUrlReplacement {
from: string;
to: string;
}

export interface MapLayerCMRTimeseriesProps {
id: string;
stacCol: string;
date?: Date;
mapInstance: MapboxMap;
sourceParams?: Record<string, any>;
stacApiEndpoint?: string;
tileApiEndpoint?: string;
assetUrlReplacements?: AssetUrlReplacement;
zoomExtent?: number[];
onStatusChange?: (result: { status: ActionStatus; id: string }) => void;
isHidden?: boolean;
idSuffix?: string;
}

export function MapLayerCMRTimeseries(props:MapLayerCMRTimeseriesProps) {
export function MapLayerCMRTimeseries(props:MapLayerRasterTimeseriesProps) {
const {
id,
stacCol,
stacApiEndpoint,
date,
assetUrlReplacements,
onStatusChange,
sourceParams,
} = props;

const stacApiEndpointToUse = stacApiEndpoint?? process.env.API_STAC_ENDPOINT;
const assetUrl = useCMR({ id, stacCol, stacApiEndpointToUse, date, assetUrlReplacements, stacApiEndpoint, onStatusChange });
return <ZarrPaintLayer {...props} assetUrl={assetUrl} />;
const tileParams = useCMRSTAC({ id, stacCol, stacApiEndpointToUse, date, assetUrlReplacements, stacApiEndpoint, onStatusChange, sourceParams });
return <RasterPaintLayer {...props} tileParams={tileParams} />;
}
Loading
Loading