Skip to content

Commit

Permalink
Create new raster paint layer module and factor out BaseTimeseriesPro…
Browse files Browse the repository at this point in the history
…pos (#1105)

**Related Ticket:** #1055
### Description of Changes
Simply put, this PR is a small refactoring to create a basic
`RasterPaintLayer` available, which both ZarrTimeseries and
CmrTimeseries use.
* Both CmrTimeseires and ZarrTimeseries were using `ZarrPaintLayer`. It
seems appropriate then to rename `ZarrPaintLayer` to `RasterPaintLayer`
and put it in a separate file.
* Merged common attributes of `Raster`, `Zarr`, and
`CmrTimeseriesPropos` into `BaseTimeseriesProps

### Notes & Questions About Changes
1. Should I create a new issue to remove the Zarr and Cmr layers from
the app/scripts/common/components/mapbox/layers?
2. I noticed that a request is made for tiles even when the selected
date falls outside of the datasets temporal range. Is this the expected
behavior? I raised an issue in titiler-xarray
(developmentseed/titiler-xarray#62) that the
API should not respond with tiles in this case, but also wondering if a
request should be made at all 🤔
* _This is probably something we need to fix in useZarr and useCmr, so
the question is, should we do that in this PR or a new PR? I would
propose a new PR to keep changes as minimal as possible._

### Validation / Testing
I loaded the layers in the deploy preview, used the show/hide and time
slider to make sure all that functionality was working as expected. I
came across the issue described in (2) under Notes & Questions About
Changes
  • Loading branch information
abarciauskas-bgse authored Aug 16, 2024
2 parents bae32d5 + 1568002 commit 41b77fb
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 171 deletions.
Original file line number Diff line number Diff line change
@@ -1,27 +1,9 @@
import React from 'react';
import { BaseGeneratorParams } from '../types';
import { ZarrPaintLayer } from './zarr-timeseries';
import { CMRTimeseriesProps } from '../types';
import { RasterPaintLayer } from './raster-paint-layer';
import { useCMR } from './hooks';
import { ActionStatus } from '$utils/status';

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

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

export function CMRTimeseries(props:CMRTimeseriesProps) {
export function CMRTimeseries(props: CMRTimeseriesProps) {
const {
id,
stacCol,
Expand All @@ -33,5 +15,5 @@ export function CMRTimeseries(props:CMRTimeseriesProps) {

const stacApiEndpointToUse = stacApiEndpoint?? process.env.API_STAC_ENDPOINT;
const assetUrl = useCMR({ id, stacCol, stacApiEndpointToUse, date, assetUrlReplacements, stacApiEndpoint, onStatusChange });
return <ZarrPaintLayer {...props} assetUrl={assetUrl} />;
return <RasterPaintLayer {...props} assetUrl={assetUrl} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { useEffect, useMemo } from 'react';
import qs from 'qs';
import { RasterSource, RasterLayer } from 'mapbox-gl';

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

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

export function RasterPaintLayer(props: RasterPaintLayerProps) {
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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from 'mapbox-gl';
import { useTheme } from 'styled-components';
import { featureCollection, point } from '@turf/helpers';
import { BaseGeneratorParams, StacFeature } from '../types';
import { RasterTimeseriesProps, StacFeature } from '../types';
import useMapStyle from '../hooks/use-map-style';
import {
FIT_BOUNDS_PADDING,
Expand All @@ -36,19 +36,6 @@ import {
// Whether or not to print the request logs.
const LOG = true;

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

enum STATUS_KEY {
Global,
Layer,
Expand Down
140 changes: 5 additions & 135 deletions app/scripts/components/common/map/style-generators/zarr-timeseries.tsx
Original file line number Diff line number Diff line change
@@ -1,140 +1,10 @@
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 { BaseGeneratorParams } from '../types';
import React from 'react';

import { BaseTimeseriesProps } from '../types';
import { useZarr } from './hooks';
import { ActionStatus } from '$utils/status';
import { RasterPaintLayer } from './raster-paint-layer';

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;
}

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) {
export function ZarrTimeseries(props: BaseTimeseriesProps) {
const {
id,
stacCol,
Expand All @@ -145,5 +15,5 @@ export function ZarrTimeseries(props:ZarrTimeseriesProps) {

const stacApiEndpointToUse = stacApiEndpoint?? process.env.API_STAC_ENDPOINT;
const assetUrl = useZarr({id, stacCol, stacApiEndpointToUse, date, onStatusChange});
return <ZarrPaintLayer {...props} assetUrl={assetUrl} />;
return <RasterPaintLayer {...props} assetUrl={assetUrl} />;
}
31 changes: 31 additions & 0 deletions app/scripts/components/common/map/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Feature, Polygon } from 'geojson';
import { AnyLayer, AnySourceImpl } from 'mapbox-gl';
import { ActionStatus } from '$utils/status';

export interface ExtendedMetadata {
layerOrderPosition?: LayerOrderPosition;
Expand Down Expand Up @@ -42,3 +43,33 @@ export type AoIFeature = Feature<Polygon> & {
selected: boolean;
id: string;
};

export interface BaseTimeseriesProps 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 interface ZarrTimeseriesProps extends BaseTimeseriesProps {
// // No additional properties, using BaseTimeseriesProps as is
// }

export interface RasterTimeseriesProps extends BaseTimeseriesProps {
bounds?: number[];
isPositionSet?: boolean;
}

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

export interface CMRTimeseriesProps extends BaseTimeseriesProps {
assetUrlReplacements?: AssetUrlReplacement;
}

0 comments on commit 41b77fb

Please sign in to comment.