Skip to content

Commit

Permalink
Display selected datasets on the map (#668)
Browse files Browse the repository at this point in the history
Done so far:
- Displays selected datasets on the map
- Dataset order support
- Dataset opacity support

Compare is not possible yet.
  • Loading branch information
nerik authored Oct 2, 2023
2 parents e23f228 + c055024 commit 0a33ddb
Show file tree
Hide file tree
Showing 10 changed files with 302 additions and 129 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useMemo } from 'react';
import { BaseGeneratorParams } from '../types';

export default function useGeneratorParams(props: BaseGeneratorParams) {
return useMemo(() => {
return props;
// Memoize only required abse params
}, [props.generatorOrder, props.hidden, props.opacity]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ import {
FIT_BOUNDS_PADDING,
getFilterPayload,
getMergedBBox,
requestQuickCache,
requestQuickCache
} from '../utils';
import useFitBbox from '../hooks/use-fit-bbox';
import useLayerInteraction from '../hooks/use-layer-interaction';
import { MARKER_LAYOUT } from '../hooks/use-custom-marker';
import useMaps from '../hooks/use-maps';
import useGeneratorParams from '../hooks/use-generator-params';

import {
ActionStatus,
Expand All @@ -32,7 +33,6 @@ import {
S_SUCCEEDED
} from '$utils/status';


// Whether or not to print the request logs.
const LOG = true;

Expand Down Expand Up @@ -70,16 +70,16 @@ export function RasterTimeseries(props: RasterTimeseriesProps) {
onStatusChange,
isPositionSet,
hidden,
opacity
} = props;


const { current: mapInstance } = useMaps();

const theme = useTheme();
const { updateStyle } = useMapStyle();

const minZoom = zoomExtent?.[0] ?? 0;
const generatorId = 'raster-timeseries' + id;
const generatorId = `raster-timeseries-${id}`;

// Status tracking.
// A raster timeseries layer has a base layer and may have markers.
Expand Down Expand Up @@ -288,11 +288,7 @@ export function RasterTimeseries(props: RasterTimeseriesProps) {
}
LOG &&
/* eslint-disable-next-line no-console */
console.log(
'RasterTimeseries %cAborted Mosaic',
'color: red;',
id
);
console.log('RasterTimeseries %cAborted Mosaic', 'color: red;', id);
return;
}
};
Expand Down Expand Up @@ -331,6 +327,8 @@ export function RasterTimeseries(props: RasterTimeseriesProps) {
[sourceParams]
);

const generatorParams = useGeneratorParams(props);

useEffect(
() => {
const controller = new AbortController();
Expand All @@ -343,7 +341,7 @@ export function RasterTimeseries(props: RasterTimeseriesProps) {
const tileParams = qs.stringify(
{
assets: 'cog_default',
...sourceParams
...(sourceParams ?? {})
},
// Temporary solution to pass different tile parameters for hls data
{
Expand Down Expand Up @@ -376,12 +374,14 @@ export function RasterTimeseries(props: RasterTimeseriesProps) {
url: tilejsonUrl
};

const rasterOpacity = typeof opacity === 'number' ? opacity / 100 : 1;

const mosaicLayer: RasterLayer = {
id: id,
type: 'raster',
source: id,
paint: {
'raster-opacity': Number(!hidden),
'raster-opacity': hidden ? 0 : rasterOpacity,
'raster-opacity-transition': {
duration: 320
}
Expand Down Expand Up @@ -416,7 +416,7 @@ export function RasterTimeseries(props: RasterTimeseriesProps) {
id: pointsSourceId,
source: pointsSourceId,
layout: {
...MARKER_LAYOUT as any,
...(MARKER_LAYOUT as any),
'icon-allow-overlap': true
},
paint: {
Expand All @@ -440,7 +440,7 @@ export function RasterTimeseries(props: RasterTimeseriesProps) {
generatorId,
sources,
layers,
params: props as BaseGeneratorParams
params: generatorParams
});
}

Expand All @@ -450,16 +450,16 @@ export function RasterTimeseries(props: RasterTimeseriesProps) {
controller.abort();
};
},
// sourceParams not included, but using a stringified version of it to detect changes (haveSourceParamsChanged)
// sourceParams not included, but using a stringified version of it to
// detect changes (haveSourceParamsChanged)
[
updateStyle,
id,
mosaicUrl,
minZoom,
points,
haveSourceParamsChanged,
hidden,
generatorId
generatorParams
]
);

Expand Down
17 changes: 11 additions & 6 deletions app/scripts/components/common/map/styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import React, {
} from 'react';
import {
ExtendedLayer,
ExtendedMetadata,
GeneratorStyleParams,
LayerOrderPosition
} from './types';
Expand Down Expand Up @@ -52,7 +53,7 @@ const generateStyle = (stylesData: Record<string, GeneratorStyleParams>) => {
};

const generatorLayers = generatorParams.layers.map((generatorLayer) => {
const metadata = generatorLayer.metadata ?? {};
const metadata: ExtendedMetadata = generatorLayer.metadata ?? {};
metadata.generatorId = generatorId;

const mapLayer = { ...generatorLayer, metadata } as Layer;
Expand All @@ -76,13 +77,17 @@ const generateStyle = (stylesData: Record<string, GeneratorStyleParams>) => {
const layerBOrder = layerB.metadata?.layerOrderPosition;
const layerAIndex = LAYER_ORDER.indexOf(layerAOrder);
const layerBIndex = LAYER_ORDER.indexOf(layerBOrder);
const sortDeltaOrder = layerAIndex - layerBIndex;
const sortDeltaGeneratorId = layerA.metadata?.generatorId.localeCompare(
layerB.metadata?.generatorId
);
const layerOrder = layerAIndex - layerBIndex;
const generatorA = stylesData[layerA.metadata?.generatorId];
const generatorB = stylesData[layerB.metadata?.generatorId];
const generatorOrder =
generatorA.params?.generatorOrder !== undefined &&
generatorB.params?.generatorOrder !== undefined
? generatorA.params.generatorOrder - generatorB.params.generatorOrder
: 0;
// If compared layers have different layer orders, sort by layer order, otherwise
// fallback on generatorId to ensure layer stacks from the same generator stay contiguous
return sortDeltaOrder !== 0 ? sortDeltaOrder : sortDeltaGeneratorId;
return layerOrder !== 0 ? layerOrder : generatorOrder;
});

return {
Expand Down
12 changes: 8 additions & 4 deletions app/scripts/components/common/map/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { AnyLayer, AnySourceImpl } from "mapbox-gl";

export interface ExtendedMetadata {
layerOrderPosition?: LayerOrderPosition;
[key: string]: any;
}

export type ExtendedLayer = AnyLayer & {
metadata?: {
layerOrderPosition?: LayerOrderPosition;
[key: string]: any;
};
metadata?: ExtendedMetadata;
};

export interface BaseGeneratorParams {
hidden?: boolean;
generatorOrder?: number;
opacity?: number;
}
export interface GeneratorStyleParams {
generatorId: string;
Expand Down
79 changes: 68 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,61 @@ 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>;
export function resolveConfigFunctions<T extends any[]>(
datum: T,
bag: DatasetDatumFnResolverBag
): Res<T[number]>[];
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: 6 additions & 10 deletions app/scripts/components/exploration/atoms/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,13 @@ export function useTimelineDatasetAtom(id: string) {
return datasetAtom as PrimitiveAtom<TimelineDataset>;
}

type Settings = TimelineDataset['settings'];

type TimelineDatasetSettingsReturn = [
(
prop: keyof TimelineDataset['settings']
) => TimelineDataset['settings'][keyof TimelineDataset['settings']],
(
prop: keyof TimelineDataset['settings'],
value:
| TimelineDataset['settings'][keyof TimelineDataset['settings']]
| ((
prev: TimelineDataset['settings'][keyof TimelineDataset['settings']]
) => TimelineDataset['settings'][keyof TimelineDataset['settings']])
<T extends keyof Settings>(prop: T) => Settings[T],
<T extends keyof Settings>(
prop: T,
value: Settings[T] | ((prev: Settings[T]) => Settings[T])
) => void
];

Expand Down
Loading

0 comments on commit 0a33ddb

Please sign in to comment.