From 92f05747e5928f053201631fcc1ba22a16029eb7 Mon Sep 17 00:00:00 2001
From: Hanbyul Jo
Date: Fri, 22 Sep 2023 13:06:02 -0400
Subject: [PATCH 01/53] Thorw errors if too many statistics are requested
---
app/scripts/components/analysis/results/timeseries-data.ts | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/app/scripts/components/analysis/results/timeseries-data.ts b/app/scripts/components/analysis/results/timeseries-data.ts
index eaae0ffc9..8fc621d32 100644
--- a/app/scripts/components/analysis/results/timeseries-data.ts
+++ b/app/scripts/components/analysis/results/timeseries-data.ts
@@ -85,6 +85,8 @@ interface StacDatasetsTimeseriesEvented {
off: (event: 'data') => void;
}
+const MAX_QUERY_NUM = 300;
+
export function requestStacDatasetsTimeseries({
start,
end,
@@ -252,6 +254,11 @@ async function requestTimeseries({
const { assets, ...otherCollectionProps } = layerInfoFromSTAC;
+ if (assets.length > MAX_QUERY_NUM)
+ throw Error(
+ `Too many requests. We currently only allow requests up to ${MAX_QUERY_NUM} and this analysis requires ${assets.length} requests.`
+ );
+
onData({
...layersBase,
status: 'loading',
From 441f059f32c05892031cfdffb5c0017a98072229 Mon Sep 17 00:00:00 2001
From: Erik Escoffier
Date: Mon, 25 Sep 2023 16:25:35 +0200
Subject: [PATCH 02/53] Return the guessed number of items based on collection
metadata
---
.../define/use-stac-collection-search.ts | 50 +++++++++++++++++--
.../analysis/results/timeseries-data.ts | 2 +-
2 files changed, 48 insertions(+), 4 deletions(-)
diff --git a/app/scripts/components/analysis/define/use-stac-collection-search.ts b/app/scripts/components/analysis/define/use-stac-collection-search.ts
index 2fed3097f..975172783 100644
--- a/app/scripts/components/analysis/define/use-stac-collection-search.ts
+++ b/app/scripts/components/analysis/define/use-stac-collection-search.ts
@@ -4,8 +4,9 @@ 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, eachDayOfInterval, eachMonthOfInterval, eachYearOfInterval } from 'date-fns';
+import { TimeseriesDataResult } from '../results/timeseries-data';
import { allAvailableDatasetsLayers } from '.';
import { utcString2userTzDate } from '$utils/date';
@@ -16,6 +17,12 @@ interface UseStacSearchProps {
aoi?: FeatureCollection | null;
}
+const DATE_INTERVAL_FN = {
+ day: eachDayOfInterval,
+ month: eachMonthOfInterval,
+ year: eachYearOfInterval
+};
+
const collectionUrl = `${process.env.API_STAC_ENDPOINT}/collections`;
export function useStacCollectionSearch({
@@ -47,13 +54,36 @@ export function useStacCollectionSearch({
}
}, [result.data, aoi, start, end]);
+ const selectableDatasetLayersWithNumberOfItems = selectableDatasetLayers.map(
+ (l) => {
+ const numberOfItems = getNumberOfItemsWithinTimeRange(
+ start,
+ end,
+ l
+ );
+ return { ...l, numberOfItems };
+ }
+ );
+
return {
- selectableDatasetLayers: selectableDatasetLayers,
+ selectableDatasetLayers: selectableDatasetLayersWithNumberOfItems,
stacSearchStatus: result.status,
readyToLoadDatasets
};
}
+function getNumberOfItemsWithinTimeRange(start, end, collection) {
+ const {isPeriodic, timeDensity, domain, timeseries} = collection;
+ if (!isPeriodic) {
+ return timeseries.length; // Check in with back-end team
+ }
+ const eachOf = DATE_INTERVAL_FN[timeDensity];
+ const statStart = +(new Date(domain[0])) > +(new Date(start))? new Date(domain[0]): new Date(start);
+ const statEnd = +(new Date(domain[1])) < +(new Date(end))? new Date(domain[1]): new Date(end);
+
+ return eachOf({start: statStart, end: statEnd}).length;
+}
+
function getInTemporalAndSpatialExtent(collectionData, aoi, timeRange) {
const matchingCollectionIds = collectionData.reduce((acc, col) => {
const id = col.id;
@@ -95,7 +125,21 @@ function getInTemporalAndSpatialExtent(collectionData, aoi, timeRange) {
}
}, []);
- return allAvailableDatasetsLayers.filter((l) =>
+ const filteredDatasets = allAvailableDatasetsLayers.filter((l) =>
matchingCollectionIds.includes(l.stacCol)
);
+
+ const filteredDatasetsWithCollections: TimeseriesDataResult[] =
+ filteredDatasets.map((l) => {
+ const collection = collectionData.find((c) => c.id === l.stacCol);
+ return {
+ ...l,
+ isPeriodic: collection['dashboard:is_periodic'],
+ timeDensity: collection['dashboard:time_density'],
+ domain: collection.extent.temporal.interval[0],
+ timeseries: collection.summaries.datetime
+ };
+ });
+
+ return filteredDatasetsWithCollections;
}
diff --git a/app/scripts/components/analysis/results/timeseries-data.ts b/app/scripts/components/analysis/results/timeseries-data.ts
index 8fc621d32..628c7e4b4 100644
--- a/app/scripts/components/analysis/results/timeseries-data.ts
+++ b/app/scripts/components/analysis/results/timeseries-data.ts
@@ -30,7 +30,7 @@ export interface TimeseriesDataUnit {
percentile_98: number;
}
-interface TimeseriesDataResult {
+export interface TimeseriesDataResult {
isPeriodic: boolean;
timeDensity: TimeDensity;
domain: string[];
From 42de6bc77f92cbd8b0e234b83b73dc8caa45ce83 Mon Sep 17 00:00:00 2001
From: Erik Escoffier
Date: Mon, 25 Sep 2023 17:12:48 +0200
Subject: [PATCH 03/53] Fixed types and variable names
---
.../analysis/{define => }/constants.ts | 2 +
.../analysis/define/aoi-selector.tsx | 2 +-
.../define/use-stac-collection-search.ts | 42 ++++++++++++-------
.../analysis/results/timeseries-data.ts | 3 +-
4 files changed, 32 insertions(+), 17 deletions(-)
rename app/scripts/components/analysis/{define => }/constants.ts (94%)
diff --git a/app/scripts/components/analysis/define/constants.ts b/app/scripts/components/analysis/constants.ts
similarity index 94%
rename from app/scripts/components/analysis/define/constants.ts
rename to app/scripts/components/analysis/constants.ts
index 79c6101ba..bee2fe167 100644
--- a/app/scripts/components/analysis/define/constants.ts
+++ b/app/scripts/components/analysis/constants.ts
@@ -27,3 +27,5 @@ export const FeatureByRegionPreset: Record<
}
])
};
+
+export const MAX_QUERY_NUM = 300;
\ No newline at end of file
diff --git a/app/scripts/components/analysis/define/aoi-selector.tsx b/app/scripts/components/analysis/define/aoi-selector.tsx
index 2915c87c4..ce7b05da7 100644
--- a/app/scripts/components/analysis/define/aoi-selector.tsx
+++ b/app/scripts/components/analysis/define/aoi-selector.tsx
@@ -25,7 +25,7 @@ import {
CollecticonPencil,
CollecticonUpload2
} from '@devseed-ui/collecticons';
-import { FeatureByRegionPreset, RegionPreset } from './constants';
+import { FeatureByRegionPreset, RegionPreset } from '../constants';
import AoIUploadModal from './aoi-upload-modal';
import {
Fold,
diff --git a/app/scripts/components/analysis/define/use-stac-collection-search.ts b/app/scripts/components/analysis/define/use-stac-collection-search.ts
index 975172783..a95d2b6d3 100644
--- a/app/scripts/components/analysis/define/use-stac-collection-search.ts
+++ b/app/scripts/components/analysis/define/use-stac-collection-search.ts
@@ -4,7 +4,13 @@ import axios from 'axios';
import { useQuery } from '@tanstack/react-query';
import booleanIntersects from '@turf/boolean-intersects';
import bboxPolygon from '@turf/bbox-polygon';
-import { areIntervalsOverlapping, eachDayOfInterval, eachMonthOfInterval, eachYearOfInterval } from 'date-fns';
+import {
+ areIntervalsOverlapping,
+ eachDayOfInterval,
+ eachMonthOfInterval,
+ eachYearOfInterval
+} from 'date-fns';
+import { DatasetLayer } from 'veda';
import { TimeseriesDataResult } from '../results/timeseries-data';
import { allAvailableDatasetsLayers } from '.';
@@ -17,6 +23,8 @@ interface UseStacSearchProps {
aoi?: FeatureCollection | null;
}
+export type DatasetWithTimeseriesData = TimeseriesDataResult & DatasetLayer & { numberOfItems?: number };
+
const DATE_INTERVAL_FN = {
day: eachDayOfInterval,
month: eachMonthOfInterval,
@@ -54,13 +62,9 @@ export function useStacCollectionSearch({
}
}, [result.data, aoi, start, end]);
- const selectableDatasetLayersWithNumberOfItems = selectableDatasetLayers.map(
+ const selectableDatasetLayersWithNumberOfItems: DatasetWithTimeseriesData[] = selectableDatasetLayers.map(
(l) => {
- const numberOfItems = getNumberOfItemsWithinTimeRange(
- start,
- end,
- l
- );
+ const numberOfItems = getNumberOfItemsWithinTimeRange(start, end, l);
return { ...l, numberOfItems };
}
);
@@ -72,16 +76,26 @@ export function useStacCollectionSearch({
};
}
-function getNumberOfItemsWithinTimeRange(start, end, collection) {
- const {isPeriodic, timeDensity, domain, timeseries} = collection;
+/**
+ * For each collection, get the number of items within the time range,
+ * taking into account the time density.
+ */
+function getNumberOfItemsWithinTimeRange(userStart, userEnd, collection) {
+ const { isPeriodic, timeDensity, domain, timeseries } = collection;
if (!isPeriodic) {
return timeseries.length; // Check in with back-end team
}
const eachOf = DATE_INTERVAL_FN[timeDensity];
- const statStart = +(new Date(domain[0])) > +(new Date(start))? new Date(domain[0]): new Date(start);
- const statEnd = +(new Date(domain[1])) < +(new Date(end))? new Date(domain[1]): new Date(end);
-
- return eachOf({start: statStart, end: statEnd}).length;
+ const start =
+ +new Date(domain[0]) > +new Date(userStart)
+ ? new Date(domain[0])
+ : new Date(userStart);
+ const end =
+ +new Date(domain[1]) < +new Date(userEnd)
+ ? new Date(domain[1])
+ : new Date(userEnd);
+
+ return eachOf({ start, end }).length;
}
function getInTemporalAndSpatialExtent(collectionData, aoi, timeRange) {
@@ -129,7 +143,7 @@ function getInTemporalAndSpatialExtent(collectionData, aoi, timeRange) {
matchingCollectionIds.includes(l.stacCol)
);
- const filteredDatasetsWithCollections: TimeseriesDataResult[] =
+ const filteredDatasetsWithCollections =
filteredDatasets.map((l) => {
const collection = collectionData.find((c) => c.id === l.stacCol);
return {
diff --git a/app/scripts/components/analysis/results/timeseries-data.ts b/app/scripts/components/analysis/results/timeseries-data.ts
index 628c7e4b4..9d8b16ba8 100644
--- a/app/scripts/components/analysis/results/timeseries-data.ts
+++ b/app/scripts/components/analysis/results/timeseries-data.ts
@@ -3,6 +3,7 @@ import { QueryClient } from '@tanstack/react-query';
import { FeatureCollection, Polygon } from 'geojson';
import { DatasetLayer } from 'veda';
+import { MAX_QUERY_NUM } from '../constants';
import { getFilterPayload, combineFeatureCollection } from '../utils';
import EventEmitter from './mini-events';
import { ConcurrencyManager, ConcurrencyManagerInstance } from './concurrency';
@@ -85,8 +86,6 @@ interface StacDatasetsTimeseriesEvented {
off: (event: 'data') => void;
}
-const MAX_QUERY_NUM = 300;
-
export function requestStacDatasetsTimeseries({
start,
end,
From c0f6b72288c6eec12435325ce8f5f720c6e711e0 Mon Sep 17 00:00:00 2001
From: Erik Escoffier
Date: Mon, 25 Sep 2023 17:14:17 +0200
Subject: [PATCH 04/53] Memoize datasets
---
.../define/use-stac-collection-search.ts | 37 ++++++++++---------
1 file changed, 19 insertions(+), 18 deletions(-)
diff --git a/app/scripts/components/analysis/define/use-stac-collection-search.ts b/app/scripts/components/analysis/define/use-stac-collection-search.ts
index a95d2b6d3..66bfd7d3c 100644
--- a/app/scripts/components/analysis/define/use-stac-collection-search.ts
+++ b/app/scripts/components/analysis/define/use-stac-collection-search.ts
@@ -23,7 +23,8 @@ interface UseStacSearchProps {
aoi?: FeatureCollection | null;
}
-export type DatasetWithTimeseriesData = TimeseriesDataResult & DatasetLayer & { numberOfItems?: number };
+export type DatasetWithTimeseriesData = TimeseriesDataResult &
+ DatasetLayer & { numberOfItems?: number };
const DATE_INTERVAL_FN = {
day: eachDayOfInterval,
@@ -62,12 +63,13 @@ export function useStacCollectionSearch({
}
}, [result.data, aoi, start, end]);
- const selectableDatasetLayersWithNumberOfItems: DatasetWithTimeseriesData[] = selectableDatasetLayers.map(
- (l) => {
- const numberOfItems = getNumberOfItemsWithinTimeRange(start, end, l);
- return { ...l, numberOfItems };
- }
- );
+ const selectableDatasetLayersWithNumberOfItems: DatasetWithTimeseriesData[] =
+ useMemo(() => {
+ return selectableDatasetLayers.map((l) => {
+ const numberOfItems = getNumberOfItemsWithinTimeRange(start, end, l);
+ return { ...l, numberOfItems };
+ });
+ }, [selectableDatasetLayers, start, end]);
return {
selectableDatasetLayers: selectableDatasetLayersWithNumberOfItems,
@@ -143,17 +145,16 @@ function getInTemporalAndSpatialExtent(collectionData, aoi, timeRange) {
matchingCollectionIds.includes(l.stacCol)
);
- const filteredDatasetsWithCollections =
- filteredDatasets.map((l) => {
- const collection = collectionData.find((c) => c.id === l.stacCol);
- return {
- ...l,
- isPeriodic: collection['dashboard:is_periodic'],
- timeDensity: collection['dashboard:time_density'],
- domain: collection.extent.temporal.interval[0],
- timeseries: collection.summaries.datetime
- };
- });
+ const filteredDatasetsWithCollections = filteredDatasets.map((l) => {
+ const collection = collectionData.find((c) => c.id === l.stacCol);
+ return {
+ ...l,
+ isPeriodic: collection['dashboard:is_periodic'],
+ timeDensity: collection['dashboard:time_density'],
+ domain: collection.extent.temporal.interval[0],
+ timeseries: collection.summaries.datetime
+ };
+ });
return filteredDatasetsWithCollections;
}
From 64a81aaba7b36069ee225e97a564ff1a5b2be1dd Mon Sep 17 00:00:00 2001
From: Hanbyul Jo
Date: Mon, 25 Sep 2023 16:57:08 -0400
Subject: [PATCH 05/53] Check only day level equality
---
app/scripts/components/datasets/s-explore/index.tsx | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/app/scripts/components/datasets/s-explore/index.tsx b/app/scripts/components/datasets/s-explore/index.tsx
index 7afcbe429..31fca123f 100644
--- a/app/scripts/components/datasets/s-explore/index.tsx
+++ b/app/scripts/components/datasets/s-explore/index.tsx
@@ -8,6 +8,7 @@ import React, {
import styled from 'styled-components';
import { useNavigate } from 'react-router';
import useQsStateCreator from 'qs-state-hook';
+import { isSameDay } from 'date-fns';
import { themeVal } from '@devseed-ui/theme-provider';
import {
CollecticonExpandFromLeft,
@@ -113,9 +114,10 @@ const isSelectedDateValid = (dateList, selectedDate) => {
// Since the available dates changes, check if the currently selected
// one is valid.
const validDate = !!dateList.find(
- (d) => d.getTime() === selectedDate?.getTime()
+ // Only check if the date of the selected date is in the range.
+ // Because Dashboard can only provide daily level selection.
+ (d) => isSameDay(new Date(d), new Date(selectedDate))
);
-
return !!validDate;
}
return true;
@@ -175,7 +177,6 @@ const useDatePickerValue = (
}),
[value]
);
-
return [val, onConfirm] as [typeof val, typeof onConfirm];
};
@@ -518,6 +519,7 @@ function DatasetsExplore() {
selectedDatetime,
setSelectedDatetime
);
+
const [datePickerCompareValue, datePickerCompareConfirm] = useDatePickerValue(
selectedCompareDatetime,
setSelectedCompareDatetime
From f1e7171c64e8198772ee2c9848e4cb00f9ab7fb9 Mon Sep 17 00:00:00 2001
From: Erik Escoffier
Date: Tue, 26 Sep 2023 11:06:22 +0200
Subject: [PATCH 06/53] Updated analysis flow wording
---
.../components/analysis/define/aoi-selector.tsx | 6 +++++-
app/scripts/components/analysis/define/index.tsx | 14 +++++++++++---
app/scripts/components/common/fold.tsx | 4 +++-
3 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/app/scripts/components/analysis/define/aoi-selector.tsx b/app/scripts/components/analysis/define/aoi-selector.tsx
index ce7b05da7..3bbe5a8a3 100644
--- a/app/scripts/components/analysis/define/aoi-selector.tsx
+++ b/app/scripts/components/analysis/define/aoi-selector.tsx
@@ -128,7 +128,11 @@ export default function AoiSelector({
/>
- Area
+ Select area of interest
+
+ Use the pencil tool to draw a shape on the map or upload your own
+ shapefile.
+
diff --git a/app/scripts/components/analysis/define/index.tsx b/app/scripts/components/analysis/define/index.tsx
index 503613123..0864735eb 100644
--- a/app/scripts/components/analysis/define/index.tsx
+++ b/app/scripts/components/analysis/define/index.tsx
@@ -237,7 +237,7 @@ export default function Analysis() {
description='Generate timeseries data for your area of interest.'
/>
(
- Date
+ Pick a date period
+
+ Select start and end date of time series, or choose a pre-set date
+ range.
+
Select start and end date of time series, or choose a pre-set
date range.
@@ -338,7 +382,7 @@ export default function Analysis() {
variation='base-outline'
radius='square'
>
-
+
-
+
- Select datasets
+ Select datasets
Select from available dataset layers for the area and date range
selected.
@@ -464,7 +508,7 @@ export default function Analysis() {
)}
-