Add a next-8-hours strip to the Radar panel's forecast
CI / check (push) Successful in 17s
CI / deploy (push) Successful in 13s

Same Open-Meteo call and cache as the daily outlook, just with an hourly
field added — a rolling window from now rather than "rest of today", so
it doesn't shrink to nothing right before midnight.
This commit is contained in:
randy committed 2026-09-10 22:39:51 +02:00
1 parent 254e9b4c61
commit 6911e67c81
2 files changed
+55 -2

No files matched your search

+23
View File
@@ -218,6 +218,29 @@ html, body {
border-top: 1px solid #eee;
}
.radar-control__forecast-hours {
display: flex;
gap: 10px;
overflow-x: auto;
padding-bottom: 6px;
margin-bottom: 4px;
border-bottom: 1px solid #eee;
}
.radar-control__forecast-hour {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
flex: 0 0 auto;
font-variant-numeric: tabular-nums;
}
.radar-control__forecast-hour span:first-child {
color: #888;
font-size: 11px;
}
.radar-control__forecast-day {
display: flex;
align-items: center;
+32 -2
View File
@@ -12,6 +12,7 @@ const RAINVIEWER_API = "https://api.rainviewer.com/public/weather-maps.json";
// radar animation above can't.
const FORECAST_API = "https://api.open-meteo.com/v1/forecast";
const FORECAST_DAYS = 3;
const HOURLY_FORECAST_COUNT = 8;
const FORECAST_CACHE_KEY = "maptool:forecast";
// A small subset of WMO weather codes (open-meteo's `weather_code`) mapped
@@ -30,6 +31,21 @@ function weatherEmoji(code) {
return WEATHER_EMOJI.find(([codes]) => codes.includes(code))?.[1] ?? "🌡️";
}
// A rolling window from "now" rather than "rest of today" — the latter
// shrinks to nothing right before midnight and balloons to ~24 entries just
// after, for no good reason.
function nextHours(hourly, count) {
const now = new Date();
return hourly.time
.map((time, i) => ({
time,
code: hourly.weather_code[i],
temp: Math.round(hourly.temperature_2m[i]),
}))
.filter((hour) => new Date(hour.time) >= now)
.slice(0, count);
}
// Matches the default window used by github.com/jpettitt/weather-radar-card
// (60 minutes of history at RainViewer's native 10-minute cadence = 7
// frames, the last one being "now").
@@ -180,7 +196,17 @@ export class RadarControl {
precip: data.daily.precipitation_probability_max[i],
}));
const rows = days
const hourRows = nextHours(data.hourly, HOURLY_FORECAST_COUNT)
.map(
(hour) => `
<div class="radar-control__forecast-hour">
<span>${new Date(hour.time).toLocaleTimeString([], { hour: "2-digit" })}</span>
<span>${weatherEmoji(hour.code)}</span>
<span>${hour.temp}°</span>
</div>`,
)
.join("");
const dayRows = days
.map(
(day) => `
<div class="radar-control__forecast-day">
@@ -194,7 +220,7 @@ export class RadarControl {
const staleNote = cachedAt
? `<p class="radar-control__forecast-stale">Cached from ${new Date(cachedAt).toLocaleString([], { dateStyle: "short", timeStyle: "short" })}</p>`
: "";
this._forecastEl.innerHTML = rows + staleNote;
this._forecastEl.innerHTML = `<div class="radar-control__forecast-hours">${hourRows}</div>` + dayRows + staleNote;
}
_addLayers() {
@@ -299,6 +325,10 @@ async function fetchForecast(lat, lon) {
url.searchParams.set("latitude", lat);
url.searchParams.set("longitude", lon);
url.searchParams.set("daily", "weather_code,temperature_2m_max,temperature_2m_min,precipitation_probability_max");
// Same call, same cached JSON (writeCachedForecast below) — the next-few-
// hours strip alongside the multi-day outlook rather than a second
// request/cache entry.
url.searchParams.set("hourly", "weather_code,temperature_2m");
url.searchParams.set("timezone", "auto");
url.searchParams.set("forecast_days", String(FORECAST_DAYS));
const response = await fetch(url);