feat: Oct-2024 flood replay, simulation hooks, and static replay data
CI/CD Pipeline - Northern Thailand Ping River Monitor / Test Suite (3.11) (push) Failing after 30s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Build Docker Image (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Integration Test with Services (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Staging (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Production (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Performance Test (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Code Quality (push) Successful in 17s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Cleanup (push) Successful in 0s

- header button 'Replay Oct 2024 flood': animates the real Sep 15 -
  Oct 12 2024 P.1 hourly series (~45 s) through the flood-zone display;
  zones flood blue as the river climbs to the 5.30 m record and recede
  as it falls; click again to stop; restores live state when done
- replay reads a pre-extracted 15 kB static snapshot
  (static/flood-2024-p1.json, 335 frames) instead of pulling the 4.6 MB
  all-time history on every click; falls back to the history API if the
  snapshot is missing
- demo hooks for testing/presentations: ?demo_level=4.4 pins a simulated
  P.1 level, ?demo_rise=1 animates rising water to 5.30 m; both label
  the outlook line as SIMULATION with the real level alongside
This commit is contained in:
grabowski committed 2026-08-10 17:44:36 +07:00
1 parent c62a03e778
commit 0672ea5ffa
2 files changed
+76 -1

No files matched your search

+75 -1
View File
@@ -182,6 +182,7 @@
</div>
<div class="header-actions">
<div class="live-pill"><span class="live-dot"></span> LIVE DATA</div>
<button id="replay-2024" type="button">▶ Replay Oct 2024 flood</button>
<button id="refresh-button" type="button">↻ Refresh</button>
</div>
</header>
@@ -732,6 +733,61 @@
return `<div class="popup"><div class="popup-code">Marker</div><h3>${escapeHtml(poi.name)}</h3>${poi.note ? `<div class="popup-th">${escapeHtml(poi.note)}</div>` : ''}${riskHtml}</div>`;
}
async function replayFlood2024() {
const button = $('replay-2024');
if (state.replayTimer) { // stop a running replay
window.clearInterval(state.replayTimer);
state.replayTimer = null;
state.p1Now = state.p1RealNow;
restyleFloodZones();
button.textContent = '▶ Replay Oct 2024 flood';
$('p1-peak').textContent = state.p1PeakText || '';
return;
}
button.textContent = 'Loading 2024 data…';
try {
// pre-extracted static snapshot (15 kB); fall back to the live history API
let frames;
const staticResponse = await fetch('/static/flood-2024-p1.json');
if (staticResponse.ok) {
frames = await staticResponse.json();
} else {
const response = await fetch('/measurements/history/P.1?hours=876000&limit=100000');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const rows = await response.json();
frames = rows.filter((row) => {
const t = row.timestamp;
return t >= '2024-09-15' && t <= '2024-10-12' && row.water_level != null;
});
}
if (frames.length < 10) throw new Error('no 2024 data in history');
state.p1RealNow = state.p1Now;
state.p1PeakText = $('p1-peak').textContent;
state.zonesUserHidden = false;
await showFloodZones(false);
button.textContent = '⏹ Stop replay';
let i = 0;
state.replayTimer = window.setInterval(() => {
const frame = frames[Math.min(i, frames.length - 1)];
state.p1Now = Number(frame.water_level);
const when = new Date(frame.timestamp).toLocaleString([], { day: 'numeric', month: 'short', hour: '2-digit' });
$('p1-peak').textContent = `⏪ 2024 flood replay · ${when} · P.1 at ${state.p1Now.toFixed(2)} m`;
restyleFloodZones();
i += 1; // 1 h per frame (~45 s total)
if (i >= frames.length + 25) { // hold the last frame briefly
window.clearInterval(state.replayTimer);
state.replayTimer = null;
state.p1Now = state.p1RealNow;
restyleFloodZones();
button.textContent = '▶ Replay Oct 2024 flood';
$('p1-peak').textContent = state.p1PeakText || '';
}
}, 120);
} catch (error) {
button.textContent = `Replay unavailable: ${error.message}`;
}
}
async function loadCustomMarkers() {
try {
const response = await fetch('/static/custom-markers.json');
@@ -773,8 +829,25 @@
const row = p1rows.reduce((best, r) => r.horizon_hours > best.horizon_hours ? r : best);
state.p1Stages = row.stages;
state.p1Now = row.current_level == null ? null : Number(row.current_level);
// Demo hooks: ?demo_level=4.4 pins a simulated P.1 level; ?demo_rise=1 animates
// the water from the real level up to the 2024 record (5.30 m).
const params = new URLSearchParams(location.search);
const demoLevel = Number(params.get('demo_level'));
const simulated = Number.isFinite(demoLevel) && demoLevel > 0;
if (simulated) state.p1Now = demoLevel;
if (params.has('demo_rise') && !state.demoRiseTimer) {
let level = state.p1Now ?? 2.2;
state.demoRiseTimer = window.setInterval(() => {
level = Math.min(level + 0.05, 5.30);
state.p1Now = level;
$('p1-peak').textContent = `⚠ SIMULATION: rising water · P.1 at ${level.toFixed(2)} m (real: ${Number(row.current_level).toFixed(2)} m)`;
restyleFloodZones();
$('p1-peak').textContent = `Now ${Number(row.current_level).toFixed(2)} m · predicted peak next ${row.horizon_hours} h: ${Number(row.predicted_max_level).toFixed(2)} m`;
if (level >= 5.30) window.clearInterval(state.demoRiseTimer);
}, 700);
}
restyleFloodZones();
$('p1-peak').textContent = (simulated ? `⚠ SIMULATION: P.1 at ${demoLevel.toFixed(2)} m (real: ${Number(row.current_level).toFixed(2)} m)` :
`Now ${Number(row.current_level).toFixed(2)} m`) + ` · predicted peak next ${row.horizon_hours} h: ${Number(row.predicted_max_level).toFixed(2)} m`;
const strip = $('p1-stages');
strip.replaceChildren();
row.stages.forEach((s) => {
@@ -835,6 +908,7 @@
$('refresh-button').addEventListener('click', loadDashboard);
$('zones-toggle').addEventListener('click', toggleFloodZones);
$('replay-2024').addEventListener('click', replayFlood2024);
$('history-range').addEventListener('change', () => { if (state.selectedStation) loadHistory(state.selectedStation); });
loadDashboard();
window.setInterval(loadDashboard, 5 * 60 * 1000);
+1
View File
@@ -0,0 +1 @@
[{"timestamp": "2024-09-15T01:00:00", "water_level": 1.81}, {"timestamp": "2024-09-15T02:00:00", "water_level": 1.81}, {"timestamp": "2024-09-15T03:00:00", "water_level": 1.8}, {"timestamp": "2024-09-15T04:00:00", "water_level": 1.8}, {"timestamp": "2024-09-15T05:00:00", "water_level": 1.79}, {"timestamp": "2024-09-15T06:00:00", "water_level": 1.78}, {"timestamp": "2024-09-15T07:00:00", "water_level": 1.78}, {"timestamp": "2024-09-15T08:00:00", "water_level": 1.78}, {"timestamp": "2024-09-15T09:00:00", "water_level": 1.77}, {"timestamp": "2024-09-15T10:00:00", "water_level": 1.77}, {"timestamp": "2024-09-15T11:00:00", "water_level": 1.77}, {"timestamp": "2024-09-15T12:00:00", "water_level": 1.76}, {"timestamp": "2024-09-15T13:00:00", "water_level": 1.76}, {"timestamp": "2024-09-15T14:00:00", "water_level": 1.75}, {"timestamp": "2024-09-15T15:00:00", "water_level": 1.75}, {"timestamp": "2024-09-15T16:00:00", "water_level": 1.74}, {"timestamp": "2024-09-15T17:00:00", "water_level": 1.73}, {"timestamp": "2024-09-15T18:00:00", "water_level": 1.73}, {"timestamp": "2024-09-15T19:00:00", "water_level": 1.75}, {"timestamp": "2024-09-15T20:00:00", "water_level": 1.77}, {"timestamp": "2024-09-15T21:00:00", "water_level": 1.82}, {"timestamp": "2024-09-15T22:00:00", "water_level": 1.84}, {"timestamp": "2024-09-15T23:00:00", "water_level": 1.85}, {"timestamp": "2024-09-16T00:00:00", "water_level": 1.83}, {"timestamp": "2024-09-17T01:00:00", "water_level": 1.71}, {"timestamp": "2024-09-17T02:00:00", "water_level": 1.71}, {"timestamp": "2024-09-17T03:00:00", "water_level": 1.72}, {"timestamp": "2024-09-17T04:00:00", "water_level": 1.72}, {"timestamp": "2024-09-17T05:00:00", "water_level": 1.73}, {"timestamp": "2024-09-17T06:00:00", "water_level": 1.73}, {"timestamp": "2024-09-17T07:00:00", "water_level": 1.74}, {"timestamp": "2024-09-17T08:00:00", "water_level": 1.75}, {"timestamp": "2024-09-17T09:00:00", "water_level": 1.75}, {"timestamp": "2024-09-17T10:00:00", "water_level": 1.75}, {"timestamp": "2024-09-17T11:00:00", "water_level": 1.76}, {"timestamp": "2024-09-17T12:00:00", "water_level": 1.76}, {"timestamp": "2024-09-17T13:00:00", "water_level": 1.76}, {"timestamp": "2024-09-17T14:00:00", "water_level": 1.77}, {"timestamp": "2024-09-17T15:00:00", "water_level": 1.76}, {"timestamp": "2024-09-17T16:00:00", "water_level": 1.76}, {"timestamp": "2024-09-17T17:00:00", "water_level": 1.76}, {"timestamp": "2024-09-17T18:00:00", "water_level": 1.76}, {"timestamp": "2024-09-17T19:00:00", "water_level": 1.76}, {"timestamp": "2024-09-17T20:00:00", "water_level": 1.76}, {"timestamp": "2024-09-17T21:00:00", "water_level": 1.76}, {"timestamp": "2024-09-17T22:00:00", "water_level": 1.76}, {"timestamp": "2024-09-17T23:00:00", "water_level": 1.76}, {"timestamp": "2024-09-18T00:00:00", "water_level": 1.76}, {"timestamp": "2024-09-19T01:00:00", "water_level": 1.72}, {"timestamp": "2024-09-19T02:00:00", "water_level": 1.71}, {"timestamp": "2024-09-19T03:00:00", "water_level": 1.71}, {"timestamp": "2024-09-19T04:00:00", "water_level": 1.7}, {"timestamp": "2024-09-19T05:00:00", "water_level": 1.7}, {"timestamp": "2024-09-19T06:00:00", "water_level": 1.69}, {"timestamp": "2024-09-19T07:00:00", "water_level": 1.68}, {"timestamp": "2024-09-19T08:00:00", "water_level": 1.67}, {"timestamp": "2024-09-19T09:00:00", "water_level": 1.66}, {"timestamp": "2024-09-19T10:00:00", "water_level": 1.66}, {"timestamp": "2024-09-19T11:00:00", "water_level": 1.66}, {"timestamp": "2024-09-19T12:00:00", "water_level": 1.66}, {"timestamp": "2024-09-19T13:00:00", "water_level": 1.66}, {"timestamp": "2024-09-19T14:00:00", "water_level": 1.66}, {"timestamp": "2024-09-19T15:00:00", "water_level": 1.66}, {"timestamp": "2024-09-19T16:00:00", "water_level": 1.66}, {"timestamp": "2024-09-19T17:00:00", "water_level": 1.66}, {"timestamp": "2024-09-19T18:00:00", "water_level": 1.65}, {"timestamp": "2024-09-19T19:00:00", "water_level": 1.65}, {"timestamp": "2024-09-19T20:00:00", "water_level": 1.64}, {"timestamp": "2024-09-19T21:00:00", "water_level": 1.64}, {"timestamp": "2024-09-19T22:00:00", "water_level": 1.63}, {"timestamp": "2024-09-19T23:00:00", "water_level": 1.63}, {"timestamp": "2024-09-20T00:00:00", "water_level": 1.63}, {"timestamp": "2024-09-21T01:00:00", "water_level": 1.74}, {"timestamp": "2024-09-21T02:00:00", "water_level": 1.75}, {"timestamp": "2024-09-21T03:00:00", "water_level": 1.75}, {"timestamp": "2024-09-21T04:00:00", "water_level": 1.75}, {"timestamp": "2024-09-21T05:00:00", "water_level": 1.74}, {"timestamp": "2024-09-21T06:00:00", "water_level": 1.74}, {"timestamp": "2024-09-21T07:00:00", "water_level": 1.74}, {"timestamp": "2024-09-21T08:00:00", "water_level": 1.73}, {"timestamp": "2024-09-21T09:00:00", "water_level": 1.72}, {"timestamp": "2024-09-21T10:00:00", "water_level": 1.71}, {"timestamp": "2024-09-21T11:00:00", "water_level": 1.71}, {"timestamp": "2024-09-21T12:00:00", "water_level": 1.71}, {"timestamp": "2024-09-21T13:00:00", "water_leLine truncated