Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 52 additions & 20 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { useCallback, useMemo, useState, useReducer, ChangeEvent } from 'react';
import { MapGroupResponse, SourceGroup, Box } from './types/maps';
import { ColorMapControls } from './components/ColorMapControls';
import { fetchBoxes, fetchMaps, fetchSources } from './utils/fetchUtils';
import {
fetchBoxes,
fetchMaps,
fetchSources,
getHistogramData,
} from './utils/fetchUtils';
import {
assertInternalBaselayer,
baselayersReducer,
Expand Down Expand Up @@ -34,14 +39,35 @@ function App() {
// map baselayers
const { mapGroups, internalBaselayers } = await fetchMaps();

// // If we end up with no maps for some reason, return early
// If we end up with no maps for some reason, return early
if (!mapGroups.length || !internalBaselayers.length) return;

// Set the baselayersState with the finalBands; note that this action will also set the
// Get what will be the default baselayer's histogram data to set in the reducer state
const defaultInitialBaselayer = { ...internalBaselayers[0] };
const histogramData = await getHistogramData(
defaultInitialBaselayer.layer_id
);

// Check if the default baselayer has an undefined vmin or vmax; if so, set the
// vmin and vmax for the baselayer
if (
defaultInitialBaselayer.vmin === undefined ||
defaultInitialBaselayer.vmax === undefined
) {
const histogramData = await getHistogramData(
defaultInitialBaselayer.layer_id
);
defaultInitialBaselayer.vmin = histogramData.vmin;
defaultInitialBaselayer.vmax = histogramData.vmax;
internalBaselayers[0] = defaultInitialBaselayer;
}

// Set the baselayersState with the internalBaselayers; note that this action will also set the
// activeBaselayer to be finalBands[0]
dispatchBaselayersChange({
type: SET_BASELAYERS_STATE,
internalBaselayers: internalBaselayers,
histogramData,
});

return mapGroups;
Expand Down Expand Up @@ -181,7 +207,8 @@ function App() {
[baselayersState.activeBaselayer]
);

const { activeBaselayer, internalBaselayers } = baselayersState;
const { activeBaselayer, internalBaselayers, histogramData } =
baselayersState;
return (
<>
<Login
Expand All @@ -206,22 +233,27 @@ function App() {
submapData={submapData}
/>
)}
{isAuthenticated !== null && assertInternalBaselayer(activeBaselayer) && (
<ColorMapControls
values={[activeBaselayer.vmin, activeBaselayer.vmax]}
cmapRange={activeBaselayer.recommendedCmapValuesRange}
onCmapValuesChange={onCmapValuesChange}
cmap={activeBaselayer.cmap}
onCmapChange={onCmapChange}
activeBaselayerId={activeBaselayer.layer_id}
units={activeBaselayer.units}
quantity={activeBaselayer.quantity}
isLogScale={activeBaselayer.isLogScale}
isAbsoluteValue={activeBaselayer.isAbsoluteValue}
onLogScaleChange={onLogScaleChange}
onAbsoluteValueChange={onAbsoluteValueChange}
/>
)}
{isAuthenticated !== null &&
assertInternalBaselayer(activeBaselayer) &&
activeBaselayer.vmin !== undefined &&
activeBaselayer.vmax !== undefined &&
histogramData && (
<ColorMapControls
values={[activeBaselayer.vmin, activeBaselayer.vmax]}
cmapRange={histogramData.vmax - histogramData.vmin}
onCmapValuesChange={onCmapValuesChange}
cmap={activeBaselayer.cmap}
onCmapChange={onCmapChange}
activeBaselayerId={activeBaselayer.layer_id}
units={activeBaselayer.units}
quantity={activeBaselayer.quantity}
isLogScale={activeBaselayer.isLogScale}
isAbsoluteValue={activeBaselayer.isAbsoluteValue}
onLogScaleChange={onLogScaleChange}
onAbsoluteValueChange={onAbsoluteValueChange}
histogramData={histogramData}
/>
)}
</>
);
}
Expand Down
45 changes: 17 additions & 28 deletions src/components/ColorMapControls.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import {
ChangeEventHandler,
useCallback,
useEffect,
useState,
useMemo,
useEffect,
} from 'react';
import { SERVICE_URL } from '../configs/mapSettings';
import {
CMAP_OPTIONS,
HISTOGRAM_SIZE_X,
Expand All @@ -17,6 +16,7 @@ import { ColorMapHistogram } from './ColorMapHistogram';
import { CustomColorMapDialog } from './CustomColorMapDialog';
import { safeLog } from '../utils/numberUtils';
import { getAbsoluteHistogramData } from '../utils/histogramUtils';
import { getCmapImage } from '../utils/fetchUtils';

export type ColorMapControlsProps = {
/** the selected or default min and max values for the slider */
Expand All @@ -43,6 +43,8 @@ export type ColorMapControlsProps = {
onLogScaleChange: (checked: boolean) => void;
/** handler to update isAbsoluteValue state and to set cmap values as necessary */
onAbsoluteValueChange: (checked: boolean) => void;
/** the histogramData that is fetched/cached with each baselayer change */
histogramData: HistogramResponse;
};

/**
Expand All @@ -59,21 +61,29 @@ export function ColorMapControls(props: ColorMapControlsProps) {
onCmapValuesChange,
cmap,
onCmapChange,
activeBaselayerId,
units,
quantity,
isLogScale,
isAbsoluteValue,
onLogScaleChange,
onAbsoluteValueChange,
histogramData,
} = props;
const [cmapImage, setCmapImage] = useState<undefined | string>(undefined);
const [histogramData, setHistogramData] = useState<
HistogramResponse | undefined
>(undefined);
const [cmapImage, setCmapImage] = useState<string | undefined>(undefined);
const [showCustomDialog, setShowCustomDialog] = useState(false);
const [cmapOptions, setCmapOptions] = useState(CMAP_OPTIONS);

/**
* Fetch or retrieve from cache the cmap image when user changes cmap selection
*/
useEffect(() => {
async function getImage() {
const image = await getCmapImage(cmap);
setCmapImage(image);
}
getImage();
}, [cmap]);

/** Processes the histogram data so that it's ready to create the polygon in ColorMapHistogram */
const processedHistogramData = useMemo(() => {
if (histogramData) {
Expand Down Expand Up @@ -122,27 +132,6 @@ export function ColorMapControls(props: ColorMapControlsProps) {
}
}, [histogramData, isLogScale, isAbsoluteValue]);

/** Fetch and set the URL to the color map image if/when cmap or its setter changes */
useEffect(() => {
async function getCmapImage() {
const image = await fetch(`${SERVICE_URL}/histograms/${cmap}.png`);
setCmapImage(image.url);
}
getCmapImage();
}, [cmap, setCmapImage]);

/** Fetch and set the histogram data if/when the active layer and/or setHistogramData changes */
useEffect(() => {
async function getHistogramData() {
const response = await fetch(
`${SERVICE_URL}/histograms/data/${activeBaselayerId}`
);
const data: HistogramResponse = await response.json();
setHistogramData(data);
}
getHistogramData();
}, [activeBaselayerId, setHistogramData]);

/** Determines the min, max, and step attributes for the range slider. Min and max are
found by comparing the user-controlled (or default) 'values' to the histogram's 'edges',
which allows for the range slider to resize itself according to min/max values that may
Expand Down
6 changes: 3 additions & 3 deletions src/components/ColorMapHistogram.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { useMemo } from 'react';
import { PointArray } from '@svgdotjs/svg.js';
import { HistogramResponse } from '../types/maps';
import { HistogramData } from '../types/maps';
import {
HISTOGRAM_SIZE_X,
HISTOGRAM_SIZE_Y,
} from '../configs/cmapControlSettings';

type Props = {
/** The data from the histogram response */
data?: HistogramResponse;
/** The applicable histogram data from the histogram response */
data?: HistogramData;
/** The user's min and max values for the range slider to use as edgeStart or edgeEnd
in the event the user sets these beyond the histogram's min or max edges */
userMinAndMaxValues: { min: number; max: number };
Expand Down
5 changes: 4 additions & 1 deletion src/components/ColorMapSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
import { ColorMapControlsProps } from './ColorMapControls';
import './styles/color-map-controls.css';

let THUMB_KEY_COUNTER = 1;

interface ColorMapSliderProps
extends Omit<
ColorMapControlsProps,
Expand All @@ -16,6 +18,7 @@ interface ColorMapSliderProps
| 'onCmapChange'
| 'onLogScaleChange'
| 'onAbsoluteValueChange'
| 'histogramData'
> {
/** The URL to the color map image */
cmapImage?: string;
Expand Down Expand Up @@ -301,7 +304,7 @@ export function ColorMapSlider(props: ColorMapSliderProps) {
renderThumb={({ props, isDragged }) => (
<div
{...props}
key={props.key}
key={'thumb-key-' + ++THUMB_KEY_COUNTER}
style={{
...props.style,
height: '20px',
Expand Down
50 changes: 43 additions & 7 deletions src/components/OpenLayersMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import {
} from '../utils/layerUtils';
import { ToggleSwitch } from './ToggleSwitch';
import { CenterMapFeature } from './CenterMapFeature';
import { getHistogramData } from '../utils/fetchUtils';

export type MapProps = {
mapGroups: MapGroupResponse[];
Expand Down Expand Up @@ -111,7 +112,7 @@ export function OpenLayersMap({
* TileLayer requests.
*/
const onBaselayerChange = useCallback(
(
async (
selectedBaselayerId: string,
context: 'layerMenu' | 'goBack' | 'goForward',
flipped?: boolean
Expand Down Expand Up @@ -167,10 +168,41 @@ export function OpenLayersMap({
setFlipTiles(flipped);
}

dispatchBaselayersChange({
type: CHANGE_BASELAYER,
newBaselayer: newActiveBaselayer,
});
// If we switch to an internal baselayer, we need to fetch its histogram data
// in order to update it in the reducer
if (assertInternalBaselayer(newActiveBaselayer)) {
const histogramData = await getHistogramData(
newActiveBaselayer.layer_id
);

// If the new layer doesn't yet have vmin or vmax set, set it with the
// histogram data
if (
newActiveBaselayer.vmin === undefined ||
newActiveBaselayer.vmax === undefined
) {
dispatchBaselayersChange({
type: CHANGE_BASELAYER,
newBaselayer: {
...newActiveBaselayer,
vmin: histogramData.vmin,
vmax: histogramData.vmax,
},
histogramData,
});
} else {
dispatchBaselayersChange({
type: CHANGE_BASELAYER,
newBaselayer: newActiveBaselayer,
histogramData,
});
}
} else {
dispatchBaselayersChange({
type: CHANGE_BASELAYER,
newBaselayer: newActiveBaselayer,
});
}
},
[baselayersState, dispatchBaselayersChange, flipTiles, setFlipTiles]
);
Expand All @@ -191,7 +223,7 @@ export function OpenLayersMap({
new TileLayer({
properties: { id: 'baselayer-' + layer.layer_id },
source: new XYZ({
url: `${SERVICE_URL}/maps/${layer.layer_id}/{z}/{-y}/{x}/tile.png?cmap=${layer.cmap}&vmin=${layer.isLogScale ? Math.pow(10, layer.vmin) : layer.vmin}&vmax=${layer.isLogScale ? Math.pow(10, layer.vmax) : layer.vmax}&flip=${flipTiles}&log_norm=${layer.isLogScale}&abs=${layer.isAbsoluteValue}`,
url: `${SERVICE_URL}/maps/${layer.layer_id}/{z}/{-y}/{x}/tile.png?cmap=${layer.cmap}&vmin=${layer.isLogScale ? Math.pow(10, layer.vmin!) : layer.vmin}&vmax=${layer.isLogScale ? Math.pow(10, layer.vmax!) : layer.vmax}&flip=${flipTiles}&log_norm=${layer.isLogScale}&abs=${layer.isAbsoluteValue}`,
tileGrid: new TileGrid({
extent: [-180, -90, 180, 90],
origin: [-180, 90],
Expand Down Expand Up @@ -392,6 +424,10 @@ export function OpenLayersMap({
const activeLayer = tileLayers!.find(
(t) => t.get('id') === 'baselayer-' + activeBaselayer!.layer_id
)!;
const activeLayerSource = activeLayer.getSource();
activeLayerSource?.setUrl(
`${SERVICE_URL}/maps/${activeBaselayer.layer_id}/{z}/{-y}/{x}/tile.png?cmap=${activeBaselayer.cmap}&vmin=${activeBaselayer.isLogScale ? Math.pow(10, activeBaselayer.vmin!) : activeBaselayer.vmin}&vmax=${activeBaselayer.isLogScale ? Math.pow(10, activeBaselayer.vmax!) : activeBaselayer.vmax}&flip=${flipTiles}&log_norm=${activeBaselayer.isLogScale}&abs=${activeBaselayer.isAbsoluteValue}`
);
mapRef.current.addLayer(activeLayer);
} else {
const externalBaselayer = EXTERNAL_BASELAYERS.find(
Expand All @@ -406,7 +442,7 @@ export function OpenLayersMap({
mapRef.current.addLayer(activeLayer);
}
}
}, [activeBaselayer, tileLayers, externalTileLayers]);
}, [activeBaselayer, tileLayers, externalTileLayers, flipTiles]);

/**
* Add keyboard support for switching baselayers
Expand Down
Loading