show rainfall data

This commit is contained in:
2026-01-28 16:04:07 +11:00
parent d941a62365
commit 3d6687e3d7
3 changed files with 132 additions and 4 deletions

View File

@@ -17,6 +17,8 @@ const colors = {
uvi: "#f4d35e",
light: "#b8f2e6",
precip: "#4ea8de",
rain: "#4ea8de",
rainStart: "#f77f00",
};
function formatNumber(value, digits) {
@@ -74,6 +76,69 @@ function safeDate(value) {
return Number.isNaN(dt.getTime()) ? null : dt;
}
function hourBucketMs(ts, tz) {
const d = new Date(ts);
if (Number.isNaN(d.getTime())) return null;
if (tz === "utc") {
return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours());
}
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours()).getTime();
}
function computeRainIncrements(points) {
const out = [];
let prev = null;
for (const p of points) {
const t = new Date(p.ts).getTime();
if (Number.isNaN(t)) continue;
if (p.rain_mm === null || p.rain_mm === undefined) {
out.push({ x: t, y: null });
continue;
}
let delta = null;
if (prev !== null) {
delta = p.rain_mm - prev;
if (delta < 0) delta = 0;
}
prev = p.rain_mm;
out.push({ x: t, y: delta });
}
return out;
}
function computeRollingSum(points, windowMs) {
const out = [];
let sum = 0;
let j = 0;
const values = points.map((p) => ({
x: p.x,
y: p.y == null ? 0 : p.y,
}));
for (let i = 0; i < values.length; i++) {
const cur = values[i];
sum += cur.y;
while (values[j].x < cur.x - windowMs) {
sum -= values[j].y;
j += 1;
}
out.push({ x: cur.x, y: sum });
}
return out;
}
function computeHourlySums(points, tz) {
const buckets = new Map();
for (const p of points) {
if (p.y == null) continue;
const bucket = hourBucketMs(p.x, tz);
if (bucket === null) continue;
buckets.set(bucket, (buckets.get(bucket) || 0) + p.y);
}
return Array.from(buckets.entries())
.sort((a, b) => a[0] - b[0])
.map(([x, y]) => ({ x, y }));
}
function startOfDay(date, tz) {
if (tz === "utc") {
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
@@ -476,6 +541,38 @@ function renderDashboard(data) {
};
upsertChart("chart-power", powerChart);
const rainOptions = baseOptions(range);
rainOptions.scales.y1 = {
position: "right",
ticks: { color: "#a4c4c4" },
grid: { drawOnChartArea: false },
};
const rainIncs = computeRainIncrements(obsFiltered);
const rainRolling = computeRollingSum(rainIncs, 24 * 60 * 60 * 1000);
const rainHourly = computeHourlySums(rainIncs, state.tz);
const rainTitle = document.getElementById("chart-rain-title");
const isHourly = state.range === "6h";
if (rainTitle) {
rainTitle.textContent = isHourly ? "Rain (hourly sums)" : "Rain (24h rolling)";
}
const rainChart = {
type: "line",
data: {
datasets: [
{
label: isHourly ? "rain hourly sum (mm)" : "rain rolling 24h (mm)",
data: isHourly ? rainHourly : rainRolling,
borderColor: colors.rain,
yAxisID: "y",
},
],
},
options: rainOptions,
};
upsertChart("chart-rain", rainChart);
const precipChart = {
type: "bar",
data: {

View File

@@ -146,6 +146,15 @@
<canvas id="chart-power"></canvas>
</div>
</div>
<div class="chart-card" data-chart="chart-rain">
<div class="chart-header">
<div class="chart-title" id="chart-rain-title">Rain (24h rolling)</div>
<button class="chart-link" data-chart="chart-rain" title="Copy chart link">Share</button>
</div>
<div class="chart-canvas">
<canvas id="chart-rain"></canvas>
</div>
</div>
<div class="chart-card wide" data-chart="chart-precip">
<div class="chart-header">
<div class="chart-title">Precipitation (forecast)</div>