Skip to content

Commit

Permalink
Merge branch 'feature/exploration' into feature/exploration-map-controls
Browse files Browse the repository at this point in the history
  • Loading branch information
nerik authored Sep 14, 2023
2 parents f027c94 + 29f79cc commit e1cbdd5
Show file tree
Hide file tree
Showing 10 changed files with 429 additions and 60 deletions.
27 changes: 24 additions & 3 deletions app/scripts/components/exploration/atoms/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,24 @@ import { focusAtom } from 'jotai-optics';
import { add, max } from 'date-fns';

import { DAY_SIZE_MAX } from '../constants';
import { TimelineDataset, TimelineDatasetStatus } from '../types.d.ts';
import {
TimeDensity,
TimelineDataset,
TimelineDatasetStatus,
TimelineDatasetSuccess
} from '../types.d.ts';
import { timelineDatasetsAtom, timelineSizesAtom } from './atoms';

function addDurationToDate(date, timeDensity: TimeDensity) {
const duration = {
[TimeDensity.YEAR]: { years: 1 },
[TimeDensity.MONTH]: { months: 1 },
[TimeDensity.DAY]: { days: 1 }
}[timeDensity];

return add(date, duration);
}

/**
* Calculates the date domain of the datasets, if any are selected.
* @returns Dataset date domain or undefined.
Expand All @@ -20,15 +35,21 @@ export function useTimelineDatasetsDomain() {

return useMemo(() => {
const successDatasets = datasets.filter(
(d) => d.status === TimelineDatasetStatus.SUCCEEDED
(d): d is TimelineDatasetSuccess =>
d.status === TimelineDatasetStatus.SUCCESS
);
if (!successDatasets.length) return undefined;

// To speed up the calculation of the extent, we assume the dataset's domain
// is ordered and only look at first and last dates.
const [start, end] = extent(
successDatasets.flatMap((d) =>
d.data.domain ? [d.data.domain[0], d.data.domain.last] : []
d.data.domain.length
? [
d.data.domain[0],
addDurationToDate(d.data.domain.last, d.data.timeDensity)
]
: []
)
) as [Date, Date];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export function DatasetTrackError(props: {
<TrackMessage>
<p>{message}</p>
{typeof onRetryClick === 'function' ? (
<Button variation='danger-fill' size='small'>
<Button variation='danger-fill' size='small' onClick={onRetryClick}>
Retry <CollecticonArrowLoop size='small' />
</Button>
) : null}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, { useCallback, useMemo } from 'react';
import { useAtomValue } from 'jotai';
import { Reorder, useDragControls } from 'framer-motion';
import styled, { useTheme } from 'styled-components';
Expand All @@ -13,6 +13,7 @@ import {
startOfYear,
areIntervalsOverlapping
} from 'date-fns';
import { useQueryClient } from '@tanstack/react-query';
import { ScaleTime } from 'd3';
import {
CollecticonEye,
Expand Down Expand Up @@ -41,8 +42,8 @@ import {
} from '$components/common/mapbox/layer-legend';
import {
TimeDensity,
TimelineDataset,
TimelineDatasetStatus
TimelineDatasetStatus,
TimelineDatasetSuccess
} from '$components/exploration/types.d.ts';
import {
DATASET_TRACK_BLOCK_HEIGHT,
Expand Down Expand Up @@ -157,6 +158,18 @@ export function DatasetListItem(props: DatasetListItemProps) {

const [isVisible, setVisible] = useTimelineDatasetVisibility(datasetAtom);

const queryClient = useQueryClient();

const retryDatasetMetadata = useCallback(() => {
queryClient.invalidateQueries(
{
queryKey: ['dataset', datasetId],
exact: true
},
{ throwOnError: false }
);
}, [queryClient, datasetId]);

const controls = useDragControls();

// Hook to handle the hover state of the dataset. Check the source file as to
Expand All @@ -174,7 +187,7 @@ export function DatasetListItem(props: DatasetListItemProps) {
xScaled,
containerWidth: width,
layerX,
data: dataset.analysis.data.timeseries
data: dataset.analysis.data?.timeseries
});

const {
Expand All @@ -187,16 +200,16 @@ export function DatasetListItem(props: DatasetListItemProps) {
data: dataPoint
});

const isDatasetError = dataset.status === TimelineDatasetStatus.ERRORED;
const isDatasetError = dataset.status === TimelineDatasetStatus.ERROR;
const isDatasetLoading = dataset.status === TimelineDatasetStatus.LOADING;
const isDatasetSucceeded = dataset.status === TimelineDatasetStatus.SUCCEEDED;
const isDatasetSuccess = dataset.status === TimelineDatasetStatus.SUCCESS;

const isAnalysisAndError =
isAnalysis && dataset.analysis.status === TimelineDatasetStatus.ERRORED;
isAnalysis && dataset.analysis.status === TimelineDatasetStatus.ERROR;
const isAnalysisAndLoading =
isAnalysis && dataset.analysis.status === TimelineDatasetStatus.LOADING;
const isAnalysisAndSucceeded =
isAnalysis && dataset.analysis.status === TimelineDatasetStatus.SUCCEEDED;
const isAnalysisAndSuccess =
isAnalysis && dataset.analysis.status === TimelineDatasetStatus.SUCCESS;

const datasetLegend = dataset.data.legend;

Expand Down Expand Up @@ -264,13 +277,12 @@ export function DatasetListItem(props: DatasetListItemProps) {
<DatasetTrackError
message='Oh no, something went wrong'
onRetryClick={() => {
/* eslint-disable-next-line no-console */
console.log('Retry metadata loading');
retryDatasetMetadata();
}}
/>
)}

