diff --git a/server/scripts/index.mjs b/server/scripts/index.mjs
index 040627b..edd632c 100644
--- a/server/scripts/index.mjs
+++ b/server/scripts/index.mjs
@@ -189,11 +189,35 @@ const init = async () => {
const normalizeArcGisLocation = (rawLocation = {}, fallbackLabel = '') => {
const attributes = rawLocation.attributes ?? rawLocation.address ?? {};
- const countryCode = attributes.CountryCode ?? attributes.countryCode ?? attributes.country_code ?? null;
- const country = attributes.Country ?? attributes.countryName ?? attributes.country ?? null;
- const state = attributes.RegionAbbr ?? attributes.Region ?? attributes.Subregion ?? attributes.region ?? '';
- const city = attributes.City ?? attributes.CityName ?? attributes.MetroArea ?? rawLocation.name ?? '';
- const label = fallbackLabel || rawLocation.name || [city, state || country].filter(Boolean).join(', ');
+ const label = fallbackLabel || rawLocation.name || attributes.LongLabel || attributes.Match_addr || '';
+ const labelParts = label.split(',').map((part) => part.trim()).filter(Boolean);
+ const fallbackCountryCode = labelParts[labelParts.length - 1] === 'USA' ? 'USA' : null;
+ const fallbackCountry = fallbackCountryCode ? 'United States' : null;
+ const fallbackState = labelParts.length >= 2 && /^[A-Z]{2,3}$/.test(labelParts[labelParts.length - 2]) ? labelParts[labelParts.length - 2] : '';
+ const fallbackCity = labelParts[0] ?? rawLocation.name ?? '';
+
+ const countryCode = attributes.CountryCode
+ ?? attributes.countryCode
+ ?? attributes.country_code
+ ?? fallbackCountryCode
+ ?? null;
+ const country = attributes.CntryName
+ ?? attributes.Country
+ ?? attributes.countryName
+ ?? attributes.country
+ ?? fallbackCountry
+ ?? null;
+ const state = attributes.RegionAbbr
+ ?? attributes.Region
+ ?? attributes.Subregion
+ ?? attributes.region
+ ?? fallbackState;
+ const city = attributes.City
+ ?? attributes.CityName
+ ?? attributes.PlaceName
+ ?? attributes.MetroArea
+ ?? rawLocation.name
+ ?? fallbackCity;
return {
city,
diff --git a/server/scripts/modules/hazards.mjs b/server/scripts/modules/hazards.mjs
index fb1b9c6..575f167 100644
--- a/server/scripts/modules/hazards.mjs
+++ b/server/scripts/modules/hazards.mjs
@@ -18,6 +18,8 @@ const hazardModifiers = {
'Severe Thunderstorm Warning': 1,
};
+const getAlertSignature = (alerts = []) => alerts.map((alert) => alert.id).sort().join('|');
+
class Hazards extends WeatherDisplay {
constructor(navId, elemId, defaultActive) {
// special height and width for scrolling
@@ -34,6 +36,7 @@ class Hazards extends WeatherDisplay {
// take note of the already-shown alert ids
this.viewedAlerts = new Set();
this.viewedGetCount = 0;
+ this.alertSignature = '';
// cache for scroll calculations
// This cache is essential because baseCountChange() is called 25 times per second (every 40ms)
@@ -52,7 +55,7 @@ class Hazards extends WeatherDisplay {
async getData(weatherParameters, refresh) {
// super checks for enabled
const superResult = super.getData(weatherParameters, refresh);
- if (!this.weatherParameters?.supportsNoaaDisplays) {
+ if (!this.weatherParameters?.supportsNoaaAlerts) {
this.data = [];
this.timing.totalScreens = 0;
this.getDataCallback();
@@ -75,6 +78,7 @@ class Hazards extends WeatherDisplay {
}
try {
+ const previousSignature = this.alertSignature;
// get the forecast using centralized safe handling
const url = new URL('https://api.weather.gov/alerts/active');
url.searchParams.append('point', `${this.weatherParameters.latitude},${this.weatherParameters.longitude}`);
@@ -94,6 +98,14 @@ class Hazards extends WeatherDisplay {
const filteredAlerts = sortedAlerts.filter((hazard) => hazard.properties.severity !== 'Unknown' && (!hasImmediate || (hazard.properties.urgency === 'Immediate')));
this.data = filteredAlerts;
}
+ this.alertSignature = getAlertSignature(this.data);
+ const alertsChanged = previousSignature !== this.alertSignature;
+ if (alertsChanged) {
+ this.viewedAlerts.clear();
+ if (this.data.length > 0) {
+ postMessage({ type: 'current-weather-scroll', method: 'reload' });
+ }
+ }
// every 10 times through the get process (10 minutes), reset the viewed messages
if (this.viewedGetCount >= 10) {
@@ -137,10 +149,11 @@ class Hazards extends WeatherDisplay {
const list = this.elem.querySelector('.hazard-lines');
list.innerHTML = '';
- // filter viewed alerts
+ // Prefer new alerts, but keep active alerts visible even after they've been viewed once.
const unViewed = this.data.filter((data) => !this.viewedAlerts.has(data.id));
+ const alertsToDisplay = unViewed.length > 0 ? unViewed : this.data;
- const lines = unViewed.map((data) => {
+ const lines = alertsToDisplay.map((data) => {
const fillValues = {};
const description = data.properties.description
.replaceAll('\n\n', '
')
@@ -230,8 +243,6 @@ class Hazards extends WeatherDisplay {
const superValue = super.screenIndexFromBaseCount();
// false is returned when we reach the end of the scroll
if (superValue === false) {
- // set total screens to zero to take this out of the rotation
- this.timing.totalScreens = 0;
// note the ids shown
this?.data?.forEach((alert) => this.viewedAlerts.add(alert.id));
}
diff --git a/server/scripts/modules/navigation.mjs b/server/scripts/modules/navigation.mjs
index c220738..cbf4af9 100644
--- a/server/scripts/modules/navigation.mjs
+++ b/server/scripts/modules/navigation.mjs
@@ -95,18 +95,22 @@ const getWeather = async (latLon, haveDataCallback) => {
try {
const supportsNoaaDisplays = isUsLocation(location);
+ const shouldTryNoaaPoint = supportsNoaaDisplays || !location.countryCode;
let point = null;
let stations = null;
let stationId = '';
- if (supportsNoaaDisplays) {
+ if (shouldTryNoaaPoint) {
point = await getPoint(latLon.lat, latLon.lon);
- if (point?.properties?.observationStations) {
- stations = await safeJson(point.properties.observationStations);
- stationId = stations?.features?.[0]?.properties?.stationIdentifier ?? '';
- }
}
+ if (supportsNoaaDisplays && point?.properties?.observationStations) {
+ stations = await safeJson(point.properties.observationStations);
+ stationId = stations?.features?.[0]?.properties?.stationIdentifier ?? '';
+ }
+
+ const supportsNoaaAlerts = !!(isUsLocation(location) || point);
+
const state = location.state || point?.properties?.relativeLocation?.properties?.state || '';
let city = location.city || point?.properties?.relativeLocation?.properties?.city || localStorage.getItem('latLonQuery') || '';
if (stationId && stationId in StationInfo) {
@@ -124,6 +128,7 @@ const getWeather = async (latLon, haveDataCallback) => {
weatherParameters.countryCode = location.countryCode ?? '';
weatherParameters.timeZone = openMeteoForecast.timezone;
weatherParameters.forecast = aggregatedForecast;
+ weatherParameters.supportsNoaaAlerts = supportsNoaaAlerts;
weatherParameters.supportsNoaaDisplays = !!(supportsNoaaDisplays && point && stations?.features?.length);
weatherParameters.zoneId = point?.properties?.forecastZone?.substr(-6) ?? '';
weatherParameters.radarId = point?.properties?.radarStation?.substr(-3) ?? '';