osiris-ai-manu / src /components /OsirisMap.tsx
Claude
Add resource intelligence layer: mining/RAM datasets, macro & utilities APIs, map layers
5aeda2b unverified
Raw
History Blame Contribute Delete
104 kB
'use client';
import { useEffect, useRef, useState, useCallback, memo } from 'react';
import maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';
interface OsirisMapProps {
data: any;
activeLayers: Record<string, boolean>;
onEntityClick?: (entity: any) => void;
onMouseCoords?: (coords: { lat: number; lng: number }) => void;
onRightClick?: (coords: { lat: number; lng: number }) => void;
onViewStateChange?: (vs: { zoom: number; latitude: number }) => void;
flyToLocation?: { lat: number; lng: number; zoom?: number; ts: number } | null;
projection?: 'mercator' | 'globe';
mapStyle?: string;
sweepData?: any;
scanTargets?: any[];
demoMode?: boolean;
theme?: 'core' | 'ghost';
}
function computeSolarTerminator(): [number, number][] {
const now = new Date();
const dayOfYear = Math.floor((now.getTime() - new Date(now.getFullYear(), 0, 0).getTime()) / 86400000);
const declination = -23.44 * Math.cos((2 * Math.PI / 365) * (dayOfYear + 10));
const decRad = declination * Math.PI / 180;
const utcHours = now.getUTCHours() + now.getUTCMinutes() / 60;
const subsolarLng = (12 - utcHours) * 15;
const points: [number, number][] = [];
for (let lng = -180; lng <= 180; lng += 2) {
const lngRad = (lng - subsolarLng) * Math.PI / 180;
const lat = Math.atan(-Math.cos(lngRad) / Math.tan(decRad)) * 180 / Math.PI;
points.push([lng, lat]);
}
const darkSide = declination >= 0 ? -90 : 90;
points.push([180, darkSide]);
points.push([-180, darkSide]);
points.push(points[0]);
return points;
}
const EMPTY_FC = { type: 'FeatureCollection' as const, features: [] };
function OsirisMap({ data, activeLayers, onEntityClick, onMouseCoords, onRightClick, onViewStateChange, flyToLocation, projection = 'globe', mapStyle = 'dark', sweepData, scanTargets = [], demoMode = false, theme = 'core' }: OsirisMapProps) {
const containerRef = useRef<HTMLDivElement>(null);
const mapRef = useRef<maplibregl.Map | null>(null);
const popupRef = useRef<maplibregl.Popup | null>(null);
const [mapReady, setMapReady] = useState(false);
const prevStyleRef = useRef(mapStyle);
// Create aircraft icon on canvas (for WebGL symbol layer)
const createIcon = useCallback((map: maplibregl.Map, id: string, color: string, size: number) => {
if (map.hasImage(id)) return;
const canvas = document.createElement('canvas');
canvas.width = size; canvas.height = size;
const ctx = canvas.getContext('2d')!;
const cx = size / 2, cy = size / 2;
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(cx, cy - size * 0.4);
ctx.lineTo(cx - size * 0.12, cy + size * 0.1);
ctx.lineTo(cx - size * 0.4, cy + size * 0.2);
ctx.lineTo(cx - size * 0.4, cy + size * 0.3);
ctx.lineTo(cx - size * 0.12, cy + size * 0.15);
ctx.lineTo(cx, cy + size * 0.35);
ctx.lineTo(cx + size * 0.12, cy + size * 0.15);
ctx.lineTo(cx + size * 0.4, cy + size * 0.3);
ctx.lineTo(cx + size * 0.4, cy + size * 0.2);
ctx.lineTo(cx + size * 0.12, cy + size * 0.1);
ctx.closePath();
ctx.fill();
map.addImage(id, { width: size, height: size, data: new Uint8Array(ctx.getImageData(0, 0, size, size).data) });
}, []);
const createDot = useCallback((map: maplibregl.Map, id: string, color: string, size: number) => {
if (map.hasImage(id)) return;
const canvas = document.createElement('canvas');
canvas.width = size; canvas.height = size;
const ctx = canvas.getContext('2d')!;
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(size/2, size/2, size/2 - 1, 0, Math.PI * 2);
ctx.fill();
map.addImage(id, { width: size, height: size, data: new Uint8Array(ctx.getImageData(0, 0, size, size).data) });
}, []);
useEffect(() => {
if (!mapRef.current) return;
const map = mapRef.current;
// ── DEMO MODE SPINNING ──
let spinReq: number | undefined = undefined;
let isSpinning = false;
const startSpinning = () => {
if (!map) return;
isSpinning = true;
let lastTime = performance.now();
const frame = (time: number) => {
if (!isSpinning) return;
// Only spin if the user is not actively dragging or zooming the map
if (!map.isMoving() && !map.isZooming()) {
const dt = time - lastTime;
const center = map.getCenter();
// Adjust spin speed: 0.5 degrees per second
center.lng += (0.5 * dt) / 1000;
map.setCenter(center);
}
lastTime = time;
spinReq = requestAnimationFrame(frame);
};
spinReq = requestAnimationFrame(frame);
};
if (demoMode) {
startSpinning();
} else {
isSpinning = false;
if (spinReq) cancelAnimationFrame(spinReq);
}
return () => {
isSpinning = false;
if (spinReq) cancelAnimationFrame(spinReq);
if (typeof window !== 'undefined' && (window as any)._globeSpinTimer) {
clearInterval((window as any)._globeSpinTimer);
}
};
}, [mapReady, demoMode]);
useEffect(() => {
if (!containerRef.current || mapRef.current) return;
// Select basemap style
const styleUrl = 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json';
const map = new maplibregl.Map({
container: containerRef.current,
style: styleUrl,
center: [25.48, 42.70], zoom: 6.5, minZoom: 1.5, maxZoom: 18,
attributionControl: false,
maxPitch: 85,
transformRequest: (url: string) => {
// Route all CARTO CDN requests through the internal Next.js proxy API
if (url.includes('cartocdn.com')) {
const baseUrl = typeof window !== 'undefined' ? window.location.origin : '';
return { url: `${baseUrl}/api/proxy-tiles?url=${encodeURIComponent(url)}` };
}
return { url };
},
});
map.on('load', () => {
mapRef.current = map;
// Theme colors
const isGhost = theme === 'ghost';
const phantomPurple = '#B388FF';
const phantomDark = '#1A0040';
const cameraColor = isGhost ? '#B388FF' : '#00E676';
const flightCom = isGhost ? phantomPurple : '#00E5FF';
const flightPriv = isGhost ? phantomPurple : '#FFD700';
const flightGov = isGhost ? phantomPurple : '#FF9500';
const flightMil = isGhost ? phantomPurple : '#FF3D3D';
// Create icons — OSIRIS Unified Palette
createIcon(map, 'plane-cyan', flightCom, 24);
createIcon(map, 'plane-green', flightPriv, 24);
createIcon(map, 'plane-pink', flightGov, 24);
createIcon(map, 'plane-red', flightMil, 24);
createIcon(map, 'plane-grey', isGhost ? phantomPurple : '#546E7A', 24);
createDot(map, 'dot-gold', isGhost ? phantomPurple : '#D4AF37', 8);
createDot(map, 'dot-red', isGhost ? phantomPurple : '#D32F2F', 10);
createDot(map, 'dot-orange', isGhost ? phantomPurple : '#E65100', 10);
createDot(map, 'dot-green', isGhost ? phantomPurple : '#26A69A', 10);
createDot(map, 'dot-fire', isGhost ? phantomPurple : '#E65100', 10);
createDot(map, 'dot-cctv', cameraColor, 10);
const sources = ['flights','military','jets','private-fl','satellites','earthquakes','gdelt','gps-jamming','day-night','cctv','fires','weather','infrastructure','maritime','maritime-choke','maritime-ships','live-news','sigint-news','conflict-zones', 'war-alerts-targets', 'war-alerts-lines', 'balloons', 'radiation', 'ip-sweep-devices', 'ip-sweep-pulse', 'ip-sweep-connections', 'scan-targets', 'sdk-entities', 'sdk-links', 'malware-nodes', 'network-mesh', 'mining-sites', 'ram-sites'];
sources.forEach(s => map.addSource(s, { type: 'geojson', data: EMPTY_FC }));
// Warning icon generator (parameterized — eliminates 3x copy-paste)
const createWarningIcon = (id: string, color: string) => {
const s = 20;
const c = document.createElement('canvas');
c.width = s; c.height = s;
const ctx = c.getContext('2d')!;
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(s/2, 1);
ctx.lineTo(s - 1, s - 1);
ctx.lineTo(1, s - 1);
ctx.closePath();
ctx.fill();
ctx.fillStyle = '#000';
ctx.font = 'bold 11px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('!', s/2, s - 4);
map.addImage(id, { width: s, height: s, data: new Uint8Array(ctx.getImageData(0, 0, s, s).data) });
};
createWarningIcon('warn-icon', '#D32F2F');
createWarningIcon('warn-orange', '#E65100');
createWarningIcon('warn-yellow', '#F9A825');
map.addLayer({ id: 'conflict-icons', type: 'symbol', source: 'conflict-zones', layout: {
'icon-image': ['match', ['get','severity'], 'war','warn-icon', 'high','warn-orange', 'warn-yellow'],
'icon-size': ['interpolate',['linear'],['zoom'], 1,0.6, 4,0.8, 8,1],
'icon-allow-overlap': true,
'text-field': ['get','label'],
'text-size': ['interpolate',['linear'],['zoom'], 1,7, 4,9, 8,11],
'text-font': ['Open Sans Bold'],
'text-offset': [0, 1.4],
'text-allow-overlap': false,
}, paint: {
'text-color': ['match', ['get','severity'], 'war','#D32F2F', 'high','#E65100', '#F9A825'],
'text-halo-color': '#000', 'text-halo-width': 1.5, 'text-opacity': 0.9,
}});
// Day/Night
map.addLayer({ id: 'day-night-fill', type: 'fill', source: 'day-night', paint: { 'fill-color': isGhost ? '#0D0030' : '#000022', 'fill-opacity': 0.35 }});
// Earthquakes — amber threat spectrum
map.addLayer({ id: 'eq-circles', type: 'circle', source: 'earthquakes', paint: {
'circle-radius': ['interpolate',['linear'],['get','magnitude'], 2.5,4, 5,12, 7,24],
'circle-color': ['interpolate',['linear'],['get','magnitude'], 2.5,'#F9A825', 4,'#E65100', 6,'#D32F2F'],
'circle-opacity': 0.55, 'circle-blur': 0.3, 'circle-stroke-width': 1, 'circle-stroke-color': '#F9A825', 'circle-stroke-opacity': 0.25,
}});
map.addLayer({ id: 'eq-label', type: 'symbol', source: 'earthquakes', filter: ['>=',['get','magnitude'],4.5], layout: {
'text-field': ['concat','M',['to-string',['get','magnitude']]], 'text-size': 9, 'text-font': ['Open Sans Regular'], 'text-offset': [0,1.5],
}, paint: { 'text-color': '#F9A825', 'text-halo-color': '#000', 'text-halo-width': 1 }});
// Fires — burnt sienna
map.addLayer({ id: 'fires-heat', type: 'circle', source: 'fires', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,2, 5,4, 10,8],
'circle-color': '#E65100', 'circle-opacity': 0.45, 'circle-blur': 0.5,
}});
// CCTV — outer glow ring (black/white depending on theme)
map.addLayer({ id: 'cctv-glow', type: 'circle', source: 'cctv', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,5, 5,8, 10,14, 14,20],
'circle-color': '#000000', 'circle-opacity': 0.35, 'circle-blur': 1,
}});
// CCTV — main dot
map.addLayer({ id: 'cctv-dots', type: 'circle', source: 'cctv', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,3, 5,5, 10,8, 14,12],
'circle-color': cameraColor, 'circle-opacity': 0.9,
'circle-stroke-width': 2.5, 'circle-stroke-color': '#000000', 'circle-stroke-opacity': 0.9,
}});
// CCTV — labels at zoom 10+
map.addLayer({ id: 'cctv-label', type: 'symbol', source: 'cctv', minzoom: 10, layout: {
'text-field': ['get','name'], 'text-size': 9, 'text-font': ['Open Sans Regular'],
'text-offset': [0, 1.8], 'text-max-width': 12, 'text-allow-overlap': false,
}, paint: { 'text-color': cameraColor, 'text-halo-color': '#000000', 'text-halo-width': 1.5, 'text-opacity': 0.8 }});
// GDELT
// ══ NETWORK INTEL — Live Malware (abuse.ch) — crimson threat ══
map.addLayer({ id: 'malware-glow', type: 'circle', source: 'malware-nodes', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,6, 5,12, 10,20],
'circle-color': '#D32F2F', 'circle-opacity': 0.06, 'circle-blur': 0.5,
}});
map.addLayer({ id: 'malware-dots', type: 'circle', source: 'malware-nodes', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,2, 5,4, 10,6],
'circle-color': '#D32F2F',
'circle-opacity': 0.9,
'circle-stroke-width': 1, 'circle-stroke-color': '#000000', 'circle-stroke-opacity': 0.8,
}});
map.addLayer({ id: 'malware-label', type: 'symbol', source: 'malware-nodes', minzoom: 5, layout: {
'text-field': ['get','malware'], 'text-size': 8, 'text-font': ['JetBrains Mono Bold', 'Open Sans Bold'],
'text-offset': [0, 1.5], 'text-max-width': 10, 'text-allow-overlap': false,
}, paint: { 'text-color': '#D32F2F', 'text-halo-color': '#111', 'text-halo-width': 1.5, 'text-opacity': 0.85 }});
// ── NETWORK INTEL MESH (SDK STYLE) ──
map.addLayer({ id: 'network-mesh-atmo', type: 'line', source: 'network-mesh', paint: {
'line-width': ['interpolate',['linear'],['zoom'], 1, 2, 5, 4, 10, 8],
'line-opacity': 0.08,
'line-blur': 4,
}});
map.addLayer({ id: 'network-mesh-glow', type: 'line', source: 'network-mesh', paint: {
'line-width': ['interpolate',['linear'],['zoom'], 1, 1, 5, 2, 10, 4],
'line-opacity': 0.2,
'line-blur': 1.5,
}});
map.addLayer({ id: 'network-mesh-core', type: 'line', source: 'network-mesh', paint: {
'line-width': ['interpolate',['linear'],['zoom'], 1, 0.2, 5, 0.5, 10, 1.5],
'line-opacity': 0.4,
}});
map.addLayer({ id: 'gdelt-dots', type: 'circle', source: 'gdelt', paint: {
'circle-radius': 4, 'circle-color': '#D32F2F', 'circle-opacity': 0.5, 'circle-stroke-width': 1, 'circle-stroke-color': '#D32F2F', 'circle-stroke-opacity': 0.25,
}});
// GPS Jamming — crimson
map.addLayer({ id: 'jam-fill', type: 'circle', source: 'gps-jamming', paint: { 'circle-radius': 30, 'circle-color': '#D32F2F', 'circle-opacity': 0.12, 'circle-blur': 1 }});
map.addLayer({ id: 'jam-label', type: 'symbol', source: 'gps-jamming', layout: {
'text-field': ['concat','GPS JAM ',['to-string',['get','severity']],'%'], 'text-size': 10, 'text-font': ['Open Sans Bold'], 'text-allow-overlap': true,
}, paint: { 'text-color': '#D32F2F', 'text-halo-color': '#000', 'text-halo-width': 1 }});
// Weather Events (NASA EONET) — deep violet
map.addLayer({ id: 'weather-glow', type: 'circle', source: 'weather', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,12, 5,20, 10,30],
'circle-color': '#7E57C2', 'circle-opacity': 0.08, 'circle-blur': 1,
}});
map.addLayer({ id: 'weather-dots', type: 'circle', source: 'weather', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,5, 5,8, 10,14],
'circle-color': ['match', ['get','icon'], 'cyclone','#7E57C2', 'volcano','#D32F2F', '#7E57C2'],
'circle-opacity': 0.75,
'circle-stroke-width': 1.5, 'circle-stroke-color': '#7E57C2', 'circle-stroke-opacity': 0.35,
}});
map.addLayer({ id: 'weather-label', type: 'symbol', source: 'weather', layout: {
'text-field': ['get','title'], 'text-size': 9, 'text-font': ['Open Sans Regular'],
'text-offset': [0, 2], 'text-max-width': 14, 'text-allow-overlap': false,
}, paint: { 'text-color': '#7E57C2', 'text-halo-color': '#000', 'text-halo-width': 1, 'text-opacity': 0.8 }});
// Nuclear Infrastructure — teal / amber risk
map.addLayer({ id: 'infra-glow', type: 'circle', source: 'infrastructure', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,8, 5,14, 10,22],
'circle-color': ['case', ['in', 'SEISMIC RISK', ['get', 'status']], '#E65100', '#26A69A'],
'circle-opacity': 0.08, 'circle-blur': 1,
}});
map.addLayer({ id: 'infra-dots', type: 'circle', source: 'infrastructure', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,4, 5,6, 10,10],
'circle-color': ['case',
['in', 'SEISMIC RISK', ['get', 'status']], '#E65100',
['==', ['get','status'], 'Active Conflict Zone'], '#D32F2F',
['==', ['get','status'], 'Destroyed / Decommissioning'], '#546E7A',
'#26A69A'
],
'circle-opacity': 0.75,
'circle-stroke-width': 1.5, 'circle-stroke-color': ['case', ['in', 'SEISMIC RISK', ['get', 'status']], '#E65100', '#26A69A'], 'circle-stroke-opacity': 0.35,
}});
map.addLayer({ id: 'infra-label', type: 'symbol', source: 'infrastructure', minzoom: 5, layout: {
'text-field': ['get','name'], 'text-size': 9, 'text-font': ['Open Sans Regular'],
'text-offset': [0, 2], 'text-max-width': 14, 'text-allow-overlap': false,
}, paint: { 'text-color': ['case', ['in', 'SEISMIC RISK', ['get', 'status']], '#E65100', '#26A69A'], 'text-halo-color': '#000', 'text-halo-width': 1, 'text-opacity': 0.7 }});
// Mining Companies — colored by commodity category
const miningColor: any = ['match', ['get','category'],
'copper','#FF8A65', 'gold','#FFD54F', 'silver','#CFD8DC',
'pgm','#4DB6AC', 'rare_earth','#BA68C8', '#FFB74D'];
map.addLayer({ id: 'mining-glow', type: 'circle', source: 'mining-sites', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,8, 5,14, 10,22],
'circle-color': miningColor, 'circle-opacity': 0.08, 'circle-blur': 1,
}});
map.addLayer({ id: 'mining-dots', type: 'circle', source: 'mining-sites', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,4, 5,6, 10,10],
'circle-color': miningColor, 'circle-opacity': 0.8,
'circle-stroke-width': 1.5, 'circle-stroke-color': miningColor, 'circle-stroke-opacity': 0.35,
}});
map.addLayer({ id: 'mining-label', type: 'symbol', source: 'mining-sites', minzoom: 4, layout: {
'text-field': ['get','site'], 'text-size': 9, 'text-font': ['Open Sans Regular'],
'text-offset': [0, 2], 'text-max-width': 14, 'text-allow-overlap': false,
}, paint: { 'text-color': miningColor, 'text-halo-color': '#000', 'text-halo-width': 1, 'text-opacity': 0.75 }});
// Memory / RAM Fabs — colored by ecosystem role
const ramColor: any = ['match', ['get','category'],
'dram_hbm','#4FC3F7', 'nand','#81C784', 'controller_ip','#FFF176',
'module_maker','#90A4AE', 'packaging_test','#F06292', 'equipment','#9575CD', '#4FC3F7'];
map.addLayer({ id: 'ram-glow', type: 'circle', source: 'ram-sites', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,8, 5,14, 10,22],
'circle-color': ramColor, 'circle-opacity': 0.08, 'circle-blur': 1,
}});
map.addLayer({ id: 'ram-dots', type: 'circle', source: 'ram-sites', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,4, 5,6, 10,10],
'circle-color': ramColor, 'circle-opacity': 0.8,
'circle-stroke-width': 1.5, 'circle-stroke-color': ramColor, 'circle-stroke-opacity': 0.35,
}});
map.addLayer({ id: 'ram-label', type: 'symbol', source: 'ram-sites', minzoom: 4, layout: {
'text-field': ['get','company'], 'text-size': 9, 'text-font': ['Open Sans Regular'],
'text-offset': [0, 2], 'text-max-width': 14, 'text-allow-overlap': false,
}, paint: { 'text-color': ramColor, 'text-halo-color': '#000', 'text-halo-width': 1, 'text-opacity': 0.75 }});
// Satellites
map.addLayer({ id: 'sat-glow', type: 'circle', source: 'satellites', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,3, 5,6], 'circle-color': ['get','color'], 'circle-opacity': 0.3, 'circle-blur': 1,
}});
map.addLayer({ id: 'sat-dots', type: 'circle', source: 'satellites', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,1.5, 5,3], 'circle-color': ['get','color'], 'circle-opacity': 1.0,
}});
// Maritime — ports & naval bases — ocean teal
map.addLayer({ id: 'maritime-glow', type: 'circle', source: 'maritime', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,6, 5,12, 10,20],
'circle-color': ['match', ['get','type'], 'naval','#D32F2F', 'energy','#E65100', '#26C6DA'],
'circle-opacity': 0.08, 'circle-blur': 1,
}});
map.addLayer({ id: 'maritime-dots', type: 'circle', source: 'maritime', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,3, 5,5, 10,9],
'circle-color': ['match', ['get','type'], 'naval','#D32F2F', 'energy','#E65100', '#26C6DA'],
'circle-opacity': 0.8,
'circle-stroke-width': 1.5, 'circle-stroke-color': ['match', ['get','type'], 'naval','#D32F2F', 'energy','#E65100', '#26C6DA'], 'circle-stroke-opacity': 0.35,
}});
map.addLayer({ id: 'maritime-label', type: 'symbol', source: 'maritime', minzoom: 4, layout: {
'text-field': ['get','name'], 'text-size': 9, 'text-font': ['Open Sans Regular'],
'text-offset': [0, 1.8], 'text-max-width': 12, 'text-allow-overlap': false,
}, paint: { 'text-color': '#26C6DA', 'text-halo-color': '#000', 'text-halo-width': 1, 'text-opacity': 0.7 }});
// Maritime chokepoints — amber threat spectrum
map.addLayer({ id: 'choke-glow', type: 'circle', source: 'maritime-choke', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,10, 5,18, 10,28],
'circle-color': '#E65100', 'circle-opacity': 0.1, 'circle-blur': 1,
}});
map.addLayer({ id: 'choke-dots', type: 'circle', source: 'maritime-choke', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,4, 5,7, 10,12],
'circle-color': ['match', ['get','risk'], 'CRITICAL','#D32F2F', 'HIGH','#E65100', 'ELEVATED','#F9A825', '#26A69A'],
'circle-opacity': 0.85,
'circle-stroke-width': 1.5, 'circle-stroke-color': '#E65100', 'circle-stroke-opacity': 0.4,
}});
map.addLayer({ id: 'choke-label', type: 'symbol', source: 'maritime-choke', minzoom: 3, layout: {
'text-field': ['get','name'], 'text-size': 10, 'text-font': ['Open Sans Bold'],
'text-offset': [0, 2], 'text-max-width': 14, 'text-allow-overlap': false,
}, paint: { 'text-color': '#E65100', 'text-halo-color': '#000', 'text-halo-width': 1, 'text-opacity': 0.9 }});
// Live News — muted rose
map.addLayer({ id: 'news-glow', type: 'circle', source: 'live-news', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,8, 5,14, 10,22],
'circle-color': '#EC407A', 'circle-opacity': 0.08, 'circle-blur': 1,
}});
map.addLayer({ id: 'news-dots', type: 'circle', source: 'live-news', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,4, 5,6, 10,10],
'circle-color': '#EC407A', 'circle-opacity': 0.8,
'circle-stroke-width': 1.5, 'circle-stroke-color': '#EC407A', 'circle-stroke-opacity': 0.4,
}});
map.addLayer({ id: 'news-label', type: 'symbol', source: 'live-news', minzoom: 4, layout: {
'text-field': ['get','name'], 'text-size': 9, 'text-font': ['Open Sans Regular'],
'text-offset': [0, 1.8], 'text-max-width': 12, 'text-allow-overlap': false,
}, paint: { 'text-color': '#EC407A', 'text-halo-color': '#000', 'text-halo-width': 1, 'text-opacity': 0.8 }});
// SIGINT RSS news - gold markers
map.addLayer({ id: 'sigint-news-glow', type: 'circle', source: 'sigint-news', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,6, 5,10, 10,18],
'circle-color': '#D4AF37', 'circle-opacity': 0.12, 'circle-blur': 1,
}});
map.addLayer({ id: 'sigint-news-dots', type: 'circle', source: 'sigint-news', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,3, 5,5, 10,8],
'circle-color': '#D4AF37', 'circle-opacity': 0.9,
'circle-stroke-width': 1.5, 'circle-stroke-color': '#FFF8DC', 'circle-stroke-opacity': 0.6,
}});
map.addLayer({ id: 'sigint-news-label', type: 'symbol', source: 'sigint-news', minzoom: 5, layout: {
'text-field': ['get','source'], 'text-size': 9, 'text-font': ['Open Sans Regular'],
'text-offset': [0, 1.6], 'text-max-width': 10, 'text-allow-overlap': false,
}, paint: { 'text-color': '#D4AF37', 'text-halo-color': '#000', 'text-halo-width': 1, 'text-opacity': 0.85 }});
// ══ IP SWEEP — Neighborhood device visualization ══
map.addLayer({ id: 'sweep-connections', type: 'line', source: 'ip-sweep-connections', paint: {
'line-color': ['get', 'color'], 'line-width': 1, 'line-opacity': 0.3, 'line-dasharray': [2, 4],
}});
map.addLayer({ id: 'sweep-pulse-ring', type: 'circle', source: 'ip-sweep-pulse', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 8,40, 12,80, 16,160],
'circle-color': 'transparent', 'circle-opacity': 0.6,
'circle-stroke-width': 2, 'circle-stroke-color': '#FF3D3D', 'circle-stroke-opacity': 0.4,
}});
map.addLayer({ id: 'sweep-device-glow', type: 'circle', source: 'ip-sweep-devices', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 8,8, 12,16, 16,30],
'circle-color': ['get', 'color'], 'circle-opacity': 0.15, 'circle-blur': 1,
}});
map.addLayer({ id: 'sweep-device-dots', type: 'circle', source: 'ip-sweep-devices', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 8,3, 12,6, 16,10],
'circle-color': ['get', 'color'], 'circle-opacity': 0.95,
'circle-stroke-width': 1.5, 'circle-stroke-color': '#FFFFFF', 'circle-stroke-opacity': 0.6,
}});
map.addLayer({ id: 'sweep-device-labels', type: 'symbol', source: 'ip-sweep-devices', minzoom: 13, layout: {
'text-field': ['concat', ['get', 'device_type'], '\n', ['get', 'ip']],
'text-size': 9, 'text-font': ['Open Sans Regular'],
'text-offset': [0, 2.2], 'text-max-width': 12, 'text-allow-overlap': false,
}, paint: {
'text-color': ['get', 'color'], 'text-halo-color': '#000', 'text-halo-width': 1.5, 'text-opacity': 0.9,
}});
// ══ SCAN TARGETS — Geolocated individual scans ══
map.addLayer({ id: 'scan-targets-glow', type: 'circle', source: 'scan-targets', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,12, 5,25, 10,40],
'circle-color': '#D32F2F', 'circle-opacity': 0.15, 'circle-blur': 1,
}});
map.addLayer({ id: 'scan-targets-dots', type: 'circle', source: 'scan-targets', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,5, 5,8, 10,12],
'circle-color': '#D32F2F', 'circle-opacity': 0.9,
'circle-stroke-width': 1.5, 'circle-stroke-color': '#ECEFF1', 'circle-stroke-opacity': 0.7,
}});
map.addLayer({ id: 'scan-targets-label', type: 'symbol', source: 'scan-targets', layout: {
'text-field': ['get', 'id'], 'text-size': 11, 'text-font': ['Open Sans Bold'],
'text-offset': [0, 2], 'text-max-width': 14, 'text-allow-overlap': false,
}, paint: { 'text-color': '#D32F2F', 'text-halo-color': '#000', 'text-halo-width': 1.5, 'text-opacity': 0.9 }});
// Flight layers (WebGL symbol — GPU rendered, handles 50K+ smooth)
const flightLayers = [
{ id: 'fl-commercial', src: 'flights', icon: 'plane-cyan' },
{ id: 'fl-private', src: 'private-fl', icon: 'plane-green' },
{ id: 'fl-jets', src: 'jets', icon: 'plane-pink' },
{ id: 'fl-military', src: 'military', icon: 'plane-red' },
];
flightLayers.forEach(l => {
map.addLayer({ id: l.id, type: 'symbol', source: l.src, layout: {
'icon-image': l.icon, 'icon-size': ['interpolate',['linear'],['zoom'], 1,0.4, 5,0.7, 10,1],
'icon-rotate': ['get','heading'], 'icon-rotation-alignment': 'map', 'icon-allow-overlap': true, 'icon-ignore-placement': true,
}, paint: { 'icon-opacity': 0.85 }});
});
// Balloons (moving entities)
map.addLayer({ id: 'balloon-dots', type: 'circle', source: 'balloons', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,3, 5,5, 10,7],
'circle-color': ['get', 'color'],
'circle-opacity': 0.8,
'circle-stroke-width': 1, 'circle-stroke-color': '#fff', 'circle-stroke-opacity': 0.5,
}});
map.addLayer({ id: 'balloon-label', type: 'symbol', source: 'balloons', minzoom: 4, layout: {
'text-field': ['get','callsign'], 'text-size': 9, 'text-font': ['Open Sans Regular'],
'text-offset': [0, 1.2], 'text-max-width': 12, 'text-allow-overlap': false,
}, paint: { 'text-color': ['get', 'color'], 'text-halo-color': '#000', 'text-halo-width': 1 }});
// Radiation — violet base, threat spectrum for danger/warning
map.addLayer({ id: 'rad-glow', type: 'circle', source: 'radiation', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,10, 5,20, 10,40],
'circle-color': ['match', ['get','status'], 'DANGER','#D32F2F', 'WARNING','#E65100', '#7E57C2'],
'circle-opacity': 0.12, 'circle-blur': 1,
}});
map.addLayer({ id: 'rad-dots', type: 'circle', source: 'radiation', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,4, 5,6, 10,8],
'circle-color': ['match', ['get','status'], 'DANGER','#D32F2F', 'WARNING','#E65100', '#7E57C2'],
'circle-opacity': 0.85,
'circle-stroke-width': 1.5, 'circle-stroke-color': ['match', ['get','status'], 'DANGER','#D32F2F', 'WARNING','#E65100', '#7E57C2'], 'circle-stroke-opacity': 0.35,
}});
map.addLayer({ id: 'rad-label', type: 'symbol', source: 'radiation', minzoom: 5, layout: {
'text-field': ['concat', ['to-string', ['get','reading']], ' nSv/h'], 'text-size': 9, 'text-font': ['Open Sans Bold'],
'text-offset': [0, 1.5], 'text-allow-overlap': false,
}, paint: { 'text-color': ['match', ['get','status'], 'DANGER','#D32F2F', 'WARNING','#E65100', '#7E57C2'], 'text-halo-color': '#000', 'text-halo-width': 1 }});
// ══ OSIRIS SDK — Lattice Intelligence Mesh ══
// Polybolos Style: Delicate, translucent, steel-blue splined mesh
// ── SEA domain (Distinct Solid Lines) ──
// Removed glow to match the clean, diagrammatic look of submarinecablemap.com
map.addLayer({ id: 'sdk-sea', type: 'line', source: 'sdk-links', filter: ['==',['get','domain'],'SEA'], paint: {
'line-color': ['coalesce', ['get', 'color'], '#1976D2'], // Single solid color from properties
'line-width': ['interpolate',['linear'],['zoom'], 1, 0.8, 5, 1.5, 10, 2.5],
'line-opacity': ['interpolate',['linear'],['zoom'], 1, 0.3, 5, 0.5, 10, 0.7],
}});
// ── AIR domain (Steel Gray / Cyan) ──
map.addLayer({ id: 'sdk-air-atmo', type: 'line', source: 'sdk-links', filter: ['==',['get','domain'],'AIR'], paint: {
'line-color': '#4DD0E1',
'line-width': ['interpolate',['linear'],['zoom'], 1, 1.5, 5, 5, 10, 8],
'line-opacity': 0.04,
'line-blur': 3,
}});
map.addLayer({ id: 'sdk-air-glow', type: 'line', source: 'sdk-links', filter: ['==',['get','domain'],'AIR'], paint: {
'line-color': '#80DEEA',
'line-width': ['interpolate',['linear'],['zoom'], 1, 0.8, 5, 2, 10, 4],
'line-opacity': ['interpolate',['linear'],['zoom'], 1, 0.08, 5, 0.12, 10, 0.18],
'line-blur': 1,
}});
map.addLayer({ id: 'sdk-air', type: 'line', source: 'sdk-links', filter: ['==',['get','domain'],'AIR'], paint: {
'line-color': '#B2EBF2',
'line-width': ['interpolate',['linear'],['zoom'], 1, 0.15, 5, 0.6, 10, 1.2],
'line-opacity': ['interpolate',['linear'],['zoom'], 1, 0.2, 5, 0.35, 10, 0.5],
}});
// ── INTEL domain (Deep Steel / Violet) ──
map.addLayer({ id: 'sdk-intel-atmo', type: 'line', source: 'sdk-links', filter: ['==',['get','domain'],'INTEL'], paint: {
'line-color': '#7986CB',
'line-width': ['interpolate',['linear'],['zoom'], 1, 2.5, 5, 7, 10, 12],
'line-opacity': 0.06,
'line-blur': 5,
}});
map.addLayer({ id: 'sdk-intel-glow', type: 'line', source: 'sdk-links', filter: ['==',['get','domain'],'INTEL'], paint: {
'line-color': '#9FA8DA',
'line-width': ['interpolate',['linear'],['zoom'], 1, 1.2, 5, 3, 10, 6],
'line-opacity': ['interpolate',['linear'],['zoom'], 1, 0.12, 5, 0.18, 10, 0.25],
'line-blur': 2,
}});
map.addLayer({ id: 'sdk-intel', type: 'line', source: 'sdk-links', filter: ['==',['get','domain'],'INTEL'], paint: {
'line-color': '#C5CAE9',
'line-width': ['interpolate',['linear'],['zoom'], 1, 0.3, 5, 1, 10, 2],
'line-opacity': ['interpolate',['linear'],['zoom'], 1, 0.3, 5, 0.45, 10, 0.7],
}});
// Maritime Ships (moving entities) — ocean teal family
map.addLayer({ id: 'ship-dots', type: 'circle', source: 'maritime-ships', paint: {
'circle-radius': ['interpolate',['linear'],['zoom'], 1,2, 5,4, 10,6],
'circle-color': ['match', ['get','type'], 'military','#D32F2F', 'tanker','#E65100', 'cargo','#26C6DA', '#B0BEC5'],
'circle-opacity': 0.75,
}});
map.addLayer({ id: 'ship-label', type: 'symbol', source: 'maritime-ships', minzoom: 5, layout: {
'text-field': ['get','name'], 'text-size': 9, 'text-font': ['Open Sans Regular'],
'text-offset': [0, 1.2], 'text-allow-overlap': false,
}, paint: { 'text-color': ['match', ['get','type'], 'military','#D32F2F', 'tanker','#E65100', 'cargo','#26C6DA', '#B0BEC5'], 'text-halo-color': '#000', 'text-halo-width': 1 }});
setMapReady(true);
});
// Events
let lastMove = 0;
map.on('mousemove', e => {
const now = Date.now();
if (now - lastMove > 100) {
lastMove = now;
onMouseCoords?.({ lat: e.lngLat.lat, lng: e.lngLat.lng });
}
});
map.on('contextmenu', e => { e.preventDefault(); onRightClick?.({ lat: e.lngLat.lat, lng: e.lngLat.lng }); });
map.on('moveend', () => { const c = map.getCenter(); onViewStateChange?.({ zoom: map.getZoom(), latitude: c.lat }); });
// ── POPUP HELPER ──
const popup = (coords: any, html: string) => {
popupRef.current?.remove();
popupRef.current = new maplibregl.Popup({ closeButton: true, maxWidth: '420px', offset: 14 }).setLngLat(coords).setHTML(html).addTo(map);
};
const pStyle = `background:rgba(12,14,26,0.95);backdrop-filter:blur(16px);border-radius:10px;padding:16px;font-family:'JetBrains Mono',monospace;`;
const linkStyle = `display:inline-block;margin-top:8px;padding:5px 12px;font-size:10px;letter-spacing:0.12em;text-decoration:none;border-radius:5px;font-family:'JetBrains Mono',monospace;`;
// ── XSS PROTECTION HELPERS ──
const htmlEsc = (s: any): string => String(s ?? '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');
const idSafe = (s: any): string => String(s ?? '').replace(/[^a-zA-Z0-9_\.\-]/g, '');
const urlSafe = (s: any): string => { const u = String(s ?? ''); return /^https?:\/\//i.test(u) ? u : '#'; };
const colorSafe = (s: any): string => /^#[0-9a-fA-F]{3,8}$/.test(String(s ?? '')) ? String(s) : '#aaa';
// ── Flights (with FlightAware + ADS-B Exchange links) ──
['fl-commercial','fl-private','fl-jets','fl-military'].forEach(layer => {
map.on('click', layer, e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
const cs = (p.callsign||'').trim();
popup(coords, `<div style="${pStyle}border:1px solid rgba(212,175,55,0.3);">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;">
<span style="color:#D4AF37;font-size:16px;font-weight:700;letter-spacing:0.1em;">${htmlEsc(cs)}</span>
<span style="color:#5C5A54;font-size:10px;">${htmlEsc(p.icao24||'')}</span>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;font-size:11px;">
<div><span style="color:#5C5A54;font-size:9px;">MODEL</span><br/><span style="color:#E8E6E0;">${htmlEsc(p.model||'—')}</span></div>
<div><span style="color:#5C5A54;font-size:9px;">ALT</span><br/><span style="color:#00E5FF;">${p.alt?Math.round(p.alt)+'m':'—'}</span></div>
<div><span style="color:#5C5A54;font-size:9px;">SPEED</span><br/><span style="color:#E8E6E0;">${p.speed_knots||'—'}kt</span></div>
<div><span style="color:#5C5A54;font-size:9px;">HDG</span><br/><span style="color:#E8E6E0;">${Math.round(p.heading||0)}°</span></div>
<div><span style="color:#5C5A54;font-size:9px;">REG</span><br/><span style="color:#E8E6E0;">${htmlEsc(p.registration||'—')}</span></div>
<div><span style="color:#5C5A54;font-size:9px;">POS</span><br/><span style="color:#E8E6E0;">${coords[1].toFixed(2)},${coords[0].toFixed(2)}</span></div>
</div>
<div style="margin-top:12px;display:flex;gap:6px;flex-wrap:wrap;">
<a href="https://www.flightaware.com/live/flight/${encodeURIComponent(cs)}" target="_blank" style="${linkStyle}color:#D4AF37;border:1px solid rgba(212,175,55,0.4);background:rgba(212,175,55,0.1);">⚡ FLIGHTAWARE</a>
<a href="https://globe.adsbexchange.com/?icao=${encodeURIComponent(p.icao24||'')}" target="_blank" style="${linkStyle}color:#00E5FF;border:1px solid rgba(0,229,255,0.4);background:rgba(0,229,255,0.1);">📡 ADS-B</a>
<a href="https://www.radarbox.com/data/flights/${encodeURIComponent(cs)}" target="_blank" style="${linkStyle}color:#FF69B4;border:1px solid rgba(255,105,180,0.4);background:rgba(255,105,180,0.1);">📍 RADARBOX</a>
</div>
<button onclick="window.openOsirisIntel({ callsign: '${idSafe(cs)}', icao24: '${idSafe(p.icao24||'')}', model: '${idSafe(p.model||'')}', registration: '${idSafe(p.registration||'')}' })" style="width:100%;margin-top:8px;padding:6px 12px;background:rgba(212,175,55,0.15);border:1px solid rgba(212,175,55,0.5);color:#D4AF37;font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:bold;letter-spacing:0.1em;border-radius:4px;cursor:pointer;">[ DEEP DIVE INTEL ]</button>
</div>`);
});
map.on('mouseenter', layer, () => { map.getCanvas().style.cursor = 'pointer'; });
map.on('mouseleave', layer, () => { map.getCanvas().style.cursor = ''; });
});
// ── CCTV (opens CameraViewer panel) ──
map.on('click', 'cctv-dots', e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
// Emit the camera data so the CameraViewer opens
onEntityClick?.({
type: 'cctv',
id: p.id,
name: p.name,
city: p.city,
country: p.country,
source: p.source,
feed_url: p.feed_url,
stream_url: p.stream_url,
stream_type: p.stream_type,
external_url: p.external_url,
lat: coords[1],
lng: coords[0],
});
// Also fly to the camera
map.flyTo({ center: coords, zoom: Math.max(map.getZoom(), 13), duration: 1000 });
});
// ── Earthquakes (with USGS link) ──
map.on('click', 'eq-circles', e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
popup(coords, `<div style="${pStyle}border:1px solid rgba(255,149,0,0.3);">
<div style="color:#FF9500;font-size:14px;font-weight:700;margin-bottom:4px;">M${p.magnitude} EARTHQUAKE</div>
<div style="font-size:9px;color:#E8E6E0;margin-bottom:8px;">${htmlEsc(p.place||'Unknown location')}</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;font-size:9px;">
<div><span style="color:#5C5A54;">DEPTH</span><br/><span style="color:#E8E6E0;">${p.depth||'—'}km</span></div>
<div><span style="color:#5C5A54;">COORDS</span><br/><span style="color:#E8E6E0;">${coords[1].toFixed(3)}, ${coords[0].toFixed(3)}</span></div>
</div>
<a href="${p.source === 'NIGGG-BAS' ? 'https://ndc.niggg.bas.bg/' : `https://earthquake.usgs.gov/earthquakes/eventpage/${encodeURIComponent(p.id||'')}`}" target="_blank" style="${linkStyle}color:#FF9500;border:1px solid rgba(255,149,0,0.4);background:rgba(255,149,0,0.1);">📊 ${p.source === 'NIGGG-BAS' ? 'NIGGG-BAS' : 'USGS DETAILS'}</a>
</div>`);
});
// ── Satellites (SatNOGS powered) ──
map.on('click', 'sat-dots', e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
popup(coords, `<div style="${pStyle}border:1px solid rgba(212,175,55,0.3);">
<div style="color:#D4AF37;font-size:12px;font-weight:700;letter-spacing:0.1em;margin-bottom:4px;">🛰️ ${htmlEsc(p.name)}</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:4px;font-size:9px;margin-bottom:8px;">
<div><span style="color:#5C5A54;">MISSION</span><br/><span style="color:${colorSafe(p.color)};">${htmlEsc(p.mission||'Unknown')}</span></div>
<div><span style="color:#5C5A54;">ALT</span><br/><span style="color:#00E5FF;">${p.alt ? p.alt+' km' : '—'}</span></div>
<div><span style="color:#5C5A54;">POS</span><br/><span style="color:#E8E6E0;">${coords[1].toFixed(2)}°, ${coords[0].toFixed(2)}°</span></div>
</div>
${p.noradId ? `<a href="https://www.n2yo.com/satellite/?s=${p.noradId}" target="_blank" style="display:block;text-align:center;padding:4px;margin-top:6px;font-size:8px;font-family:monospace;letter-spacing:0.1em;text-decoration:none;color:#00E5FF;border:1px solid rgba(0,229,255,0.4);background:rgba(0,229,255,0.1);border-radius:2px;cursor:pointer;">📡 TRACK ON N2YO</a>` : ''}
</div>`);
});
// ── Fires (with NASA FIRMS link) ──
map.on('click', 'fires-heat', e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
popup(coords, `<div style="${pStyle}border:1px solid rgba(255,107,0,0.3);">
<div style="color:#FF6B00;font-size:12px;font-weight:700;margin-bottom:6px;">🔥 ACTIVE FIRE DETECTED</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;font-size:9px;margin-bottom:8px;">
<div><span style="color:#5C5A54;">BRIGHTNESS</span><br/><span style="color:#FF6B00;">${p.brightness||'—'}K</span></div>
<div><span style="color:#5C5A54;">COORDS</span><br/><span style="color:#E8E6E0;">${coords[1].toFixed(3)}°, ${coords[0].toFixed(3)}°</span></div>
</div>
<a href="https://firms.modaps.eosdis.nasa.gov/map/#d:24hrs;l:noaa20-viirs,viirs,modis_a,modis_t;@${coords[0]},${coords[1]},10z" target="_blank" style="${linkStyle}color:#FF6B00;border:1px solid rgba(255,107,0,0.4);background:rgba(255,107,0,0.1);">🛰️ NASA FIRMS MAP</a>
</div>`);
});
// ── Malware Threats (Abuse.ch) ──
map.on('click', 'malware-dots', e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
const tType = (p.threat_type || 'MALWARE').toUpperCase();
const statusColor = p.status === 'online' ? '#39FF14' : '#FF1744';
popup(coords, `<div style="${pStyle}border:1px solid rgba(255,23,68,0.4);box-shadow:inset 0 0 12px rgba(255,23,68,0.1);">
<div style="display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid rgba(255,23,68,0.3);padding-bottom:6px;margin-bottom:8px;">
<div style="color:#FF1744;font-size:12px;font-weight:700;letter-spacing:0.1em;text-shadow:0 0 4px rgba(255,23,68,0.5);">[ ${htmlEsc(tType)} ]</div>
<div style="color:#5C5A54;font-size:9px;">${htmlEsc(p.country || 'UNKNOWN')}</div>
</div>
<div style="color:#E8E6E0;font-size:11px;font-weight:bold;margin-bottom:10px;">${htmlEsc(p.malware || 'Unidentified Threat Payload')}</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;font-size:9px;margin-bottom:12px;background:rgba(0,0,0,0.3);padding:6px;border-radius:4px;">
<div><span style="color:#5C5A54;">TARGET IP</span><br/><span style="color:#00E5FF;font-family:monospace;">${htmlEsc(p.ip)}</span></div>
<div><span style="color:#5C5A54;">STATUS</span><br/><span style="color:${statusColor};">${(p.status||'UNKNOWN').toUpperCase()}</span></div>
</div>
<div style="display:flex;gap:6px;">
<a href="https://feodotracker.abuse.ch/browse/" target="_blank" style="${linkStyle}flex:1;text-align:center;color:#E8E6E0;border:1px solid rgba(255,255,255,0.2);background:rgba(255,255,255,0.05);">THREAT INTEL ↗</a>
</div>
<button onclick="window.openOsirisIntel({ type: 'ip', ip: '${idSafe(p.ip)}', threat_type: '${idSafe(p.malware || p.threat_type || '')}', status: '${idSafe(p.status || '')}' })" style="width:100%;margin-top:8px;padding:8px 12px;background:linear-gradient(90deg, rgba(255,23,68,0.1) 0%, rgba(255,23,68,0.2) 100%);border:1px solid rgba(255,23,68,0.6);color:#FF1744;font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:bold;letter-spacing:0.15em;border-radius:4px;cursor:pointer;transition:all 0.2s;">DEEP DIVE ANALYTICS</button>
</div>`);
});
// ── GDELT Conflicts (with source article) ──
map.on('click', 'gdelt-dots', e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
// Map coordinates to Liveuamap regions
let sourceUrl = p.url || '';
if (!sourceUrl || sourceUrl.includes('google.com')) {
const [lng, lat] = coords;
if (lat > 44 && lat < 53 && lng > 22 && lng < 40) sourceUrl = 'https://liveuamap.com/'; // Ukraine
else if (lat > 30 && lat < 33 && lng > 34 && lng < 36) sourceUrl = 'https://israelpalestine.liveuamap.com/'; // Gaza
else if (lat > 33 && lat < 34.5 && lng > 35 && lng < 36.5) sourceUrl = 'https://lebanon.liveuamap.com/'; // Lebanon
else if (lat > 32 && lat < 37 && lng > 35 && lng < 42) sourceUrl = 'https://syria.liveuamap.com/'; // Syria
else if (lat > 10 && lat < 22 && lng > 22 && lng < 38) sourceUrl = 'https://sudan.liveuamap.com/'; // Sudan
else if (lat > 12 && lat < 20 && lng > 42 && lng < 55) sourceUrl = 'https://yemen.liveuamap.com/'; // Yemen
else sourceUrl = 'https://liveuamap.com/'; // Global fallback
}
popup(coords, `<div style="${pStyle}border:1px solid rgba(255,61,61,0.3);">
<div style="color:#FF3D3D;font-size:12px;font-weight:700;margin-bottom:6px;">⚠️ CONFLICT EVENT</div>
<div style="font-size:9px;color:#E8E6E0;margin-bottom:8px;line-height:1.4;">${htmlEsc(p.name||'Unclassified incident')}</div>
<a href="${urlSafe(sourceUrl)}" target="_blank" style="${linkStyle}flex:1;text-align:center;color:#FF3D3D;border:1px solid rgba(255,61,61,0.4);background:rgba(255,61,61,0.15);display:inline-block;width:100%;box-sizing:border-box;margin-top:4px;">[ OPEN SOURCE ↗ ]</a>
</div>`);
});
// ── Global Event / Conflict Markers ──
map.on('click', 'conflict-icons', e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
const color = p.severity === 'war' ? '#FF1744' : p.severity === 'high' ? '#FF9500' : '#FFD500';
popup(coords, `<div style="${pStyle}border:1px solid ${color}40;">
<div style="color:${color};font-size:12px;font-weight:700;margin-bottom:6px;">⚠️ ${htmlEsc(p.label || 'WARNING EVENT')}</div>
<div style="font-size:10px;color:#E8E6E0;margin-bottom:8px;line-height:1.4;">${htmlEsc(p.description || 'Global event detected at this location.')}</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;font-size:9px;margin-bottom:8px;">
<div><span style="color:#5C5A54;">SEVERITY</span><br/><span style="color:${color};">${(p.severity||'unknown').toUpperCase()}</span></div>
<div><span style="color:#5C5A54;">COORDS</span><br/><span style="color:#E8E6E0;">${coords[1].toFixed(3)}°, ${coords[0].toFixed(3)}°</span></div>
</div>
${p.sourceUrl ? `<a href="${urlSafe(p.sourceUrl)}" target="_blank" style="${linkStyle}flex:1;text-align:center;color:${color};border:1px solid ${color}40;background:${color}15;display:inline-block;width:100%;box-sizing:border-box;margin-top:4px;">[ OPEN SOURCE ↗ ]</a>` : ''}
</div>`);
});
// ── OSIRIS SDK link click ──
const SDK_SOURCE_URLS: Record<string, string> = {
'AIS Maritime': 'https://www.marinetraffic.com',
'AIS Stream': 'https://aisstream.io',
'AIS → Lattice': 'https://aisstream.io',
'ADS-B / OpenSky': 'https://opensky-network.org',
'ADS-B → Lattice': 'https://opensky-network.org',
'Naval Intelligence': 'https://www.odni.gov',
};
['sdk-sea','sdk-sea-glow','sdk-air','sdk-air-glow','sdk-intel','sdk-intel-glow'].forEach(layer => {
map.on('click', layer, e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = e.lngLat;
const srcUrl = p.url || SDK_SOURCE_URLS[p.source] || 'https://osirisai.live';
const domainLabel = p.domain === 'SEA' ? '⚓ MARITIME' : p.domain === 'AIR' ? '✈ AIR CORRIDOR' : '🛡 NAVAL INTEL';
const domainColor = p.domain === 'SEA' ? '#4FC3F7' : p.domain === 'AIR' ? '#B3E5FC' : '#81D4FA';
const linkStyle = 'text-decoration:none;padding:3px 8px;border-radius:4px;font-size:9px;font-weight:700;letter-spacing:0.05em;';
popup([coords.lng, coords.lat], `<div style="${pStyle}border:1px solid ${domainColor}40;">
<div style="display:flex;align-items:center;gap:6px;margin-bottom:8px;">
<div style="width:8px;height:8px;border-radius:50%;background:${domainColor};box-shadow:0 0 8px ${domainColor};"></div>
<span style="color:${domainColor};font-size:11px;font-weight:700;letter-spacing:0.1em;">${domainLabel}</span>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;font-size:9px;margin-bottom:8px;">
<div><span style="color:#5C5A54;">FROM</span><br/><span style="color:#E8E6E0;">${htmlEsc(p.fromName || 'Origin')}</span></div>
<div><span style="color:#5C5A54;">TO</span><br/><span style="color:#E8E6E0;">${htmlEsc(p.toName || 'Destination')}</span></div>
<div><span style="color:#5C5A54;">DOMAIN</span><br/><span style="color:${domainColor};">${p.domain}</span></div>
<div><span style="color:#5C5A54;">SOURCE</span><br/><a href="${urlSafe(srcUrl)}" target="_blank" style="color:${domainColor};text-decoration:underline;cursor:pointer;">${htmlEsc(p.source || 'OSIRIS')}</a></div>
</div>
<a href="${urlSafe(srcUrl)}" target="_blank" style="${linkStyle}color:${domainColor};border:1px solid ${domainColor}40;background:${domainColor}18;display:inline-block;margin-top:4px;">OPEN SOURCE ↗</a>
</div>`);
});
});
// ── Generic hover for clickables ──
['conflict-icons','cctv-dots','eq-circles','sat-dots','fires-heat','gdelt-dots','weather-dots','infra-dots','maritime-dots','choke-dots','news-dots','sigint-news-dots','balloon-dots','rad-dots','ship-dots','sweep-device-dots','scan-targets-dots','sdk-sea','sdk-sea-glow','sdk-sea-atmo','sdk-air','sdk-air-glow','sdk-air-atmo','sdk-intel','sdk-intel-glow','sdk-intel-atmo','malware-dots','mining-dots','ram-dots'].forEach(layer => {
map.on('mouseenter', layer, () => { map.getCanvas().style.cursor = 'pointer'; });
map.on('mouseleave', layer, () => { map.getCanvas().style.cursor = ''; });
});
// ── Scan Targets click ──
map.on('click', 'scan-targets-dots', (e: any) => {
const p = e.features?.[0]?.properties;
if (!p) return;
const coords = e.features[0].geometry.coordinates.slice();
popup(coords, `<div style="${pStyle}border:1px solid rgba(255,61,61,0.5);">
<div style="color:#FF3D3D;font-size:12px;font-weight:700;margin-bottom:6px;">🎯 TARGET: ${htmlEsc(p.id)}</div>
<div style="font-size:9px;color:#E8E6E0;margin-bottom:8px;">${htmlEsc(p.city || 'Unknown')}, ${htmlEsc(p.country || 'Unknown')}${htmlEsc(p.isp || 'Unknown ISP')}</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;font-size:9px;">
<div><span style="color:#5C5A54;">TYPE</span><br/><span style="color:#00E5FF;">${(p.type || 'UNKNOWN').toUpperCase()}</span></div>
<div><span style="color:#5C5A54;">COORDS</span><br/><span style="color:#E8E6E0;">${coords[1].toFixed(3)}°, ${coords[0].toFixed(3)}°</span></div>
</div>
<button onclick="window.openOsirisIntel({ type: 'ip', ip: '${idSafe(p.id)}' })" style="width:100%;margin-top:8px;padding:6px 12px;background:rgba(255,109,0,0.15);border:1px solid rgba(255,109,0,0.5);color:#FF6D00;font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:bold;letter-spacing:0.1em;border-radius:4px;cursor:pointer;">[ IP INTEL DEEP DIVE ]</button>
</div>`);
});
// ── SCM Suppliers ──
map.on('click', 'scm-dots', e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
const color = p.risk_level === 'CRITICAL' ? '#FF1744' : p.risk_level === 'HIGH' ? '#FF9500' : '#00BCD4';
const activeThreats = p.active_threats ? JSON.parse(p.active_threats) : [];
let threatsHtml = '';
if (activeThreats.length > 0) {
threatsHtml = `<div style="margin-top:8px;padding-top:6px;border-top:1px solid ${color}40;color:${color};font-size:9px;font-weight:bold;">
ACTIVE THREATS:<br/>${activeThreats.map((t: string) => `⚠ ${htmlEsc(t)}`).join('<br/>')}
</div>`;
}
popup(coords, `<div style="${pStyle}border:1px solid ${color}40;">
<div style="color:${color};font-size:12px;font-weight:700;margin-bottom:4px;">🏢 ${htmlEsc(p.name)}</div>
<div style="font-size:9px;color:#aaa;margin-bottom:8px;">${htmlEsc(p.category)} | ${htmlEsc(p.city)}, ${htmlEsc(p.country)}</div>
<div style="display:grid;grid-template-columns:1fr;gap:4px;font-size:11px;">
<div><span style="color:#5C5A54;font-size:9px;">SCM RISK LEVEL</span><br/><span style="color:${color};font-weight:bold;">${p.risk_level}</span></div>
</div>
${threatsHtml}
</div>`);
});
// ── Mining Companies ──
const MINING_COLORS: Record<string, string> = { copper: '#FF8A65', gold: '#FFD54F', silver: '#CFD8DC', pgm: '#4DB6AC', rare_earth: '#BA68C8', specialty: '#FFB74D' };
map.on('click', 'mining-dots', e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
const color = MINING_COLORS[p.category] || '#FFB74D';
const commodities = p.commodities ? JSON.parse(p.commodities) : [];
popup(coords, `<div style="${pStyle}border:1px solid ${color}40;">
<div style="color:${color};font-size:12px;font-weight:700;margin-bottom:4px;">⛏️ ${htmlEsc(p.company)}</div>
<div style="font-size:9px;color:#aaa;margin-bottom:8px;">${htmlEsc(p.site)}${htmlEsc(p.country)}</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;font-size:9px;margin-bottom:8px;">
<div><span style="color:#5C5A54;">CATEGORY</span><br/><span style="color:${color};font-weight:bold;">${htmlEsc((p.category || '').toUpperCase().replace('_',' '))}</span></div>
<div><span style="color:#5C5A54;">TICKER</span><br/><span style="color:#E8E6E0;">${htmlEsc(p.ticker || '—')}${p.exchange ? ' · ' + htmlEsc(p.exchange) : ''}</span></div>
<div><span style="color:#5C5A54;">SITE TYPE</span><br/><span style="color:#E8E6E0;">${htmlEsc((p.site_type || 'mine').toUpperCase())}</span></div>
<div><span style="color:#5C5A54;">HQ</span><br/><span style="color:#E8E6E0;">${htmlEsc(p.hq || '—')}</span></div>
</div>
<div style="font-size:9px;color:#8A8880;margin-bottom:6px;">COMMODITIES: <span style="color:${color};">${commodities.map((c: string) => htmlEsc(c)).join(', ')}</span></div>
${p.notes ? `<div style="font-size:9px;color:#aaa;line-height:1.4;margin-bottom:6px;">${htmlEsc(p.notes)}</div>` : ''}
<a href="https://www.google.com/maps/@${coords[1]},${coords[0]},13z/data=!3m1!1e3" target="_blank" style="${linkStyle}color:${color};border:1px solid ${color}40;background:${color}15;">SATELLITE VIEW</a>
</div>`);
});
// ── Memory / RAM Fabs ──
const RAM_COLORS: Record<string, string> = { dram_hbm: '#4FC3F7', nand: '#81C784', controller_ip: '#FFF176', module_maker: '#90A4AE', packaging_test: '#F06292', equipment: '#9575CD' };
map.on('click', 'ram-dots', e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
const color = RAM_COLORS[p.category] || '#4FC3F7';
popup(coords, `<div style="${pStyle}border:1px solid ${color}40;">
<div style="color:${color};font-size:12px;font-weight:700;margin-bottom:4px;">💾 ${htmlEsc(p.company)}</div>
<div style="font-size:9px;color:#aaa;margin-bottom:8px;">${htmlEsc(p.site)}${htmlEsc(p.country)}</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;font-size:9px;margin-bottom:8px;">
<div><span style="color:#5C5A54;">ROLE</span><br/><span style="color:${color};font-weight:bold;">${htmlEsc((p.category || '').toUpperCase().replace(/_/g,' '))}</span></div>
<div><span style="color:#5C5A54;">TICKER</span><br/><span style="color:#E8E6E0;">${htmlEsc(p.ticker || '—')}${p.exchange ? ' · ' + htmlEsc(p.exchange) : ''}</span></div>
<div><span style="color:#5C5A54;">SITE TYPE</span><br/><span style="color:#E8E6E0;">${htmlEsc((p.site_type || 'fab').toUpperCase())}</span></div>
<div><span style="color:#5C5A54;">HQ</span><br/><span style="color:#E8E6E0;">${htmlEsc(p.hq || '—')}</span></div>
</div>
${p.notes ? `<div style="font-size:9px;color:#aaa;line-height:1.4;">${htmlEsc(p.notes)}</div>` : ''}
</div>`);
});
// ── IP Sweep device click ──
map.on('click', 'sweep-device-dots', (e: any) => {
const p = e.features?.[0]?.properties;
if (!p) return;
const coords = e.features[0].geometry.coordinates.slice();
const ports = JSON.parse(p.ports || '[]');
const vulns = JSON.parse(p.vulns || '[]');
const hostnames = JSON.parse(p.hostnames || '[]');
const riskColors: Record<string, string> = { CRITICAL: '#FF3D3D', HIGH: '#FF6B00', MEDIUM: '#FFD700', LOW: '#76FF03', INFO: '#5C5A54' };
popup(coords, `<div style="font-family:monospace;font-size:11px;color:#E8E6E0;">
<div style="font-size:13px;font-weight:bold;margin-bottom:6px;color:${p.color};">${p.device_type}</div>
<div style="font-size:12px;margin-bottom:8px;color:#fff;">${p.ip}</div>
${hostnames.length > 0 ? `<div style="font-size:9px;color:#8A8880;margin-bottom:6px;">${hostnames.join(', ')}</div>` : ''}
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-bottom:8px;">
<div><span style="color:#5C5A54;">PORTS</span><br/><span style="color:#E8E6E0;">${ports.length}</span></div>
<div><span style="color:#5C5A54;">RISK</span><br/><span style="color:${riskColors[p.risk_level] || '#666'};">${p.risk_level}</span></div>
</div>
<div style="font-size:9px;color:#8A8880;margin-bottom:6px;">Open: ${ports.slice(0, 12).join(', ')}${ports.length > 12 ? ' ...' : ''}</div>
${vulns.length > 0 ? `<div style="font-size:9px;color:#FF3D3D;margin-bottom:6px;">⚠ CVEs: ${vulns.slice(0, 5).join(', ')}${vulns.length > 5 ? ` +${vulns.length - 5} more` : ''}</div>` : ''}
<button onclick="window.openOsirisIntel({ type: 'ip', ip: '${p.ip}' })" style="width:100%;margin-top:6px;padding:6px 12px;background:rgba(255,109,0,0.15);border:1px solid rgba(255,109,0,0.5);color:#FF6D00;font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:bold;letter-spacing:0.1em;border-radius:4px;cursor:pointer;">[ IP INTEL DEEP DIVE ]</button>
</div>`);
});
// ── Balloons / Sondes ──
map.on('click', 'balloon-dots', e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
popup(coords, `<div style="${pStyle}border:1px solid ${p.color}40;">
<div style="color:${p.color};font-size:12px;font-weight:700;letter-spacing:0.1em;margin-bottom:4px;">🎈 ${p.callsign}</div>
<div style="font-size:9px;color:#aaa;margin-bottom:8px;">${p.type.toUpperCase()} / STATUS: ${p.status.toUpperCase()}</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;font-size:9px;">
<div><span style="color:#5C5A54;">ALTITUDE</span><br/><span style="color:#E8E6E0;">${p.altitude} m</span></div>
<div><span style="color:#5C5A54;">SPEED</span><br/><span style="color:#E8E6E0;">${Math.round(p.speed)} km/h</span></div>
<div><span style="color:#5C5A54;">VERT RATE</span><br/><span style="color:${p.verticalRate > 0 ? '#00E676' : '#FF3D3D'};">${p.verticalRate.toFixed(1)} m/s</span></div>
<div><span style="color:#5C5A54;">TEMP</span><br/><span style="color:#E8E6E0;">${p.temperature}°C</span></div>
</div>
</div>`);
});
// ── Radiation ──
map.on('click', 'rad-dots', e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
const color = p.status === 'DANGER' ? '#FF1744' : p.status === 'WARNING' ? '#FF9500' : '#AB47BC';
popup(coords, `<div style="${pStyle}border:1px solid ${color}40;">
<div style="color:${color};font-size:12px;font-weight:700;margin-bottom:4px;">☢️ ${p.name}</div>
<div style="font-size:9px;color:#aaa;margin-bottom:8px;">${p.city}, ${p.country}</div>
<div style="display:grid;grid-template-columns:1fr;gap:4px;font-size:11px;">
<div><span style="color:#5C5A54;font-size:9px;">READING</span><br/><span style="color:${color};font-weight:bold;">${p.reading} nSv/h</span></div>
<div><span style="color:#5C5A54;font-size:9px;">STATUS</span><br/><span style="color:${color};">${p.status}</span></div>
<div><span style="color:#5C5A54;font-size:9px;">NETWORK</span><br/><span style="color:#E8E6E0;">${p.network}</span></div>
</div>
</div>`);
});
// ── Maritime Ships ──
map.on('click', 'ship-dots', e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
const color = p.type === 'military' ? '#FF1744' : p.type === 'tanker' ? '#FF9500' : '#00E5FF';
const icon = p.type === 'military' ? '⚔️' : p.type === 'tanker' ? '🛢️' : '🚢';
popup(coords, `<div style="${pStyle}border:1px solid ${color}60;box-shadow:inset 0 0 12px ${color}15;">
<div style="display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid ${color}40;padding-bottom:6px;margin-bottom:8px;">
<div style="color:${color};font-size:12px;font-weight:700;letter-spacing:0.1em;">${icon} [ ${(p.type||'VESSEL').toUpperCase()} ]</div>
<div style="color:#5C5A54;font-size:9px;">FLAG: ${p.flag||'UNK'}</div>
</div>
<div style="color:#E8E6E0;font-size:11px;font-weight:bold;margin-bottom:10px;">${p.name || 'UNIDENTIFIED VESSEL'}</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;font-size:9px;margin-bottom:8px;background:rgba(0,0,0,0.3);padding:6px;border-radius:4px;">
<div><span style="color:#5C5A54;">SPEED</span><br/><span style="color:${color};font-family:monospace;">${Number(p.speed).toFixed(1)} kn</span></div>
<div><span style="color:#5C5A54;">HEADING</span><br/><span style="color:${color};font-family:monospace;">${Number(p.heading).toFixed(0)}°</span></div>
<div><span style="color:#5C5A54;">LATITUDE</span><br/><span style="color:#E8E6E0;font-family:monospace;">${coords[1].toFixed(4)}°</span></div>
<div><span style="color:#5C5A54;">LONGITUDE</span><br/><span style="color:#E8E6E0;font-family:monospace;">${coords[0].toFixed(4)}°</span></div>
</div>
<div><span style="color:#5C5A54;font-size:9px;">DESTINATION: </span><span style="color:#E8E6E0;font-size:9px;">${p.destination || 'UNKNOWN'}</span></div>
<a href="https://www.marinetraffic.com/en/ais/details/ships/mmsi:${p.mmsi}" target="_blank" style="${linkStyle}flex:1;text-align:center;color:${color};border:1px solid ${color}40;background:${color}15;display:inline-block;width:100%;box-sizing:border-box;margin-top:4px;">[ OPEN SOURCE ↗ ]</a>
</div>`);
});
// ── Weather Events (NASA EONET + NOAA/NWS + GDACS) ──
map.on('click', 'weather-dots', e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
const iconEmoji = p.icon === 'cyclone' ? '🌀' : p.icon === 'volcano' ? '🌋' : p.icon === 'flood' ? '🌊' : p.icon === 'drought' ? '🏜️' : p.icon === 'ice' ? '🧊' : p.icon === 'weather' ? '⚠️' : '⚡';
popup(coords, `<div style="${pStyle}border:1px solid rgba(224,64,251,0.3);">
<div style="color:#E040FB;font-size:14px;font-weight:700;margin-bottom:6px;">${iconEmoji} ${p.type || 'Weather Event'}</div>
<div style="font-size:10px;color:#E8E6E0;margin-bottom:8px;line-height:1.4;">${p.title || 'Unknown event'}</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;font-size:9px;margin-bottom:8px;">
<div><span style="color:#5C5A54;">SEVERITY</span><br/><span style="color:${p.severity === 'high' ? '#FF1744' : '#FFD700'};">${(p.severity||'low').toUpperCase()}</span></div>
<div><span style="color:#5C5A54;">COORDS</span><br/><span style="color:#E8E6E0;">${coords[1].toFixed(3)}°, ${coords[0].toFixed(3)}°</span></div>
</div>
<div style="display:flex;gap:6px;">
${p.source ? `<a href="${p.source}" target="_blank" style="${linkStyle}color:#E040FB;border:1px solid rgba(224,64,251,0.4);background:rgba(224,64,251,0.1);">📡 SOURCE</a>` : ''}
</div>
</div>`);
});
// ── Nuclear Infrastructure ──
map.on('click', 'infra-dots', e => {
if (!e.features?.length) return;
const p = e.features[0].properties as any;
const coords = (e.features[0].geometry as any).coordinates;
const statusColor = p.status.includes('SEISMIC RISK') ? '#FF9500' : p.status === 'Active Conflict Zone' ? '#FF1744' : p.status === 'Operational' ? '#76FF03' : '#757575';
popup(coords, `<div style="${pStyle}border:1px solid rgba(118,255,3,0.3);">
<div style="color:#76FF03;font-size:14px;font-weight:700;margin-bottom:4px;">☢️ ${p.name || 'Nuclear Facility'}</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;font-size:9px;margin-bottom:8px;">
<div><span style="color:#5C5A54;">STATUS</span><br/><span style="color:${statusColor};">${p.status || '—'}</span></div>
<div><span style="color:#5C5A54;">CITY</span><br/><span style="color:#E8E6E0;">${p.city || '—'}, ${p.country || ''}</span></div>
<div><span style="color:#5C5A54;">REACTORS</span><br/><span style="color:#76FF03;">${p.reactors || '—'}</span></div>
<div><span style="color:#5C5A54;">CAPACITY</span><br/><span style="color:#E8E6E0;">${p.capacityMW ? p.capacityMW.toLocaleString() + ' MW' : '—'}</span></div>
<div><span style="color:#5C5A54;">OWNER</span><br/><span style="color:#E8E6E0;">${p.owner || '—'}</span></div>
<div><span style="color:#5C5A54;">COORDS</span><br/><span style="color:#E8E6E0;">${coords[1].toFixed(3)}°, ${coords[0].toFixed(3)}°</span></div>
</div>
<a href="https://www.google.com/maps/@${coords[1]},${coords[0]},14z/data=!3m1!1e3" target="_blank" style="${linkStyle}color:#76FF03;border:1px solid rgba(118,255,3,0.4);background:rgba(118,255,3,0.1);">SATELLITE VIEW</a>
</div>`);
});
// ── Maritime Ports & Naval Bases ──
map.on('click', 'maritime-dots', e => {
const p = e.features?.[0]?.properties;
if (!p) return;
const coords = (e.features![0].geometry as any).coordinates;
const typeColor = p.type === 'naval' ? '#FF3D3D' : p.type === 'energy' ? '#FF9500' : '#00BCD4';
const typeLabel = p.type === 'naval' ? 'NAVAL BASE' : p.type === 'energy' ? 'ENERGY PORT' : 'CONTAINER PORT';
const congestionHtml = p.congestion ? `
<div style="margin-top:8px;padding-top:6px;border-top:1px solid rgba(255,255,255,0.1);">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;">
<div><span style="color:#5C5A54;font-size:9px;">CONGESTION</span><br/><span style="color:${p.congestion === 'SEVERE' ? '#FF1744' : p.congestion === 'CONGESTED' ? '#FF9500' : '#00E676'};font-weight:bold;font-size:10px;">${p.congestion}</span></div>
<div><span style="color:#5C5A54;font-size:9px;">EST. DWELL TIME</span><br/><span style="color:#E8E6E0;font-weight:bold;font-size:10px;">${p.dwell_time || 'Unknown'}</span></div>
</div>
</div>` : '';
popup(coords, `<div style="${pStyle}border:1px solid ${typeColor}40;">
<div style="color:${typeColor};font-weight:bold;font-size:11px;margin-bottom:4px;">${p.name}</div>
<div style="color:#999;font-size:9px;margin-bottom:6px;">${typeLabel}${p.country}</div>
${p.volume ? `<div style="font-size:9px;color:#aaa;">Volume: <span style="color:${typeColor};font-weight:bold;">${p.volume}</span></div>` : ''}
${p.fleet ? `<div style="font-size:9px;color:#aaa;">Fleet: <span style="color:${typeColor};font-weight:bold;">${p.fleet}</span></div>` : ''}
${p.rank ? `<div style="font-size:9px;color:#aaa;">Global Rank: <span style="color:${typeColor};font-weight:bold;">#${p.rank}</span></div>` : ''}
${congestionHtml}
</div>`);
});
// ── Maritime Chokepoints ──
map.on('click', 'choke-dots', e => {
const p = e.features?.[0]?.properties;
if (!p) return;
const coords = (e.features![0].geometry as any).coordinates;
const riskCol = p.risk === 'CRITICAL' ? '#FF1744' : p.risk === 'HIGH' ? '#FF9500' : p.risk === 'ELEVATED' ? '#FFD700' : '#00E676';
popup(coords, `<div style="${pStyle}border:1px solid ${riskCol}40;">
<div style="color:#FF9500;font-weight:bold;font-size:11px;margin-bottom:4px;">${p.name}</div>
<div style="font-size:9px;color:#aaa;">Traffic: <span style="color:#fff;">${p.traffic}</span></div>
<div style="font-size:9px;color:#aaa;">Risk: <span style="color:${riskCol};font-weight:bold;">${p.risk}</span></div>
</div>`);
});
// ── Live News (opens feed viewer) ──
map.on('click', 'news-dots', e => {
const p = e.features?.[0]?.properties;
if (!p) return;
onEntityClick?.({
type: 'live_news',
name: p.name,
city: p.city,
country: p.country,
url: p.url,
category: p.category,
embed_allowed: p.embed_allowed !== false && p.embed_allowed !== 'false',
});
});
return () => { map.remove(); mapRef.current = null; };
}, []);
// Day/Night
useEffect(() => {
if (!mapReady || !mapRef.current) return;
const map = mapRef.current;
const update = () => {
const src = map.getSource('day-night') as any;
if (!src) return;
if (!activeLayers.day_night) { src.setData(EMPTY_FC); return; }
src.setData({ type: 'FeatureCollection', features: [{ type: 'Feature', geometry: { type: 'Polygon', coordinates: [computeSolarTerminator()] }, properties: {} }] });
};
update();
const iv = setInterval(update, 300000); // 5 min (was 1 min — shadow barely moves)
return () => clearInterval(iv);
}, [mapReady, activeLayers.day_night]);
// Helper to set GeoJSON
const setGeo = useCallback((source: string, features: any[]) => {
const src = mapRef.current?.getSource(source) as any;
if (src) src.setData({ type: 'FeatureCollection', features });
}, []);
const setVis = useCallback((ids: string[], visible: boolean) => {
const map = mapRef.current;
if (!map) return;
ids.forEach(id => { if (map.getLayer(id)) map.setLayoutProperty(id, 'visibility', visible ? 'visible' : 'none'); });
}, []);
// Flight data → GeoJSON (GPU rendered)
useEffect(() => {
if (!mapReady) return;
const toFeatures = (arr: any[], decimate: number = 1) => {
let filtered = arr || [];
if (decimate > 1) {
filtered = filtered.filter((_, i) => i % decimate === 0);
}
return filtered.map((f: any) => ({
type: 'Feature' as const, geometry: { type: 'Point' as const, coordinates: [f.lng, f.lat] },
properties: { callsign: f.callsign, heading: f.heading || 0, alt: f.alt, model: f.model, speed_knots: f.speed_knots, registration: f.registration, icao24: f.icao24 },
}));
};
setGeo('flights', activeLayers.flights ? toFeatures(data.commercial_flights, 10) : []);
setGeo('private-fl', activeLayers.private ? toFeatures(data.private_flights, 2) : []);
setGeo('jets', activeLayers.jets ? toFeatures(data.private_jets, 2) : []);
setGeo('military', activeLayers.military ? toFeatures(data.military_flights) : []);
}, [mapReady, data.commercial_flights, data.private_flights, data.private_jets, data.military_flights, activeLayers.flights, activeLayers.private, activeLayers.jets, activeLayers.military]);
// Update aircraft icon colors dynamically on theme switch
useEffect(() => {
if (!mapReady || !mapRef.current) return;
const map = mapRef.current;
const isGhost = theme === 'ghost';
const phantomPurple = '#B388FF';
const ghostPriv = '#CE93D8';
const ghostGov = '#D500F9';
const flightCom = isGhost ? phantomPurple : '#00E5FF';
const flightPriv = isGhost ? ghostPriv : '#FFD700';
const flightGov = isGhost ? ghostGov : '#FF9500';
const flightMil = '#FF0000';
const updateMapIcon = (id: string, color: string, size: number) => {
if (!map.hasImage(id)) return;
const canvas = document.createElement('canvas');
canvas.width = size; canvas.height = size;
const ctx = canvas.getContext('2d')!;
const cx = size / 2, cy = size / 2;
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(cx, cy - size * 0.4);
ctx.lineTo(cx - size * 0.12, cy + size * 0.1);
ctx.lineTo(cx - size * 0.4, cy + size * 0.2);
ctx.lineTo(cx - size * 0.4, cy + size * 0.3);
ctx.lineTo(cx - size * 0.12, cy + size * 0.15);
ctx.lineTo(cx, cy + size * 0.35);
ctx.lineTo(cx + size * 0.12, cy + size * 0.15);
ctx.lineTo(cx + size * 0.4, cy + size * 0.3);
ctx.lineTo(cx + size * 0.4, cy + size * 0.2);
ctx.lineTo(cx + size * 0.12, cy + size * 0.1);
ctx.closePath();
ctx.fill();
map.updateImage(id, { width: size, height: size, data: new Uint8Array(ctx.getImageData(0, 0, size, size).data) });
};
updateMapIcon('plane-cyan', flightCom, 24);
updateMapIcon('plane-green', flightPriv, 24);
updateMapIcon('plane-pink', flightGov, 24);
updateMapIcon('plane-red', flightMil, 24);
updateMapIcon('plane-grey', isGhost ? phantomPurple : '#546E7A', 24);
}, [mapReady, theme]);
// ── DECOUPLED LAYER RENDERERS (Performance Optimized) ──
useEffect(() => {
if (!mapReady) return;
setGeo('earthquakes', activeLayers.earthquakes && data.earthquakes ? data.earthquakes.map((eq: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [eq.lng, eq.lat] }, properties: { magnitude: eq.magnitude, place: eq.place } })) : []);
}, [mapReady, data.earthquakes, activeLayers.earthquakes, setGeo]);
useEffect(() => {
if (!mapReady) return;
const sats = data.satellites || [];
const al = activeLayers as any;
// If 'All Satellites' is on, show everything
if (al.satellites) {
setGeo('satellites', sats.map((s: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [s.lng, s.lat] }, properties: { name: s.name, color: s.color, mission: s.mission, alt: s.alt, noradId: s.noradId, category: s.category } })));
return;
}
// Otherwise filter by enabled sub-layers
const enabledCategories: string[] = [];
if (al.sat_comms) enabledCategories.push('comms');
if (al.sat_military) enabledCategories.push('military');
if (al.sat_navigation) enabledCategories.push('navigation');
if (al.sat_earth) enabledCategories.push('earth_obs');
if (al.sat_science) enabledCategories.push('science');
if (enabledCategories.length === 0) {
setGeo('satellites', []);
return;
}
const filtered = sats.filter((s: any) => enabledCategories.includes(s.category));
setGeo('satellites', filtered.map((s: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [s.lng, s.lat] }, properties: { name: s.name, color: s.color, mission: s.mission, alt: s.alt, noradId: s.noradId, category: s.category } })));
}, [mapReady, data.satellites, activeLayers.satellites, (activeLayers as any).sat_comms, (activeLayers as any).sat_military, (activeLayers as any).sat_navigation, (activeLayers as any).sat_earth, (activeLayers as any).sat_science, setGeo]);
useEffect(() => {
if (!mapReady) return;
setGeo('gdelt', activeLayers.global_incidents && data.gdelt ? data.gdelt.map((e: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [e.lng, e.lat] }, properties: { name: e.name } })) : []);
}, [mapReady, data.gdelt, activeLayers.global_incidents, setGeo]);
// Malware Threats
useEffect(() => {
if (!mapReady) return;
setGeo('malware-nodes', activeLayers.malware && data.malware_threats ? data.malware_threats.map((t: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [t.lng, t.lat] }, properties: { ip: t.ip, malware: t.malware, status: t.status, threat_type: t.threat_type, country: t.country } })) : []);
}, [mapReady, data.malware_threats, activeLayers.malware, setGeo]);
// Network Mesh Generation (Nearest Neighbor Lattice)
useEffect(() => {
if (!mapReady) return;
const meshLinks: any[] = [];
// Generate Malware Botnet Mesh
if (activeLayers.malware && data.malware_threats && data.malware_threats.length > 1) {
const nodes = data.malware_threats;
for (let i = 0; i < nodes.length; i++) {
// Connect each to next 2 for a global web
for (let j = 1; j <= 2; j++) {
const target = nodes[(i + j) % nodes.length];
meshLinks.push({
type: 'Feature',
geometry: { type: 'LineString', coordinates: [[nodes[i].lng, nodes[i].lat], [target.lng, target.lat]] },
properties: { threat_type: 'malware' }
});
}
}
}
setGeo('network-mesh', meshLinks);
}, [mapReady, activeLayers.malware, data.malware_threats, setGeo]);
useEffect(() => {
if (!mapReady) return;
setGeo('gps-jamming', activeLayers.gps_jamming && data.gps_jamming ? data.gps_jamming.map((z: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [z.lng, z.lat] }, properties: { severity: z.severity } })) : []);
}, [mapReady, data.gps_jamming, activeLayers.gps_jamming, setGeo]);
useEffect(() => {
if (!mapReady) return;
setGeo('cctv', activeLayers.cctv && data.cameras ? data.cameras.map((c: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [c.lng, c.lat] }, properties: { id: c.id, name: c.name, city: c.city, country: c.country, source: c.source, feed_url: c.feed_url, stream_url: c.stream_url, stream_type: c.stream_type, external_url: c.external_url } })) : []);
}, [mapReady, data.cameras, activeLayers.cctv, setGeo]);
useEffect(() => {
if (!mapReady) return;
setGeo('fires', activeLayers.fires && data.fires ? data.fires.map((f: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [f.lng, f.lat] }, properties: { brightness: f.brightness } })) : []);
}, [mapReady, data.fires, activeLayers.fires, setGeo]);
useEffect(() => {
if (!mapReady) return;
setGeo('weather', activeLayers.weather && data.weather_events ? data.weather_events.map((w: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [w.lng, w.lat] }, properties: { title: w.title, type: w.type, icon: w.icon, severity: w.severity, source: w.source, id: w.id } })) : []);
}, [mapReady, data.weather_events, activeLayers.weather, setGeo]);
useEffect(() => {
if (!mapReady) return;
setGeo('infrastructure', activeLayers.infrastructure && data.infrastructure ? data.infrastructure.map((i: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [i.lng, i.lat] }, properties: { name: i.name, city: i.city, country: i.country, status: i.status, reactors: i.reactors, capacityMW: i.capacityMW, owner: i.owner } })) : []);
}, [mapReady, data.infrastructure, activeLayers.infrastructure, setGeo]);
useEffect(() => {
if (!mapReady) return;
setGeo('mining-sites', activeLayers.mining && data.mining_companies ? data.mining_companies.map((m: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [m.lng, m.lat] }, properties: { company: m.company, site: m.site, site_type: m.site_type, country: m.country, category: m.category, commodities: m.commodities, ticker: m.ticker, exchange: m.exchange, hq: m.hq, notes: m.notes } })) : []);
}, [mapReady, data.mining_companies, activeLayers.mining, setGeo]);
useEffect(() => {
if (!mapReady) return;
setGeo('ram-sites', activeLayers.ram_fabs && data.ram_companies ? data.ram_companies.map((m: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [m.lng, m.lat] }, properties: { company: m.company, site: m.site, site_type: m.site_type, country: m.country, category: m.category, commodities: m.commodities, ticker: m.ticker, exchange: m.exchange, hq: m.hq, notes: m.notes } })) : []);
}, [mapReady, data.ram_companies, activeLayers.ram_fabs, setGeo]);
useEffect(() => {
if (!mapReady) return;
setGeo('maritime', activeLayers.maritime && data.maritime_ports ? data.maritime_ports.map((p: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [p.lng, p.lat] }, properties: { name: p.name, country: p.country, type: p.type, volume: p.volume, fleet: p.fleet, rank: p.rank } })) : []);
setGeo('maritime-choke', activeLayers.maritime && data.maritime_chokepoints ? data.maritime_chokepoints.map((c: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [c.lng, c.lat] }, properties: { name: c.name, traffic: c.traffic, risk: c.risk } })) : []);
setGeo('maritime-ships', activeLayers.maritime && data.maritime_ships ? data.maritime_ships.map((s: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [s.lng, s.lat] }, properties: { name: s.name || s.mmsi?.toString(), type: s.type || 'cargo', speed: s.speed, heading: s.heading, destination: s.destination, flag: s.flag } })) : []);
}, [mapReady, data.maritime_ports, data.maritime_chokepoints, data.maritime_ships, activeLayers.maritime, setGeo]);
useEffect(() => {
if (!mapReady) return;
setGeo('balloons', activeLayers.balloons && data.balloons ? data.balloons.map((b: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [b.lng, b.lat] }, properties: { callsign: b.callsign, type: b.type, status: b.status, altitude: b.altitude, speed: b.speed, verticalRate: b.verticalRate, temperature: b.temperature, color: b.color } })) : []);
}, [mapReady, data.balloons, activeLayers.balloons, setGeo]);
useEffect(() => {
if (!mapReady) return;
setGeo('radiation', activeLayers.radiation && data.radiation ? data.radiation.map((r: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [r.lng, r.lat] }, properties: { name: r.name, city: r.city, country: r.country, reading: r.reading, status: r.status, network: r.network } })) : []);
}, [mapReady, data.radiation, activeLayers.radiation, setGeo]);
// ══ OSIRIS SDK — Lattice Sensor Mesh ══
// Uses real submarine cable data for SEA domain, curated routes for AIR/INTEL
useEffect(() => {
if (!mapReady) return;
setGeo('sdk-entities', []);
const anySDK = activeLayers.sdk_sea || activeLayers.sdk_air || activeLayers.sdk_naval;
if (!anySDK) {
setGeo('sdk-links', []);
return;
}
const links: any[] = [];
// ── SEA DOMAIN: Real submarine cable data (1-for-1 Match) ──
if (activeLayers.sdk_sea && data.submarine_cables) {
const ignoredColors = new Set(['#9BB5CC', '#A0B8CD', '#8EABC2', '#9bb5cc', '#a0b8cd', '#8eabc2']);
for (const cable of data.submarine_cables) {
if (!cable.geometry) continue;
// Remove the light blue background arcs
if (cable.properties?.color && ignoredColors.has(cable.properties.color)) continue;
links.push({
type: 'Feature',
geometry: cable.geometry, // Raw topographic paths exactly from Submarine Map
properties: {
domain: 'SEA',
fromName: cable.properties?.name || 'Submarine Cable',
toName: cable.properties?.landing_points || '',
source: 'Global Subsea Cable Network',
url: 'https://www.submarinecablemap.com/',
...cable.properties,
color: '#1976D2', // Darker blue as requested, more transparent in layer paint
},
});
}
}
setGeo('sdk-links', links);
}, [mapReady, activeLayers.sdk_sea, activeLayers.sdk_air, activeLayers.sdk_naval, data.submarine_cables, setGeo]);
useEffect(() => {
if (!mapReady) return;
setGeo('live-news', activeLayers.live_news && data.live_feeds ? data.live_feeds.map((f: any) => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [f.lng, f.lat] }, properties: { name: f.name, city: f.city, country: f.country, url: f.url, category: f.category, embed_allowed: f.embed_allowed !== false } })) : []);
}, [mapReady, data.live_feeds, activeLayers.live_news, setGeo]);
useEffect(() => {
if (!mapReady) return;
const items = data.news || [];
setGeo('sigint-news', activeLayers.news_intel && items.length > 0
? items.filter((n: any) => n.coords?.length === 2).map((n: any) => ({
type: 'Feature',
geometry: { type: 'Point', coordinates: [n.coords[1], n.coords[0]] },
properties: { title: n.title, source: n.source, risk_score: n.risk_score, link: n.link }
}))
: []);
}, [mapReady, data.news, activeLayers.news_intel, setGeo]);
useEffect(() => {
if (!mapReady) return;
// 🔴 CONFLICT ZONES - Live from /api/conflicts 🔴
let cancelled = false;
(async () => {
try {
const res = await fetch('/api/conflicts');
if (cancelled) return;
if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
const conflictData = await res.json();
if (cancelled) return;
// Zone anchor markers (war/high/elevated labels)
const zoneFeatures = (conflictData.zones || []).map((z: any) => ({
type: 'Feature' as const,
geometry: { type: 'Point' as const, coordinates: [z.lng, z.lat] },
properties: {
label: z.label,
severity: z.severity,
description: `${z.description}${z.eventCount > 0 ? ` [${z.eventCount} live events detected]` : ''}`,
sourceUrl: z.sourceUrl,
eventCount: z.eventCount,
},
}));
// Individual live conflict events (scatter dots across conflict zones)
const eventFeatures = (conflictData.liveEvents || [])
.filter((e: any) => e.lat && e.lng)
.map((e: any) => ({
type: 'Feature' as const,
geometry: { type: 'Point' as const, coordinates: [e.lng, e.lat] },
properties: {
label: (e.title || 'CONFLICT EVENT').substring(0, 60).toUpperCase(),
severity: 'war',
description: e.title || 'Live conflict event detected by GDELT.',
sourceUrl: e.url || '',
},
}));
setGeo('conflict-zones', [...zoneFeatures, ...eventFeatures]);
} catch (e) {
// Fallback: if API fails, use minimal known zones
const FALLBACK_ZONES = [
{ label: 'UKRAINE WAR', severity: 'war', lat: 48.5, lng: 31.2, description: 'Ongoing Russian invasion of Ukraine.', sourceUrl: 'https://liveuamap.com/' },
{ label: 'GAZA CONFLICT', severity: 'war', lat: 31.35, lng: 34.35, description: 'Active military operations in Gaza.', sourceUrl: 'https://israelpalestine.liveuamap.com/' },
{ label: 'SUDAN CIVIL WAR', severity: 'war', lat: 15.0, lng: 30.0, description: 'SAF vs RSF armed conflict.', sourceUrl: 'https://sudan.liveuamap.com/' },
{ label: 'YEMEN WAR', severity: 'war', lat: 15.5, lng: 48.0, description: 'Houthi operations and Red Sea threats.', sourceUrl: 'https://yemen.liveuamap.com/' },
{ label: 'MYANMAR CONFLICT', severity: 'war', lat: 19.5, lng: 96.5, description: 'Military junta vs opposition forces.', sourceUrl: 'https://myanmar.liveuamap.com/' },
{ label: 'SYRIA', severity: 'high', lat: 35.0, lng: 38.5, description: 'Ongoing civil conflict.', sourceUrl: 'https://syria.liveuamap.com/' },
];
const fallbackFeatures = FALLBACK_ZONES.map(z => ({
type: 'Feature' as const,
geometry: { type: 'Point' as const, coordinates: [z.lng, z.lat] },
properties: { label: z.label, severity: z.severity, description: z.description, sourceUrl: z.sourceUrl },
}));
setGeo('conflict-zones', fallbackFeatures);
}
})();
return () => { cancelled = true; };
}, [mapReady, setGeo]);
// Visibility
useEffect(() => {
if (!mapReady) return;
setVis(['eq-circles','eq-label'], activeLayers.earthquakes);
const anySat = activeLayers.satellites || (activeLayers as any).sat_comms || (activeLayers as any).sat_military || (activeLayers as any).sat_navigation || (activeLayers as any).sat_earth || (activeLayers as any).sat_science;
setVis(['sat-glow','sat-dots'], anySat);
setVis(['gdelt-dots'], activeLayers.global_incidents);
setVis(['malware-glow','malware-dots','malware-label'], activeLayers.malware);
setVis(['network-mesh-atmo', 'network-mesh-glow', 'network-mesh-core'], activeLayers.internet_outages || activeLayers.malware);
setVis(['jam-fill','jam-label'], activeLayers.gps_jamming);
setVis(['day-night-fill'], activeLayers.day_night);
setVis(['fl-commercial'], activeLayers.flights);
setVis(['fl-private'], activeLayers.private);
setVis(['fl-jets'], activeLayers.jets);
setVis(['fl-military'], activeLayers.military);
setVis(['cctv-glow','cctv-dots','cctv-label'], activeLayers.cctv);
setVis(['fires-heat'], activeLayers.fires);
setVis(['weather-glow','weather-dots','weather-label'], activeLayers.weather);
setVis(['infra-glow','infra-dots','infra-label'], activeLayers.infrastructure);
setVis(['mining-glow','mining-dots','mining-label'], activeLayers.mining);
setVis(['ram-glow','ram-dots','ram-label'], activeLayers.ram_fabs);
setVis(['maritime-glow','maritime-dots','maritime-label'], activeLayers.maritime);
setVis(['choke-glow','choke-dots','choke-label'], activeLayers.maritime);
setVis(['ship-dots','ship-label'], activeLayers.maritime);
setVis(['news-glow','news-dots','news-label'], activeLayers.live_news);
setVis(['sigint-news-glow','sigint-news-dots','sigint-news-label'], activeLayers.news_intel);
setVis(['conflict-icons'], activeLayers.conflict_zones !== false);
setVis(['balloon-dots','balloon-label'], activeLayers.balloons);
setVis(['rad-glow','rad-dots','rad-label'], activeLayers.radiation);
setVis(['sdk-sea','sdk-sea-glow','sdk-sea-atmo'], activeLayers.sdk_sea !== false);
setVis(['sdk-air','sdk-air-glow','sdk-air-atmo'], activeLayers.sdk_air !== false);
setVis(['sdk-intel','sdk-intel-glow','sdk-intel-atmo'], activeLayers.sdk_naval !== false);
// Sweep layers always visible when data is present (controlled by useEffect)
setVis(['sweep-connections','sweep-pulse-ring','sweep-device-glow','sweep-device-dots','sweep-device-labels'], true);
}, [mapReady, activeLayers, setVis]);
// IP Sweep visualization
useEffect(() => {
if (!mapReady) return;
if (!sweepData?.devices?.length) {
setGeo('ip-sweep-devices', []);
setGeo('ip-sweep-pulse', []);
setGeo('ip-sweep-connections', []);
return;
}
const map = mapRef.current;
if (!map) return;
const { center, devices } = sweepData;
const centerCoord: [number, number] = [center.lng, center.lat];
// Switch to globe and fly to the sweep location
try {
(map as any).setProjection({ type: 'globe' });
map.setSky({ 'sky-color': '#0A0A0F', 'sky-horizon-blend': 0.02, 'horizon-color': '#0A0A0F', 'horizon-fog-blend': 0.02 });
} catch { /* projection may not be supported */ }
map.flyTo({ center: centerCoord, zoom: 14, pitch: 50, bearing: -20, duration: 3000, essential: true });
// Set center pulse
setGeo('ip-sweep-pulse', [{
type: 'Feature' as const,
geometry: { type: 'Point' as const, coordinates: centerCoord },
properties: { ip: sweepData.target_ip },
}]);
// Build device features spread in a circle around center
const allDeviceFeatures = devices.map((d: any, i: number) => {
const angle = (i / devices.length) * Math.PI * 2;
const radius = 0.001 + ((i % 7 + 1) * 0.0004);
const dLng = centerCoord[0] + Math.cos(angle) * radius * (1 / Math.cos(center.lat * Math.PI / 180));
const dLat = centerCoord[1] + Math.sin(angle) * radius;
return {
type: 'Feature' as const,
geometry: { type: 'Point' as const, coordinates: [dLng, dLat] },
properties: {
ip: d.ip, device_type: d.device_type, device_icon: d.device_icon,
color: d.device_color, risk_level: d.risk_level,
ports: JSON.stringify(d.ports), hostnames: JSON.stringify(d.hostnames),
vulns: JSON.stringify(d.vulns), cpes: JSON.stringify(d.cpes), tags: JSON.stringify(d.tags),
},
};
});
// Connection lines from center to each device
const connectionFeatures = allDeviceFeatures.map((f: any) => ({
type: 'Feature' as const,
geometry: { type: 'LineString' as const, coordinates: [centerCoord, f.geometry.coordinates] },
properties: { color: f.properties.color },
}));
// Stagger the appearance after 3s flyTo completes
const timer = setTimeout(() => {
setGeo('ip-sweep-connections', connectionFeatures);
const batchSize = 5;
const batches = Math.ceil(allDeviceFeatures.length / batchSize);
for (let b = 0; b < batches; b++) {
setTimeout(() => {
setGeo('ip-sweep-devices', allDeviceFeatures.slice(0, (b + 1) * batchSize));
}, b * 100);
}
}, 3000);
return () => clearTimeout(timer);
}, [mapReady, sweepData, setGeo]);
// Scan Targets visualization
useEffect(() => {
if (!mapReady || !mapRef.current || !scanTargets) return;
const map = mapRef.current;
const features = scanTargets.map(t => ({
type: 'Feature' as const,
geometry: { type: 'Point' as const, coordinates: [t.lng, t.lat] },
properties: { ...t }
}));
const src = map.getSource('scan-targets') as maplibregl.GeoJSONSource;
if (src) src.setData({ type: 'FeatureCollection', features });
}, [scanTargets, mapReady]);
// Fly-to
useEffect(() => {
if (!mapReady || !mapRef.current || !flyToLocation) return;
mapRef.current.flyTo({ center: [flyToLocation.lng, flyToLocation.lat], zoom: flyToLocation.zoom || 8, duration: 2000 });
}, [mapReady, flyToLocation]);
// Dynamic projection switching (lightweight — no terrain DEM)
useEffect(() => {
if (!mapReady || !mapRef.current) return;
const map = mapRef.current;
try {
(map as any).setProjection({ type: projection });
if (projection === 'globe') {
map.easeTo({ pitch: 20, duration: 1200 });
try {
(map as any).setSky({
'sky-color': '#04040A',
'sky-horizon-blend': 0.5,
'horizon-color': '#0a0a1a',
'horizon-fog-blend': 0.3,
'fog-color': '#04040A',
'fog-ground-blend': 0.9,
});
} catch (e) { console.warn('[OSIRIS] Suppressed error:', e instanceof Error ? e.message : e); }
} else {
map.easeTo({ pitch: 0, duration: 800 });
}
} catch (e) {
console.warn('Projection switch failed:', e);
}
}, [mapReady, projection]);
// 3D Terrain & Buildings layer
useEffect(() => {
if (!mapReady || !mapRef.current) return;
const map = mapRef.current;
const enabled = activeLayers.terrain_3d;
try {
if (enabled) {
// ── 3D BUILDINGS SOURCE (OpenFreeMap CDN — no API key, globally cached) ──
if (!map.getSource('osiris-buildings')) {
map.addSource('osiris-buildings', {
type: 'vector',
url: 'https://tiles.openfreemap.org/planet',
});
}
// ── 3D BUILDING EXTRUSION LAYER ──
if (!map.getLayer('osiris-3d-buildings')) {
map.addLayer({
id: 'osiris-3d-buildings',
source: 'osiris-buildings',
'source-layer': 'building',
type: 'fill-extrusion',
minzoom: 14.5,
paint: {
'fill-extrusion-color': [
'interpolate', ['linear'], ['get', 'render_height'],
0, '#1a1a2e',
20, '#16213e',
50, '#0f3460',
120, '#533483',
300, '#e94560',
],
'fill-extrusion-height': [
'interpolate', ['linear'], ['zoom'],
14.5, 0,
15.5, ['get', 'render_height']
],
'fill-extrusion-base': [
'interpolate', ['linear'], ['zoom'],
14.5, 0,
15.5, ['get', 'render_min_height']
],
'fill-extrusion-opacity': [
'interpolate', ['linear'], ['zoom'],
14.5, 0,
15, 0.7,
],
},
});
}
// Pitch the camera to reveal the 3D skyline
if (map.getPitch() < 40) {
map.easeTo({ pitch: 50, duration: 1200 });
}
} else {
// ── DISABLE 3D ──
if (map.getLayer('osiris-3d-buildings')) map.removeLayer('osiris-3d-buildings');
}
} catch (e) {
console.warn('[OSIRIS] 3D terrain toggle error:', e);
}
}, [mapReady, activeLayers.terrain_3d]);
// Satellite / Dark style switching
useEffect(() => {
if (!mapReady || !mapRef.current) return;
if (mapStyle === prevStyleRef.current) return;
prevStyleRef.current = mapStyle;
const map = mapRef.current;
try {
if (mapStyle !== 'dark') {
// Add satellite raster tiles
if (!map.getSource('satellite-tiles')) {
map.addSource('satellite-tiles', {
type: 'raster',
tiles: ['https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'],
tileSize: 256,
maxzoom: 18,
});
map.addLayer({ id: 'satellite-layer', type: 'raster', source: 'satellite-tiles', paint: { 'raster-opacity': 0.85 } }, 'day-night-fill');
} else {
map.setLayoutProperty('satellite-layer', 'visibility', 'visible');
}
} else {
if (map.getLayer('satellite-layer')) {
map.setLayoutProperty('satellite-layer', 'visibility', 'none');
}
}
} catch (e) {
console.warn('Style switch failed:', e);
}
}, [mapReady, mapStyle]);
return <div ref={containerRef} className="absolute inset-0 w-full h-full" />;
}
export default memo(OsirisMap);