feat: uuroverzicht per dag (temperatuur, regen, wind)
Tik op een dag in de verwachtingsstrook voor een uuroverzicht van
temperatuur, regen en wind (richting + snelheid), om de 2 uur, 12
punten per dag. Sluit na 20 seconden vanzelf of bij een tik op de
overlay zelf.
Backend: Open-Meteo levert hourly-data in dezelfde call als daily
(geen extra verzoek, dezelfde 60min-cache). Nieuwe HourlyForecastMapper
groepeert per kalenderdatum en bemonstert om het andere uur, robuust
voor de zomertijddag (23/25 uur). OpenMeteoClient::dailyForecast()
hernoemd naar forecast(), geeft nu {daily, hourly} terug. Elke dag in
de forecast-array krijgt een hourly-lijst.
Frontend: temperatuurlijn hergebruikt sparklinePath (nu ook als
sparklinePoints voor exacte uitlijning met de 12-koloms grid), regen
als kleine staafjes, windpijl (nieuw icoon) wijst waarheen de wind
waait. 20s-balk via CSS transition met reflow-restart. Servicewerker
naar v5.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+47
-10
@@ -148,23 +148,60 @@ export function seriesRange(values) {
|
||||
return { min: Math.min(...nums), max: Math.max(...nums) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Coördinaten van een reeks binnen een box van w×h met `pad` px marge, geschaald op min..max.
|
||||
* Een vlakke reeks komt op het midden. `y` is null voor een onbekende waarde; `x` blijft altijd gezet
|
||||
* zodat labels/staafjes/pijlen op dezelfde plek als de lijn kunnen worden gepositioneerd.
|
||||
*/
|
||||
export function sparklinePoints(values, w, h, pad = 3) {
|
||||
const range = seriesRange(values);
|
||||
const innerW = w - 2 * pad;
|
||||
const innerH = h - 2 * pad;
|
||||
const step = values.length > 1 ? innerW / (values.length - 1) : 0;
|
||||
const span = range ? range.max - range.min : 0;
|
||||
return values.map((v, i) => {
|
||||
const x = pad + i * step;
|
||||
if (!isNum(v) || !range) return { x, y: null };
|
||||
const y = span === 0 ? h / 2 : pad + innerH - ((v - range.min) / span) * innerH;
|
||||
return { x, y };
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* SVG-pad voor een sparkline: punten gelijkmatig over de breedte, waarden geschaald op min..max
|
||||
* met `pad` px marge. Een vlakke reeks wordt een lijn door het midden. Minder dan 2 punten -> "".
|
||||
*/
|
||||
export function sparklinePath(values, w, h, pad = 3) {
|
||||
const range = seriesRange(values);
|
||||
if (!range || values.length < 2) return '';
|
||||
const span = range.max - range.min;
|
||||
const innerW = w - 2 * pad;
|
||||
const innerH = h - 2 * pad;
|
||||
const step = innerW / (values.length - 1);
|
||||
if (values.length < 2) return '';
|
||||
const parts = [];
|
||||
values.forEach((v, i) => {
|
||||
if (!isNum(v)) return;
|
||||
const x = pad + i * step;
|
||||
const y = span === 0 ? h / 2 : pad + innerH - ((v - range.min) / span) * innerH;
|
||||
sparklinePoints(values, w, h, pad).forEach(({ x, y }) => {
|
||||
if (y === null) return;
|
||||
parts.push(`${parts.length === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`);
|
||||
});
|
||||
return parts.join(' ');
|
||||
}
|
||||
|
||||
/** Windrichting (meteorologisch: vanwaar de wind komt) -> rotatie zodat de pijl waarheen wijst. */
|
||||
export function windArrowDeg(dir) {
|
||||
return isNum(dir) ? (dir + 180) % 360 : null;
|
||||
}
|
||||
|
||||
/** 8.4 -> "8 km/u"; null -> "–" */
|
||||
export function fKmh(v) {
|
||||
return isNum(v) ? `${Math.round(v)} km/u` : DASH;
|
||||
}
|
||||
|
||||
/** ISO-tijdstip -> "14" (tweecijferig uur) in de tijdzone; null -> "–" */
|
||||
export function formatHour(iso, tz) {
|
||||
const d = iso instanceof Date ? iso : new Date(iso);
|
||||
if (!iso || Number.isNaN(d.getTime())) return DASH;
|
||||
const parts = new Intl.DateTimeFormat('en-GB', { hour: '2-digit', hourCycle: 'h23', timeZone: tz }).formatToParts(d);
|
||||
return parts.find((p) => p.type === 'hour').value;
|
||||
}
|
||||
|
||||
/** "2026-09-14" -> "Maandag 14 september" in de tijdzone (net als formatDate, maar vanuit een datumstring). */
|
||||
export function formatFullDate(dateStr, tz) {
|
||||
const d = new Date(dateStr + 'T12:00:00Z');
|
||||
if (Number.isNaN(d.getTime())) return DASH;
|
||||
return formatDate(d, tz);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user