Skip to content

Commit

Permalink
Merge pull request #12 from RENCI/obs-work
Browse files Browse the repository at this point in the history
Updates to add observation layers to the map.
  • Loading branch information
PhillipsOwen authored May 1, 2024
2 parents c9f1288 + 5d35c24 commit 93e01fb
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 71 deletions.
229 changes: 164 additions & 65 deletions src/components/map/default-layers.js
Original file line number Diff line number Diff line change
@@ -1,78 +1,177 @@
import React, { Fragment, useEffect } from 'react';
import { WMSTileLayer } from 'react-leaflet';
import React, { Fragment, useEffect, useState } from 'react';
import { WMSTileLayer, GeoJSON, useMap } from 'react-leaflet';
import { CircleMarker } from 'leaflet';
import { useLayers } from '@context';
import { markClicked } from '@utils/map-utils';

export const DefaultLayers = () => {
const {
defaultModelLayers,
setDefaultModelLayers,
} = useLayers();

// Create the authorization header
const requestOptions = {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.REACT_APP_UI_DATA_TOKEN}`
}
};
const [obsData, setObsData] = useState("");
const map = useMap();

// create the URLs to the data endpoints
const data_url = `${process.env.REACT_APP_UI_DATA_URL}get_ui_data_secure?limit=1&use_v3_sp=true`;
const gs_url = `${process.env.REACT_APP_GS_DATA_URL}ADCIRC_2024/wms`;
const {
defaultModelLayers,
setDefaultModelLayers,
} = useLayers();

useEffect(() => {
// React advises to declare the async function directly inside useEffect
// TODO: Need to store this url in some website config file and
// it should change to reflect the namspace we are running in
async function getDefaultLayers() {
const layer_list = [];
const response = await fetch(data_url, requestOptions);
const data = await response.json();

if (data) {
// get layer id in workbench and find catalog entries for each
//for (let layer_id in data.workbench) {
data.workbench.forEach(function (layer_id) {
const layer = getCatalogEntry(data.catalog, layer_id);
if (layer)
layer_list.push(layer);
});
setDefaultModelLayers(layer_list);
}
}
// Create the authorization header
const requestOptions = {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.REACT_APP_UI_DATA_TOKEN}`
}
};

// retrieve the catalog member with the provided id
const getCatalogEntry = (catalog, id) => {
let entry = "";

for (const idx in catalog) {
catalog[idx].members.forEach (function (e) {
if (e.id === id) {
entry = e;
const obsPointToLayer = ((feature, latlng) => {
let obs_color = "#FFFFFF";

switch (feature.properties.gauge_owner) {
case 'NOAA/NDBC':
obs_color = "#FFFF00";
break;
case 'NCEM':
obs_color = "#3D4849";
break;
case 'NOAA/NOS':
obs_color = "#BEAEFA";
break;
}

return new CircleMarker(latlng, {
radius: 6,
weight: 0.7,
color: '#000000',
fillColor: obs_color,
fillOpacity: 1
});
}
return entry;
});

const onEachObsFeature = (feature, layer) => {
if (feature.properties && feature.properties.location_name) {
const popupContent = feature.properties.location_name;

layer.on("mouseover", function (e) {
this.bindPopup(popupContent).openPopup(e.latlng);
});

layer.on("mousemove", function (e) {
this.getPopup().setLatLng(e.latlng);
});

layer.on("mouseout", function () {
this.closePopup();
});
layer.on("click", function (e) {
// Do stuff here for retrieving time series data, in csv fomat,
// from the feature.properties.csv_url and create a fancy plot
console.log("Observation Station '" + feature.properties.location_name + "' clicked");
markClicked(map, e);
});
}
};
getDefaultLayers();
}, []);

return (
<>
{defaultModelLayers.map((layer, index) => {
return(
<WMSTileLayer
key = {index}
url ={gs_url}
layers={layer.layers}
params={{
format:"image/png",
transparent: true,
}}
>
</WMSTileLayer>
// create the URLs to the data endpoints
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 gs_wms_url = `${process.env.REACT_APP_GS_DATA_URL}wms`;
const gs_wfs_url = `${process.env.REACT_APP_GS_DATA_URL}`;

useEffect(() => {
// React advises to declare the async function directly inside useEffect
// TODO: Need to store this url in some website config file and
// it should change to reflect the namspace we are running in
async function getDefaultLayers() {
const layer_list = [];
const response = await fetch(data_url, requestOptions);
const data = await response.json();
let obs_url = null;

if (data) {
// get layer id in workbench and find catalog entries for each
data.workbench.forEach(function (layer_id) {
const layer = getCatalogEntry(data.catalog, layer_id);
if (layer)
layer_list.push(layer);

// TODO: do we really need to do this here??!
// if this is an obs layer, need to retrieve
// the json data for it from GeoServer
const pieces = layer.id.split('-');
const type = pieces[pieces.length-1];
if( type === "obs") {
obs_url = gs_wfs_url +
"/ows?service=WFS&version=1.0.0&request=GetFeature&outputFormat=application/json" +
"&typeName=" +
layer.layers;
}
});
setDefaultModelLayers(layer_list);
}

if (obs_url) {
const obs_response = await fetch(obs_url);
const obs_data = await obs_response.json();
//console.log("obs_data 1: " + JSON.stringify(obs_data, null, 2))

setObsData(obs_data);
}

}

// retrieve the catalog member with the provided id
const getCatalogEntry = (catalog, id) => {
let entry = "";

for (const idx in catalog) {
catalog[idx].members.forEach (function (e) {
if (e.id === id) {
entry = e;
}
});
}
return entry;
};
getDefaultLayers().then();
}, []);

//console.log("defaultModelLayers: " + JSON.stringify(defaultModelLayers, null, 2))

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 (
<GeoJSON
key = {index}
data = {obsData}
pointToLayer = {obsPointToLayer}
onEachFeature = {onEachObsFeature}
/>
);
}
else {
return (
<WMSTileLayer
key = {index}
/* eventHandlers={{
click: () => {
console.log('marker clicked')
},
}} */
url={gs_wms_url}
layers={layer.layers}
params={{
format:"image/png",
transparent: true,
}}

/>
);
}
})};
</>
);
})};
</>
);
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 17 additions & 6 deletions src/utils/map-utils.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
import useLayers from '../context/map-context';
import { StacApiProvider } from "stac-react";
//import { useLayers } from '@context';
import locationIcon from '@images/location_searching_FILL0_wght400_GRAD0_opsz24.png';

const {
/* const {
defaultModelLayers,
setDefaultModelLayers,
filteredModelLayers,
setFilteredModelLayers
} = useLayers();
} = useLayers(); */


// Utilities to access the stac catalog items to load on map
const stacCatalog = ({stacUrl}) => {
// function to add a location marker where ever and obs mod layer
// feature is clicked icon downloaded as png from here:
// https://fonts.google.com/icons?selected=Material+Symbols+Outlined:location_searching:FILL@0;wght@400;GRAD@0;opsz@24&icon.query=location
export const markClicked = (map, event) => {

const targetIcon = L.icon({

Check warning on line 17 in src/utils/map-utils.js

View workflow job for this annotation

GitHub Actions / eslint

'L' is not defined
iconUrl: locationIcon,
iconSize: [38, 38],
iconAnchor: [19, 19],
popupAnchor: [0, 0],
});

L.marker([event.latlng.lat, event.latlng.lng], {icon: targetIcon}).addTo(map);

Check warning on line 24 in src/utils/map-utils.js

View workflow job for this annotation

GitHub Actions / eslint

'L' is not defined
};

0 comments on commit 93e01fb

Please sign in to comment.