diff --git a/src/world_intel_mcp/dashboard/index.html b/src/world_intel_mcp/dashboard/index.html
index c3f696a..9876f10 100644
--- a/src/world_intel_mcp/dashboard/index.html
+++ b/src/world_intel_mcp/dashboard/index.html
@@ -8,7 +8,9 @@
+
+
@@ -627,17 +643,29 @@ function initMap() {
map = L.map('map', { zoomControl: false, attributionControl: false, worldCopyJump: true }).setView([20, 0], 2.5);
L.control.zoom({ position: 'bottomleft' }).addTo(map);
L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', { maxZoom: 18 }).addTo(map);
+ var clusterOpts = function(cls, size) {
+ return {
+ maxClusterRadius: size || 40,
+ spiderfyOnMaxZoom: true,
+ showCoverageOnHover: false,
+ iconCreateFunction: function(cluster) {
+ var n = cluster.getChildCount();
+ var sz = n < 50 ? 'small' : n < 200 ? 'medium' : 'large';
+ return L.divIcon({ html: '' + n + '
', className: 'marker-cluster marker-cluster-' + sz + (cls ? ' ' + cls : ''), iconSize: L.point(38, 38) });
+ }
+ };
+ };
mapLayers.quakes = L.layerGroup().addTo(map);
- mapLayers.military = L.layerGroup().addTo(map);
- mapLayers.conflict = L.layerGroup().addTo(map);
- mapLayers.fires = L.layerGroup().addTo(map);
+ mapLayers.military = L.markerClusterGroup(clusterOpts('cluster-military', 50)).addTo(map);
+ mapLayers.conflict = L.markerClusterGroup(clusterOpts('cluster-conflict', 45)).addTo(map);
+ mapLayers.fires = L.markerClusterGroup(clusterOpts('cluster-fire', 45)).addTo(map);
mapLayers.convergence = L.layerGroup().addTo(map);
mapLayers.nuclear = L.layerGroup().addTo(map);
mapLayers.infra = L.layerGroup().addTo(map);
mapLayers.exposure = L.layerGroup().addTo(map);
- mapLayers.airtraffic = L.layerGroup().addTo(map);
+ mapLayers.airtraffic = L.markerClusterGroup(clusterOpts(null, 60)).addTo(map);
mapLayers.traffic = L.layerGroup().addTo(map);
- mapLayers.navwarnings = L.layerGroup().addTo(map);
+ mapLayers.navwarnings = L.markerClusterGroup(clusterOpts(null, 40)).addTo(map);
mapLayers.webcams = L.layerGroup().addTo(map);
}
@@ -1105,7 +1133,17 @@ document.addEventListener('click', function(e) {
});
document.addEventListener('keydown', function(e) {
- if (e.key === 'Escape') { closeDetail(); }
+ if (e.key === 'Escape') {
+ closeDetail();
+ var dr = document.getElementById('drawer');
+ if (dr) dr.classList.remove('open');
+ }
+ if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
+ if (e.key === 'd' || e.key === 'D') {
+ var dr = document.getElementById('drawer');
+ if (dr) dr.classList.toggle('open');
+ }
+ if (e.key === 'r' || e.key === 'R') { map.setView([20, 0], 2.5); }
});
// ════════════ MAP MARKER MANAGEMENT ════════════
@@ -1334,23 +1372,16 @@ function updateMapInfra(data) {
});
// Submarine cable corridors (shaded rectangles)
var cables = (data.cable_corridors && data.cable_corridors.corridors) || [];
- var cableStyle = { color: '#4da8ff', weight: 1, opacity: 0.2, fillColor: '#4da8ff', fillOpacity: 0.04, dashArray: '4 3', className: 'cable-corridor' };
+ var cableStyle = { color: '#4da8ff', weight: 1, opacity: 0.2, fillColor: '#4da8ff', fillOpacity: 0.04, dashArray: '4 3', className: 'cable-corridor', interactive: false };
cables.forEach(function(c) {
if (!c.lat_range || !c.lon_range) return;
total++;
- var tip = '' + esc(c.name.replace(/_/g, ' ')) + '
' + (c.cables || []).join(', ') + '';
// Handle antimeridian crossing (lon_start > lon_end means wrapping past 180)
if (c.lon_range[0] > c.lon_range[1]) {
- var r1 = L.rectangle([[c.lat_range[0], c.lon_range[0]], [c.lat_range[1], 180]], cableStyle);
- var r2 = L.rectangle([[c.lat_range[0], -180], [c.lat_range[1], c.lon_range[1]]], cableStyle);
- r1.bindTooltip(tip, { className: 'mk-tip', sticky: true });
- r2.bindTooltip(tip, { className: 'mk-tip', sticky: true });
- r1.addTo(mapLayers.infra);
- r2.addTo(mapLayers.infra);
+ L.rectangle([[c.lat_range[0], c.lon_range[0]], [c.lat_range[1], 180]], cableStyle).addTo(mapLayers.infra);
+ L.rectangle([[c.lat_range[0], -180], [c.lat_range[1], c.lon_range[1]]], cableStyle).addTo(mapLayers.infra);
} else {
- var rect = L.rectangle([[c.lat_range[0], c.lon_range[0]], [c.lat_range[1], c.lon_range[1]]], cableStyle);
- rect.bindTooltip(tip, { className: 'mk-tip', sticky: true });
- rect.addTo(mapLayers.infra);
+ L.rectangle([[c.lat_range[0], c.lon_range[0]], [c.lat_range[1], c.lon_range[1]]], cableStyle).addTo(mapLayers.infra);
}
});
$('#cntInfra').textContent = total;
@@ -1393,6 +1424,7 @@ function updateMapAirTraffic(data) {
if (!data || data.error) { $('#cntAirTraffic').textContent = '0'; return; }
var positions = data.positions || [];
$('#cntAirTraffic').textContent = data.total_aircraft || positions.length;
+ var markers = [];
positions.forEach(function(p) {
if (p.lat == null || p.lon == null) return;
var mk = L.marker([p.lat, p.lon], {
@@ -1402,8 +1434,9 @@ function updateMapAirTraffic(data) {
if (p.alt) tip += ' @ ' + p.alt + 'm';
mk.bindTooltip(tip, { className: 'mk-tip', direction: 'top', offset: [0, -3] });
mk.on('click', function() { showDetail('airtraffic', p); });
- mk.addTo(mapLayers.airtraffic);
+ markers.push(mk);
});
+ mapLayers.airtraffic.addLayers(markers);
}
function updateMapTraffic(data) {