Pressure tendency chart: own range and change-window controls
Publish Container / publish (push) Successful in 2m28s
Publish Container / publish (push) Successful in 2m28s
Range: same as page / 6h / 24h / 2d / 7d / 30d. Change over: 1h / 3h / 6h / 12h / 24h. Choices persist in localStorage. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
74198330d9
commit
fc390b0bb1
2 files changed
+77
-16
No files matched your search
+70
-16
@@ -611,6 +611,7 @@ let charts = {};
|
||||
// ── Formatting helpers ──────────────────────────────────────────────────────
|
||||
function fmtTime(ts, range) {
|
||||
const d = new Date(ts);
|
||||
if (range === '2d') return d.toLocaleDateString('en-US', { weekday: 'short' }) + ' ' + d.toLocaleTimeString('en-US', { hour: 'numeric', hour12: true });
|
||||
if (['7d','30d','90d','1y','2y','3y'].includes(range)) {
|
||||
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
||||
}
|
||||
@@ -779,6 +780,64 @@ function drawWindRose(canvas, readings) {
|
||||
})(t0);
|
||||
}
|
||||
|
||||
// ── Pressure tendency (own range + change window) ────────────────────────────
|
||||
let presTendRange = 'follow', presTendWin = 3;
|
||||
try { presTendRange = localStorage.getItem('ptRange') || 'follow'; presTendWin = parseInt(localStorage.getItem('ptWin')) || 3; } catch {}
|
||||
const presTendCache = {};
|
||||
let presTendLast = { pts: null, range: null };
|
||||
|
||||
async function drawPresTend(pagePts, pageRange) {
|
||||
const canvas = document.getElementById('cPresTend');
|
||||
if (!canvas) return;
|
||||
let pts = pagePts, range = pageRange;
|
||||
if (presTendRange !== 'follow') {
|
||||
range = presTendRange;
|
||||
const hit = presTendCache[range];
|
||||
if (hit && Date.now() - hit.at < 120000) pts = hit.data;
|
||||
else {
|
||||
try {
|
||||
const bucket = ['7d', '30d'].includes(range) ? '&points=720' : '';
|
||||
const r = await fetch(`/api/station/history?range=${range}${bucket}`);
|
||||
pts = r.ok ? await r.json() : [];
|
||||
} catch { pts = []; }
|
||||
presTendCache[range] = { at: Date.now(), data: pts };
|
||||
}
|
||||
if (document.getElementById('cPresTend') !== canvas) return; // charts were re-rendered meanwhile
|
||||
}
|
||||
if (pts.length > 900) { const step = Math.ceil(pts.length / 720); pts = pts.filter((_, i) => i % step === 0); }
|
||||
const winMs = presTendWin * 3600000;
|
||||
const c = chartColors();
|
||||
const tend = pts.map(r => {
|
||||
const t = r.dateutc - winMs;
|
||||
if (!pts.length || t < pts[0].dateutc) return null;
|
||||
let lo = 0, hi = pts.length - 1;
|
||||
while (lo < hi) { const m = (lo + hi) >> 1; if (pts[m].dateutc < t) lo = m + 1; else hi = m; }
|
||||
const base = pts[lo].baromrelin;
|
||||
return r.baromrelin != null && base != null ? +(r.baromrelin - base).toFixed(3) : null;
|
||||
});
|
||||
const ds = barDataset(`${presTendWin}h change`, tend, c.cool);
|
||||
ds.backgroundColor = tend.map(v => v == null ? 'transparent' : (v >= 0 ? c.cool + 'bb' : c.accent + 'bb'));
|
||||
ds.borderWidth = 0;
|
||||
makeChart('cPresTend', 'bar', [ds], pts.map(r => fmtTime(r.dateutc, range)), `inHg / ${presTendWin}h`);
|
||||
const title = document.getElementById('ptTitle');
|
||||
if (title) title.textContent = `Pressure Tendency · ${presTendWin}-hour change (inHg)`;
|
||||
if (!tend.some(v => v != null) && title) title.textContent += ' — not enough data for this window';
|
||||
}
|
||||
|
||||
function initPresTend(pagePts, pageRange) {
|
||||
presTendLast = { pts: pagePts, range: pageRange };
|
||||
const selRange = document.getElementById('ptRange'), selWin = document.getElementById('ptWin');
|
||||
if (selRange) {
|
||||
selRange.value = presTendRange;
|
||||
selRange.addEventListener('change', () => { presTendRange = selRange.value; try { localStorage.setItem('ptRange', presTendRange); } catch {} drawPresTend(presTendLast.pts, presTendLast.range); });
|
||||
}
|
||||
if (selWin) {
|
||||
selWin.value = String(presTendWin);
|
||||
selWin.addEventListener('change', () => { presTendWin = parseInt(selWin.value); try { localStorage.setItem('ptWin', presTendWin); } catch {} drawPresTend(presTendLast.pts, presTendLast.range); });
|
||||
}
|
||||
drawPresTend(pagePts, pageRange);
|
||||
}
|
||||
|
||||
// ── Render charts ────────────────────────────────────────────────────────────
|
||||
function renderCharts(readings, range, yoy = null) {
|
||||
const area = document.getElementById('chartsArea');
|
||||
@@ -835,7 +894,15 @@ function renderCharts(readings, range, yoy = null) {
|
||||
</div>
|
||||
|
||||
<div class="chart-card wide" id="cardPresTend">
|
||||
<div class="chart-title">Pressure Tendency · 3-hour change (inHg)</div>
|
||||
<div class="chart-title"><span id="ptTitle">Pressure Tendency (inHg)</span>
|
||||
<span class="chart-ctl">
|
||||
<label>Range <select id="ptRange" aria-label="Pressure tendency range">
|
||||
<option value="follow">Same as page</option><option value="6h">6 hours</option><option value="24h">24 hours</option>
|
||||
<option value="2d">2 days</option><option value="7d">7 days</option><option value="30d">30 days</option></select></label>
|
||||
<label>Change over <select id="ptWin" aria-label="Pressure change window">
|
||||
<option value="1">1 hour</option><option value="3">3 hours</option><option value="6">6 hours</option>
|
||||
<option value="12">12 hours</option><option value="24">24 hours</option></select></label>
|
||||
</span></div>
|
||||
<div class="chart-wrap" style="height:140px"><canvas id="cPresTend"></canvas></div>
|
||||
</div>
|
||||
|
||||
@@ -921,21 +988,8 @@ function renderCharts(readings, range, yoy = null) {
|
||||
}
|
||||
makeChart('cSolar', 'line', solarSets, lbls, 'W/m²', { scales: { y: { min: 0 } } });
|
||||
|
||||
// Pressure tendency: change vs 3 hours earlier (what forecasters watch)
|
||||
if (hasPres) {
|
||||
const tend = pts.map(r => {
|
||||
const t = r.dateutc - 3 * 3600000;
|
||||
let lo = 0, hi = pts.length - 1;
|
||||
if (t < pts[0].dateutc) return null;
|
||||
while (lo < hi) { const m = (lo + hi) >> 1; if (pts[m].dateutc < t) lo = m + 1; else hi = m; }
|
||||
const base = pts[lo].baromrelin;
|
||||
return r.baromrelin != null && base != null ? +(r.baromrelin - base).toFixed(3) : null;
|
||||
});
|
||||
const ds = barDataset('3h change', tend, c.cool);
|
||||
ds.backgroundColor = tend.map(v => v == null ? 'transparent' : (v >= 0 ? c.cool + 'bb' : c.accent + 'bb'));
|
||||
ds.borderWidth = 0;
|
||||
makeChart('cPresTend', 'bar', [ds], lbls, 'inHg / 3h');
|
||||
}
|
||||
// Pressure tendency: change vs N hours earlier (what forecasters watch); has its own range + window
|
||||
if (hasPres) initPresTend(pts, range);
|
||||
|
||||
// Indoor vs outdoor
|
||||
if (pts.some(r => r.tempinf != null)) {
|
||||
|
||||
Reference in new issue
Block a user