Skip to content

Commit

Permalink
Merge pull request #326 from RENCI/issue-296-field-units
Browse files Browse the repository at this point in the history
Issue 296 field units
  • Loading branch information
PhillipsOwen authored Nov 12, 2024
2 parents 210c957 + b3d44d8 commit 5dd4801
Show file tree
Hide file tree
Showing 10 changed files with 576 additions and 66 deletions.
21 changes: 20 additions & 1 deletion src/components/map/hurricane-track.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
} from "@utils/hurricane/track";
import { useLayers } from '@context';
import { getNamespacedEnvParam } from "@utils";
import { useSettings } from '@context';
import { mphToMps, mphToKnots } from '@utils/map-utils';


export const HurricaneTrackGeoJson = ({index}) => {
Expand All @@ -17,6 +19,11 @@ export const HurricaneTrackGeoJson = ({index}) => {
} = useLayers();
const [hurricaneData, setHurricaneData] = useState();

const {
unitsType,
speedType,
} = useSettings();

function coneStyle() {
return {
fillColor: '#858585',
Expand Down Expand Up @@ -84,9 +91,21 @@ export const HurricaneTrackGeoJson = ({index}) => {

const onEachHurrFeature = (feature, layer) => {
if (feature.properties && feature.properties.time_utc) {

// set wind speed to default units setting values
let windSpeed = mphToMps(feature.properties.max_wind_speed_mph);
let unitStr = "mps";
// now check to see the imperial setting has been selected instead
if(unitsType.current === "imperial") {
windSpeed = (speedType.current === "mph") ? feature.properties.max_wind_speed_mph
:
mphToKnots(feature.properties.max_wind_speed_mph);
unitStr = speedType.current;
}

const popupContent = feature.properties.storm_name + ": " +
feature.properties.time_utc + ", " +
feature.properties.max_wind_speed_mph + "mph";
windSpeed.toFixed(1) + unitStr;

layer.on("mouseover", function (e) {
this.bindPopup(popupContent).openPopup(e.latlng);
Expand Down
83 changes: 53 additions & 30 deletions src/components/trays/settings/colormaps/colormap-slider.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import PropTypes from 'prop-types';
import SldStyleParser from 'geostyler-sld-parser';
import { Slider, Box } from '@mui/joy';
import { useSettings } from '@context';
import { restoreColorMapType } from '@utils/map-utils';
import { maxSliderValues } from './utils';
import { restoreColorMapType, feetToMeters, knotsToMps, mphToMps } from '@utils/map-utils';
import { getFloatNumberFromLabel, maxSliderValues, sliderSteps, sliderMarkSteps } from './utils';

const MAXELE = 'maxele';
const MAXWVEL = 'maxwvel';
Expand All @@ -20,36 +20,37 @@ export const ColormapSlider = ({style}) => {

const {
mapStyle,
unitsType,
speedType,
} = useSettings();

const sldParser = new SldStyleParser();

// set the correct slider values for the appropriate style
// set the correct slider values for the appropriate style and unit type
const setSliderParams = (style) => {

let max_slider_value = 0;
let slider_step = 0;
const marks = [];

if (style.name.includes("maxwvel")) {
max_slider_value = maxSliderValues[MAXWVEL];
slider_step = 1;
for (let i = 0; i <= max_slider_value; i+=10) {
if (style.name.includes(MAXWVEL)) {
max_slider_value = maxSliderValues[MAXWVEL][unitsType.current];
slider_step = sliderSteps[MAXWVEL][unitsType.current];
for (let i = 0; i <= max_slider_value; i+=sliderMarkSteps[MAXWVEL][unitsType.current]) {
marks.push({ label: i, value: i });
}
}
else
if (style.name.includes("swan")) {
max_slider_value = maxSliderValues[SWAN];
slider_step = 0.5;
for (let i = 0; i <= max_slider_value; i+=5) {
if (style.name.includes(SWAN)) {
max_slider_value = maxSliderValues[SWAN][unitsType.current];
slider_step = sliderSteps[SWAN][unitsType.current];
for (let i = 0; i <= max_slider_value; i+=sliderMarkSteps[SWAN][unitsType.current]) {
marks.push({ label: i, value: i });
}
}
else { // maxele
max_slider_value = maxSliderValues[MAXELE];
slider_step = 0.25;
for (let i = 0; i <= max_slider_value; i++) {
max_slider_value = maxSliderValues[MAXELE][unitsType.current];
slider_step = sliderSteps[MAXELE][unitsType.current];
for (let i = 0; i <= max_slider_value; i+=sliderMarkSteps[MAXELE][unitsType.current]) {
marks.push({ label: i, value: i });
}
}
Expand All @@ -60,7 +61,8 @@ export const ColormapSlider = ({style}) => {
setMinSliderValue(0);

const colormapEntries = style.rules[0].symbolizers[0].colorMap.colorMapEntries;
setValue([parseFloat(colormapEntries[colormapEntries.length-1].quantity), parseFloat(colormapEntries[0].quantity)]);
setValue([parseFloat(colormapEntries[colormapEntries.length-1].label.match(/[+-]?\d+(\.\d+)?/g)),
parseFloat(colormapEntries[0].quantity)]);
};

useEffect(() => {
Expand All @@ -75,7 +77,8 @@ export const ColormapSlider = ({style}) => {
const colorMapEntries = geostylerStyle.output.rules[0].symbolizers[0].colorMap.colorMapEntries;
// now temporarily set that max range for the style
colorMapEntries[colorMapEntries.length-1].quantity =
parseFloat(colorMapEntries[colorMapEntries.length-1].label.match(/[+-]?\d+(\.\d+)?/g)).toFixed(2);
getFloatNumberFromLabel(colorMapEntries[colorMapEntries.length-1].label, 2);
//parseFloat(colorMapEntries[colorMapEntries.length-1].label.match(/[+-]?\d+(\.\d+)?/g)).toFixed(2);
}

setCurrentStyle(geostylerStyle.output);
Expand All @@ -86,7 +89,7 @@ export const ColormapSlider = ({style}) => {
};
getDefaultStyle();

}, []);
}, [unitsType]);

const storeStyle = useCallback((style) => {
// save colormap type for later restoration when it is lost
Expand All @@ -109,12 +112,7 @@ export const ColormapSlider = ({style}) => {
const dataRange = [];
const colormapEntries = style.rules[0].symbolizers[0].colorMap.colorMapEntries;
for(let i = colormapEntries.length-1; i >= 0; i--) {
dataRange.push(colormapEntries[i].quantity);
}
// if this is an intervals type of colormap, correct last entry in range
if (style.rules[0].symbolizers[0].colorMap.type === "intervals") {
const colorMapEntries = style.rules[0].symbolizers[0].colorMap.colorMapEntries;
dataRange[0] = parseFloat(colorMapEntries[colorMapEntries.length-1].label.match(/[+-]?\d+(\.\d+)?/g)).toFixed(2);
dataRange.push(getFloatNumberFromLabel(colormapEntries[i].label, 2));
}

return(dataRange.reverse());
Expand Down Expand Up @@ -149,22 +147,47 @@ export const ColormapSlider = ({style}) => {
const colormapEntries = [...style.rules[0].symbolizers[0].colorMap.colorMapEntries];
colormapEntries.forEach((entry, idx) => {
if (idx <= range.length) {
entry.quantity = range[idx];
// need to convert actual quantity in style if unit is set to imperial
// must always refer to GeoServer adcirc layers using metric units
if (unitsType.current === "imperial") {
if (style.name.includes("maxwvel"))
entry.quantity = ((speedType.current === "mph")?
mphToMps(range[idx])
:
knotsToMps(range[idx]));
else entry.quantity = feetToMeters(range[idx]);
}
else entry.quantity = range[idx];

// now set labels
if (style.name.includes("maxwvel")) {
entry.label = range[idx] + " m/s";
// 2 different speed types for imperial
if (unitsType.current === "imperial") {
entry.label = range[idx] + ((speedType.current === "knots")? " kn" : " mph");
}
else {
entry.label = range[idx] + " " + speedType.current;
}
}
else {
entry.label = range[idx] + " m";
entry.label = range[idx] + ((unitsType.current === "metric")? " m" : " ft");
}
// if the colormap type is set to intervals, the last entry is a special case,
// so must change the last entry values
if (style.rules[0].symbolizers[0].colorMap.type === "intervals") {
if ( idx === range.length-1) {
if (style.name.includes("maxwvel")) {
entry.label = ">= " + entry.quantity + " m/s";
// 2 different speed types for imperial
if (unitsType.current === "imperial") {
entry.label = ">= " + range[idx] + ((speedType.current === "knots")? " kn" : " mph");
}
else {
entry.label = ">= " + range[idx] + " " + speedType.current;
}
}
else {
entry.label = ">= " + entry.quantity + " m";
//entry.label = ">= " + entry.quantity + ((unitsType.current === "metric")? " m" : " ft");
entry.label = ">= " + range[idx] + ((unitsType.current === "metric")? " m" : " ft");
}
entry.quantity = maxSliderValue;
}
Expand Down Expand Up @@ -216,7 +239,7 @@ export const ColormapSlider = ({style}) => {
};

return (
<Box width={300} >
<Box width={400} >
<Slider
getAriaLabel={() => 'Y-Axis'}
value={ value }
Expand Down
22 changes: 19 additions & 3 deletions src/components/trays/settings/colormaps/data-range.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export const DataRangeEdit = () => {

const {
mapStyle,
unitsType,
speedType,
} = useSettings();

return (
Expand All @@ -36,21 +38,35 @@ export const DataRangeEdit = () => {
style={mapStyle.maxele.current}
/>
</Box>
<Typography startDecorator={<MaxElevationIcon />} mb={2} level="title-md">Maximum Water Level</Typography>
<Typography
startDecorator={<MaxElevationIcon />}
mb={2}
level="title-md">
Maximum Water Level {unitsType.current==="metric"? "(meters)" : "(feet)"}
</Typography>

<Box width={300} >
<ColormapSlider
style={mapStyle.maxwvel.current}
/>
</Box>
<Typography startDecorator={<MaxWindVelocityIcon />} mb={2} level="title-md">Maximum Wind Speed</Typography>
<Typography
startDecorator={<MaxWindVelocityIcon />}
mb={2}
level="title-md">
Maximum Wind Speed ({speedType.current})
</Typography>

<Box width={300} >
<ColormapSlider
style={mapStyle.swan.current}
/>
</Box>
<Typography startDecorator={<SwanIcon />} level="title-md">Maximum Significant Wave Height</Typography>
<Typography
startDecorator={<SwanIcon />}
level="title-md">
Maximum Significant Wave Height {unitsType.current==="metric"? "(meters)" : "(feet)"}
</Typography>
</Stack>
</TabPanel>
<TabPanel value={1}>
Expand Down
31 changes: 24 additions & 7 deletions src/components/trays/settings/colormaps/style-edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import SldStyleParser from 'geostyler-sld-parser';
import { ColorMapEditor } from '@renci/apsviz-geostyler';
import { restoreColorMapType } from '@utils/map-utils';
import _cloneDeep from 'lodash/cloneDeep';
import { maxSliderValues } from './utils';
import { getFloatNumberFromLabel, maxSliderValues } from './utils';

const MAXELE = 'maxele';
const MAXWVEL = 'maxwvel';
Expand All @@ -30,6 +30,8 @@ export const StyleEditor = () => {
const {
mapStyle,
layerOpacity,
unitsType,
speedType,
} = useSettings();

const [colormap, setColormap] = useState();
Expand Down Expand Up @@ -99,7 +101,7 @@ export const StyleEditor = () => {

// save the label units for later restoration
let labelUnit = colormap.colorMapEntries[0].label.split("").reverse().join("").split(" ")[0];
// reverse again if this was a m/s unit - whew!
// reverse again if this was anything other than meters unit - whew!
if (labelUnit.length > 1) labelUnit = labelUnit.split("").reverse().join("");

//update type of colorMap
Expand All @@ -113,7 +115,7 @@ export const StyleEditor = () => {
// check to see if this an intervals type of colormap
// must handle weird last entry case, if so
const topRange = (colormap.type === "intervals") ?
Number(colormap.colorMapEntries[colormap.colorMapEntries.length-1].label.match(/[+-]?\d+(\.\d+)?/g))
getFloatNumberFromLabel(colormap.colorMapEntries[colormap.colorMapEntries.length-1].label, 0)
:
Number(colormap.colorMapEntries[colormap.colorMapEntries.length-1].quantity);

Expand All @@ -135,7 +137,7 @@ export const StyleEditor = () => {
else {
if (value.colorMapEntries[value.colorMapEntries.length-1].label.includes(">=")) {
const last = value.colorMapEntries.length-1;
value.colorMapEntries[last].quantity = parseFloat(value.colorMapEntries[last].label.match(/[+-]?\d+(\.\d+)?/g)).toFixed(2);
value.colorMapEntries[last].quantity = getFloatNumberFromLabel(value.colorMapEntries[last].label, 2);
const labelParts = value.colorMapEntries[last].label.split(" ");
if (labelParts.length >= 3)
value.colorMapEntries[last].label = parseFloat(labelParts[1]).toFixed(2) + " " + labelParts[2];
Expand All @@ -158,13 +160,28 @@ export const StyleEditor = () => {
const styleName = geoStylerStyle.output.name.split('_')[0];
if (colormap.type === "intervals") {
const lastIndex = colormap.colorMapEntries.length-1;
let labelUnit = "";
// need to get value for last value in the range from the label
// for imperial based values, because the value is always represented
// in it metric form.
const lastQuantity = getFloatNumberFromLabel(colormap.colorMapEntries[lastIndex].label, 1);
console.log(lastQuantity);
if (styleName === MAXWVEL) {
geoStylerStyle.output.rules[0].symbolizers[0].colorMap.colorMapEntries[lastIndex].label = ">= " + colormap.colorMapEntries[lastIndex].quantity + " m/s";
if (unitsType.current === "imperial") {
labelUnit = ((speedType.current === "knots")? " kn" : " mph");
}
else {
labelUnit = "mps";
}
}
else {
geoStylerStyle.output.rules[0].symbolizers[0].colorMap.colorMapEntries[lastIndex].label = ">= " + colormap.colorMapEntries[lastIndex].quantity + " m";
if (unitsType.current === "imperial")
labelUnit = " ft";
}
geoStylerStyle.output.rules[0].symbolizers[0].colorMap.colorMapEntries[lastIndex].quantity = maxSliderValues[styleName];
geoStylerStyle.output.rules[0].symbolizers[0].colorMap.colorMapEntries[lastIndex].label = ">= " + lastQuantity + labelUnit;
// when the colormap type is intervals, have to set last colormap entry to be an estimated mav value for that layer
// to account for the way interval colormaps work.
geoStylerStyle.output.rules[0].symbolizers[0].colorMap.colorMapEntries[lastIndex].quantity = maxSliderValues[styleName][unitsType.current];
}

// save colormap type - it seems to get wiped out when the parser
Expand Down
29 changes: 26 additions & 3 deletions src/components/trays/settings/colormaps/utils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
export const maxSliderValues = {
"maxele": 10,
"maxwvel": 100,
"swan": 30
"maxele": { "metric": 10, "imperial": 30 },
"maxwvel": { "metric": 100, "imperial": 200 },
"swan": { "metric": 30, "imperial": 100 }
};

export const sliderSteps = {
"maxele": { "metric": 0.25, "imperial": 1 },
"maxwvel": { "metric": 1, "imperial": 5 },
"swan": { "metric": 0.5, "imperial": 5 }
};

export const sliderMarkSteps = {
"maxele": { "metric": 1, "imperial": 5 },
"maxwvel": { "metric": 10, "imperial": 20 },
"swan": { "metric": 5, "imperial": 10 }
};

// find a float number in a colormap entry label and
// return with designated decimal places.
export const getFloatNumberFromLabel = (label, decimalPlaces) => {
let num = 0.0;
const labelMatch = label.match(/[+-]?\d+(\.\d+)?/g);
if (labelMatch)
num = parseFloat(labelMatch).toFixed(decimalPlaces);

return num;
};
5 changes: 5 additions & 0 deletions src/components/trays/settings/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Tune as SettingsIcon } from '@mui/icons-material';
import { DarkModeToggle } from './dark-mode';
import { BaseMaps } from './basemap';
import { DataRangeEdit } from './colormaps';
import { Units } from './units';

export const icon = <SettingsIcon />;

Expand All @@ -18,7 +19,11 @@ export const trayContents = () => (
<Typography level="title-lg">Select a Basemap</Typography>
<BaseMaps />
<Divider sx={{marginTop: 3}}/>
<Typography level="title-lg">Units of measurement</Typography>
<Units/>
<Divider sx={{marginTop: 3}}/>
<Typography mb={1} level="title-lg">Edit ADCIRC Layer Colormaps</Typography>
<DataRangeEdit />
<Divider/>
</Stack>
);
Loading

0 comments on commit 5dd4801

Please sign in to comment.