From 0b19f11ad0d2fbdded50ece29f4b88fb19fc3658 Mon Sep 17 00:00:00 2001 From: Rudy Celekli Date: Sat, 24 Oct 2026 00:21:05 -0400 Subject: [PATCH 1/3] fix(cyclones): convert NHC movement speed from mph to knots --- server/providers/cyclones.js | 7 ++++++- src/data/cyclonesProxy.test.mjs | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/server/providers/cyclones.js b/server/providers/cyclones.js index 3e5f2f6d1..5aab0f246 100644 --- a/server/providers/cyclones.js +++ b/server/providers/cyclones.js @@ -43,6 +43,11 @@ function number(value, min, max) { const n = Number(value); return Number.isFinite(n) && n >= min && n <= max ? n : null; } +// CurrentStorms.json reports movementSpeed in miles per hour (intensity is in +// knots); convert so `speedKt` means what it says. +function knotsFromMph(mph) { + return mph === null ? null : Math.round(mph * 0.868976); +} // NHC stamps an advisory with its nominal time (e.g. 03:00Z) and publishes it // up to about half an hour before; allow that, but not a clock far ahead. const EARLY_ADVISORY_MS = 90 * 60_000; @@ -120,7 +125,7 @@ export function parseCycloneStatus(payload, now = Date.now()) { pressureHpa: number(raw.pressure, 800, 1100), movement: { directionDegrees: number(raw.movementDir, 0, 360), - speedKt: number(raw.movementSpeed, 0, 200), + speedKt: knotsFromMph(number(raw.movementSpeed, 0, 200)), }, advisoryUrl: officialLink(forecast.url), outlookUrl: raw.id.startsWith('al') diff --git a/src/data/cyclonesProxy.test.mjs b/src/data/cyclonesProxy.test.mjs index 23e84826c..6757f6e5c 100644 --- a/src/data/cyclonesProxy.test.mjs +++ b/src/data/cyclonesProxy.test.mjs @@ -115,7 +115,8 @@ test('status retains official clocks, knots, source links and finite current pos assert.equal(storm.issuedAt, TIME); assert.equal(storm.positionAt, TIME); assert.deepEqual(storm.position, { longitude: -125.8, latitude: 15.5 }); - assert.deepEqual(storm.movement, { directionDegrees: 255, speedKt: 12 }); + // NHC publishes movementSpeed in mph: 12 mph is 10 kt. + assert.deepEqual(storm.movement, { directionDegrees: 255, speedKt: 10 }); assert.equal(storm.windKt, 25); assert.equal(storm.pressureHpa, 1006); assert.match(storm.advisoryUrl, /^https:\/\/www\.nhc\.noaa\.gov\/text\//); From 1b52298c6c0949e8b15df71eaf4827569487a2ad Mon Sep 17 00:00:00 2001 From: Rudy Celekli Date: Sat, 24 Oct 2026 00:21:39 -0400 Subject: [PATCH 2/3] docs(changelog): add cyclone movement speed fragment --- changelog.d/1350-cyclone-movement-knots.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog.d/1350-cyclone-movement-knots.md diff --git a/changelog.d/1350-cyclone-movement-knots.md b/changelog.d/1350-cyclone-movement-knots.md new file mode 100644 index 000000000..3fc8c8f9f --- /dev/null +++ b/changelog.d/1350-cyclone-movement-knots.md @@ -0,0 +1,3 @@ +- Convert NHC cyclone movement speed from miles per hour to knots, so + `movement.speedKt` in cyclone query results is no longer about 15% high + (rudycelekli, #1350). From cf4e8e1d87fe1c57315f1107aae996122086e669 Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Sat, 24 Oct 2026 21:19:07 -0700 Subject: [PATCH 3/3] docs(cyclones): record upstream movement speed units --- docs/CURRENT-STATE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index ede889fdc..3db6d3fb9 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -423,6 +423,8 @@ than relabeling older geometry. The cone represents forecast center uncertainty, not storm size or the full hazard area. Successful empty and unavailable states are distinct. One owned native Cesium data source preserves polygon holes and geographic seams and releases on disable; forecast animation is not implied. +NHC movement speed is converted from miles per hour to rounded knots by the +proxy; advisory wind intensity is already in knots and is passed through. Cyclone markers, labels, tracks and cones are hidden beyond the horizon on every map source. Consult the linked official advisory for safety decisions.