From 03548948f838f9103f7add32537454bed00d064c Mon Sep 17 00:00:00 2001 From: Tucker Willenborg Date: Fri, 7 Jun 2024 19:38:42 -0400 Subject: [PATCH] Switch NWS gauges to use new API --- server/gauges/nwsGauges.js | 31 +++++++++++---- server/gauges/processNWSResponse.js | 61 +++++++++++------------------ 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/server/gauges/nwsGauges.js b/server/gauges/nwsGauges.js index 1c7b6ba4..6f61e74e 100644 --- a/server/gauges/nwsGauges.js +++ b/server/gauges/nwsGauges.js @@ -1,21 +1,38 @@ -//Load flow data from NWS (National Weather Service) - A United States Agency - -const path = require("path") - +//Load flow data from NWS (National Weather Service) const bent = require("bent") -let getXML = bent("https://water.weather.gov/ahps2/", "string") + +// Flow data for a gauge. +// https://api.water.noaa.gov/nwps/v1/gauges/{sidecode}/stageflow +let getFlowInfoJSON = bent("https://api.water.noaa.gov/nwps/v1/gauges/", "json") const processNWSResponse = require("./processNWSResponse.js") async function loadSiteFromNWS(siteCode) { - let siteData = await getXML("hydrograph_to_xml.php?gage=" + siteCode + "&output=xml") + let siteData = await getFlowInfoJSON(siteCode + "/stageflow") - let output = processNWSResponse(siteData) + let output = processNWSResponse(siteData, siteCode) + output.name = await getSiteName(siteCode) + console.log("NAME", output.name) return output } +//Info about a gauge: +// https://api.water.noaa.gov/nwps/v1/gauges/{sidecode} +let getSiteDataJSON = bent("https://api.water.noaa.gov/nwps/v1/gauges/", "json") + +//We will assume that site names will not change while the server it up. +let siteNameCache = {} +async function getSiteName(siteCode) { + if (!siteNameCache[siteCode]) { + let obj = await getSiteDataJSON(siteCode) + siteNameCache[siteCode] = obj.name + } + + return siteNameCache[siteCode] +} + module.exports = { loadSiteFromNWS diff --git a/server/gauges/processNWSResponse.js b/server/gauges/processNWSResponse.js index dc202f3f..85b7f9bd 100644 --- a/server/gauges/processNWSResponse.js +++ b/server/gauges/processNWSResponse.js @@ -1,68 +1,51 @@ //Processes flow data from NWS (National Weather Service) -const fastXMLParser = require("fast-xml-parser"); -const xmlParser = new fastXMLParser.XMLParser({ - attributesGroupName: "attributes", - ignoreAttributes: false, - attributeNamePrefix: "", - textNodeName : "value", -}) - -//Returns object for this specific site. -function processNWSResponse(siteData) { - let jsonObj = xmlParser.parse(siteData); - - let siteCode = jsonObj.site.attributes.id - +function processNWSResponse(siteData, siteCode) { let readings = [] - //TODO: If conversion tables are supplied, but not used, should we exterpolate CFS from feet? - //Example with SUMW2 (Gauley River AT Summersville Lake) - - function processValues(values, forecast = false) { - values.forEach((value) => { + function processStageflowSeries(series, forecast = false) { + //Processes either the observed series or the forecast series. + series.data.forEach((value) => { let reading = {} - reading.dateTime = new Date(value.valid.value).getTime() + reading.dateTime = new Date(value.validTime).getTime() - function processMeasurement(measurement, pedts = value.pedts) { - if (measurement.attributes.units === "kcfs") { + function processMeasurement(value, units) { + if (units === "kcfs") { //Round in case of floating point error (ex, .0000000001) - reading.cfs = Math.round(measurement.value * 1000000) / 1000 + reading.cfs = Math.round(value * 1000000) / 1000 } - else if (measurement.attributes.units === "cfs") { - reading.cfs = measurement.value + else if (units === "cfs") { + reading.cfs = value } - else if (measurement.attributes.units === "ft") { - reading.feet = measurement.value + else if (units === "ft") { + reading.feet = value } - else {console.log("Unknown units " + measurement.attributes.units + ". Gauge is " + siteCode)} + else {console.log(`Unknown units ${units} for gauge ${siteCode}`)} - if (reading.cfs === -999000) {delete reading.cfs} + if (reading.cfs === -999000) {delete reading.cfs} //TODO: Does this happen still with the new API? } - processMeasurement(value.primary) - if (value.secondary) {processMeasurement(value.secondary)} + processMeasurement(value.primary, series.primaryUnits) + if (value.secondary) {processMeasurement(value.secondary, series.secondaryUnits)} if (forecast === true) {reading.forecast = true} //Label forecasted values. readings.push(reading) }) } - if (!jsonObj.site.observed) { - console.log(siteCode + " has no observed data. ") + if (!siteData.observed) { + console.log("Gauge has no observed data. ", siteCode) return; } - processValues(jsonObj.site.observed.datum) - if (jsonObj.site.forecast.datum) {processValues(jsonObj.site.forecast.datum, true)} + processStageflowSeries(siteData.observed) + if (siteData.forecast) {processStageflowSeries(siteData.forecast, true)} readings.sort((a,b) => {return a.dateTime - b.dateTime}) - let output = { + //name properly added separately will be added later. + return { readings, - name: jsonObj.site.attributes.name, //These names are REALLY weird. May need to revert back to using nwsToName if we can get it working. } - - return output } module.exports = processNWSResponse