From 249f2845934b9d4e5178dc5e9bd1c0d0a4eb34fc Mon Sep 17 00:00:00 2001 From: fraxle Date: Sun, 2 Aug 2026 16:55:48 +0100 Subject: [PATCH] 3.8.2 Fix event banner to use sunsoak for hot and air for frost --- assets/js/events/weather-checks.js | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/assets/js/events/weather-checks.js b/assets/js/events/weather-checks.js index 5b7b9a2..362a398 100644 --- a/assets/js/events/weather-checks.js +++ b/assets/js/events/weather-checks.js @@ -117,27 +117,31 @@ export function checkSunset(rows, location) { }; } -// Thresholds are for plain shade/air temperature, not UTCI felt temperature, -// so they sit well below the UTCI Caution/Extreme/Danger bands -// (utils.js UTCI_BANDS). 27°C is the midpoint of the Met Office's official UK -// heatwave threshold (25°C in cooler regions to 28°C in the SE); 32°C and -// 38°C scale up from there, with 38°C sitting just under the UK's all-time -// record shade temperature of 40.3°C. +// Heat stress is what a body actually experiences, so this reads SunSoak +// (utciAdj — UTCI felt temperature with the precipitation penalty), not shade +// air temperature: standing in full sun at 24°C air is a real heat risk that +// an air-temp check misses entirely. Because the input is now on the UTCI +// scale, the thresholds are simply the UTCI_BANDS boundaries (utils.js): +// 27°C = Caution, 32°C = Extreme, 41°C = Danger. That keeps the banner's +// severity in lockstep with the band colour shown in the table and day tabs. +const HEAT_CAUTION = 27, HEAT_EXTREME = 32, HEAT_DANGER = 41; +const feltOf = r => r.utciAdj ?? r.utci ?? r.Ta; + export function checkHeatSpike(rows) { - const maxTemp = Math.max(...rows.map(r => r.Ta).filter(isFinite)); - if (maxTemp < 27) return null; - const severity = maxTemp >= 38 ? 'danger' : maxTemp >= 32 ? 'extreme-caution' : 'caution'; + const maxTemp = Math.max(...rows.map(feltOf).filter(isFinite)); + if (maxTemp < HEAT_CAUTION) return null; + const severity = maxTemp >= HEAT_DANGER ? 'danger' : maxTemp >= HEAT_EXTREME ? 'extreme-caution' : 'caution'; // Only flag the hours that are actually at/above the heat threshold. - const affected = rows.filter(r => isFinite(r.Ta) && r.Ta >= 27); + const affected = rows.filter(r => { const t = feltOf(r); return isFinite(t) && t >= HEAT_CAUTION; }); const isoRange = affected.length ? [affected[0].iso, affected[affected.length - 1].iso] : undefined; const msgs = { - 'caution': `Air temperature reaching ${maxTemp.toFixed(1)}°C. Avoid strenuous activity outdoors, drink plenty of fluids, and wear light clothing.`, - 'extreme-caution': `Heat warning: Air temp ${maxTemp.toFixed(1)}°C expected today. Take frequent breaks indoors in the air conditioning, avoid strenuous activity, drink plenty of fluids, and wear light clothing.`, - 'danger': `Extreme heat alert: Air temp ${maxTemp.toFixed(1)}°C forecast. Limit time spent outdoors and stay indoors with the air conditioning on as much as possible. Avoid strenuous activity, drink plenty of fluids, and wear light clothing.`, + 'caution': `Feeling like ${maxTemp.toFixed(1)}°C in the sun. Avoid strenuous activity outdoors, drink plenty of fluids, and wear light clothing.`, + 'extreme-caution': `Heat warning: feeling like ${maxTemp.toFixed(1)}°C in the sun today. Take frequent breaks indoors in the air conditioning, avoid strenuous activity, drink plenty of fluids, and wear light clothing.`, + 'danger': `Extreme heat alert: feeling like ${maxTemp.toFixed(1)}°C in the sun. Limit time spent outdoors and stay indoors with the air conditioning on as much as possible. Avoid strenuous activity, drink plenty of fluids, and wear light clothing.`, }; return { id: 'heat-spike',