From 60a7233cf8df9f7136dd9324419e8e452eaaa265 Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:08:36 -0400 Subject: [PATCH 01/17] rendering the timeseries data on a obs dialog. --- src/components/map/default-layers.js | 42 +++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/components/map/default-layers.js b/src/components/map/default-layers.js index 92748367..520c835c 100644 --- a/src/components/map/default-layers.js +++ b/src/components/map/default-layers.js @@ -29,10 +29,50 @@ export const DefaultLayers = () => { const [obsData, setObsData] = useState(""); const map = useMap(); + const L = window.L; + + map.on('click', function(e) { + L.DomEvent.stopPropagation; + + // create a id for the point + const id = e.latlng.lat + ", " + e.latlng.lng; + + // does this point already exist + if(Object.values(selectedObservations).indexOf(id) === -1) { + // create a marker target icon around the observation clicked + markClicked(map, e, id); + + // get the FQDN of the UI data server + const data_url = `${ getNamespacedEnvParam('REACT_APP_UI_DATA_URL') }`; + + // create a set of properties for this object + const pointProps = + { + "station_name": "Ocean: " + id, + "lat": e.latlng.lat, + "lon": e.latlng.lng, + "location_name": "Ocean: " + id, + "model_run_id": "4489-2024101606-gfsforecast", + "data_source": "GFSFORECAST_NCSC_SAB_V1.23", + "source_name": "adcirc", + "source_instance": "ncsc123_gfs_da", + "source_archive": "RENCI", + "forcing_metclass": "synoptic", + "location_type": "ocean", + "grid_name": "NCSC_SAB_V1.23", + "csvurl": data_url + "get_geo_point_data?lon=" + e.latlng.lng + "&lat=" + e.latlng.lat + "&url=https%3A%2F%2Ftds.renci.org%2Fthredds%2FdodsC%2F2024%2Fgfs%2F2024041712%2FNCSC_SAB_v1.23%2Fht-ncfs.renci.org%2Fncsc123_gfs_sb55.01%2Fgfsforecast%2Ffort.63.nc&ensemble=nowcast", + "id": id + }; + + // populate selectedObservations list with the newly selected observation point + setSelectedObservations(previous => [...previous, pointProps]); + } + }); + const { defaultModelLayers, setDefaultModelLayers, - setSelectedObservations, + selectedObservations, setSelectedObservations, setShowShareComment } = useLayers(); From f95f5aa4d3c9c5b861e45406efd9a86c47e7a1b0 Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Thu, 17 Oct 2024 08:09:45 -0400 Subject: [PATCH 02/17] removing previous attempt --- src/components/map/default-layers.js | 42 +--------------------------- 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/src/components/map/default-layers.js b/src/components/map/default-layers.js index 520c835c..92748367 100644 --- a/src/components/map/default-layers.js +++ b/src/components/map/default-layers.js @@ -29,50 +29,10 @@ export const DefaultLayers = () => { const [obsData, setObsData] = useState(""); const map = useMap(); - const L = window.L; - - map.on('click', function(e) { - L.DomEvent.stopPropagation; - - // create a id for the point - const id = e.latlng.lat + ", " + e.latlng.lng; - - // does this point already exist - if(Object.values(selectedObservations).indexOf(id) === -1) { - // create a marker target icon around the observation clicked - markClicked(map, e, id); - - // get the FQDN of the UI data server - const data_url = `${ getNamespacedEnvParam('REACT_APP_UI_DATA_URL') }`; - - // create a set of properties for this object - const pointProps = - { - "station_name": "Ocean: " + id, - "lat": e.latlng.lat, - "lon": e.latlng.lng, - "location_name": "Ocean: " + id, - "model_run_id": "4489-2024101606-gfsforecast", - "data_source": "GFSFORECAST_NCSC_SAB_V1.23", - "source_name": "adcirc", - "source_instance": "ncsc123_gfs_da", - "source_archive": "RENCI", - "forcing_metclass": "synoptic", - "location_type": "ocean", - "grid_name": "NCSC_SAB_V1.23", - "csvurl": data_url + "get_geo_point_data?lon=" + e.latlng.lng + "&lat=" + e.latlng.lat + "&url=https%3A%2F%2Ftds.renci.org%2Fthredds%2FdodsC%2F2024%2Fgfs%2F2024041712%2FNCSC_SAB_v1.23%2Fht-ncfs.renci.org%2Fncsc123_gfs_sb55.01%2Fgfsforecast%2Ffort.63.nc&ensemble=nowcast", - "id": id - }; - - // populate selectedObservations list with the newly selected observation point - setSelectedObservations(previous => [...previous, pointProps]); - } - }); - const { defaultModelLayers, setDefaultModelLayers, - selectedObservations, setSelectedObservations, + setSelectedObservations, setShowShareComment } = useLayers(); From c1fbb6deca39ca690f3045406dbd49dcf1570632 Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Thu, 17 Oct 2024 08:10:09 -0400 Subject: [PATCH 03/17] adding anywhere point selection --- src/components/map/adcirc-raster-layer.js | 60 +++++++++++++++++++++-- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/src/components/map/adcirc-raster-layer.js b/src/components/map/adcirc-raster-layer.js index 9a405c4f..dc7e9ae7 100644 --- a/src/components/map/adcirc-raster-layer.js +++ b/src/components/map/adcirc-raster-layer.js @@ -1,8 +1,8 @@ -import React, { useEffect, useMemo, useState } from 'react'; -import { WMSTileLayer } from 'react-leaflet'; +import React, { useEffect, useMemo, useState, useCallback } from 'react'; +import { WMSTileLayer, useMap, useMapEvent } from 'react-leaflet'; import SldStyleParser from 'geostyler-sld-parser'; -import { getNamespacedEnvParam, restoreColorMapType } from '@utils/map-utils'; -import { useSettings } from '@context'; +import {getNamespacedEnvParam, markClicked, restoreColorMapType} from '@utils/map-utils'; +import {useLayers, useSettings} from '@context'; export const AdcircRasterLayer = (layer) => { const sldParser = new SldStyleParser(); @@ -15,6 +15,57 @@ export const AdcircRasterLayer = (layer) => { const [currentStyle, setCurrentStyle] = useState(""); + // get a handle to the map + const map = useMap(); + + // create a callback to handle a map click event + const onClick = useCallback ( (e) => { + // create an id for the point + const id = e.latlng.lat + ", " + e.latlng.lng; + + // does this point already exist + if(Object.values(selectedObservations).indexOf(id) === -1) { + // create a marker target icon around the observation clicked + markClicked(map, e, id); + + console.log(id); + + // get the FQDN of the UI data server + const data_url = `${ getNamespacedEnvParam('REACT_APP_UI_DATA_URL') }`; + + // create a set of properties for this object + const pointProps = + { + "station_name": "Ocean (lat, lon): " + id, + "lat": e.latlng.lat, + "lon": e.latlng.lng, + "location_name": "Ocean (lat, lon): " + id, + "model_run_id": "4489-2024101606-gfsforecast", + "data_source": "GFSFORECAST_NCSC_SAB_V1.23", + "source_name": "adcirc", + "source_instance": "ncsc123_gfs_da", + "source_archive": "RENCI", + "forcing_metclass": "synoptic", + "location_type": "ocean", + "grid_name": "NCSC_SAB_V1.23", + "csvurl": data_url + "get_geo_point_data?lon=" + e.latlng.lng + "&lat=" + e.latlng.lat + "&url=https%3A%2F%2Ftds.renci.org%2Fthredds%2FdodsC%2F2024%2Fgfs%2F2024041712%2FNCSC_SAB_v1.23%2Fht-ncfs.renci.org%2Fncsc123_gfs_sb55.01%2Fgfsforecast%2Ffort.63.nc&ensemble=nowcast", + "id": id + }; + + // populate selectedObservations list with the newly selected observation point + setSelectedObservations(previous => [...previous, pointProps]); + } + }); + + // assign the map click event + useMapEvent('click', onClick); + + // get the observation point selected state + const { + selectedObservations, + setSelectedObservations, + } = useLayers(); + useEffect(() => { if(layer.layer.properties) { let style = ""; @@ -58,6 +109,7 @@ export const AdcircRasterLayer = (layer) => { layers={layer.layer.layers} params={wmsLayerParams} opacity={layer.layer.state.opacity} + onClick={console.log} /> ); From d198d86d50793657bc3188247470971ac7541848 Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Thu, 17 Oct 2024 08:59:51 -0400 Subject: [PATCH 04/17] additional check of the data before trying to render --- src/components/dialog/observation-chart.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dialog/observation-chart.js b/src/components/dialog/observation-chart.js index 32076e76..d28f1ece 100644 --- a/src/components/dialog/observation-chart.js +++ b/src/components/dialog/observation-chart.js @@ -205,7 +205,7 @@ function formatX_axis(value) { */ function get_yaxis_ticks(data) { // insure there is something to work with - if (data !== undefined) { + if (data !== undefined && data.length > 0) { // init the max value found let maxVal = 0; From 372abd231e69d013a0899c714666d47d37e2b476 Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Thu, 17 Oct 2024 09:00:44 -0400 Subject: [PATCH 05/17] tidying up the addition of the geo point data --- src/components/map/adcirc-raster-layer.js | 66 +++++++++++------------ 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/src/components/map/adcirc-raster-layer.js b/src/components/map/adcirc-raster-layer.js index dc7e9ae7..90ad8fc9 100644 --- a/src/components/map/adcirc-raster-layer.js +++ b/src/components/map/adcirc-raster-layer.js @@ -21,40 +21,35 @@ export const AdcircRasterLayer = (layer) => { // create a callback to handle a map click event const onClick = useCallback ( (e) => { // create an id for the point - const id = e.latlng.lat + ", " + e.latlng.lng; - - // does this point already exist - if(Object.values(selectedObservations).indexOf(id) === -1) { - // create a marker target icon around the observation clicked - markClicked(map, e, id); - - console.log(id); - - // get the FQDN of the UI data server - const data_url = `${ getNamespacedEnvParam('REACT_APP_UI_DATA_URL') }`; - - // create a set of properties for this object - const pointProps = - { - "station_name": "Ocean (lat, lon): " + id, - "lat": e.latlng.lat, - "lon": e.latlng.lng, - "location_name": "Ocean (lat, lon): " + id, - "model_run_id": "4489-2024101606-gfsforecast", - "data_source": "GFSFORECAST_NCSC_SAB_V1.23", - "source_name": "adcirc", - "source_instance": "ncsc123_gfs_da", - "source_archive": "RENCI", - "forcing_metclass": "synoptic", - "location_type": "ocean", - "grid_name": "NCSC_SAB_V1.23", - "csvurl": data_url + "get_geo_point_data?lon=" + e.latlng.lng + "&lat=" + e.latlng.lat + "&url=https%3A%2F%2Ftds.renci.org%2Fthredds%2FdodsC%2F2024%2Fgfs%2F2024041712%2FNCSC_SAB_v1.23%2Fht-ncfs.renci.org%2Fncsc123_gfs_sb55.01%2Fgfsforecast%2Ffort.63.nc&ensemble=nowcast", - "id": id - }; - - // populate selectedObservations list with the newly selected observation point - setSelectedObservations(previous => [...previous, pointProps]); - } + const id = e.latlng.lng + ", " + e.latlng.lat; + + // create a marker target icon around the observation clicked + markClicked(map, e, id); + + // get the FQDN of the UI data server + const data_url = `${ getNamespacedEnvParam('REACT_APP_UI_DATA_URL') }`; + + // create a set of properties for this object + const pointProps = + { + "station_name": "Point (lon, lat): " + id, + "lat": e.latlng.lat, + "lon": e.latlng.lng, + "location_name": "Point (lon, lat): " + id, + "model_run_id": "4489-2024101606-gfsforecast", + "data_source": "GFSFORECAST_NCSC_SAB_V1.23", + "source_name": "adcirc", + "source_instance": "ncsc123_gfs_da", + "source_archive": "RENCI", + "forcing_metclass": "synoptic", + "location_type": "ocean", + "grid_name": "NCSC_SAB_V1.23", + "csvurl": data_url + "get_geo_point_data?lon=" + e.latlng.lng + "&lat=" + e.latlng.lat + "&url=https%3A%2F%2Ftds.renci.org%2Fthredds%2FdodsC%2F2024%2Fgfs%2F2024041712%2FNCSC_SAB_v1.23%2Fht-ncfs.renci.org%2Fncsc123_gfs_sb55.01%2Fgfsforecast%2Ffort.63.nc&ensemble=nowcast", + "id": id + }; + + // populate selectedObservations list with the newly selected observation point + setSelectedObservations(previous => [...previous, pointProps]); }); // assign the map click event @@ -62,8 +57,7 @@ export const AdcircRasterLayer = (layer) => { // get the observation point selected state const { - selectedObservations, - setSelectedObservations, + setSelectedObservations } = useLayers(); useEffect(() => { From 1f4cd82bb74d3911495569c0d4152c8351262d2a Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:07:07 -0400 Subject: [PATCH 06/17] adding the swan data rendering --- src/components/dialog/base-floating-dialog.js | 14 ++++++++++ src/components/dialog/observation-chart.js | 26 ++++++++++++++++--- src/components/dialog/observation-dialog.js | 4 +++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/components/dialog/base-floating-dialog.js b/src/components/dialog/base-floating-dialog.js index 1af0b75a..0c87327c 100644 --- a/src/components/dialog/base-floating-dialog.js +++ b/src/components/dialog/base-floating-dialog.js @@ -135,6 +135,20 @@ export default function BaseFloatingDialog({ title, index, dialogObject, dataKey APS Forecast : '' } + {(showLineButtonView("SWAN Nowcast")) ? + + SWAN Nowcast : '' + } + + {(showLineButtonView("SWAN Forecast")) ? + + SWAN Forecast : '' + } + {(showLineButtonView("NOAA Tidal Predictions")) ? { status === 'pending' ? (
Gathering chart data...
) : - status === 'error' ? (
There was a problem with observation data for this location.
) : + status === 'error' ? (
There was a problem with collecting data for this location.
) : @@ -284,6 +302,8 @@ function CreateObsChart(c) { + + diff --git a/src/components/dialog/observation-dialog.js b/src/components/dialog/observation-dialog.js index a5fea962..edd43d64 100644 --- a/src/components/dialog/observation-dialog.js +++ b/src/components/dialog/observation-dialog.js @@ -24,6 +24,8 @@ export const ObservationDialog = (obs_data) => { "NOAA Tidal Predictions": false, "APS Nowcast": false, "APS Forecast": false, + "SWAN Nowcast": false, + "SWAN Forecast": false, "Difference (APS-OBS)": false }); // method to toggle the line view polarity @@ -38,6 +40,8 @@ export const ObservationDialog = (obs_data) => { "NOAA Tidal Predictions": false, "APS Nowcast": false, "APS Forecast": false, + "SWAN Nowcast": false, + "SWAN Forecast": false, "Difference (APS-OBS)": false }); const setLineButtonView = (item) => { From 929d045c856c9bbcfbb4a6a11de570b3083af423 Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:13:30 -0400 Subject: [PATCH 07/17] adding more details to the geo-point properties, adding SWAN detection, adding the correct TDS url --- src/components/map/adcirc-raster-layer.js | 105 ++++++++++++---------- 1 file changed, 57 insertions(+), 48 deletions(-) diff --git a/src/components/map/adcirc-raster-layer.js b/src/components/map/adcirc-raster-layer.js index 90ad8fc9..a2a87757 100644 --- a/src/components/map/adcirc-raster-layer.js +++ b/src/components/map/adcirc-raster-layer.js @@ -15,13 +15,51 @@ export const AdcircRasterLayer = (layer) => { const [currentStyle, setCurrentStyle] = useState(""); + useEffect(() => { + if(layer.layer.properties) { + let style = ""; + switch(layer.layer.properties.product_type) { + case ("maxwvel63"): + style = mapStyle.maxwvel.current; + break; + case ("swan_HS_max63"): + style = mapStyle.swan.current; + break; + default: + style = mapStyle.maxele.current; + break; + } + + sldParser + .readStyle(style) + .then((geostylerStyle) => { + geostylerStyle.output.name = (' ' + layer.layer.layers).slice(1); + const colorMapType = geostylerStyle.output.rules[0].symbolizers[0].colorMap.type; + sldParser.writeStyle(geostylerStyle.output) + .then((sldStyle) => { + const updatedStyle = restoreColorMapType(colorMapType, sldStyle.output); + setCurrentStyle(updatedStyle); + }); + }); + } + }, [mapStyle]); + + // get the observation points selected and default layers from state + const { + setSelectedObservations, + defaultModelLayers + } = useLayers(); + + // capture the default layers + const layers = defaultModelLayers; + // get a handle to the map const map = useMap(); // create a callback to handle a map click event const onClick = useCallback ( (e) => { // create an id for the point - const id = e.latlng.lng + ", " + e.latlng.lat; + const id = Number(e.latlng.lng).toFixed(6) + ', ' + Number(e.latlng.lat).toFixed(6); // create a marker target icon around the observation clicked markClicked(map, e, id); @@ -29,22 +67,28 @@ export const AdcircRasterLayer = (layer) => { // get the FQDN of the UI data server const data_url = `${ getNamespacedEnvParam('REACT_APP_UI_DATA_URL') }`; + // get the visible layer on the map + const layer = layers.find((layer) => layer.properties['product_type'] !== "obs" && layer.state.visible === true); + + // create the correct TDS URL + const tds_url = layer.properties['tds_download_url'].replace('catalog', 'dodsC').replace('catalog.html', (layer.id.indexOf('swan') < 0 ? 'fort' : 'swan_HS') + '.63.nc'); + // create a set of properties for this object const pointProps = { - "station_name": "Point (lon, lat): " + id, - "lat": e.latlng.lat, - "lon": e.latlng.lng, - "location_name": "Point (lon, lat): " + id, - "model_run_id": "4489-2024101606-gfsforecast", - "data_source": "GFSFORECAST_NCSC_SAB_V1.23", - "source_name": "adcirc", - "source_instance": "ncsc123_gfs_da", - "source_archive": "RENCI", - "forcing_metclass": "synoptic", + "station_name": layer.properties['product_name'] + " at (lon, lat): " + id, + "lat": Number(e.latlng.lat).toFixed(6), + "lon": Number(e.latlng.lng).toFixed(6), + "location_name": layer.properties['product_name'] + " at (lon, lat): " + id, + "model_run_id": layer.group, + "data_source": (layer.properties['event_type'] + '_' + layer.properties['grid_type']).toUpperCase(), + "source_name": layer.properties['model'], + "source_instance": layer.properties['instance_name'], + "source_archive": layer.properties['location'], + "forcing_metclass": layer.properties['met_class'], "location_type": "ocean", "grid_name": "NCSC_SAB_V1.23", - "csvurl": data_url + "get_geo_point_data?lon=" + e.latlng.lng + "&lat=" + e.latlng.lat + "&url=https%3A%2F%2Ftds.renci.org%2Fthredds%2FdodsC%2F2024%2Fgfs%2F2024041712%2FNCSC_SAB_v1.23%2Fht-ncfs.renci.org%2Fncsc123_gfs_sb55.01%2Fgfsforecast%2Ffort.63.nc&ensemble=nowcast", + "csvurl": data_url + "get_geo_point_data?lon=" + e.latlng.lng + "&lat=" + e.latlng.lat + "&ensemble=nowcast&url=" + tds_url, "id": id }; @@ -52,43 +96,9 @@ export const AdcircRasterLayer = (layer) => { setSelectedObservations(previous => [...previous, pointProps]); }); - // assign the map click event + // assign the map click event for geo-point selections useMapEvent('click', onClick); - // get the observation point selected state - const { - setSelectedObservations - } = useLayers(); - - useEffect(() => { - if(layer.layer.properties) { - let style = ""; - switch(layer.layer.properties.product_type) { - case ("maxwvel63"): - style = mapStyle.maxwvel.current; - break; - case ("swan_HS_max63"): - style = mapStyle.swan.current; - break; - default: - style = mapStyle.maxele.current; - break; - } - - sldParser - .readStyle(style) - .then((geostylerStyle) => { - geostylerStyle.output.name = (' ' + layer.layer.layers).slice(1); - const colorMapType = geostylerStyle.output.rules[0].symbolizers[0].colorMap.type; - sldParser.writeStyle(geostylerStyle.output) - .then((sldStyle) => { - const updatedStyle = restoreColorMapType(colorMapType, sldStyle.output); - setCurrentStyle(updatedStyle); - }); - }); - } - }, [mapStyle]); - // memorizing this params object prevents // that map flicker on state changes. const wmsLayerParams = useMemo(() => ({ @@ -106,5 +116,4 @@ export const AdcircRasterLayer = (layer) => { onClick={console.log} /> ); - }; From c1ac7dbdc0fa4934fcd833f53d60dbe197ce90d4 Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:36:07 -0400 Subject: [PATCH 08/17] adding more error checks before trying to render --- src/components/dialog/observation-chart.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dialog/observation-chart.js b/src/components/dialog/observation-chart.js index fa6c10ef..7a448488 100644 --- a/src/components/dialog/observation-chart.js +++ b/src/components/dialog/observation-chart.js @@ -79,7 +79,7 @@ function getObsChartData(url, setLineButtonView) { */ function csvToJSON(csvData, setLineButtonView) { // ensure that there is csv data to convert - if (csvData !== "" && csvData.indexOf('fail') < 0) { + if (csvData !== "" && csvData.indexOf('Error') < 0 && csvData.indexOf('fail') < 0) { // split on carriage returns const lines = csvData.split("\n"); From 4c05a246ff627d4f270acfee92715de55666b7f0 Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:36:21 -0400 Subject: [PATCH 09/17] tidying up --- src/components/dialog/base-floating-dialog.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/dialog/base-floating-dialog.js b/src/components/dialog/base-floating-dialog.js index 0c87327c..3f306c3d 100644 --- a/src/components/dialog/base-floating-dialog.js +++ b/src/components/dialog/base-floating-dialog.js @@ -55,7 +55,6 @@ export default function BaseFloatingDialog({ title, index, dialogObject, dataKey const minHeight = 175; const maxHeight = 500; - /** * the close dialog handler */ From 6ea6140616880dc9d397b1a1365d7f0a72220674 Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:42:00 -0400 Subject: [PATCH 10/17] splitting the tds url into pieces to support using service names, tidying up --- src/components/map/adcirc-raster-layer.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/components/map/adcirc-raster-layer.js b/src/components/map/adcirc-raster-layer.js index a2a87757..93ba8faa 100644 --- a/src/components/map/adcirc-raster-layer.js +++ b/src/components/map/adcirc-raster-layer.js @@ -57,7 +57,7 @@ export const AdcircRasterLayer = (layer) => { const map = useMap(); // create a callback to handle a map click event - const onClick = useCallback ( (e) => { + const onClick = useCallback((e) => { // create an id for the point const id = Number(e.latlng.lng).toFixed(6) + ', ' + Number(e.latlng.lat).toFixed(6); @@ -65,13 +65,21 @@ export const AdcircRasterLayer = (layer) => { markClicked(map, e, id); // get the FQDN of the UI data server - const data_url = `${ getNamespacedEnvParam('REACT_APP_UI_DATA_URL') }`; + const data_url = `${getNamespacedEnvParam('REACT_APP_UI_DATA_URL')}`; // get the visible layer on the map const layer = layers.find((layer) => layer.properties['product_type'] !== "obs" && layer.state.visible === true); - // create the correct TDS URL - const tds_url = layer.properties['tds_download_url'].replace('catalog', 'dodsC').replace('catalog.html', (layer.id.indexOf('swan') < 0 ? 'fort' : 'swan_HS') + '.63.nc'); + // create the correct TDS URL without the hostname + const tds_url = layer.properties['tds_download_url'].replace('catalog', 'dodsC').replace('catalog.html', (layer.id.indexOf('swan') < 0 ? + 'fort' : 'swan_HS') + '.63.nc').split('/thredds')[1]; + + // get the hostname + const tds_svr = layer.properties['tds_download_url'].split('https://')[1].split('/thredds')[0].split('.')[0]; + + // generate the full url + const fullTDSURL = data_url + "get_geo_point_data?lon=" + e.latlng.lng + "&lat=" + e.latlng.lat + "&ensemble=nowcast&url=" + + tds_url + '&tds_svr=' + tds_svr; // create a set of properties for this object const pointProps = @@ -88,7 +96,7 @@ export const AdcircRasterLayer = (layer) => { "forcing_metclass": layer.properties['met_class'], "location_type": "ocean", "grid_name": "NCSC_SAB_V1.23", - "csvurl": data_url + "get_geo_point_data?lon=" + e.latlng.lng + "&lat=" + e.latlng.lat + "&ensemble=nowcast&url=" + tds_url, + "csvurl": fullTDSURL, "id": id }; @@ -102,7 +110,7 @@ export const AdcircRasterLayer = (layer) => { // memorizing this params object prevents // that map flicker on state changes. const wmsLayerParams = useMemo(() => ({ - format:"image/png", + format: "image/png", transparent: true, sld_body: currentStyle, }), [currentStyle]); From 4d7c449508c71bfac991766e6bef9c859f5f729a Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:57:17 -0400 Subject: [PATCH 11/17] adding a reference to the location of the user alert code --- webpack.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index 9d9a08e5..37249934 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -123,7 +123,8 @@ module.exports = { '@compare-layers': path.resolve(__dirname, 'src/components/trays/compare-layers/'), '@share': path.resolve(__dirname, 'src/components/trays/share/'), '@utils': path.resolve(__dirname, 'src/utils/'), - '@side-by-side': path.resolve(__dirname, 'src/components/side-by-side') + '@side-by-side': path.resolve(__dirname, 'src/components/side-by-side/'), + '@alert-user': path.resolve(__dirname, 'src/components/alert-user/') } }, From b080b0de0a3b152aa8a3838199d1e6a0cad9e84c Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:58:43 -0400 Subject: [PATCH 12/17] adding the user alert component --- src/app.js | 8 +- src/components/alert-user/alert-user.js | 23 ++++++ src/components/alert-user/index.js | 1 + src/components/map/adcirc-raster-layer.js | 98 ++++++++++++----------- 4 files changed, 81 insertions(+), 49 deletions(-) create mode 100644 src/components/alert-user/alert-user.js create mode 100644 src/components/alert-user/index.js diff --git a/src/app.js b/src/app.js index 74697f7c..40f2a27e 100644 --- a/src/app.js +++ b/src/app.js @@ -7,13 +7,12 @@ import { Sidebar } from '@components/sidebar'; import { ControlPanel } from '@components/control-panel'; import { ComparePanel } from '@components/compare-panel'; import { MapLegend } from '@components/legend'; - - +import { AlertUser } from '@components/alert-user'; /** * renders the main content * - * @returns {JSX.Element} + * @returns JSX.Element * @constructor */ const Content = () => { @@ -33,6 +32,7 @@ const Content = () => { return ; }) } + @@ -45,7 +45,7 @@ const Content = () => { /** * renders the application * - * @returns {JSX.Element} + * @returns JSX.Element * @constructor */ export const App = () => { diff --git a/src/components/alert-user/alert-user.js b/src/components/alert-user/alert-user.js new file mode 100644 index 00000000..de554c83 --- /dev/null +++ b/src/components/alert-user/alert-user.js @@ -0,0 +1,23 @@ +import React, { Fragment } from "react"; +import { useLayers } from "@context"; +import { Dialog, DialogContent, Alert, Tooltip } from '@mui/material'; + +export const AlertUser = () => { + // get the message alert details from state + const { alertMsg, setAlertMsg } = useLayers(); + + return( + // render an alert only if there is one + (alertMsg !== null) ? ( + + setAlertMsg(null) }> + + + { alertMsg['msg'] } + + + + + ) : '' + ); +}; \ No newline at end of file diff --git a/src/components/alert-user/index.js b/src/components/alert-user/index.js new file mode 100644 index 00000000..d700693d --- /dev/null +++ b/src/components/alert-user/index.js @@ -0,0 +1 @@ +export * from './alert-user'; diff --git a/src/components/map/adcirc-raster-layer.js b/src/components/map/adcirc-raster-layer.js index 93ba8faa..e6073dad 100644 --- a/src/components/map/adcirc-raster-layer.js +++ b/src/components/map/adcirc-raster-layer.js @@ -1,8 +1,8 @@ import React, { useEffect, useMemo, useState, useCallback } from 'react'; import { WMSTileLayer, useMap, useMapEvent } from 'react-leaflet'; import SldStyleParser from 'geostyler-sld-parser'; -import {getNamespacedEnvParam, markClicked, restoreColorMapType} from '@utils/map-utils'; -import {useLayers, useSettings} from '@context'; +import { getNamespacedEnvParam, markClicked, restoreColorMapType } from '@utils/map-utils'; +import { useLayers, useSettings } from '@context'; export const AdcircRasterLayer = (layer) => { const sldParser = new SldStyleParser(); @@ -44,10 +44,11 @@ export const AdcircRasterLayer = (layer) => { } }, [mapStyle]); - // get the observation points selected and default layers from state + // get the observation points selected, default layers and alert message from state const { setSelectedObservations, - defaultModelLayers + defaultModelLayers, + setAlertMsg, } = useLayers(); // capture the default layers @@ -58,50 +59,57 @@ export const AdcircRasterLayer = (layer) => { // create a callback to handle a map click event const onClick = useCallback((e) => { - // create an id for the point - const id = Number(e.latlng.lng).toFixed(6) + ', ' + Number(e.latlng.lat).toFixed(6); - - // create a marker target icon around the observation clicked - markClicked(map, e, id); - - // get the FQDN of the UI data server - const data_url = `${getNamespacedEnvParam('REACT_APP_UI_DATA_URL')}`; - // get the visible layer on the map const layer = layers.find((layer) => layer.properties['product_type'] !== "obs" && layer.state.visible === true); - // create the correct TDS URL without the hostname - const tds_url = layer.properties['tds_download_url'].replace('catalog', 'dodsC').replace('catalog.html', (layer.id.indexOf('swan') < 0 ? - 'fort' : 'swan_HS') + '.63.nc').split('/thredds')[1]; - - // get the hostname - const tds_svr = layer.properties['tds_download_url'].split('https://')[1].split('/thredds')[0].split('.')[0]; - - // generate the full url - const fullTDSURL = data_url + "get_geo_point_data?lon=" + e.latlng.lng + "&lat=" + e.latlng.lat + "&ensemble=nowcast&url=" + - tds_url + '&tds_svr=' + tds_svr; - - // create a set of properties for this object - const pointProps = - { - "station_name": layer.properties['product_name'] + " at (lon, lat): " + id, - "lat": Number(e.latlng.lat).toFixed(6), - "lon": Number(e.latlng.lng).toFixed(6), - "location_name": layer.properties['product_name'] + " at (lon, lat): " + id, - "model_run_id": layer.group, - "data_source": (layer.properties['event_type'] + '_' + layer.properties['grid_type']).toUpperCase(), - "source_name": layer.properties['model'], - "source_instance": layer.properties['instance_name'], - "source_archive": layer.properties['location'], - "forcing_metclass": layer.properties['met_class'], - "location_type": "ocean", - "grid_name": "NCSC_SAB_V1.23", - "csvurl": fullTDSURL, - "id": id - }; - - // populate selectedObservations list with the newly selected observation point - setSelectedObservations(previous => [...previous, pointProps]); + // check to see if this is a layer we can operate on + if (layer.properties['product_name'].indexOf('Level') > 0 || layer.properties['product_name'].indexOf('Height') > 0) { + // create an id for the point + const id = Number(e.latlng.lng).toFixed(6) + ', ' + Number(e.latlng.lat).toFixed(6); + + // create a marker target icon around the observation clicked + markClicked(map, e, id); + + // get the FQDN of the UI data server + const data_url = `${getNamespacedEnvParam('REACT_APP_UI_DATA_URL')}`; + + // create the correct TDS URL without the hostname + const tds_url = layer.properties['tds_download_url'].replace('catalog', 'dodsC').replace('catalog.html', (layer.id.indexOf('swan') < 0 ? + 'fort' : 'swan_HS') + '.63.nc').split('/thredds')[1]; + + // get the hostname + const tds_svr = layer.properties['tds_download_url'].split('https://')[1].split('/thredds')[0].split('.')[0]; + + // generate the full url + const fullTDSURL = data_url + "get_geo_point_data?lon=" + e.latlng.lng + "&lat=" + e.latlng.lat + "&ensemble=nowcast&url=" + + tds_url + '&tds_svr=' + tds_svr; + + const l_props = layer.properties; + + // create a set of properties for this object + const pointProps = + { + "station_name": l_props['product_name'] + " at (lon, lat): " + id, + "lat": Number(e.latlng.lat).toFixed(6), + "lon": Number(e.latlng.lng).toFixed(6), + "location_name": l_props['product_name'] + " at (lon, lat): " + id, + "model_run_id": layer.group, + "data_source": (l_props['event_type'] + '_' + l_props['grid_type']).toUpperCase(), + "source_name": l_props['model'], + "source_instance": l_props['instance_name'], + "source_archive": l_props['location'], + "forcing_metclass": l_props['met_class'], + "location_type": "ocean", + "grid_name": l_props['grid_type'].toUpperCase(), + "csvurl": fullTDSURL, + "id": id + }; + + // populate selectedObservations list with the newly selected observation point + setSelectedObservations(previous => [...previous, pointProps]); + } + else + setAlertMsg({'severity': 'warning', 'msg': 'Geo-point selection is only available for water level or water height products.'}); }); // assign the map click event for geo-point selections From 71f4b6b4769b7e61f21bd30980bd403b6386b6a0 Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:58:59 -0400 Subject: [PATCH 13/17] adding the user alert component state --- src/context/map-context.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/context/map-context.js b/src/context/map-context.js index ec3d4138..0ad8e6ee 100644 --- a/src/context/map-context.js +++ b/src/context/map-context.js @@ -366,6 +366,9 @@ export const LayersProvider = ({ children }) => { // used to track the view state of the share comment const [showShareComment, setShowShareComment] = useState(true); + // used to show alerts + const [alertMsg, setAlertMsg] = useState(null); + return ( { selectedObservations, setSelectedObservations, showShareComment, setShowShareComment, layerTypes, + alertMsg, setAlertMsg, toggleHurricaneLayerVisibility, toggleLayerVisibility, toggleLayerVisibility2, getAllLayersInvisible, getAllHurricaneLayersInvisible, getAllRasterLayersInvisible, From 5fffa01ee9b6b20148a95abea35172def35c1edf Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:59:16 -0400 Subject: [PATCH 14/17] tidying up --- src/components/dialog/base-floating-dialog.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/components/dialog/base-floating-dialog.js b/src/components/dialog/base-floating-dialog.js index 3f306c3d..e5e12cd2 100644 --- a/src/components/dialog/base-floating-dialog.js +++ b/src/components/dialog/base-floating-dialog.js @@ -1,18 +1,11 @@ import React, { Fragment, useState, useRef, forwardRef } from 'react'; -import { ToggleButtonGroup, ToggleButton, Box, Stack, Typography } from '@mui/material'; +import { ToggleButtonGroup, ToggleButton, Box, Stack, Typography, + CssBaseline, Dialog, DialogContent, DialogTitle, Paper, Slide, IconButton} from '@mui/material'; import Draggable from "react-draggable"; import PropTypes from 'prop-types'; import { Resizable } from "react-resizable"; import "react-resizable/css/styles.css"; -import CssBaseline from '@mui/material/CssBaseline'; -import Dialog from '@mui/material/Dialog'; -import DialogContent from '@mui/material/DialogActions'; -import DialogTitle from '@mui/material/DialogTitle'; - -import Paper from '@mui/material/Paper'; -import Slide from '@mui/material/Slide'; -import IconButton from '@mui/material/IconButton'; import CloseOutlinedIcon from '@mui/icons-material/CloseOutlined'; import { markUnclicked } from '@utils/map-utils'; From 212b7e5744d0d186339bb0cf15887472a7ed6350 Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Fri, 18 Oct 2024 11:00:43 -0400 Subject: [PATCH 15/17] updating the list of layer products that cant geo-point --- src/components/map/adcirc-raster-layer.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/components/map/adcirc-raster-layer.js b/src/components/map/adcirc-raster-layer.js index e6073dad..90f2541f 100644 --- a/src/components/map/adcirc-raster-layer.js +++ b/src/components/map/adcirc-raster-layer.js @@ -57,13 +57,24 @@ export const AdcircRasterLayer = (layer) => { // get a handle to the map const map = useMap(); + /** + * method to determine if this is a layer that we can geo-point on + * + */ + const validLayerForGeoPointing = (product_type) => { + // if this layer is not worthy of geo-pointing + return ( + product_type.indexOf('Hi-Res Maximum Water Level') !== 0 && + product_type.indexOf('Maximum Wind Speed') !== 0); + }; + // create a callback to handle a map click event const onClick = useCallback((e) => { // get the visible layer on the map const layer = layers.find((layer) => layer.properties['product_type'] !== "obs" && layer.state.visible === true); // check to see if this is a layer we can operate on - if (layer.properties['product_name'].indexOf('Level') > 0 || layer.properties['product_name'].indexOf('Height') > 0) { + if (validLayerForGeoPointing(layer.properties['product_name'])) { // create an id for the point const id = Number(e.latlng.lng).toFixed(6) + ', ' + Number(e.latlng.lat).toFixed(6); @@ -99,7 +110,7 @@ export const AdcircRasterLayer = (layer) => { "source_instance": l_props['instance_name'], "source_archive": l_props['location'], "forcing_metclass": l_props['met_class'], - "location_type": "ocean", + "location_type": "GeoPoint", "grid_name": l_props['grid_type'].toUpperCase(), "csvurl": fullTDSURL, "id": id @@ -109,7 +120,7 @@ export const AdcircRasterLayer = (layer) => { setSelectedObservations(previous => [...previous, pointProps]); } else - setAlertMsg({'severity': 'warning', 'msg': 'Geo-point selection is only available for water level or water height products.'}); + setAlertMsg({'severity': 'warning', 'msg': 'Geo-point selection is not available for this layer type.'}); }); // assign the map click event for geo-point selections From 0855d74635af1f2f64bce9cbd1a231c159b76ad0 Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Fri, 18 Oct 2024 11:30:32 -0400 Subject: [PATCH 16/17] updating the list of layer products that cant geo-point --- src/components/map/adcirc-raster-layer.js | 32 ++++++++++------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/components/map/adcirc-raster-layer.js b/src/components/map/adcirc-raster-layer.js index 90f2541f..01fafd49 100644 --- a/src/components/map/adcirc-raster-layer.js +++ b/src/components/map/adcirc-raster-layer.js @@ -57,26 +57,22 @@ export const AdcircRasterLayer = (layer) => { // get a handle to the map const map = useMap(); - /** - * method to determine if this is a layer that we can geo-point on - * - */ - const validLayerForGeoPointing = (product_type) => { - // if this layer is not worthy of geo-pointing - return ( - product_type.indexOf('Hi-Res Maximum Water Level') !== 0 && - product_type.indexOf('Maximum Wind Speed') !== 0); - }; + // create a list of worthy geo-point layer types + const validLayerTypes = new Set(['Maximum Water Level', 'Maximum Significant Wave Height']); // create a callback to handle a map click event const onClick = useCallback((e) => { // get the visible layer on the map const layer = layers.find((layer) => layer.properties['product_type'] !== "obs" && layer.state.visible === true); - // check to see if this is a layer we can operate on - if (validLayerForGeoPointing(layer.properties['product_name'])) { + // if this is a layer we can geo-point on + if (validLayerTypes.has(layer.properties['product_name'])) { + // round the coordinates + const lon = Number(e.latlng.lng).toFixed(5); + const lat = Number(e.latlng.lat).toFixed(5); + // create an id for the point - const id = Number(e.latlng.lng).toFixed(6) + ', ' + Number(e.latlng.lat).toFixed(6); + const id = lon + ', ' + lat; // create a marker target icon around the observation clicked markClicked(map, e, id); @@ -100,10 +96,10 @@ export const AdcircRasterLayer = (layer) => { // create a set of properties for this object const pointProps = { - "station_name": l_props['product_name'] + " at (lon, lat): " + id, - "lat": Number(e.latlng.lat).toFixed(6), - "lon": Number(e.latlng.lng).toFixed(6), - "location_name": l_props['product_name'] + " at (lon, lat): " + id, + "station_name": l_props['product_name'] + " " + id, + "lat": lat, + "lon": lon, + "location_name": l_props['product_name'] + "s over time (lon, lat): " + id, "model_run_id": layer.group, "data_source": (l_props['event_type'] + '_' + l_props['grid_type']).toUpperCase(), "source_name": l_props['model'], @@ -120,7 +116,7 @@ export const AdcircRasterLayer = (layer) => { setSelectedObservations(previous => [...previous, pointProps]); } else - setAlertMsg({'severity': 'warning', 'msg': 'Geo-point selection is not available for this layer type.'}); + setAlertMsg({'severity': 'warning', 'msg': 'Geo-point selection is not available for the ' + layer.properties['product_name'] + ' product.'}); }); // assign the map click event for geo-point selections From df41d0dcb317ff2282a8f09f1d14864e99970fa0 Mon Sep 17 00:00:00 2001 From: Phil Owen <19691521+PhillipsOwen@users.noreply.github.com> Date: Fri, 18 Oct 2024 12:45:56 -0400 Subject: [PATCH 17/17] removing console.log --- src/components/map/adcirc-raster-layer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/map/adcirc-raster-layer.js b/src/components/map/adcirc-raster-layer.js index 01fafd49..cbd3d4b2 100644 --- a/src/components/map/adcirc-raster-layer.js +++ b/src/components/map/adcirc-raster-layer.js @@ -136,7 +136,6 @@ export const AdcircRasterLayer = (layer) => { layers={layer.layer.layers} params={wmsLayerParams} opacity={layer.layer.state.opacity} - onClick={console.log} /> ); };