From e857fefba5c8631f4488c80035f8957721943023 Mon Sep 17 00:00:00 2001 From: Lisa Stillwell Date: Thu, 2 May 2024 14:36:15 -0400 Subject: [PATCH 01/11] starting design layout for Brian requested control panel --- src/app.js | 2 + src/components/control-panel/control-panel.js | 63 +++++++++++++++++ src/components/control-panel/index.js | 1 + src/components/map/default-layers.js | 70 ++++++++++--------- src/utils/index.js | 1 + 5 files changed, 103 insertions(+), 34 deletions(-) create mode 100644 src/components/control-panel/control-panel.js create mode 100644 src/components/control-panel/index.js create mode 100644 src/utils/index.js diff --git a/src/app.js b/src/app.js index 0794220e..c11b4032 100644 --- a/src/app.js +++ b/src/app.js @@ -1,12 +1,14 @@ import React, { Fragment } from 'react'; import { Map } from '@components/map'; import { Sidebar } from '@components/sidebar'; +import { ControlPanel } from '@components/control-panel'; export const App = () => { return ( + ); }; diff --git a/src/components/control-panel/control-panel.js b/src/components/control-panel/control-panel.js new file mode 100644 index 00000000..1758b677 --- /dev/null +++ b/src/components/control-panel/control-panel.js @@ -0,0 +1,63 @@ +import React from 'react'; +import { useLayers } from '@context/map-context'; +import { Sheet, ButtonGroup, Button, Typography, IconButton } from '@mui/joy'; +import { ArrowUpward, ArrowDownward } from '@mui/icons-material'; + + +//import { useLayers } from '@context'; + +export const ControlPanel = () => { + //const { defaultModelLayers, toggleLayerVisibility } = useLayers(); + const { defaultModelLayers } = useLayers(); + const layers = [...defaultModelLayers]; + + return ( + { (layers.length) && +

model run date: {layers[0].properties.run_date}

