Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 296 field units #326

Merged
merged 11 commits into from
Nov 12, 2024
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