{isDatasetSucceeded && (
{isDatasetSuccess && (
<>
{isAnalysisAndLoading && (
<DatasetTrackLoading
Expand All @@ -286,7 +298,7 @@ export function DatasetListItem(props: DatasetListItemProps) {
}}
/>
)}
{isAnalysisAndSucceeded && (
{isAnalysisAndSuccess && (
<DatasetChart
xScaled={xScaled!}
width={width}
Expand All @@ -299,7 +311,7 @@ export function DatasetListItem(props: DatasetListItemProps) {
</>
)}

{isDatasetSucceeded && !isAnalysis && (
{isDatasetSuccess && !isAnalysis && (
<DatasetTrack
width={width}
xScaled={xScaled!}
Expand All @@ -308,7 +320,7 @@ export function DatasetListItem(props: DatasetListItemProps) {
/>
)}

{isVisible && isPopoverVisible && dataPoint && (
{isDatasetSuccess && isVisible && isPopoverVisible && dataPoint && (
<DatasetPopover
ref={popoverRefs.setFloating}
style={floatingStyles}
Expand All @@ -326,7 +338,7 @@ export function DatasetListItem(props: DatasetListItemProps) {
interface DatasetTrackProps {
width: number;
xScaled: ScaleTime<number, number>;
dataset: TimelineDataset;
dataset: TimelineDatasetSuccess;
isVisible: boolean;
}

Expand Down Expand Up @@ -373,7 +385,7 @@ function DatasetTrack(props: DatasetTrackProps) {
interface DatasetTrackBlockProps {
xScaled: ScaleTime<number, number>;
date: Date;
dataset: TimelineDataset;
dataset: TimelineDatasetSuccess;
isVisible: boolean;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ export default function Timeline(props: TimelineProps) {
);

const successDatasets = datasets.filter(
(d) => d.status === TimelineDatasetStatus.SUCCEEDED
(d) => d.status === TimelineDatasetStatus.SUCCESS
);

// When a loaded dataset is added from an empty state, compute the correct
Expand Down
57 changes: 54 additions & 3 deletions app/scripts/components/exploration/data-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
import {
eachDayOfInterval,
eachMonthOfInterval,
eachYearOfInterval
} from 'date-fns';
import { DatasetLayer, datasets } from 'veda';
import { TimelineDataset, TimelineDatasetStatus } from './types.d.ts';
import {
StacDatasetData,
TimeDensity,
TimelineDataset,
TimelineDatasetStatus
} from './types.d.ts';

import { utcString2userTzDate } from '$utils/date';

export const findParentDataset = (layerId: string) => {
const parentDataset = Object.values(datasets).find((dataset) =>
Expand All @@ -24,7 +36,7 @@ export function reconcileDatasets(
ids: string[],
datasetsList: DatasetLayer[],
reconciledDatasets: TimelineDataset[]
) {
): TimelineDataset[] {
return ids.map((id) => {
const alreadyReconciled = reconciledDatasets.find((d) => d.data.id === id);

Expand All @@ -34,6 +46,10 @@ export function reconcileDatasets(

const dataset = datasetsList.find((d) => d.id === id);

if (!dataset) {
throw new Error(`Dataset [${id}] not found`);
}

return {
status: TimelineDatasetStatus.IDLE,
data: dataset,
Expand All @@ -44,10 +60,45 @@ export function reconcileDatasets(
},
analysis: {
status: TimelineDatasetStatus.IDLE,
data: {},
data: null,
error: null,
meta: {}
}
};
});
}

export function resolveLayerTemporalExtent(
datasetId: string,
datasetData: StacDatasetData
): Date[] {
const { domain, isPeriodic, timeDensity } = datasetData;

if (!domain || domain.length === 0) {
throw new Error(`Invalid domain on dataset [${datasetId}]`);
}

if (!isPeriodic) return domain.map((d) => utcString2userTzDate(d));

switch (timeDensity) {
case TimeDensity.YEAR:
return eachYearOfInterval({
start: utcString2userTzDate(domain[0]),
end: utcString2userTzDate(domain.last)
});
case TimeDensity.MONTH:
return eachMonthOfInterval({
start: utcString2userTzDate(domain[0]),
end: utcString2userTzDate(domain.last)
});
case TimeDensity.DAY:
return eachDayOfInterval({
start: utcString2userTzDate(domain[0]),
end: utcString2userTzDate(domain.last)
});
default:
throw new Error(
`Invalid time density [${timeDensity}] on dataset [${datasetId}]`
);
}
}
Loading

0 comments on commit e1cbdd5

Please sign in to comment.