From e247005d34b8baa3983b310e6a54c7b31ec7c84a Mon Sep 17 00:00:00 2001 From: Florian Date: Thu, 28 May 2026 12:47:24 +0200 Subject: [PATCH] fix(climato): combler le lag de publication via SYNOP 48h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fetcher 48h d'obs horaires (au lieu de 24h) pour avoir le jour J-1 complet. Dériver TX/TN journaliers (max/min des températures horaires) et les injecter dans les fenêtres 7j/30j/1an pour les jours absents de la climato CSV. Le tab "24h" du graphique reste filtré sur les 24 dernières heures. Co-Authored-By: Claude Sonnet 4.6 --- src/pages/departement/[code].astro | 33 +++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/pages/departement/[code].astro b/src/pages/departement/[code].astro index 219a359..bb35b6a 100644 --- a/src/pages/departement/[code].astro +++ b/src/pages/departement/[code].astro @@ -30,7 +30,7 @@ const [snapshotR, climatoR, hourlyR] = drom : ([ getVigilanceSnapshot(), getClimatoForDepartement(dept.code), - getHourlyForDepartement(dept.code, 24), + getHourlyForDepartement(dept.code, 48), ] as const); const settled = await Promise.allSettled([snapshotR, climatoR, hourlyR]); @@ -58,13 +58,35 @@ let normaleHourly: { tx: number | null; tn: number | null } | null = null; // Source data has a ~1-4 day publication lag; the chart must still end at today. const _todayParis = new Intl.DateTimeFormat('sv', { timeZone: 'Europe/Paris' }).format(new Date()); const _climaByDate = new Map((climato?.days ?? []).map((d) => [d.date, d])); + +// Derive daily TX/TN from the 48h SYNOP window to fill the climato publication gap (typically 1-2 days). +// TX = max hourly t, TN = min hourly t — approximation acceptable for gap-filling. +const _synopDaily = new Map(); +if (hourly) { + const _byDate = new Map(); + for (const obs of hourly.observations) { + if (obs.t === null) continue; + const d = new Intl.DateTimeFormat('sv', { timeZone: 'Europe/Paris' }).format(new Date(obs.time)); + if (!_byDate.has(d)) _byDate.set(d, []); + _byDate.get(d)!.push(obs.t); + } + for (const [date, temps] of _byDate) { + _synopDaily.set(date, { tx: +Math.max(...temps).toFixed(1), tn: +Math.min(...temps).toFixed(1) }); + } +} + function _buildRange(n: number) { const result = []; for (let i = n - 1; i >= 0; i--) { const d = new Date(_todayParis + 'T12:00:00Z'); // noon UTC to avoid DST drift d.setUTCDate(d.getUTCDate() - i); const dateStr = new Intl.DateTimeFormat('sv', { timeZone: 'Europe/Paris' }).format(d); - result.push(_climaByDate.get(dateStr) ?? { date: dateStr, tn: null, tx: null, tm: null, rr: null, stations: 0 }); + if (_climaByDate.has(dateStr)) { + result.push(_climaByDate.get(dateStr)!); + } else { + const synop = _synopDaily.get(dateStr); + result.push({ date: dateStr, tn: synop?.tn ?? null, tx: synop?.tx ?? null, tm: null, rr: null, stations: 0 }); + } } return result; } @@ -88,6 +110,11 @@ if (!drom) { if (normaleHourly) normaleHourly = { tx: normaleHourly.tx, tn: normaleHourly.tn }; } +// Pass only the last 24h slice to the chart (we fetched 48h for synop gap-filling above). +const hourlyFor24h = hourly + ? { ...hourly, observations: hourly.observations.filter((o) => new Date(o.time).getTime() >= Date.now() - 24 * 3600 * 1000) } + : null; + const stationLabel = hourly ? `${hourly.stationName} (${hourly.distKm} km)` : null; const ech = snapshot ? currentEcheance(snapshot) : 'J'; @@ -361,7 +388,7 @@ const topPhen = topAlert ? PHENOMENA[topAlert.phenomenonId] : null;

Températures récentes