2.6
New Logo Updated cache system now 15/30mins but scope updates every minute Font update
This commit is contained in:
@@ -116,11 +116,41 @@ export function checkHeatSpike(rows) {
|
||||
// wet + calm → rain icon (🌧️)
|
||||
// windy + wet → storm icon (🌩️), and the wind/rain icons are suppressed
|
||||
// for that hour so a single hour never shows two icons.
|
||||
const GUST_BLUSTERY = 25 / 2.237; // m/s (= 25 mph) - matches the table's gust highlight
|
||||
const GUST_STORM = 20; // m/s ≈ 45 mph - storm-force
|
||||
const GUST_BLUSTERY = 25 / 2.237; // m/s (= 25 mph) - Beaufort Force 6 threshold
|
||||
const GUST_STORM = 20; // m/s ≈ 45 mph - Beaufort Force 8+ threshold
|
||||
const PRECIP_WET = 5; // mm/h - meaningful rain
|
||||
const PRECIP_HEAVY = 10; // mm/h - heavy rain
|
||||
|
||||
// Beaufort scale lookup - force number and name from gust speed in m/s
|
||||
function beaufortForce(ms) {
|
||||
const mph = ms * 2.237;
|
||||
if (mph >= 73) return { force: 12, name: 'Hurricane Force' };
|
||||
if (mph >= 64) return { force: 11, name: 'Violent Storm' };
|
||||
if (mph >= 55) return { force: 10, name: 'Storm Force' };
|
||||
if (mph >= 47) return { force: 9, name: 'Strong Gale' };
|
||||
if (mph >= 39) return { force: 8, name: 'Gale' };
|
||||
if (mph >= 32) return { force: 7, name: 'Near Gale' };
|
||||
return { force: 6, name: 'Strong Breeze' };
|
||||
}
|
||||
|
||||
const BEAUFORT_DESC = {
|
||||
6: 'Large branches in motion; umbrellas used with difficulty.',
|
||||
7: 'Whole trees in motion; inconvenience felt when walking against the wind.',
|
||||
8: 'Twigs break off trees and generally impedes progress.',
|
||||
9: 'Slight structural damage possible — chimney pots and slates at risk.',
|
||||
10: 'Trees uprooted; considerable structural damage expected.',
|
||||
11: 'Very rarely experienced inland; widespread damage expected.',
|
||||
12: 'Devastating conditions — avoid all outdoor activity.',
|
||||
};
|
||||
|
||||
// Banner colour scales with severity
|
||||
function windBannerColors(force) {
|
||||
if (force >= 11) return { color: '#0c1020', textColor: '#c0d8ff' };
|
||||
if (force >= 9) return { color: '#101828', textColor: '#c0d4f0' };
|
||||
if (force >= 7) return { color: '#141e2e', textColor: '#c0d0e8' };
|
||||
return { color: '#1a2030', textColor: '#c0d8f0' };
|
||||
}
|
||||
|
||||
const gustOf = r => r.gust ?? r.va ?? 0;
|
||||
const precipOf = r => r.precip ?? 0;
|
||||
const isWindyHour = r => { const g = gustOf(r); return isFinite(g) && g >= GUST_BLUSTERY; };
|
||||
@@ -135,20 +165,24 @@ export function checkWind(rows) {
|
||||
|
||||
const maxGust = Math.max(...affected.map(gustOf).filter(isFinite));
|
||||
const mph = Math.round(maxGust * 2.237);
|
||||
const isStorm = maxGust >= GUST_STORM;
|
||||
const { force, name } = beaufortForce(maxGust);
|
||||
const desc = BEAUFORT_DESC[force] || '';
|
||||
const { color, textColor } = windBannerColors(force);
|
||||
|
||||
const title = force >= 10 ? `${name} Winds`
|
||||
: force >= 8 ? `${name} Warning`
|
||||
: name;
|
||||
|
||||
return {
|
||||
id: 'wind',
|
||||
emoji: '💨',
|
||||
title: isStorm ? 'Storm-Force Winds' : 'Blustery Conditions',
|
||||
message: isStorm
|
||||
? `Storm-force gusts to ${mph} mph expected — secure loose objects and take care outdoors.`
|
||||
: `Windy spell — gusts to ${mph} mph at peak. Hold onto hats and secure loose items outdoors.`,
|
||||
color: '#1a2030',
|
||||
textColor: '#c0d8f0',
|
||||
emoji: force >= 10 ? '🌪️' : '💨',
|
||||
title,
|
||||
message: `Gusts to ${mph} mph (Beaufort Force ${force}). ${desc}`,
|
||||
color,
|
||||
textColor,
|
||||
type: 'weather',
|
||||
nightOnly: false,
|
||||
isoRange, // only show cell icon during the windy hours
|
||||
isoRange,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -185,20 +219,23 @@ export function checkStorm(rows) {
|
||||
const maxGust = Math.max(...affected.map(gustOf).filter(isFinite));
|
||||
const maxPrecip = Math.max(...affected.map(precipOf).filter(isFinite));
|
||||
const mph = Math.round(maxGust * 2.237);
|
||||
const isSevere = maxGust >= GUST_STORM || maxPrecip >= PRECIP_HEAVY;
|
||||
const { force, name } = beaufortForce(maxGust);
|
||||
const { color, textColor } = windBannerColors(force);
|
||||
|
||||
const title = force >= 10 ? `${name} — Severe Storm`
|
||||
: force >= 8 ? `${name} with Rain`
|
||||
: 'Storm Conditions';
|
||||
|
||||
return {
|
||||
id: 'storm',
|
||||
emoji: '🌩️',
|
||||
title: isSevere ? 'Severe Storm' : 'Storm Conditions',
|
||||
message: isSevere
|
||||
? `Severe storm — driving rain to ${maxPrecip.toFixed(1)} mm/h and gusts to ${mph} mph. Avoid travel if you can.`
|
||||
: `Stormy spell — wind and rain together, gusts to ${mph} mph with ${maxPrecip.toFixed(1)} mm/h rain. Take care outdoors.`,
|
||||
color: '#141a26',
|
||||
textColor: '#c0d0e8',
|
||||
title,
|
||||
message: `Gusts to ${mph} mph (Beaufort Force ${force}) with ${maxPrecip.toFixed(1)} mm/h rain. ${BEAUFORT_DESC[force] || ''} Take care outdoors.`,
|
||||
color,
|
||||
textColor,
|
||||
type: 'weather',
|
||||
nightOnly: false,
|
||||
isoRange, // only show cell icon during the stormy hours
|
||||
isoRange,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user