+ } + + + + + + + + }> + cycle {layers.length && layers[0].properties.cycle} + + { (layers.length) && +

{layers[0].properties.grid_type} grid

+ } + + { layers.map(layer => { + if (layer.properties.product_type != "obs") + return ( + + ); + })}; + +
+ ); +}; \ No newline at end of file diff --git a/src/components/control-panel/index.js b/src/components/control-panel/index.js new file mode 100644 index 00000000..06c1453e --- /dev/null +++ b/src/components/control-panel/index.js @@ -0,0 +1 @@ +export * from './control-panel'; \ No newline at end of file diff --git a/src/components/map/default-layers.js b/src/components/map/default-layers.js index b0c3f358..689f089d 100644 --- a/src/components/map/default-layers.js +++ b/src/components/map/default-layers.js @@ -142,40 +142,42 @@ export const DefaultLayers = () => { return ( <> - {defaultModelLayers.map((layer, index) => { - const pieces = layer.id.split('-'); - const type = pieces[pieces.length-1]; - //console.log("type: " + JSON.stringify(type, null, 2)) - if( type === "obs" && obsData !== "") { - //console.log("obsData: " + JSON.stringify(obsData, null, 2)); - return ( - - ); - } - else { - return ( - { - console.log('marker clicked') - }, - }} */ - url={gs_wms_url} - layers={layer.layers} - params={{ - format:"image/png", - transparent: true, - }} - - /> - ); - } + {defaultModelLayers + .filter(({state }) => state.visible) + .map((layer, index) => { + const pieces = layer.id.split('-'); + const type = pieces[pieces.length-1]; + //console.log("type: " + JSON.stringify(type, null, 2)) + if( type === "obs" && obsData !== "") { + //console.log("obsData: " + JSON.stringify(obsData, null, 2)); + return ( + + ); + } + else { + return ( + { + console.log('marker clicked') + }, + }} */ + url={gs_wms_url} + layers={layer.layers} + params={{ + format:"image/png", + transparent: true, + }} + + /> + ); + } })}; ); diff --git a/src/utils/index.js b/src/utils/index.js new file mode 100644 index 00000000..40fa5b76 --- /dev/null +++ b/src/utils/index.js @@ -0,0 +1 @@ +export * from "./map-utils"; \ No newline at end of file From cbcf29798d468eff3860560349ae41dc2ba26323 Mon Sep 17 00:00:00 2001 From: Lisa Stillwell Date: Thu, 2 May 2024 15:03:44 -0400 Subject: [PATCH 02/11] added aps logo, needs better alignment --- src/components/control-panel/control-panel.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/control-panel/control-panel.js b/src/components/control-panel/control-panel.js index 1758b677..55c9dc27 100644 --- a/src/components/control-panel/control-panel.js +++ b/src/components/control-panel/control-panel.js @@ -1,7 +1,8 @@ import React from 'react'; import { useLayers } from '@context/map-context'; -import { Sheet, ButtonGroup, Button, Typography, IconButton } from '@mui/joy'; +import { Sheet, ButtonGroup, Button, Typography, IconButton, Box } from '@mui/joy'; import { ArrowUpward, ArrowDownward } from '@mui/icons-material'; +import apsLogo from '@images/aps-trans-logo.png'; //import { useLayers } from '@context'; @@ -12,6 +13,7 @@ export const ControlPanel = () => { const layers = [...defaultModelLayers]; return ( + <> { borderRadius: 10, }} - > { (layers.length) && + > + + { (layers.length) &&

model run date: {layers[0].properties.run_date}

} @@ -59,5 +68,6 @@ export const ControlPanel = () => { })};
+ ); }; \ No newline at end of file From 4f2d759a795fe916c8c91f3fa21febf9ddb7facf Mon Sep 17 00:00:00 2001 From: Lisa Stillwell Date: Fri, 3 May 2024 08:41:12 -0400 Subject: [PATCH 03/11] added some notes for handling tropical storms --- src/components/control-panel/control-panel.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/control-panel/control-panel.js b/src/components/control-panel/control-panel.js index 55c9dc27..64f31dd8 100644 --- a/src/components/control-panel/control-panel.js +++ b/src/components/control-panel/control-panel.js @@ -1,6 +1,6 @@ import React from 'react'; import { useLayers } from '@context/map-context'; -import { Sheet, ButtonGroup, Button, Typography, IconButton, Box } from '@mui/joy'; +import { Sheet, ButtonGroup, Button, Typography, IconButton, Box, Switch } from '@mui/joy'; import { ArrowUpward, ArrowDownward } from '@mui/icons-material'; import apsLogo from '@images/aps-trans-logo.png'; @@ -21,7 +21,7 @@ export const ControlPanel = () => { bottom: 0, right: 0, overflow: 'hidden', p: 0, - backgroundColor: '#f0f4f820', //: '#f0f4f8', + backgroundColor: '#f0f4f820', height: '40vh', width: '300px', zIndex: 999, @@ -47,11 +47,17 @@ export const ControlPanel = () => { }> + {/* NOTE: If this is a tropical storm run, we need to change cycle to advisoy + Also probabaly want to add a switch for hurricane layers - which + involves making a request to the MetGet API */} cycle {layers.length && layers[0].properties.cycle} { (layers.length) &&

{layers[0].properties.grid_type} grid

} + }> + observations + Date: Wed, 8 May 2024 09:23:21 -0400 Subject: [PATCH 04/11] cleaned up and made somewhat functional --- src/components/control-panel/control-panel.js | 113 ++++++++++++------ src/components/map/default-layers.js | 19 ++- src/components/map/map.js | 2 +- 3 files changed, 96 insertions(+), 38 deletions(-) diff --git a/src/components/control-panel/control-panel.js b/src/components/control-panel/control-panel.js index 64f31dd8..0f20ea84 100644 --- a/src/components/control-panel/control-panel.js +++ b/src/components/control-panel/control-panel.js @@ -1,16 +1,43 @@ import React from 'react'; import { useLayers } from '@context/map-context'; -import { Sheet, ButtonGroup, Button, Typography, IconButton, Box, Switch } from '@mui/joy'; -import { ArrowUpward, ArrowDownward } from '@mui/icons-material'; +import { Sheet, ButtonGroup, Typography, IconButton, Box, Switch, ToggleButtonGroup } from '@mui/joy'; +import { ArrowUpward, ArrowDownward, Tsunami, Water, Air } from '@mui/icons-material'; import apsLogo from '@images/aps-trans-logo.png'; -//import { useLayers } from '@context'; - export const ControlPanel = () => { //const { defaultModelLayers, toggleLayerVisibility } = useLayers(); - const { defaultModelLayers } = useLayers(); + const { defaultModelLayers, toggleLayerVisibility } = useLayers(); + const layers = [...defaultModelLayers]; + const maxele_layer = layers.find((layer) => layer.properties.product_type === "maxele63"); + const obs_layer = layers.find((layer) => layer.properties.product_type === "obs"); + + const [value, setValue] = React.useState('maxele63'); + const [checked, setChecked] = React.useState(true); + + const layerChange = (event, newValue) => { + + setValue(newValue); + // turn off the old + layers.map(layer => { + if (layer.layers.includes(value)) { + toggleLayerVisibility(layer.id); + } + }); + // turn on the new + layers.map(layer => { + if (layer.layers.includes(newValue)) { + console.log(newValue); + toggleLayerVisibility(layer.id); + } + }); + }; + + const toggleObsLayer = (event) => { + setChecked(event.target.checked); + toggleLayerVisibility(obs_layer.id); + }; return ( <> @@ -21,58 +48,76 @@ export const ControlPanel = () => { bottom: 0, right: 0, overflow: 'hidden', p: 0, - backgroundColor: '#f0f4f820', - height: '40vh', + backgroundColor: '#f0f4f800', + height: '30vh', width: '300px', zIndex: 999, filter: 'drop-shadow(0 0 8px rgba(0, 0, 0, 0.2))', borderRadius: 10, - + display: 'flex', + flexDirection: 'column', }} > { (layers.length) && -

