Skip to content

Commit

Permalink
Improvements to analysis page from the feedback (#751)
Browse files Browse the repository at this point in the history
Contributes to US-GHG-Center/veda-config-ghg#221 to get us close to a
delivery state.

Includes the work done in #744 plus changes so that custom api endpoints
are supported in the exploration page.
Besides the former:
- Connects the data-catalog to exploration by filtering the dataset
selector modal by the veda dataset name
- Removes connections to previous analysis and dataset explore pages
- Add info icon to dataset
#747
- Ensure that the analysis range is within the current domain (bug
identified in the feedback session)
- Add support for vector datasets - I noticed that this was no
implemented in the A&E so I added it. The zarr layer support is still
not implemented, but not sure we need it immediately.
  • Loading branch information
danielfdsilva authored Nov 17, 2023
2 parents ce45256 + bc5f4af commit b34e3b4
Show file tree
Hide file tree
Showing 30 changed files with 953 additions and 160 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import axios from 'axios';
import { useQuery } from '@tanstack/react-query';
import booleanIntersects from '@turf/boolean-intersects';
import bboxPolygon from '@turf/bbox-polygon';
import {
areIntervalsOverlapping
} from 'date-fns';
import { areIntervalsOverlapping } from 'date-fns';
import { DatasetLayer } from 'veda';

import { MAX_QUERY_NUM } from '../constants';
Expand All @@ -25,7 +23,7 @@ interface UseStacSearchProps {
export type DatasetWithTimeseriesData = TimeseriesDataResult &
DatasetLayer & { numberOfItems: number };

const collectionUrl = `${process.env.API_STAC_ENDPOINT}/collections`;
const collectionEndpointSuffix = '/collections';

export function useStacCollectionSearch({
start,
Expand All @@ -37,10 +35,29 @@ export function useStacCollectionSearch({
const result = useQuery({
queryKey: ['stacCollection'],
queryFn: async ({ signal }) => {
const collectionResponse = await axios.get(collectionUrl, {
signal
});
return collectionResponse.data.collections;

const collectionUrlsFromDataSets = allAvailableDatasetsLayers
.filter((dataset) => dataset.stacApiEndpoint)
.map(
(dataset) =>
`${dataset.stacApiEndpoint}${collectionEndpointSuffix}`
);
// Get unique values of stac api endpoints from layer data, concat with api_stac_endpoiint from env var
const collectionUrls = Array.from(new Set(collectionUrlsFromDataSets))
.concat(`${process.env.API_STAC_ENDPOINT}${collectionEndpointSuffix}`);

const collectionRequests = collectionUrls.map((url: string) =>
axios.get(url, { signal }).then((response) => {
return response.data.collections.map((col) => ({
...col,
stacApiEndpoint: url.replace(collectionEndpointSuffix, '')
}));
})
);
return axios.all(collectionRequests).then(
// Merge all responses into one array
axios.spread((...responses) => [].concat(...responses))
);
},
enabled: readyToLoadDatasets
});
Expand Down Expand Up @@ -86,13 +103,15 @@ export function useStacCollectionSearch({

function getInTemporalAndSpatialExtent(collectionData, aoi, timeRange) {
const matchingCollectionIds = collectionData.reduce((acc, col) => {
const id = col.id;
const { id, stacApiEndpoint } = col;

// Is is a dataset defined in the app?
// If not, skip other calculations.
const isAppDataset = allAvailableDatasetsLayers.some(
(l) => l.stacCol === id
);
const isAppDataset = allAvailableDatasetsLayers.some((l) => {
const stacApiEndpointUsed =
l.stacApiEndpoint ?? process.env.API_STAC_ENDPOINT;
return l.stacCol === id && stacApiEndpointUsed === stacApiEndpoint;
});

if (
!isAppDataset ||
Expand All @@ -106,7 +125,7 @@ function getInTemporalAndSpatialExtent(collectionData, aoi, timeRange) {
const start = utcString2userTzDate(col.extent.temporal.interval[0][0]);
const end = utcString2userTzDate(col.extent.temporal.interval[0][1]);

const isInAOI = aoi.features.some((feature) =>
const isInAOI = aoi.features?.some((feature) =>
booleanIntersects(feature, bboxPolygon(bbox))
);

Expand All @@ -130,7 +149,11 @@ function getInTemporalAndSpatialExtent(collectionData, aoi, timeRange) {
);

const filteredDatasetsWithCollections = filteredDatasets.map((l) => {
const collection = collectionData.find((c) => c.id === l.stacCol);
const stacApiEndpointUsed =
l.stacApiEndpoint ?? process.env.API_STAC_ENDPOINT;
const collection = collectionData.find(
(c) => c.id === l.stacCol && stacApiEndpointUsed === c.stacApiEndpoint
);
return {
...l,
isPeriodic: collection['dashboard:is_periodic'],
Expand Down
21 changes: 17 additions & 4 deletions app/scripts/components/analysis/results/timeseries-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,24 +137,33 @@ export function requestStacDatasetsTimeseries({

interface DatasetAssetsRequestParams {
stacCol: string;
stacApiEndpoint?: string;
assets: string;
dateStart: Date;
dateEnd: Date;
aoi: FeatureCollection<Polygon>;
}

async function getDatasetAssets(
{ dateStart, dateEnd, stacCol, assets, aoi }: DatasetAssetsRequestParams,
{
dateStart,
dateEnd,
stacApiEndpoint,
stacCol,
assets,
aoi
}: DatasetAssetsRequestParams,
opts: AxiosRequestConfig,
concurrencyManager: ConcurrencyManagerInstance
) {
const stacApiEndpointToUse = stacApiEndpoint ?? process.env.API_STAC_ENDPOINT;
const data = await concurrencyManager.queue(async () => {
const collectionReqRes = await axios.get(
`${process.env.API_STAC_ENDPOINT}/collections/${stacCol}`
`${stacApiEndpointToUse}/collections/${stacCol}`
);

const searchReqRes = await axios.post(
`${process.env.API_STAC_ENDPOINT}/search`,
`${stacApiEndpointToUse}/search`,
{
'filter-lang': 'cql2-json',
limit: 10000,
Expand Down Expand Up @@ -238,6 +247,7 @@ async function requestTimeseries({
getDatasetAssets(
{
stacCol: layer.stacCol,
stacApiEndpoint: layer.stacApiEndpoint,
assets: layer.sourceParams?.assets || 'cog_default',
aoi,
dateStart: start,
Expand Down Expand Up @@ -267,14 +277,17 @@ async function requestTimeseries({
}
});

const tileEndpointToUse =
layer.tileApiEndpoint ?? process.env.API_RASTER_ENDPOINT;

const layerStatistics = await Promise.all(
assets.map(async ({ date, url }) => {
const statistics = await queryClient.fetchQuery(
[TIMESERIES_DATA_BASE_ID, 'asset', url],
async ({ signal }) => {
return concurrencyManager.queue(async () => {
const { data } = await axios.post(
`${process.env.API_RASTER_ENDPOINT}/cog/statistics?url=${url}`,
`${tileEndpointToUse}/cog/statistics?url=${url}`,
// Making a request with a FC causes a 500 (as of 2023/01/20)
combineFeatureCollection(aoi),
{ signal }
Expand Down
2 changes: 2 additions & 0 deletions app/scripts/components/common/blocks/scrollytelling/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,8 @@ function Scrollytelling(props) {
key={runtimeData.id}
id={runtimeData.id}
mapInstance={mapRef.current}
stacApiEndpoint={layer.stacApiEndpoint}
tileApiEndpoint={layer.tileApiEndpoint}
stacCol={layer.stacCol}
date={runtimeData.datetime}
sourceParams={layer.sourceParams}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export interface RasterTimeseriesProps extends BaseGeneratorParams {
bounds?: number[];
onStatusChange?: (result: { status: ActionStatus; id: string }) => void;
isPositionSet?: boolean;
stacApiEndpoint?: string;
tileApiEndpoint?: string;
}

enum STATUS_KEY {
Expand All @@ -70,7 +72,9 @@ export function RasterTimeseries(props: RasterTimeseriesProps) {
onStatusChange,
isPositionSet,
hidden,
opacity
opacity,
stacApiEndpoint,
tileApiEndpoint
} = props;

const { current: mapInstance } = useMaps();
Expand All @@ -81,6 +85,11 @@ export function RasterTimeseries(props: RasterTimeseriesProps) {
const minZoom = zoomExtent?.[0] ?? 0;
const generatorId = `raster-timeseries-${id}`;

const stacApiEndpointToUse =
stacApiEndpoint ?? process.env.API_STAC_ENDPOINT ?? '';
const tileApiEndpointToUse =
tileApiEndpoint ?? process.env.API_RASTER_ENDPOINT ?? '';

// Status tracking.
// A raster timeseries layer has a base layer and may have markers.
// The status is succeeded only if all requests succeed.
Expand Down Expand Up @@ -165,7 +174,7 @@ export function RasterTimeseries(props: RasterTimeseriesProps) {
/* eslint-enable no-console */

const responseData = await requestQuickCache({
url: `${process.env.API_STAC_ENDPOINT}/search`,
url: `${stacApiEndpointToUse}/search`,
payload,
controller
});
Expand Down Expand Up @@ -262,7 +271,7 @@ export function RasterTimeseries(props: RasterTimeseriesProps) {
/* eslint-enable no-console */

const responseData = await requestQuickCache({
url: `${process.env.API_RASTER_ENDPOINT}/mosaic/register`,
url: `${tileApiEndpointToUse}/mosaic/register`,
payload,
controller
});
Expand Down
Loading

0 comments on commit b34e3b4

Please sign in to comment.