diff --git a/package-lock.json b/package-lock.json index 4f4b11a..e936f30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@mui/joy": "^5.0.0-beta.48", "@mui/material": "^6.1.1", "@mui/x-date-pickers": "^7.18.0", - "@renci/apsviz-geostyler": "15.0.18", + "@renci/apsviz-geostyler": "15.0.21", "@tanstack/react-query": "^5.56.2", "@turf/bearing": "^7.1.0", "@turf/circle": "^7.1.0", @@ -5420,9 +5420,9 @@ } }, "node_modules/@renci/apsviz-geostyler": { - "version": "15.0.18", - "resolved": "https://npm.pkg.github.com/download/@renci/apsviz-geostyler/15.0.18/8c26810f35c8c849f46b47562c186a74a71846ad", - "integrity": "sha512-lziOeUYnBSlREJQnOveLVhlyzv7gK7vnrUu8Yxd/lJIAvK9Gq0uU1arSzdR0FG34H6raLv1WCJUeCT2M1GhF1w==", + "version": "15.0.21", + "resolved": "https://npm.pkg.github.com/download/@renci/apsviz-geostyler/15.0.21/b49102ae92f765d64da543e5d836588a71a7ce91", + "integrity": "sha512-+pxCAM7UZf7aA75UmUm+IPNwH02UgXv9Zzvgs2Al7hAfKdwwJ1dETd+BEBNINQx9BMjuMPGmmkFU2ErB2nSatw==", "dependencies": { "@ant-design/icons": "^5.3.7", "@babel/polyfill": "^7.12.1", @@ -8612,9 +8612,9 @@ "dev": true }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "dev": true, "engines": { "node": ">= 0.6" @@ -10711,18 +10711,17 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dev": true, - "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", diff --git a/package.json b/package.json index 28fedbd..80f6b93 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "@mui/joy": "^5.0.0-beta.48", "@mui/material": "^6.1.1", "@mui/x-date-pickers": "^7.18.0", - "@renci/apsviz-geostyler": "15.0.18", + "@renci/apsviz-geostyler": "15.0.21", "@tanstack/react-query": "^5.56.2", "@turf/bearing": "^7.1.0", "@turf/circle": "^7.1.0", diff --git a/src/components/trays/settings/colormaps/colormap-slider.js b/src/components/trays/settings/colormaps/colormap-slider.js index 8aaf535..4532000 100644 --- a/src/components/trays/settings/colormaps/colormap-slider.js +++ b/src/components/trays/settings/colormaps/colormap-slider.js @@ -4,6 +4,7 @@ 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'; const MAXELE = 'maxele'; const MAXWVEL = 'maxwvel'; @@ -31,7 +32,7 @@ export const ColormapSlider = ({style}) => { const marks = []; if (style.name.includes("maxwvel")) { - max_slider_value = 100; + max_slider_value = maxSliderValues[MAXWVEL]; slider_step = 1; for (let i = 0; i <= max_slider_value; i+=10) { marks.push({ label: i, value: i }); @@ -39,14 +40,14 @@ export const ColormapSlider = ({style}) => { } else if (style.name.includes("swan")) { - max_slider_value = 30; + max_slider_value = maxSliderValues[SWAN]; slider_step = 0.5; for (let i = 0; i <= max_slider_value; i+=5) { marks.push({ label: i, value: i }); } } else { // maxele - max_slider_value = 10; + max_slider_value = maxSliderValues[MAXELE]; slider_step = 0.25; for (let i = 0; i <= max_slider_value; i++) { marks.push({ label: i, value: i }); @@ -59,7 +60,7 @@ export const ColormapSlider = ({style}) => { setMinSliderValue(0); const colormapEntries = style.rules[0].symbolizers[0].colorMap.colorMapEntries; - setValue([colormapEntries[colormapEntries.length-1].quantity, colormapEntries[0].quantity]); + setValue([parseFloat(colormapEntries[colormapEntries.length-1].quantity), parseFloat(colormapEntries[0].quantity)]); }; useEffect(() => { @@ -67,6 +68,16 @@ export const ColormapSlider = ({style}) => { sldParser .readStyle(style) .then((geostylerStyle) => { + // for interval type colormaps, fake out current style with + // label stated max valuein range so we can do the right calculations + // get label max - in format like this: ">= 2.00" + if (geostylerStyle.output.rules[0].symbolizers[0].colorMap.type === "intervals") { + 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); + } + setCurrentStyle(geostylerStyle.output); setSliderParams(geostylerStyle.output); }) @@ -100,6 +111,11 @@ export const ColormapSlider = ({style}) => { 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); + } return(dataRange.reverse()); }; @@ -140,28 +156,50 @@ export const ColormapSlider = ({style}) => { else { entry.label = range[idx] + " m"; } + // 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"; + } + else { + entry.label = ">= " + entry.quantity + " m"; + } + entry.quantity = maxSliderValue; + } + } } }); style.rules[0].symbolizers[0].colorMap.colorMapEntries=[...colormapEntries]; + + // set the style with a top max value to cover all + // possible values at the top of the range + return(style); }; const handleChange = (event, newValue) => { // make sure the first thumb value is not >= the second - if (newValue[0] < newValue[1]) { + // and that second is >= sliderStep + if ((newValue[0] < newValue[1]) && (newValue[1] >= sliderStep)) { setValue(newValue); } }; const handleChangeCommitted = (event, newValue) => { - if (newValue[0] < newValue[1]) { - setValue(newValue); - } - else + + // prevent overlapping values if (newValue[0] === newValue[1]) { - setValue([newValue[0]-sliderStep, newValue[1]]); + newValue[0] = newValue[0]-sliderStep; } + // since min slider value doesn't appear to work, make + // sure lower slider value is never less tha 0 + newValue[0] = (newValue[0] < 0) ? 0 : newValue[0]; + // also check for 0 upper value - set sliderStep as lowest value + newValue[1] = (newValue[1] < sliderStep) ? sliderStep : newValue[1]; + setValue([newValue[0], newValue[1]]); // now create new style with altered data range // get current data range values in reverse order diff --git a/src/components/trays/settings/colormaps/style-edit.js b/src/components/trays/settings/colormaps/style-edit.js index d0865ea..2790d2b 100644 --- a/src/components/trays/settings/colormaps/style-edit.js +++ b/src/components/trays/settings/colormaps/style-edit.js @@ -12,6 +12,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'; const MAXELE = 'maxele'; const MAXWVEL = 'maxwvel'; @@ -82,6 +83,7 @@ export const StyleEditor = () => { (_, i) => (startingNumber + i * (endingNumber - startingNumber) / (maxNumber - 1)).toFixed(2) ); + // handles any change to the colormap properties provided // by the geostyler package component - ColorMapEdit // editable properties include colormap type, @@ -91,6 +93,11 @@ export const StyleEditor = () => { // get values changed and update the style accordingly const newColorMap = _cloneDeep(colormap); + // 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! + if (labelUnit.length > 1) labelUnit = labelUnit.split("").reverse().join(""); + //update type of colorMap newColorMap.type = value.type? value.type : "ramp"; @@ -98,12 +105,19 @@ export const StyleEditor = () => { if (colormap.colorMapEntries.length !== value.colorMapEntries.length) { // changed number of classes, so must rebuild the quantity and label // values with the colormap range currently defined. - const range = - [Number(colormap.colorMapEntries[0].quantity), Number(colormap.colorMapEntries[colormap.colorMapEntries.length-1].quantity)]; + + // 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)) + : + Number(colormap.colorMapEntries[colormap.colorMapEntries.length-1].quantity); + + const range = [Number(colormap.colorMapEntries[0].quantity), topRange]; const newRangeList = getRangeList(range[0], range[1], value.colorMapEntries.length); const newColorMapEntries = value.colorMapEntries.map((entry, index) => { entry.quantity = newRangeList[index]; - entry.label = newRangeList[index]; + entry.label = newRangeList[index] + " " + labelUnit; return ( entry ); @@ -111,7 +125,17 @@ export const StyleEditor = () => { newColorMap.colorMapEntries = newColorMapEntries; } // otherwise - we can just copy the colorMapEntries into the newColorMap - incase the color ramp changed + // only other thing to look out for, is whether this was previously an intervals colormap type + // and now has been changed to ramp + // in that case we have modify the last colormap entry 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); + const labelParts = value.colorMapEntries[last].label.split(" "); + if (labelParts.length >= 3) + value.colorMapEntries[last].label = parseFloat(labelParts[1]).toFixed(2) + " " + labelParts[2]; + } newColorMap.colorMapEntries = value.colorMapEntries.map((entry) => entry); } @@ -125,20 +149,35 @@ export const StyleEditor = () => { .readStyle(style) .then((geoStylerStyle) => { geoStylerStyle.output.rules[0].symbolizers[0].colorMap = colormap; + // if the colormap type is set to intervals, the last entry is a special case, + // so must change the last entry values + const styleName = geoStylerStyle.output.name.split('_')[0]; + if (colormap.type === "intervals") { + const lastIndex = colormap.colorMapEntries.length-1; + if (styleName === MAXWVEL) { + geoStylerStyle.output.rules[0].symbolizers[0].colorMap.colorMapEntries[lastIndex].label = ">= " + colormap.colorMapEntries[lastIndex].quantity + " m/s"; + } + else { + geoStylerStyle.output.rules[0].symbolizers[0].colorMap.colorMapEntries[lastIndex].label = ">= " + colormap.colorMapEntries[lastIndex].quantity + " m"; + } + geoStylerStyle.output.rules[0].symbolizers[0].colorMap.colorMapEntries[lastIndex].quantity = maxSliderValues[styleName]; + } + // save colormap type - it seems to get wiped out when the parser // writes out the text style const colorMapType = colormap.type; sldParser.writeStyle(geoStylerStyle.output).then((sldStyle) => { const updatedStyle = restoreColorMapType(colorMapType, sldStyle.output); - const styleName = geoStylerStyle.output.name; - if (styleName.includes(MAXELE)) { + switch (styleName) { + case MAXELE: mapStyle.maxele.set(updatedStyle); - } else - if (styleName.includes(MAXWVEL)) { + break; + case MAXWVEL: mapStyle.maxwvel.set(updatedStyle); - } else - if (styleName.includes(SWAN)) { + break; + case SWAN: mapStyle.swan.set(updatedStyle); + break; } }); }); diff --git a/src/components/trays/settings/colormaps/utils.js b/src/components/trays/settings/colormaps/utils.js new file mode 100644 index 0000000..a83f3d4 --- /dev/null +++ b/src/components/trays/settings/colormaps/utils.js @@ -0,0 +1,5 @@ +export const maxSliderValues = { + "maxele": 10, + "maxwvel": 100, + "swan": 30 +}; \ No newline at end of file