show rainfall data
This commit is contained in:
@@ -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: {
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user