From 270242a685e26e4a678efe8dfb01eb2f0d1de3e6 Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 27 May 2026 22:09:11 +0200 Subject: [PATCH] =?UTF-8?q?fix(climato):=20TX=20agr=C3=A9g=C3=A9=20par=20M?= =?UTF-8?q?AX=20plut=C3=B4t=20que=20moyenne=20inter-stations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La moyenne des TX sur toutes les stations du département sous-estimait systématiquement de 2-3°C à cause des stations en altitude. MAX est la sémantique correcte pour un site canicule. Co-Authored-By: Claude Sonnet 4.6 --- src/lib/climato.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/lib/climato.ts b/src/lib/climato.ts index c38dccb..35c5464 100644 --- a/src/lib/climato.ts +++ b/src/lib/climato.ts @@ -37,7 +37,7 @@ function buildUrl(dept: string, period: string): string | null { return `${BASE}/Q_${dept}_${period}_RR-T-Vent.csv.gz`; } -type Agg = { tnSum: number; tnN: number; txSum: number; txN: number; tmSum: number; tmN: number; rrSum: number; rrN: number; stations: number }; +type Agg = { tnSum: number; tnN: number; txMax: number; txN: number; tmSum: number; tmN: number; rrSum: number; rrN: number; stations: number }; function parseCsvInto(text: string, byDate: Map): void { const lines = text.split(/\r?\n/); @@ -60,19 +60,23 @@ function parseCsvInto(text: string, byDate: Map): void { const date = `${raw.slice(0, 4)}-${raw.slice(4, 6)}-${raw.slice(6, 8)}`; let agg = byDate.get(date); if (!agg) { - agg = { tnSum: 0, tnN: 0, txSum: 0, txN: 0, tmSum: 0, tmN: 0, rrSum: 0, rrN: 0, stations: 0 }; + agg = { tnSum: 0, tnN: 0, txMax: -Infinity, txN: 0, tmSum: 0, tmN: 0, rrSum: 0, rrN: 0, stations: 0 }; byDate.set(date, agg); } agg.stations++; - const add = (raw: string | undefined, sum: 'tnSum' | 'txSum' | 'tmSum' | 'rrSum', n: 'tnN' | 'txN' | 'tmN' | 'rrN') => { + const addAvg = (raw: string | undefined, sum: 'tnSum' | 'tmSum' | 'rrSum', n: 'tnN' | 'tmN' | 'rrN') => { if (!raw) return; const v = parseFloat(raw.replace(',', '.')); if (Number.isFinite(v)) { agg![sum] += v; agg![n]++; } }; - if (idx.tn !== -1) add(cols[idx.tn], 'tnSum', 'tnN'); - if (idx.tx !== -1) add(cols[idx.tx], 'txSum', 'txN'); - if (idx.tm !== -1) add(cols[idx.tm], 'tmSum', 'tmN'); - if (idx.rr !== -1) add(cols[idx.rr], 'rrSum', 'rrN'); + // TX: max across stations (not average) — hottest reading in the dept for canicule detection + if (idx.tx !== -1 && cols[idx.tx]) { + const v = parseFloat(cols[idx.tx].replace(',', '.')); + if (Number.isFinite(v)) { if (v > agg.txMax) agg.txMax = v; agg.txN++; } + } + if (idx.tn !== -1) addAvg(cols[idx.tn], 'tnSum', 'tnN'); + if (idx.tm !== -1) addAvg(cols[idx.tm], 'tmSum', 'tmN'); + if (idx.rr !== -1) addAvg(cols[idx.rr], 'rrSum', 'rrN'); } } @@ -89,7 +93,7 @@ function aggregateDays(byDate: Map): DayObservation[] { .map(([date, agg]) => ({ date, tn: agg.tnN > 0 ? +(agg.tnSum / agg.tnN).toFixed(1) : null, - tx: agg.txN > 0 ? +(agg.txSum / agg.txN).toFixed(1) : null, + tx: agg.txN > 0 ? +agg.txMax.toFixed(1) : null, tm: agg.tmN > 0 ? +(agg.tmSum / agg.tmN).toFixed(1) : null, rr: agg.rrN > 0 ? +(agg.rrSum / agg.rrN).toFixed(1) : null, stations: agg.stations,