feat: waterway diamonds, cable corridor zones, cache resilience

- Render 9 strategic waterway chokepoints as cyan diamond markers
  (Hormuz, Malacca, Suez, Panama, Bab-el-Mandeb, Taiwan, Gibraltar,
  GIUK Gap, Bosphorus) with throughput tooltips
- Render 6 submarine cable corridors as dashed blue rectangles
  (transatlantic N/S, transpacific, asia-europe, red sea, med)
- Add waterways + cable_corridors to /api/static and SSE payloads
- Make cache.set() and evict_expired() swallow sqlite3 write errors
  instead of crashing callers (fixes "readonly database" in dashboard)
- Infrastructure layer: 134 → 173 items

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Marc Shade
2026-02-24 10:37:03 -05:00
co-authored by Claude Opus 4.6
parent 95b149ad55
commit 71fbb1cda4
3 changed files with 76 additions and 15 deletions
+35 -2
View File
@@ -393,6 +393,14 @@ a { color: var(--accent); text-decoration: none; }
.mk-nuke-fac.hot { animation: glow-gold 2s ease-in-out infinite; }
.pipeline-line { cursor: pointer; transition: opacity 0.15s; }
.pipeline-line:hover { opacity: 1 !important; stroke-width: 3 !important; }
.mk-waterway {
width: 10px; height: 10px; background: var(--accent); opacity: 0.8;
transform: rotate(45deg); border: 1.5px solid rgba(0,229,255,0.6);
cursor: pointer; transition: transform 0.15s, opacity 0.15s;
}
.mk-waterway:hover { transform: rotate(45deg) scale(1.5); opacity: 1; }
.cable-corridor { cursor: pointer; transition: fill-opacity 0.15s; }
.cable-corridor:hover { fill-opacity: 0.1 !important; stroke-opacity: 0.5 !important; }
@keyframes glow-gold {
0%,100% { box-shadow: 0 0 4px rgba(240,184,64,0.3); }
50% { box-shadow: 0 0 16px rgba(240,184,64,0.7); }
@@ -597,8 +605,8 @@ $('#drawerToggle').addEventListener('click', function() {
});
// ════════════ DETAIL MODAL ════════════
var COLORS = { earthquake: 'var(--red)', military: 'var(--blue)', conflict: 'var(--amber)', fire: 'var(--gold)', convergence: 'var(--purple)', nuclear: 'var(--green)', military_base: 'var(--teal)', port: 'var(--blue)', nuclear_facility: 'var(--gold)', pipeline: 'var(--purple)', exposure: '#e040fb' };
var LABELS = { earthquake: 'EARTHQUAKE', military: 'MILITARY AIRCRAFT', conflict: 'CONFLICT EVENT', fire: 'WILDFIRE CLUSTER', convergence: 'SIGNAL CONVERGENCE', nuclear: 'NUCLEAR MONITOR', military_base: 'MILITARY BASE', port: 'STRATEGIC PORT', nuclear_facility: 'NUCLEAR FACILITY', pipeline: 'PIPELINE', exposure: 'POPULATION EXPOSURE' };
var COLORS = { earthquake: 'var(--red)', military: 'var(--blue)', conflict: 'var(--amber)', fire: 'var(--gold)', convergence: 'var(--purple)', nuclear: 'var(--green)', military_base: 'var(--teal)', port: 'var(--blue)', nuclear_facility: 'var(--gold)', pipeline: 'var(--purple)', exposure: '#e040fb', waterway: 'var(--accent)', cable: 'var(--blue)' };
var LABELS = { earthquake: 'EARTHQUAKE', military: 'MILITARY AIRCRAFT', conflict: 'CONFLICT EVENT', fire: 'WILDFIRE CLUSTER', convergence: 'SIGNAL CONVERGENCE', nuclear: 'NUCLEAR MONITOR', military_base: 'MILITARY BASE', port: 'STRATEGIC PORT', nuclear_facility: 'NUCLEAR FACILITY', pipeline: 'PIPELINE', exposure: 'POPULATION EXPOSURE', waterway: 'STRATEGIC WATERWAY', cable: 'CABLE CORRIDOR' };
function showDetail(type, d) {
var title = '', subtitle = '', fields = [], link = '';
@@ -1155,6 +1163,31 @@ function updateMapInfra(data) {
});
line.addTo(mapLayers.infra);
});
// Strategic waterways (diamond markers at chokepoints)
var wws = (data.waterways && data.waterways.waterways) || [];
wws.forEach(function(w) {
if (w.lat == null || w.lon == null) return;
total++;
var mk = L.marker([w.lat, w.lon], {
icon: L.divIcon({ className: 'mk-waterway', iconSize: [10, 10], iconAnchor: [5, 5] })
});
mk.bindTooltip('<b>' + esc(w.name) + '</b><br><span style="opacity:0.7">' + esc(w.throughput || '') + '</span>', { className: 'mk-tip', direction: 'top', offset: [0, -6] });
mk.addTo(mapLayers.infra);
});
// Submarine cable corridors (shaded rectangles)
var cables = (data.cable_corridors && data.cable_corridors.corridors) || [];
cables.forEach(function(c) {
if (!c.lat_range || !c.lon_range) return;
total++;
var bounds = [[c.lat_range[0], c.lon_range[0]], [c.lat_range[1], c.lon_range[1]]];
var rect = L.rectangle(bounds, {
color: '#4da8ff', weight: 1, opacity: 0.2,
fillColor: '#4da8ff', fillOpacity: 0.04,
dashArray: '4 3', className: 'cable-corridor'
});
rect.bindTooltip('<b>' + esc(c.name.replace(/_/g, ' ')) + '</b><br><span style="opacity:0.7">' + (c.cables || []).join(', ') + '</span>', { className: 'mk-tip', sticky: true });
rect.addTo(mapLayers.infra);
});
$('#cntInfra').textContent = total;
}