model run date: {layers[0].properties.run_date}

+ model run date: {layers[0].properties.run_date} } - - + + - + -
}> - {/* NOTE: If this is a tropical storm run, we need to change cycle to advisoy + }> + {/* TODO: NOTE: If this is a tropical storm run, we need to change cycle to advisoy Also probabaly want to add a switch for hurricane layers - which - involves making a request to the MetGet API */} - cycle {layers.length && layers[0].properties.cycle} + involves making a request to the MetGet API + Third need to implement actual code to load different model runs each time + up/down arrows are clicked. This has to time managed in some way so that + Geoserver is not inundated with requests */} + cycle {layers.length && layers[0].properties.cycle} { (layers.length) && -

{layers[0].properties.grid_type} grid

+ {layers[0].properties.grid_type} grid } - }> + + }> observations - - { layers.map(layer => { - if (layer.properties.product_type != "obs") - return ( - - ); - })}; - + { { (maxele_layer) && + + + + } + { layers.map(layer => { + if (layer.properties.product_type != "obs" && layer.properties.product_type != "maxele63") + + return ( + + { (layer.properties.product_type === "maxwvel63") && + } + {(layer.properties.product_type === "swan_HS_max63") && + } + + ); + })} + }> + } ); diff --git a/src/components/map/default-layers.js b/src/components/map/default-layers.js index 689f089d..c9859af1 100644 --- a/src/components/map/default-layers.js +++ b/src/components/map/default-layers.js @@ -4,6 +4,21 @@ import { CircleMarker } from 'leaflet'; import { useLayers } from '@context'; import { markClicked } from '@utils/map-utils'; +const newLayerDefaultState = (layer) => { + const { product_type } = layer.properties; + + if (['obs', 'maxele63'].includes(product_type)) { + return ({ + visible: true, + }); + } + + return ({ + visible: false, + }); + }; + + export const DefaultLayers = () => { const [obsData, setObsData] = useState(""); @@ -92,9 +107,7 @@ export const DefaultLayers = () => { if (layer) layer_list.push({ ...layer, - state: { - visible: true, - } + state: newLayerDefaultState(layer, layer_list.length) }); // TODO: do we really need to do this here??! diff --git a/src/components/map/map.js b/src/components/map/map.js index d54bf614..7fb63715 100644 --- a/src/components/map/map.js +++ b/src/components/map/map.js @@ -17,7 +17,7 @@ export const Map = () => { zoom={5} zoomControl={false} scrollWheelZoom={true} - whenCreated={setMap} + ref={setMap} style={{ height: '100vh', width:'100wh' }}> Date: Fri, 10 May 2024 10:04:24 -0400 Subject: [PATCH 05/11] updated to get cycle and date saved initially and some styling tweaks --- src/components/control-panel/control-panel.js | 94 +++++++++++++++---- 1 file changed, 75 insertions(+), 19 deletions(-) diff --git a/src/components/control-panel/control-panel.js b/src/components/control-panel/control-panel.js index 0f20ea84..8bd075ad 100644 --- a/src/components/control-panel/control-panel.js +++ b/src/components/control-panel/control-panel.js @@ -1,21 +1,38 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { useLayers } from '@context/map-context'; import { Sheet, ButtonGroup, Typography, IconButton, Box, Switch, ToggleButtonGroup } from '@mui/joy'; -import { ArrowUpward, ArrowDownward, Tsunami, Water, Air } from '@mui/icons-material'; +import { KeyboardArrowLeft, KeyboardArrowRight, Tsunami, Water, Air } from '@mui/icons-material'; import apsLogo from '@images/aps-trans-logo.png'; export const ControlPanel = () => { - //const { defaultModelLayers, toggleLayerVisibility } = useLayers(); - const { defaultModelLayers, toggleLayerVisibility } = useLayers(); + + const { map, defaultModelLayers, toggleLayerVisibility } = useLayers(); const layers = [...defaultModelLayers]; const maxele_layer = layers.find((layer) => layer.properties.product_type === "maxele63"); const obs_layer = layers.find((layer) => layer.properties.product_type === "obs"); + // keep track of which model run to retrieve + let runCycle = 0; + let runDate = new Date(); + const [value, setValue] = React.useState('maxele63'); const [checked, setChecked] = React.useState(true); + useEffect(() => { + if (layers[0]) { + // set the initial run cycle + runCycle = parseInt(layers[0].properties.cycle); + + // set the initial run date string + const dateParts = layers[0].properties.run_date.split('-'); + const currentDate = new Date(dateParts[0], dateParts[1]-1, dateParts[2]); + runDate = new Date(currentDate) + } + + }, [layers]); + const layerChange = (event, newValue) => { setValue(newValue); @@ -28,7 +45,6 @@ export const ControlPanel = () => { // turn on the new layers.map(layer => { if (layer.layers.includes(newValue)) { - console.log(newValue); toggleLayerVisibility(layer.id); } }); @@ -39,6 +55,47 @@ export const ControlPanel = () => { toggleLayerVisibility(obs_layer.id); }; + const changeModelRun = async (e) => { + const data_url = `${process.env.REACT_APP_UI_DATA_URL}get_ui_data_secure?limit=1&use_new_wb=true&use_v3_sp=true`; + const direction = e.currentTarget.getAttribute("button-key"); + + // TODO: Need to update this to also support tropical storms + + // get the run details + if (layers) { + const runId = layers[0].id.split('-')[0]; + const metClass = layers[0].properties.met_class; + const eventType = layers[0].properties.event_type; + + if (direction === "next") { + // set properties for next model run + if (runCycle === 18) { // need to push date to next day + runDate.setDate(runDate.getDate() + 1); + runCycle = 0; + } else { + runCycle += 6; + } + } else { // previous + // set properties for previous model run + if (runCycle === 0) { // need to push date to previous day + runDate.setDate(runDate.getDate() - 1); + runCycle = 18; + } else { + runCycle -= 6; + } + } + + // now do the api query to retrieve the data + // here are the query parameters + console.log("query params:"); + console.log(runId); + console.log(metClass); + console.log(eventType); + console.log(runDate.toDateString()); + console.log(runCycle); + } + }; + return ( <> { { (layers.length) && model run date: {layers[0].properties.run_date} } - - - + + + - - + cycle {layers.length && layers[0].properties.cycle} + + - }> - {/* TODO: NOTE: If this is a tropical storm run, we need to change cycle to advisoy - Also probabaly want to add a switch for hurricane layers - which - involves making a request to the MetGet API - Third need to implement actual code to load different model runs each time - up/down arrows are clicked. This has to time managed in some way so that - Geoserver is not inundated with requests */} - cycle {layers.length && layers[0].properties.cycle} - + + {/* TODO: NOTE: If this is a tropical storm run, we need to change cycle to advisoy + Also probabaly want to add a switch for hurricane layers - which + involves making a request to the MetGet API + Third need to implement actual code to load different model runs each time + up/down arrows are clicked. This has to time managed in some way so that + Geoserver is not inundated with requests */} { (layers.length) && {layers[0].properties.grid_type} grid } From a005013b987a852a8cf2b310ebaef513c5baf026 Mon Sep 17 00:00:00 2001 From: Lisa Stillwell Date: Mon, 20 May 2024 17:48:25 -0400 Subject: [PATCH 06/11] more updates to control panel semi working now --- src/components/control-panel/control-panel.js | 217 +++++++++++++----- src/context/map-context.js | 18 +- 2 files changed, 177 insertions(+), 58 deletions(-) diff --git a/src/components/control-panel/control-panel.js b/src/components/control-panel/control-panel.js index 8bd075ad..95f658f9 100644 --- a/src/components/control-panel/control-panel.js +++ b/src/components/control-panel/control-panel.js @@ -1,39 +1,130 @@ import React, { useEffect } from 'react'; +import axios from 'axios'; import { useLayers } from '@context/map-context'; -import { Sheet, ButtonGroup, Typography, IconButton, Box, Switch, ToggleButtonGroup } from '@mui/joy'; -import { KeyboardArrowLeft, KeyboardArrowRight, Tsunami, Water, Air } from '@mui/icons-material'; +import { useQuery } from '@tanstack/react-query'; +import { Sheet, Typography, IconButton, Box, Switch, ToggleButtonGroup } from '@mui/joy'; +import { KeyboardArrowLeft, KeyboardArrowRight, Tsunami, Water, Air, Flood } from '@mui/icons-material'; import apsLogo from '@images/aps-trans-logo.png'; export const ControlPanel = () => { - const { map, defaultModelLayers, toggleLayerVisibility } = useLayers(); + //const { defaultModelLayers, setDefaultModelLayers, toggleLayerVisibility, makeAllLayersInvisible } = useLayers(); + const { defaultModelLayers, setDefaultModelLayers, toggleLayerVisibility } = useLayers(); + + const data_url = `${process.env.REACT_APP_UI_DATA_URL}get_ui_data?limit=1&use_v3_sp=true`; + //const gs_wms_url = `${process.env.REACT_APP_GS_DATA_URL}wms`; const layers = [...defaultModelLayers]; const maxele_layer = layers.find((layer) => layer.properties.product_type === "maxele63"); const obs_layer = layers.find((layer) => layer.properties.product_type === "obs"); - // keep track of which model run to retrieve - let runCycle = 0; - let runDate = new Date(); - const [value, setValue] = React.useState('maxele63'); const [checked, setChecked] = React.useState(true); - useEffect(() => { - if (layers[0]) { - // set the initial run cycle - runCycle = parseInt(layers[0].properties.cycle); - - // set the initial run date string - const dateParts = layers[0].properties.run_date.split('-'); - const currentDate = new Date(dateParts[0], dateParts[1]-1, dateParts[2]); - runDate = new Date(currentDate) + // keep track of which model run to retrieve + const [ runCycle, setRunCycle] = React.useState(0); + const [ runDate, setRunDate] = React.useState(""); + const [ instanceName, setInstanceName] = React.useState(""); + const [ metClass, setMetClass] = React.useState(""); + const [ eventType, setEventType] = React.useState(""); + const [ topLayers, setTopLayers ] = React.useState([]); + + // when cycle buttons are pushed on the control panel + // either the previous or next cycle of the displayed + // adcirc model run will be displayed on the map + // if the run is not already in memory, it will have + // to be retrieved from the get_ui_data api + // retrieve adcirc data and layers from filter data provided + // then populate default layers state + // all params are strings, and runDate format is YYYY-MM-DD + const [filters, setFilters] = React.useState(); + const [initialDataFetched, setInitialDataFetched] = React.useState(false); + + const newLayerDefaultState = (layer) => { + const { product_type } = layer.properties; + + if (['obs', value].includes(product_type)) { + return ({ + visible: true, + }); } + + return ({ + visible: false, + }); + }; + + const parseAndAddLayers = (d) => { + + // first see if this set of layers already exists in default layers + if (d.catalog[0].members && defaultModelLayers.find(layer => layer.id === d.catalog[0].members[0].id)) { + console.log("already have this one"); + } + // if not, add these layers to default layers + else { + // add visibity state property to retrieved catalog layers + const newLayers = []; + d.catalog[0].members.forEach((layer) => { + newLayers.push({ + ...layer, + state: newLayerDefaultState(layer) + }); + }); + //makeAllLayersInvisible(); + setTopLayers([...newLayers]); + setDefaultModelLayers([...newLayers, ...defaultModelLayers]); + } + }; + + // useQuery function + const setNewLayers = async() => { + // retrieve the set of layers for the new cycle + const { isError, data, error } = await axios.get(data_url, {params: filters}); + + if (isError) { + alert(error); + } else {} + // add data returned to default layers, if they are not already there. + parseAndAddLayers(data); + + return(data); + }; + useQuery( {queryKey: ['apsviz-data', filters], queryFn: setNewLayers, enabled: !!filters}); + + + const date2String = (date) => { + const str = date.getFullYear() + + '-' + String(date.getMonth() + 1).padStart(2, '0') + + '-' + String(date.getDate()).padStart(2, '0'); + + return str; + }; + + const string2Date = (str) => { + const dateParts = str.split('-'); + const newDate = new Date(dateParts[0], dateParts[1]-1, dateParts[2]); + + return newDate; + }; + + // set initial values the currently display layers + useEffect(() => { + if ((layers[0]) && (!initialDataFetched)) { + setInstanceName(layers[0].properties.instance_name); + setMetClass(layers[0].properties.met_class); + setEventType(layers[0].properties.event_type); + + setRunCycle(parseInt(layers[0].properties.cycle)); + setRunDate(layers[0].properties.run_date); + setInitialDataFetched(true); + setTopLayers([...defaultModelLayers]); + } }, [layers]); - const layerChange = (event, newValue) => { + // switch to the model run layer selected via icon button + const layerChange = async (event, newValue) => { setValue(newValue); // turn off the old @@ -42,6 +133,8 @@ export const ControlPanel = () => { toggleLayerVisibility(layer.id); } }); + // Yikes! need another way to do this - but it works for now + await new Promise(r => setTimeout(r, 1)); // turn on the new layers.map(layer => { if (layer.layers.includes(newValue)) { @@ -50,50 +143,55 @@ export const ControlPanel = () => { }); }; + // switch on/off the observation layer, if it exists const toggleObsLayer = (event) => { setChecked(event.target.checked); toggleLayerVisibility(obs_layer.id); }; - const changeModelRun = async (e) => { - const data_url = `${process.env.REACT_APP_UI_DATA_URL}get_ui_data_secure?limit=1&use_new_wb=true&use_v3_sp=true`; + // cycle to the next model run cycle and retrieve the + // layers associated with that cycle/date + const changeModelRunCycle = (e) => { + const direction = e.currentTarget.getAttribute("button-key"); // TODO: Need to update this to also support tropical storms - - // get the run details - if (layers) { - const runId = layers[0].id.split('-')[0]; - const metClass = layers[0].properties.met_class; - const eventType = layers[0].properties.event_type; - - if (direction === "next") { - // set properties for next model run - if (runCycle === 18) { // need to push date to next day - runDate.setDate(runDate.getDate() + 1); - runCycle = 0; - } else { - runCycle += 6; - } - } else { // previous - // set properties for previous model run - if (runCycle === 0) { // need to push date to previous day - runDate.setDate(runDate.getDate() - 1); - runCycle = 18; - } else { - runCycle -= 6; - } - } + // const runId = layers[0].id.split('-')[0]; + // const metClass = layers[0].properties.met_class; + // const eventType = layers[0].properties.event_type; + const currentDate = string2Date(runDate); + let currentCycle = Number(runCycle) + 0; - // now do the api query to retrieve the data - // here are the query parameters - console.log("query params:"); - console.log(runId); - console.log(metClass); - console.log(eventType); - console.log(runDate.toDateString()); - console.log(runCycle); + if (direction === "next") { + // set properties for next model run + if (currentCycle === 18) { // need to push date to next day + currentDate.setDate(currentDate.getDate() + 1); + currentCycle = 0; + } else { + currentCycle += 6; + } + } else { // previous + // set properties for previous model run + if (currentCycle === 0) { // need to push date to previous day + currentDate.setDate(currentDate.getDate() - 1); + currentCycle = 18; + } else { + currentCycle -= 6; + } } + + setRunDate(date2String(currentDate)); + const cycle = String(currentCycle).padStart(2, '0'); + setRunCycle(cycle); + + const newFilters = {"instance_name": instanceName, + "met_class": metClass, + "event_type": eventType, + "run_date": date2String(currentDate), + "cycle": cycle + }; + + setFilters(newFilters); }; return ( @@ -122,14 +220,16 @@ export const ControlPanel = () => { src={apsLogo} /> { (layers.length) && - model run date: {layers[0].properties.run_date} + // model run date: {layers[0].properties.run_date} + model run date: {runDate} } - + - cycle {layers.length && layers[0].properties.cycle} - + {/* cycle {layers.length && layers[0].properties.cycle} */} + cycle {runCycle} + @@ -142,11 +242,12 @@ export const ControlPanel = () => { { (layers.length) && {layers[0].properties.grid_type} grid } + { ((layers.filter((layer) => layer.properties.product_type === "obs").length) > 0) && }> observations - + } { { } - { layers.map(layer => { + { topLayers.map(layer => { if (layer.properties.product_type != "obs" && layer.properties.product_type != "maxele63") return ( @@ -169,6 +270,8 @@ export const ControlPanel = () => { } {(layer.properties.product_type === "swan_HS_max63") && } + {(layer.properties.product_type === "maxinundepth63") && + } ); })} diff --git a/src/context/map-context.js b/src/context/map-context.js index 7a4ff533..6fee5962 100644 --- a/src/context/map-context.js +++ b/src/context/map-context.js @@ -7,6 +7,7 @@ import { Air as WindVelocityIcon, Water as WaterLevelIcon, BlurOn as WaterSurfaceIcon, + Flood as FloodIcon, } from '@mui/icons-material'; export const LayersContext = createContext({}); @@ -32,8 +33,12 @@ const layerTypes = { hec_ras_water_surface: { icon: WaterSurfaceIcon, }, + maxinundepth63: { + icon: FloodIcon, + }, }; + export const LayersProvider = ({ children }) => { const [defaultModelLayers, setDefaultModelLayers] = useState([]); const [filteredModelLayers, setFilteredModelLayers] = useState([]); @@ -59,6 +64,17 @@ export const LayersProvider = ({ children }) => { ]); }; + const makeAllRasterLayersInvisible = () => { + const newLayers = []; + const currentLayers = [...defaultModelLayers]; + currentLayers.forEach((layer, idx) => { + const alteredLayer = currentLayers[idx]; + alteredLayer.state.visible = false; + newLayers.push(alteredLayer); + }); + setDefaultModelLayers([...newLayers]); + }; + const swapLayers = (i, j) => { // ensure our pair has i < j const [a, b] = [i, j].sort(); @@ -85,7 +101,6 @@ export const LayersProvider = ({ children }) => { setDefaultModelLayers(newLayers); }; - return ( { swapLayers, removeLayer, layerTypes, + makeAllRasterLayersInvisible, }} > {children} From 1cdafd1298dde24e0222a6da30285717b14bb640 Mon Sep 17 00:00:00 2001 From: Matt Watson Date: Wed, 22 May 2024 14:48:58 -0400 Subject: [PATCH 07/11] tidy react --- src/components/control-panel/control-panel.js | 211 +++++++++++------- 1 file changed, 126 insertions(+), 85 deletions(-) diff --git a/src/components/control-panel/control-panel.js b/src/components/control-panel/control-panel.js index 95f658f9..d3d8a082 100644 --- a/src/components/control-panel/control-panel.js +++ b/src/components/control-panel/control-panel.js @@ -2,10 +2,32 @@ import React, { useEffect } from 'react'; import axios from 'axios'; import { useLayers } from '@context/map-context'; import { useQuery } from '@tanstack/react-query'; -import { Sheet, Typography, IconButton, Box, Switch, ToggleButtonGroup } from '@mui/joy'; -import { KeyboardArrowLeft, KeyboardArrowRight, Tsunami, Water, Air, Flood } from '@mui/icons-material'; +import { + Box, + Card, + Divider, + IconButton, + Sheet, + Stack, + Switch, + ToggleButtonGroup, + Typography, +} from '@mui/joy'; +import { + Air as MaxWindVelocityIcon, + Flood as MaxInundationIcon, + KeyboardArrowLeft, + KeyboardArrowRight, + Tsunami as SwanIcon, + Water as MaxElevationIcon, +} from '@mui/icons-material'; import apsLogo from '@images/aps-trans-logo.png'; +const layerIcons = { + maxwvel63: , + swan_HS_max63: , + maxinundepth63: , +}; export const ControlPanel = () => { @@ -19,7 +41,7 @@ export const ControlPanel = () => { const maxele_layer = layers.find((layer) => layer.properties.product_type === "maxele63"); const obs_layer = layers.find((layer) => layer.properties.product_type === "obs"); - const [value, setValue] = React.useState('maxele63'); + const [currentLayerSelection, setCurrentLayerSelection] = React.useState('maxele63'); const [checked, setChecked] = React.useState(true); // keep track of which model run to retrieve @@ -44,7 +66,7 @@ export const ControlPanel = () => { const newLayerDefaultState = (layer) => { const { product_type } = layer.properties; - if (['obs', value].includes(product_type)) { + if (['obs', currentLayerSelection].includes(product_type)) { return ({ visible: true, }); @@ -126,10 +148,10 @@ export const ControlPanel = () => { // switch to the model run layer selected via icon button const layerChange = async (event, newValue) => { - setValue(newValue); + setCurrentLayerSelection(newValue); // turn off the old layers.map(layer => { - if (layer.layers.includes(value)) { + if (layer.layers.includes(currentLayerSelection)) { toggleLayerVisibility(layer.id); } }); @@ -184,7 +206,7 @@ export const ControlPanel = () => { const cycle = String(currentCycle).padStart(2, '0'); setRunCycle(cycle); - const newFilters = {"instance_name": instanceName, + const newFilters = {"instncae_name": instanceName, "met_class": metClass, "event_type": eventType, "run_date": date2String(currentDate), @@ -195,89 +217,108 @@ export const ControlPanel = () => { }; return ( - <> - + - { (layers.length) && - // model run date: {layers[0].properties.run_date} - model run date: {runDate} + + + + { + layers.length && ( + + Model run date: {runDate} + + ) } - - - - - {/* cycle {layers.length && layers[0].properties.cycle} */} - cycle {runCycle} - - - - + + + + Cycle {runCycle} + + + + + {/* TODO: NOTE: If this is a tropical storm run, we need to change cycle to advisoy - Also probabaly want to add a switch for hurricane layers - which - involves making a request to the MetGet API - Third need to implement actual code to load different model runs each time - up/down arrows are clicked. This has to time managed in some way so that - Geoserver is not inundated with requests */} - { (layers.length) && - {layers[0].properties.grid_type} grid + Also probabaly want to add a switch for hurricane layers - which + involves making a request to the MetGet API + Third need to implement actual code to load different model runs each time + up/down arrows are clicked. This has to time managed in some way so that + Geoserver is not inundated with requests */} + + { // grid name + layers.length && ( + {layers[0].properties.grid_type} grid + ) + } + + { // observations toggle + layers.some(layer => layer.properties.product_type === "obs") && ( + } + >Observations + ) } - { ((layers.filter((layer) => layer.properties.product_type === "obs").length) > 0) && - - }> - observations - } - { { (maxele_layer) && - - - - } - { topLayers.map(layer => { - if (layer.properties.product_type != "obs" && layer.properties.product_type != "maxele63") - - return ( - - { (layer.properties.product_type === "maxwvel63") && - } - {(layer.properties.product_type === "swan_HS_max63") && - } - {(layer.properties.product_type === "maxinundepth63") && - } - - ); - })} - }> - } - - + + {/* layer selection */} + + { // have to do wierd stuff to get maxele first and default button + maxele_layer && ( + + + + ) + } + { + topLayers + .filter(layer => layer.properties.product_type != "obs" && layer.properties.product_type != "maxele63") + .map(layer => ( + + { layerIcons[layer.properties.product_type] } + + )) + } + + + ); }; \ No newline at end of file From 027cd9b7ce508396726dc8f49bc04eef0b41de40 Mon Sep 17 00:00:00 2001 From: Matt Watson Date: Wed, 22 May 2024 15:02:31 -0400 Subject: [PATCH 08/11] remove unused import --- src/components/control-panel/control-panel.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/control-panel/control-panel.js b/src/components/control-panel/control-panel.js index d3d8a082..f43a2c28 100644 --- a/src/components/control-panel/control-panel.js +++ b/src/components/control-panel/control-panel.js @@ -7,7 +7,6 @@ import { Card, Divider, IconButton, - Sheet, Stack, Switch, ToggleButtonGroup, From e01a8751db8b5345d0491aab5ec1da2cc0985135 Mon Sep 17 00:00:00 2001 From: Matt Watson Date: Wed, 22 May 2024 15:16:08 -0400 Subject: [PATCH 09/11] add missed maxele icon to icons object --- src/components/control-panel/control-panel.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/control-panel/control-panel.js b/src/components/control-panel/control-panel.js index f43a2c28..cb961d70 100644 --- a/src/components/control-panel/control-panel.js +++ b/src/components/control-panel/control-panel.js @@ -23,6 +23,7 @@ import { import apsLogo from '@images/aps-trans-logo.png'; const layerIcons = { + maxele63: , maxwvel63: , swan_HS_max63: , maxinundepth63: , @@ -300,7 +301,7 @@ export const ControlPanel = () => { value={maxele_layer.properties.product_type} key={maxele_layer.properties.id} > - + { layerIcons[maxele_layer.properties.product_type] } ) } From e79cbbd702135141008c61f1a66b9bdffaae6fa8 Mon Sep 17 00:00:00 2001 From: Matt Watson Date: Wed, 22 May 2024 15:16:41 -0400 Subject: [PATCH 10/11] fix typo --- src/components/control-panel/control-panel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/control-panel/control-panel.js b/src/components/control-panel/control-panel.js index cb961d70..594b959a 100644 --- a/src/components/control-panel/control-panel.js +++ b/src/components/control-panel/control-panel.js @@ -206,7 +206,7 @@ export const ControlPanel = () => { const cycle = String(currentCycle).padStart(2, '0'); setRunCycle(cycle); - const newFilters = {"instncae_name": instanceName, + const newFilters = {"instance_name": instanceName, "met_class": metClass, "event_type": eventType, "run_date": date2String(currentDate), From cf2b060ab597f44a4556a5b1a9ba8429eca587e7 Mon Sep 17 00:00:00 2001 From: Lisa Stillwell Date: Fri, 24 May 2024 09:31:32 -0400 Subject: [PATCH 11/11] added keys to other icon buttons --- src/components/control-panel/control-panel.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/control-panel/control-panel.js b/src/components/control-panel/control-panel.js index 594b959a..92436109 100644 --- a/src/components/control-panel/control-panel.js +++ b/src/components/control-panel/control-panel.js @@ -254,11 +254,15 @@ export const ControlPanel = () => { Cycle {runCycle}