feat: flooded zones render as water and auto-surface
CI/CD Pipeline - Northern Thailand Ping River Monitor / Test Suite (3.11) (push) Failing after 23s
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 16s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Cleanup (push) Successful in 0s

When P.1's current level reaches a zone's trigger level the zone fills
water-blue (solid border, .62 opacity) instead of the amber risk ramp,
and the zone layer adds itself to the map automatically - no toggle
needed during an actual flood. A manual hide sets a session flag so
auto-show does not fight the user.
This commit is contained in:
2026-08-10 17:22:27 +07:00
parent 711629682d
commit c62a03e778
+31 -14
View File
@@ -627,12 +627,16 @@
const st = zoneStage(zone); const st = zoneStage(zone);
const p = st ? st.p_exceed : 0; const p = st ? st.p_exceed : 0;
const flooded = state.p1Now != null && state.p1Now >= level; const flooded = state.p1Now != null && state.p1Now >= level;
if (flooded) {
// river has reached this zone's trigger level: render as water
return { color: '#1b5e88', weight: 2, dashArray: null, fillColor: '#2f86c8', fillOpacity: .62 };
}
return { return {
color: flooded ? '#7a1512' : '#8a5a2b', color: '#8a5a2b',
weight: flooded ? 2.5 : 1.2, weight: 1.2,
dashArray: flooded ? null : '4 4', dashArray: '4 4',
fillColor: ZONE_COLORS[zone] || '#f9e3a1', fillColor: ZONE_COLORS[zone] || '#f9e3a1',
fillOpacity: flooded ? .72 : .38 + .3 * Math.min(1, p), fillOpacity: .38 + .3 * Math.min(1, p),
}; };
} }
@@ -650,15 +654,9 @@
${flooded ? '<div class="popup-time" style="color:#8d2f22;font-weight:800">River is at or above this zones level now</div>' : ''}</div>`; ${flooded ? '<div class="popup-time" style="color:#8d2f22;font-weight:800">River is at or above this zones level now</div>' : ''}</div>`;
} }
async function toggleFloodZones() { async function showFloodZones(fly) {
if (!state.map) return; if (!state.map || zoneLayer) return;
const button = $('zones-toggle'); const button = $('zones-toggle');
if (zoneLayer) {
state.map.removeLayer(zoneLayer);
zoneLayer = null;
if (button) button.textContent = 'Show flood zones on map';
return;
}
try { try {
if (!zoneData) { if (!zoneData) {
const response = await fetch('/static/flood-zones-p1.geojson'); const response = await fetch('/static/flood-zones-p1.geojson');
@@ -668,16 +666,35 @@
zoneLayer = L.geoJSON(zoneData, { style: zoneStyle }) zoneLayer = L.geoJSON(zoneData, { style: zoneStyle })
.bindPopup((layer) => zonePopupHtml(layer.feature)) .bindPopup((layer) => zonePopupHtml(layer.feature))
.addTo(state.map); .addTo(state.map);
state.map.flyToBounds(zoneLayer.getBounds(), { maxZoom: 13, duration: .8 });
if (button) button.textContent = 'Hide flood zones'; if (button) button.textContent = 'Hide flood zones';
document.getElementById('station-map').scrollIntoView({ behavior: 'smooth', block: 'nearest' }); if (fly) {
state.map.flyToBounds(zoneLayer.getBounds(), { maxZoom: 13, duration: .8 });
document.getElementById('station-map').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
} catch (error) { } catch (error) {
if (button) button.textContent = 'Flood zones unavailable'; if (button) button.textContent = 'Flood zones unavailable';
} }
} }
function toggleFloodZones() {
if (zoneLayer) {
state.map.removeLayer(zoneLayer);
zoneLayer = null;
state.zonesUserHidden = true; // don't auto-show again this session
const button = $('zones-toggle');
if (button) button.textContent = 'Show flood zones on map';
} else {
state.zonesUserHidden = false;
showFloodZones(true);
}
}
function restyleFloodZones() { function restyleFloodZones() {
if (zoneLayer) zoneLayer.setStyle(zoneStyle); if (zoneLayer) zoneLayer.setStyle(zoneStyle);
// Flooding in progress: surface the zones even if the layer is off,
// unless the user explicitly hid it.
const anyFlooded = state.p1Now != null && (state.p1Stages || []).some((s) => state.p1Now >= s.level);
if (anyFlooded && !zoneLayer && !state.zonesUserHidden) showFloodZones(false);
} }
function pointInRing(lat, lon, ring) { function pointInRing(lat, lon, ring) {