Skip to content

Commit

Permalink
Render selected datasets on map
Browse files Browse the repository at this point in the history
  • Loading branch information
danielfdsilva committed Sep 22, 2023
1 parent 3be9f44 commit 9a1c07c
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 25 deletions.
81 changes: 70 additions & 11 deletions app/scripts/components/common/map/utils.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import axios, { Method } from 'axios';
import { Map as MapboxMap } from 'mapbox-gl';
import { MapRef } from 'react-map-gl';
import { endOfDay, startOfDay } from "date-fns";
import { StacFeature } from "./types";
import { userTzDate2utcString } from "$utils/date";
import { validateRangeNum } from "$utils/utils";
import { endOfDay, startOfDay } from 'date-fns';
import {
DatasetDatumFn,
DatasetDatumFnResolverBag,
DatasetDatumReturnType
} from 'veda';

import { StacFeature } from './types';

import { userTzDate2utcString } from '$utils/date';
import { validateRangeNum } from '$utils/utils';

export const FIT_BOUNDS_PADDING = 32;

export const validateLon = validateRangeNum(-180, 180);
export const validateLat = validateRangeNum(-90, 90);



export function getMergedBBox(features: StacFeature[]) {
const mergedBBox = [
Number.POSITIVE_INFINITY,
Expand All @@ -32,8 +36,6 @@ export function getMergedBBox(features: StacFeature[]) {
) as [number, number, number, number];
}



export function checkFitBoundsFromLayer(
layerBounds?: [number, number, number, number],
mapInstance?: MapboxMap | MapRef
Expand All @@ -58,8 +60,6 @@ export function checkFitBoundsFromLayer(
return layerExtentSmaller || isOutside;
}



/**
* Creates the appropriate filter object to send to STAC.
*
Expand Down Expand Up @@ -87,7 +87,6 @@ export function getFilterPayload(date: Date, collection: string) {
};
}


// There are cases when the data can't be displayed properly on low zoom levels.
// In these cases instead of displaying the raster tiles, we display markers to
// indicate whether or not there is data in a given location. When the user
Expand Down Expand Up @@ -123,3 +122,63 @@ export async function requestQuickCache({
return quickCache.get(key);
}

type Fn = (...args: any[]) => any;

type ObjResMap<T> = {
[K in keyof T]: Res<T[K]>;
};

type Res<T> = T extends Fn
? T extends DatasetDatumFn<DatasetDatumReturnType>
? DatasetDatumReturnType
: never
: T extends any[]
? Res<T[number]>[]
: T extends object
? ObjResMap<T>
: T;

export function resolveConfigFunctions<T>(
datum: T,
bag: DatasetDatumFnResolverBag
): Res<T>;
/* eslint-disable-next-line no-redeclare */
export function resolveConfigFunctions<T extends any[]>(
datum: T,
bag: DatasetDatumFnResolverBag
): Res<T[number]>[];
/* eslint-disable-next-line no-redeclare */
export function resolveConfigFunctions(
datum: any,
bag: DatasetDatumFnResolverBag
): any {
if (Array.isArray(datum)) {
return datum.map((v) => resolveConfigFunctions(v, bag));
}

if (datum != null && typeof datum === 'object') {
// Use for loop instead of reduce as it faster.
const ready = {};
for (const [k, v] of Object.entries(datum as object)) {
ready[k] = resolveConfigFunctions(v, bag);
}
return ready;
}

if (typeof datum === 'function') {
try {
return datum(bag);
} catch (error) {
/* eslint-disable-next-line no-console */
console.error(
'Failed to resolve function %s(%o) with error %s',
datum.name,
bag,
error.message
);
return null;
}
}

return datum;
}
16 changes: 15 additions & 1 deletion app/scripts/components/exploration/data-utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import {
eachDayOfInterval,
eachMonthOfInterval,
eachYearOfInterval
eachYearOfInterval,
startOfDay,
startOfMonth,
startOfYear
} from 'date-fns';
import { DatasetLayer, datasets } from 'veda';
import {
Expand Down Expand Up @@ -102,3 +105,14 @@ export function resolveLayerTemporalExtent(
);
}
}

export function getTimeDensityStartDate(date: Date, timeDensity: TimeDensity) {
switch (timeDensity) {
case TimeDensity.MONTH:
return startOfMonth(date);
case TimeDensity.YEAR:
return startOfYear(date);
}

return startOfDay(date);
}
86 changes: 73 additions & 13 deletions app/scripts/components/exploration/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import React, { useCallback, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
import styled from 'styled-components';
import { useAtomValue } from 'jotai';
import { themeVal } from '@devseed-ui/theme-provider';
// Avoid error: node_modules/date-fns/esm/index.js does not export 'default'
import * as dateFns from 'date-fns';

import { MockControls } from './datasets-mock';
import Timeline from './components/timeline/timeline';
import { DatasetSelectorModal } from './components/dataset-selector-modal';
import { useStacMetadataOnDatasets } from './hooks/use-stac-metadata-datasets';
import { selectedDateAtom, timelineDatasetsAtom } from './atoms/atoms';
import { TimelineDatasetStatus, TimelineDatasetSuccess } from './types.d.ts';
import { getTimeDensityStartDate } from './data-utils';
import { useTimelineDatasetAtom, useTimelineDatasetSettings } from './atoms/hooks';

import { LayoutProps } from '$components/common/layout-root';
import PageHero from '$components/common/page-hero';
Expand All @@ -23,6 +30,7 @@ import MapOptionsControl from '$components/common/map/controls/options';
import { projectionDefault } from '$components/common/map/controls/map-options/projections';
import { useBasemap } from '$components/common/map/controls/hooks/use-basemap';
import { RasterTimeseries } from '$components/common/map/style-generators/raster-timeseries';
import { resolveConfigFunctions } from '$components/common/map/utils';

const Container = styled.div`
display: flex;
Expand Down Expand Up @@ -67,7 +75,7 @@ const Container = styled.div`
`;

function Exploration() {
const [compare, setCompare] = useState(true);
const [compare, setCompare] = useState(false);
const [datasetModalRevealed, setDatasetModalRevealed] = useState(true);

const openModal = useCallback(() => setDatasetModalRevealed(true), []);
Expand All @@ -85,6 +93,14 @@ function Exploration() {

useStacMetadataOnDatasets();

const datasets = useAtomValue(timelineDatasetsAtom);
const selectedDay = useAtomValue(selectedDateAtom);

const loadedDatasets = datasets.filter(
(d): d is TimelineDatasetSuccess =>
d.status === TimelineDatasetStatus.SUCCESS
);

return (
<>
<LayoutProps
Expand All @@ -105,17 +121,15 @@ function Exploration() {
labelsOption={labelsOption}
boundariesOption={boundariesOption}
/>
<RasterTimeseries
id='test'
stacCol='nightlights-hd-monthly'
date={new Date('2019-01-01')}
zoomExtent={[4, 16]}
sourceParams={{
bidx: 1,
colormap_name: 'inferno',
rescale: [0, 255]
}}
/>
{selectedDay &&
loadedDatasets.map((dataset, idx) => (
<Layer
key={dataset.data.id}
dataset={dataset}
selectedDay={selectedDay}
order={idx}
/>
))}
{/* Map controls */}
<GeocoderControl />
<NavigationControl />
Expand Down Expand Up @@ -173,3 +187,49 @@ function Exploration() {
);
}
export default Exploration;

interface LayerProps {
dataset: TimelineDatasetSuccess;
order: number;
selectedDay: Date;
}

function Layer(props: LayerProps) {
const { dataset, order, selectedDay } = props;

const datasetAtom = useTimelineDatasetAtom(dataset.data.id);
const [getSettings] = useTimelineDatasetSettings(datasetAtom);

const isVisible = getSettings('isVisible');
const opacity = getSettings('opacity');

// The date needs to match the dataset's time density.
const relevantDate = useMemo(
() => getTimeDensityStartDate(selectedDay, dataset.data.timeDensity),
[selectedDay, dataset.data.timeDensity]
);

// Resolve config functions.
const params = useMemo(() => {
const bag = {
date: relevantDate,
compareDatetime: relevantDate,
dateFns,
raw: dataset.data
};
return resolveConfigFunctions(dataset.data, bag);
}, [dataset, relevantDate]);

return (
<RasterTimeseries
id={dataset.data.id}
stacCol={dataset.data.stacCol}
date={relevantDate}
zoomExtent={params.zoomExtent}
sourceParams={params.sourceParams}
layerOrder={order}
hidden={!isVisible}
opacity={opacity}
/>
);
}

0 comments on commit 9a1c07c

Please sign in to comment.