Skip to content

Commit

Permalink
refactor: switch alerts to check polygon not city name (#501)
Browse files Browse the repository at this point in the history
  • Loading branch information
Forceh91 authored Jun 15, 2023
1 parent 42a384c commit 485f257
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 26 deletions.
25 changes: 17 additions & 8 deletions alert-monitoring.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@ const alertsFromCAP = [];
const ALERTS_FOLDER = "./alerts";
const ALERTS_FILE = "alerts.txt";

function startAlertMonitoring(city, app) {
function startAlertMonitoring(latLong, app) {
console.log("[ALERT MONITORING] Starting alert monitoring via AMQP...");

const { emitter: listener } = listen({ amqp_subtopic: "alerts.cap.#" });
listener
.on("error", (err) => console.warn(err.message))
.on("message", (date, url) => {
console.log("[ALERT MONITORING] Cap file", url, "received at", date);
fetchCapFileAndParse(url, city, (alert) => {
console.log("[ALERT MONITORING] Cap file", url, "received at", date, "checking against lat/lon of", latLong);
fetchCapFileAndParse(url, latLong, (alert) => {
pushAlertToList(alert);
});
});

// load anything we knew about before we rebooted the system
loadCurrentAlerts(city);
loadCurrentAlerts(latLong);

// setup the endpoint to fetch warnings from
setupWarningsAPI(app);
Expand Down Expand Up @@ -77,7 +77,7 @@ function saveCurrentAlerts() {
});
}

function loadCurrentAlerts(city) {
function loadCurrentAlerts(latLong) {
const alertFile = `${ALERTS_FOLDER}/${ALERTS_FILE}`;
fs.stat(alertFile, (err, stat) => {
if (err || stat.size < 1) console.log("[ALERT MONITORING] No stored alerts");
Expand All @@ -89,9 +89,14 @@ function loadCurrentAlerts(city) {

console.log(`[ALERT MONITORING] Refetching ${capFileURLArray.length} CAP files...`);
capFileURLArray.forEach((url) => {
fetchCapFileAndParse(url, city, (alert) => {
pushAlertToList(alert);
});
fetchCapFileAndParse(
url,
latLong,
(alert) => {
pushAlertToList(alert);
},
true
);
});
}
});
Expand Down Expand Up @@ -126,6 +131,10 @@ function sortAlertsInPriorityOrder(alerts) {
if (aUrgency > bUrgency) return -1;
if (bUrgency > aUrgency) return 1;

// then by date
if (a.sent > b.sent) return -1;
if (b.sent > a.sent) return 1;

// both the same
return 0;
}) || []
Expand Down
4 changes: 2 additions & 2 deletions backend.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const express = require("express");
const cors = require("cors");
const path = require("path");
const { initCurrentConditions } = require("./current-conditions");
const { initCurrentConditions, getStationLatLong } = require("./current-conditions");
const { fetchWeatherForObservedCities, latestObservations } = require("./observations.js");
const { fetchWeatherForObservedUSCities, latestUSObservations } = require("./us-observations.js");
const { fetchForecastForSunspotCities, latestSunspotForecasts } = require("./sunspot");
Expand Down Expand Up @@ -113,7 +113,7 @@ async function startBackend() {
});

// start the amqp alert monitoring of cap
startAlertMonitoring(config.primaryLocation()?.name, app);
startAlertMonitoring(getStationLatLong(), app);
}

app.listen(port);
Expand Down
40 changes: 24 additions & 16 deletions cap-file-parser.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const xmljs = require("xml-js");
const axios = require("axios");
const pointInPolygon = require("point-in-polygon");

function fetchCapFileAndParse(url, city, callback) {
if (!url || !city) return;
city = city.toLowerCase();
function fetchCapFileAndParse(url, latLong, callback, isExisting = false) {
if (!url || !latLong) return;

// url for a cap file has been given to us, so now we need to see if it's relevant to us
axios.get(url).then((resp) => {
Expand All @@ -22,24 +22,26 @@ function fetchCapFileAndParse(url, city, callback) {

if (!Array.isArray(areas)) areas = [areas];

// loop through areas and see if our tracked city is included
let isRelevant = false;
areas.some((area) => {
const areaDesc = (area.areaDesc?._text || "").toLowerCase();
const regexString = `\\b${city}\\b`;
const regexMatch = new RegExp(regexString, "gmi");
const regexResult = areaDesc.match(regexMatch);
isRelevant = regexResult && regexResult[0] === city;
if (isRelevant) return true;
});
// loop through areas and see if our tracked city (via lat/long) is included
if (!isExisting) {
const pointToCheck = [latLong.lat, latLong.long];
const isRelevant = areas.some((area) => {
const polygon = area.polygon?._text || "";
const polygonAsArray = convertPolygonStringTo3DArray(polygon);
if (!polygonAsArray) return;

// if its not relevant stop, otherwise get the data
if (!isRelevant) return;
return pointInPolygon(pointToCheck, polygonAsArray);
});

// if its not relevant stop, otherwise get the data
if (!isRelevant) return;
}

// check we have the required information here
const identifier = alert?.identifier?._text;
if (!identifier) return;

const sent = alert?.sent?._text || "";
const references = alert?.references?._text || "";

const expires = info_en?.expires?._text;
Expand All @@ -60,8 +62,14 @@ function fetchCapFileAndParse(url, city, callback) {
console.log(`[CAP PARSER] CAP ${url} has been parsed, passing along to alert monitor`);

if (typeof callback === "function")
callback({ identifier, references, expires, headline, description, severity, urgency, url });
callback({ identifier, references, expires, headline, description, severity, urgency, url, sent });
});
}

const convertPolygonStringTo3DArray = (polygonString) => {
if (!polygonString) return;

return polygonString.split(" ").map((points) => points.split(",").map((point) => parseFloat(point)));
};

module.exports = { fetchCapFileAndParse };
23 changes: 23 additions & 0 deletions current-conditions.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const conditions = {
let eventStreamInterval = null;
let amqpConnection = null;
let configRejectInHourConditonUpdates = false;
let stationLatLong = { lat: 0, long: 0 };

const initCurrentConditions = (primaryLocation, rejectInHourConditionUpdates, app, historicalDataAPI) => {
// pass in the primary location from the config and make sure its valid
Expand Down Expand Up @@ -114,6 +115,12 @@ const fetchCurrentConditions = (url) => {
return (conditions.forecast = weather.weekly);
}

// store the long/lat for later use
const {
name: { lat, lon },
} = allWeatherData.location;
parseStationLatLong(lat, lon);

// generate some objects so the FE has less work to do once it receives the data
const observedDateObject = generateConditionsObserved(weather.current?.dateTime[1]);
const city = generateObservedCity(allWeatherData.location);
Expand All @@ -138,6 +145,17 @@ const fetchCurrentConditions = (url) => {
});
};

const parseStationLatLong = (lat, long) => {
// we get these in string format with compass directions so we need to convert slightly
// N is positive, S is negative
if (lat.includes("N")) stationLatLong.lat = parseFloat(lat);
else stationLatLong.lat = -parseFloat(lat);

// E is postive, W is negative
if (long.includes("E")) stationLatLong.long = parseFloat(long);
else stationLatLong.long = -parseFloat(long);
};

const generateConditionsObserved = (date) => {
if (!date) return;

Expand Down Expand Up @@ -284,6 +302,10 @@ const generateWeatherResponse = () => {
};
};

const getStationLatLong = () => {
return stationLatLong;
};

const writeEventStream = (res) => {
res.write(`id: ${Date.now()}\n`);
res.write(`event: condition_update\n`);
Expand All @@ -297,4 +319,5 @@ module.exports = {
reloadCurrentConditions,
generateWindchill,
generateAlmanac,
getStationLatLong,
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"ec-weather-js": "^1.2.0",
"express": "^4.18.2",
"mitt": "^3.0.0",
"point-in-polygon": "^1.1.0",
"vue": "^3.3.4",
"vue-axios": "^3.5.2",
"vue-router": "4",
Expand Down
2 changes: 2 additions & 0 deletions src/components/severetstormwatch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export default {
display: flex;
flex-direction: column;
justify-content: center;
margin: 0 auto;
text-align: left;
text-transform: uppercase;
width: 380px;
}
</style>
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10762,6 +10762,11 @@ pnp-webpack-plugin@^1.6.4:
dependencies:
ts-pnp "^1.1.6"

point-in-polygon@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/point-in-polygon/-/point-in-polygon-1.1.0.tgz#b0af2616c01bdee341cbf2894df643387ca03357"
integrity sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==

portfinder@^1.0.26:
version "1.0.28"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778"
Expand Down

0 comments on commit 485f257

Please sign in to comment.