From 5dc214c6a5fb81fb887a4ac3d701f74a0c8e2cd2 Mon Sep 17 00:00:00 2001 From: Matt Walsh Date: Wed, 8 Apr 2026 09:57:18 -0500 Subject: [PATCH 1/6] filter station list upon receipt --- datagenerators/stations.mjs | 8 ++------ server/scripts/modules/currentweather.mjs | 11 ++++------- server/scripts/modules/navigation.mjs | 13 +++++++++++-- server/scripts/modules/utils/string.mjs | 7 ++++++- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/datagenerators/stations.mjs b/datagenerators/stations.mjs index 751777c..7963c61 100644 --- a/datagenerators/stations.mjs +++ b/datagenerators/stations.mjs @@ -8,13 +8,11 @@ import states from './stations-states.mjs'; import chunk from './chunk.mjs'; import overrides from './stations-overrides.mjs'; import postProcessor from './stations-postprocessor.mjs'; +import { stationFilter } from '../server/scripts/modules/utils/string.mjs'; // check for cached flag const USE_CACHE = process.argv.includes('--use-cache'); -// skip stations starting with these letters -const skipStations = ['U', 'C', 'H', 'W', 'Y', 'T', 'S', 'M', 'O', 'L', 'A', 'F', 'B', 'N', 'V', 'R', 'D', 'E', 'I', 'G', 'J']; - // chunk the list of states const chunkStates = chunk(states, 3); @@ -41,10 +39,8 @@ if (!USE_CACHE) { // eslint-disable-next-line no-await-in-loop const stationsRaw = await https(next); stations = JSON.parse(stationsRaw); - // filter stations for 4 letter identifiers - const stationsFiltered4 = stations.features.filter((station) => station.properties.stationIdentifier.match(/^[A-Z]{4}$/)); // filter against starting letter - const stationsFiltered = stationsFiltered4.filter((station) => !skipStations.includes(station.properties.stationIdentifier.slice(0, 1))); + const stationsFiltered = stations.filter(stationFilter); // add each resulting station to the output stationsFiltered.forEach((station) => { const id = station.properties.stationIdentifier; diff --git a/server/scripts/modules/currentweather.mjs b/server/scripts/modules/currentweather.mjs index 53fbd5a..50ba7b8 100644 --- a/server/scripts/modules/currentweather.mjs +++ b/server/scripts/modules/currentweather.mjs @@ -15,9 +15,6 @@ import { debugFlag } from './utils/debug.mjs'; import { isDataStale, enhanceObservationWithMapClick } from './utils/mapclick.mjs'; import { DateTime } from '../vendor/auto/luxon.mjs'; -// some stations prefixed do not provide all the necessary data -const skipStations = ['U', 'C', 'H', 'W', 'Y', 'T', 'S', 'M', 'O', 'L', 'A', 'F', 'B', 'N', 'V', 'R', 'D', 'E', 'I', 'G', 'J']; - class CurrentWeather extends WeatherDisplay { constructor(navId, elemId) { super(navId, elemId, 'Current Conditions', true); @@ -29,8 +26,8 @@ class CurrentWeather extends WeatherDisplay { // note: current weather does not use old data on a silent refresh // this is deliberate because it can pull data from more than one station in sequence - // filter for 4-letter observation stations, only those contain sky conditions and thus an icon - const filteredStations = this.weatherParameters.stations.filter((station) => station?.properties?.stationIdentifier?.length === 4 && !skipStations.includes(station.properties.stationIdentifier.slice(0, 1))); + // get the available stations + const { stations } = this.weatherParameters; // Load the observations let observations; @@ -38,9 +35,9 @@ class CurrentWeather extends WeatherDisplay { // station number counter let stationNum = 0; - while (!observations && stationNum < filteredStations.length) { + while (!observations && stationNum < stations.length) { // get the station - station = filteredStations[stationNum]; + station = stations[stationNum]; const stationId = station.properties.stationIdentifier; stationNum += 1; diff --git a/server/scripts/modules/navigation.mjs b/server/scripts/modules/navigation.mjs index 5fcad9a..f1e3e33 100644 --- a/server/scripts/modules/navigation.mjs +++ b/server/scripts/modules/navigation.mjs @@ -6,6 +6,7 @@ import { safeJson } from './utils/fetch.mjs'; import { getPoint } from './utils/weather.mjs'; import { debugFlag } from './utils/debug.mjs'; import settings from './settings.mjs'; +import { stationFilter } from './utils/string.mjs'; document.addEventListener('DOMContentLoaded', () => { init(); @@ -85,7 +86,15 @@ const getWeather = async (latLon, haveDataCallback) => { return; } - const StationId = stations.features[0].properties.stationIdentifier; + // filter stations for proper format + const stationsFiltered = stations.features.filter(stationFilter); + // check for stations available after filtering + if (stationsFiltered.length === 0) { + console.warn('No observation stations left for location after filtering'); + return; + } + + const StationId = stationsFiltered[0].properties.stationIdentifier; let { city } = point.properties.relativeLocation.properties; const { state } = point.properties.relativeLocation.properties; @@ -108,7 +117,7 @@ const getWeather = async (latLon, haveDataCallback) => { weatherParameters.timeZone = point.properties.timeZone; weatherParameters.forecast = point.properties.forecast; weatherParameters.forecastGridData = point.properties.forecastGridData; - weatherParameters.stations = stations.features; + weatherParameters.stations = stationsFiltered; weatherParameters.relativeLocation = point.properties.relativeLocation.properties; // update the main process for display purposes diff --git a/server/scripts/modules/utils/string.mjs b/server/scripts/modules/utils/string.mjs index ca20650..781a338 100644 --- a/server/scripts/modules/utils/string.mjs +++ b/server/scripts/modules/utils/string.mjs @@ -13,7 +13,12 @@ const locationCleanup = (input) => { return regexes.reduce((value, regex) => value.replace(regex, ''), input); }; +// stations must be 4 alpha characters and not start with the provided list +const skipStations = ['U', 'C', 'H', 'W', 'Y', 'T', 'S', 'M', 'O', 'L', 'A', 'F', 'B', 'N', 'V', 'R', 'D', 'E', 'I', 'G', 'J']; +const stationFilter = (station) => station.properties.stationIdentifier.match(/^[A-Z]{4}$/) && !skipStations.includes(station.properties.stationIdentifier.slice(0, 1)); + export { - // eslint-disable-next-line import/prefer-default-export + locationCleanup, + stationFilter, }; From ccc936d81aa2804d92de88ff23570c605f511ad2 Mon Sep 17 00:00:00 2001 From: Matt Walsh Date: Wed, 8 Apr 2026 09:57:24 -0500 Subject: [PATCH 2/6] 6.5.5 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a17bcf4..2c3465d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ws4kp", - "version": "6.5.4", + "version": "6.5.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ws4kp", - "version": "6.5.4", + "version": "6.5.5", "license": "MIT", "dependencies": { "dotenv": "^17.0.1", diff --git a/package.json b/package.json index 61bda2c..d25f93e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ws4kp", - "version": "6.5.4", + "version": "6.5.5", "description": "Welcome to the WeatherStar 4000+ project page!", "main": "index.mjs", "type": "module", From dae5b20bc6be695e9056c8bbcd144e0ab40176a3 Mon Sep 17 00:00:00 2001 From: Matt Walsh Date: Wed, 8 Apr 2026 11:39:25 -0500 Subject: [PATCH 3/6] fix radar round/floor mismatch in calculations close #200 --- server/scripts/modules/radar-tiles.mjs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/server/scripts/modules/radar-tiles.mjs b/server/scripts/modules/radar-tiles.mjs index 8a76916..1ee7b12 100644 --- a/server/scripts/modules/radar-tiles.mjs +++ b/server/scripts/modules/radar-tiles.mjs @@ -9,10 +9,12 @@ const pixelToFile = (xPixel, yPixel) => { return `${yTile}-${xTile}`; }; -// convert a pixel location in the overall map to a pixel location on the tile +// convert a pixel location in the overall map to a pixel location on the tile set const modTile = (xPixel, yPixel) => { - const x = Math.round(xPixel) % TILE_SIZE.x; - const y = Math.round(yPixel) % TILE_SIZE.y; + // adjust for additional 1 tile when odd + const x = (Math.floor(xPixel) % (TILE_SIZE.x)); + const y = (Math.floor(yPixel) % (TILE_SIZE.y)); + return { x, y }; }; @@ -46,8 +48,8 @@ const setTiles = (data) => { // determine which tiles are used const usedTiles = [ true, - TILE_SIZE.x - tileShift.x < RADAR_FINAL_SIZE.width, - TILE_SIZE.y - tileShift.y < RADAR_FINAL_SIZE.width, + tileShift.x + TILE_SIZE.x > RADAR_FINAL_SIZE.width, + tileShift.y + TILE_SIZE.y > RADAR_FINAL_SIZE.height, ]; // if we need t[1] and t[2] then we also need t[3] usedTiles.push(usedTiles[1] && usedTiles[2]); From ba369044774ed2e1452993dfe657a1b41329191c Mon Sep 17 00:00:00 2001 From: Matt Walsh Date: Wed, 8 Apr 2026 11:39:36 -0500 Subject: [PATCH 4/6] 6.5.6 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2c3465d..b72b4af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ws4kp", - "version": "6.5.5", + "version": "6.5.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ws4kp", - "version": "6.5.5", + "version": "6.5.6", "license": "MIT", "dependencies": { "dotenv": "^17.0.1", diff --git a/package.json b/package.json index d25f93e..2571d54 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ws4kp", - "version": "6.5.5", + "version": "6.5.6", "description": "Welcome to the WeatherStar 4000+ project page!", "main": "index.mjs", "type": "module", From 0b47cf79c1da44f2abe28a17557e620f4d1f0a79 Mon Sep 17 00:00:00 2001 From: Matt Walsh Date: Wed, 8 Apr 2026 22:41:42 -0500 Subject: [PATCH 5/6] don't overwrite timestamps when enhancing with mapclick --- server/scripts/modules/currentweather.mjs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/scripts/modules/currentweather.mjs b/server/scripts/modules/currentweather.mjs index 50ba7b8..0b5a878 100644 --- a/server/scripts/modules/currentweather.mjs +++ b/server/scripts/modules/currentweather.mjs @@ -101,7 +101,11 @@ class CurrentWeather extends WeatherDisplay { debugContext: 'currentweather', }); + // copy enhanced data and restore the timestamp if it was overwritten by older data from mapclick + const { timestamp } = candidateObservation.features[0].properties; candidateObservation.features[0].properties = enhancedResult.data; + candidateObservation.features[0].properties.timestamp = timestamp; + const { missingFields } = enhancedResult; const missingRequired = missingFields.filter((fieldName) => { const field = requiredFields.find((f) => f.name === fieldName && f.required); From 11c54391b20101ce0fcceaa84f818d55f6b3fb9a Mon Sep 17 00:00:00 2001 From: Matt Walsh Date: Wed, 8 Apr 2026 22:42:07 -0500 Subject: [PATCH 6/6] 6.5.7 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b72b4af..bad640f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ws4kp", - "version": "6.5.6", + "version": "6.5.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ws4kp", - "version": "6.5.6", + "version": "6.5.7", "license": "MIT", "dependencies": { "dotenv": "^17.0.1", diff --git a/package.json b/package.json index 2571d54..750f7d2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ws4kp", - "version": "6.5.6", + "version": "6.5.7", "description": "Welcome to the WeatherStar 4000+ project page!", "main": "index.mjs", "type": "module",