fix: aircraft categorization, fullscreen satellite imagery, region dossier rate-limit, updated map legend

- Fixed 288+ miscategorized aircraft in plane_alert_db.json (gov/police/medical)
- data_fetcher.py: tracked_names enrichment now assigns blue/lime colors for gov/law/medical operators
- region_dossier.py: fixed Nominatim 429 rate-limiting with retry/backoff
- MaplibreViewer.tsx: Sentinel-2 popup replaced with fullscreen overlay + download/copy buttons
- MapLegend.tsx: updated to show all 9 tracked aircraft color categories + POTUS fleet + wildfires + infrastructure


Former-commit-id: d109434616
This commit is contained in:
anoracleofra-code
2026-03-11 14:29:18 -06:00
parent 5ab02e821f
commit d78bf61256
7 changed files with 287 additions and 82 deletions
@@ -1 +1 @@
eb32e2f229fad1d5a14fe81e071e71591f875673
38a18cbbf1acbec5eb9266b809c28d31e2941c53
+14 -2
View File
@@ -337,10 +337,22 @@ def enrich_with_tracked_names(flight: dict) -> dict:
match = _TRACKED_NAMES_DB[callsign]
if match:
name = match["name"]
# Let Excel take precedence as it has cleaner individual names (e.g. Elon Musk instead of FALCON LANDING LLC).
flight["alert_operator"] = match["name"]
flight["alert_operator"] = name
flight["alert_category"] = match["category"]
if "alert_color" not in flight:
# Override pink default if the name implies a specific function
name_lower = name.lower()
is_gov = any(w in name_lower for w in ['state of ', 'government', 'republic', 'ministry', 'department', 'federal', 'cia'])
is_law = any(w in name_lower for w in ['police', 'marshal', 'sheriff', 'douane', 'customs', 'patrol', 'gendarmerie', 'guardia', 'law enforcement'])
is_med = any(w in name_lower for w in ['fire', 'bomberos', 'ambulance', 'paramedic', 'medevac', 'rescue', 'hospital', 'medical', 'lifeflight'])
if is_gov or is_law:
flight["alert_color"] = "blue"
elif is_med:
flight["alert_color"] = "#32cd32" # lime
elif "alert_color" not in flight:
flight["alert_color"] = "pink"
return flight
+36 -14
View File
@@ -1,6 +1,8 @@
import logging
import time
import concurrent.futures
from urllib.parse import quote
import requests as _requests
from cachetools import TTLCache
from services.network_utils import fetch_with_curl
@@ -10,26 +12,46 @@ logger = logging.getLogger(__name__)
# Key: rounded lat/lng grid (0.1 degree ≈ 11km)
dossier_cache = TTLCache(maxsize=500, ttl=86400)
# Nominatim requires max 1 req/sec — track last call time
_nominatim_last_call = 0.0
def _reverse_geocode(lat: float, lng: float) -> dict:
global _nominatim_last_call
url = (
f"https://nominatim.openstreetmap.org/reverse?"
f"lat={lat}&lon={lng}&format=json&zoom=10&addressdetails=1&accept-language=en"
)
try:
res = fetch_with_curl(url, timeout=10)
if res.status_code == 200:
data = res.json()
addr = data.get("address", {})
return {
"city": addr.get("city") or addr.get("town") or addr.get("village") or addr.get("county") or "",
"state": addr.get("state") or addr.get("region") or "",
"country": addr.get("country") or "",
"country_code": (addr.get("country_code") or "").upper(),
"display_name": data.get("display_name", ""),
}
except Exception as e:
logger.warning(f"Reverse geocode failed: {e}")
headers = {"User-Agent": "ShadowBroker-OSINT/1.0 (live-risk-dashboard; contact@shadowbroker.app)"}
for attempt in range(2):
# Enforce Nominatim's 1 req/sec policy
elapsed = time.time() - _nominatim_last_call
if elapsed < 1.1:
time.sleep(1.1 - elapsed)
_nominatim_last_call = time.time()
try:
# Use requests directly — fetch_with_curl raises on non-200 which breaks 429 handling
res = _requests.get(url, timeout=10, headers=headers)
if res.status_code == 200:
data = res.json()
addr = data.get("address", {})
return {
"city": addr.get("city") or addr.get("town") or addr.get("village") or addr.get("county") or "",
"state": addr.get("state") or addr.get("region") or "",
"country": addr.get("country") or "",
"country_code": (addr.get("country_code") or "").upper(),
"display_name": data.get("display_name", ""),
}
elif res.status_code == 429:
logger.warning(f"Nominatim 429 rate-limited, retrying after 2s (attempt {attempt+1})")
time.sleep(2)
continue
else:
logger.warning(f"Nominatim returned {res.status_code}")
except Exception as e:
logger.warning(f"Reverse geocode failed: {e}")
return {}
+2 -4
View File
@@ -656,13 +656,11 @@ export default function CesiumViewer({ data, activeLayers, activeFilters, effect
}
if (filters.tracked_owner?.length) {
const op = (f.alert_operator || '').toLowerCase();
const t1 = (f.alert_tag1 || '').toLowerCase();
const t2 = (f.alert_tag2 || '').toLowerCase();
const t3 = (f.alert_tag3 || '').toLowerCase();
const tags = (f.alert_tags || '').toLowerCase();
const cs = (f.callsign || '').toLowerCase();
if (!filters.tracked_owner.some(sv => {
const q = sv.toLowerCase();
return op.includes(q) || t1.includes(q) || t2.includes(q) || t3.includes(q) || cs.includes(q);
return op.includes(q) || tags.includes(q) || cs.includes(q);
})) return false;
}
return true;
+1 -2
View File
@@ -106,8 +106,7 @@ export default function FilterPanel({ data, activeFilters, setActiveFilters }: F
const ops = new Set<string>(trackedOperators);
for (const f of data?.tracked_flights || []) {
if (f.alert_operator) ops.add(f.alert_operator);
if (f.alert_tag1) ops.add(f.alert_tag1);
if (f.alert_tag2) ops.add(f.alert_tag2);
if (f.alert_tags) ops.add(f.alert_tags);
}
return Array.from(ops).sort();
}, [data?.tracked_flights]);
+33 -4
View File
@@ -101,10 +101,23 @@ const LEGEND: LegendCategory[] = [
name: "TRACKED AIRCRAFT (ALERT)",
color: "text-pink-400 border-pink-500/30",
items: [
{ svg: airliner("#FF1493"), label: "Alert — Low Priority (pink)" },
{ svg: airliner("#FF2020"), label: "Alert — High Priority (red)" },
{ svg: airliner("#1A3A8A"), label: "Alert — Government (navy)" },
{ svg: airliner("white"), label: "Alert — General (white)" },
{ svg: airliner("#FF1493"), label: "VIP / Celebrity / Bizjet (hot pink)" },
{ svg: airliner("#FF2020"), label: "Dictator / Oligarch (red)" },
{ svg: airliner("#3b82f6"), label: "Government / Police / Customs (blue)" },
{ svg: heli("#32CD32"), label: "Medical / Fire / Rescue (lime)" },
{ svg: airliner("yellow"), label: "Military / Intelligence (yellow)" },
{ svg: airliner("#222"), label: "PIA — Privacy / Stealth (black)" },
{ svg: airliner("#FF8C00"), label: "Private Flights / Joe Cool (orange)" },
{ svg: airliner("white"), label: "Climate Crisis (white)" },
{ svg: airliner("#9B59B6"), label: "Private Jets / Historic / Other (purple)" },
],
},
{
name: "POTUS FLEET",
color: "text-yellow-400 border-yellow-500/30",
items: [
{ svg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 32 32"><circle cx="16" cy="16" r="14" fill="none" stroke="gold" stroke-width="2" stroke-dasharray="4 2"/><g transform="translate(6,6)"><path d="M12 2C11.2 2 10.5 2.8 10.5 3.5V8.5L3 13V15L10.5 12.5V18L8 19.5V21L12 19.5L16 21V19.5L13.5 18V12.5L21 15V13L13.5 8.5V3.5C13.5 2.8 12.8 2 12 2Z" fill="#FF1493" stroke="black" stroke-width="0.5"/></g></svg>`, label: "Air Force One / Two (gold ring)" },
{ svg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 32 32"><circle cx="16" cy="16" r="14" fill="none" stroke="gold" stroke-width="2" stroke-dasharray="4 2"/><g transform="translate(8,6)"><path d="M10 6L10 14L8 16L8 18L10 17L12 22L14 17L16 18L16 16L14 14L14 6C14 4 13 2 12 2C11 2 10 4 10 6Z" fill="#FF1493" stroke="black" stroke-width="0.5"/></g></svg>`, label: "Marine One (gold ring + heli)" },
],
},
{
@@ -141,6 +154,14 @@ const LEGEND: LegendCategory[] = [
{ svg: circle("#ff6600"), label: "Earthquake (size = magnitude)" },
],
},
{
name: "WILDFIRES",
color: "text-red-400 border-red-500/30",
items: [
{ svg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path d="M12 1C8 7 5 10 5 14a7 7 0 0 0 14 0c0-4-3-7-7-13z" fill="#ff6600" stroke="#ffcc00" stroke-width="1"/></svg>`, label: "Active wildfire / hotspot" },
{ svg: clusterCircle("#cc0000", "#ff3300"), label: "Fire cluster (grouped hotspots)" },
],
},
{
name: "INCIDENTS & INTELLIGENCE",
color: "text-red-400 border-red-500/30",
@@ -166,6 +187,14 @@ const LEGEND: LegendCategory[] = [
{ svg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" fill="#ff0040" stroke="#000" stroke-width="1" opacity="0.2" rx="2"/></svg>`, label: "Low severity (25-50% degraded)" },
],
},
{
name: "INFRASTRUCTURE",
color: "text-purple-400 border-purple-500/30",
items: [
{ svg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#a78bfa" stroke-width="1.5"><rect x="3" y="3" width="18" height="6" rx="1" fill="#2e1065"/><rect x="3" y="11" width="18" height="6" rx="1" fill="#2e1065"/><circle cx="7" cy="6" r="1" fill="#a78bfa"/><circle cx="7" cy="14" r="1" fill="#a78bfa"/></svg>`, label: "Data Center" },
{ svg: circle("#ef4444"), label: "Internet Outage Zone" },
],
},
{
name: "SURVEILLANCE / CCTV",
color: "text-green-400 border-green-500/30",
+200 -55
View File
@@ -2723,64 +2723,209 @@ const MaplibreViewer = ({ data, activeLayers, onEntityClick, flyToLocation, sele
</Marker>
)}
{/* SENTINEL-2 IMAGERY — floating intel card on map near right-click */}
{selectedEntity?.type === 'region_dossier' && selectedEntity.extra && regionDossier?.sentinel2 && !regionDossierLoading && (
<Popup
longitude={selectedEntity.extra.lng}
latitude={selectedEntity.extra.lat}
anchor="top-left"
offset={[20, -10]}
closeButton={false}
closeOnClick={false}
className="sentinel-popup"
maxWidth="320px"
>
<div className="bg-black/90 backdrop-blur-md border border-blue-500/50 rounded-lg overflow-hidden shadow-[0_0_25px_rgba(59,130,246,0.3)] pointer-events-auto" style={{ width: 300 }}>
{/* Header bar */}
<div className="flex items-center justify-between px-3 py-1.5 bg-blue-950/60 border-b border-blue-500/30">
<div className="flex items-center gap-2">
<div className="w-1.5 h-1.5 rounded-full bg-blue-400 animate-pulse" />
<span className="text-[9px] text-blue-400 font-mono tracking-[0.2em] font-bold">SENTINEL-2 IMAGERY</span>
{/* SENTINEL-2 IMAGERY — fullscreen overlay modal */}
{selectedEntity?.type === 'region_dossier' && selectedEntity.extra && regionDossier?.sentinel2 && !regionDossierLoading && (() => {
const s2 = regionDossier.sentinel2;
const imgUrl = s2.fullres_url || s2.thumbnail_url;
return (
<div
style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: 9999,
background: 'rgba(0,0,0,0.85)',
backdropFilter: 'blur(8px)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '60px 20px 20px 20px',
}}
onClick={(e) => { if (e.target === e.currentTarget) onEntityClick(null); }}
onKeyDown={(e: any) => { if (e.key === 'Escape') onEntityClick(null); }}
tabIndex={-1}
ref={(el) => el?.focus()}
>
<div style={{
background: 'rgba(0,0,0,0.95)',
border: '1px solid rgba(59,130,246,0.5)',
borderRadius: 12,
overflow: 'hidden',
maxWidth: 'calc(100vw - 40px)',
maxHeight: 'calc(100vh - 80px)',
display: 'flex',
flexDirection: 'column',
boxShadow: '0 0 60px rgba(59,130,246,0.3)',
}}>
{/* Header bar */}
<div style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '10px 16px',
background: 'rgba(30,58,138,0.4)',
borderBottom: '1px solid rgba(59,130,246,0.3)',
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<div style={{ width: 6, height: 6, borderRadius: '50%', background: '#60a5fa', animation: 'pulse 2s infinite' }} />
<span style={{ fontSize: 11, color: '#60a5fa', fontFamily: 'monospace', letterSpacing: '0.2em', fontWeight: 'bold' }}>
SENTINEL-2 IMAGERY
</span>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<span style={{ fontSize: 10, color: 'rgba(147,197,253,0.6)', fontFamily: 'monospace' }}>
{selectedEntity.extra.lat.toFixed(4)}, {selectedEntity.extra.lng.toFixed(4)}
</span>
<button
onClick={() => onEntityClick(null)}
style={{
background: 'rgba(239,68,68,0.2)',
border: '1px solid rgba(239,68,68,0.4)',
borderRadius: 6,
color: '#ef4444',
fontSize: 10,
fontFamily: 'monospace',
padding: '4px 10px',
cursor: 'pointer',
letterSpacing: '0.1em',
}}
>
CLOSE
</button>
</div>
</div>
<span className="text-[8px] text-blue-300/60 font-mono">{selectedEntity.extra.lat.toFixed(3)}, {selectedEntity.extra.lng.toFixed(3)}</span>
{s2.found ? (
<>
{/* Metadata row */}
<div style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '8px 16px',
fontSize: 11,
fontFamily: 'monospace',
borderBottom: '1px solid rgba(30,58,138,0.4)',
}}>
<span style={{ color: '#93c5fd' }}>{s2.platform}</span>
<span style={{ color: '#22d3ee', fontWeight: 'bold' }}>{s2.datetime?.slice(0, 10)}</span>
<span style={{ color: '#93c5fd' }}>{s2.cloud_cover?.toFixed(0)}% cloud</span>
</div>
{/* Image */}
{imgUrl ? (
<div style={{ flex: 1, overflow: 'auto', display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: 400 }}>
<img
src={imgUrl}
alt="Sentinel-2 scene"
style={{
maxWidth: '100%',
maxHeight: 'calc(100vh - 220px)',
objectFit: 'contain',
display: 'block',
}}
/>
</div>
) : (
<div style={{ padding: '40px 16px', fontSize: 11, color: 'rgba(147,197,253,0.5)', fontFamily: 'monospace', textAlign: 'center' }}>
Scene found no preview available
</div>
)}
{/* Action buttons */}
{imgUrl && (
<div style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: 12,
padding: '10px 16px',
background: 'rgba(30,58,138,0.3)',
borderTop: '1px solid rgba(59,130,246,0.2)',
}}>
<a
href={imgUrl}
download={`sentinel2_${selectedEntity.extra.lat.toFixed(4)}_${selectedEntity.extra.lng.toFixed(4)}.jpg`}
target="_blank"
rel="noopener noreferrer"
style={{
background: 'rgba(59,130,246,0.2)',
border: '1px solid rgba(59,130,246,0.5)',
borderRadius: 6,
color: '#60a5fa',
fontSize: 10,
fontFamily: 'monospace',
padding: '6px 16px',
cursor: 'pointer',
textDecoration: 'none',
letterSpacing: '0.15em',
fontWeight: 'bold',
}}
>
DOWNLOAD
</a>
<button
onClick={async () => {
try {
const resp = await fetch(imgUrl);
const blob = await resp.blob();
await navigator.clipboard.write([
new ClipboardItem({ [blob.type]: blob })
]);
} catch {
// fallback: copy URL
await navigator.clipboard.writeText(imgUrl);
}
}}
style={{
background: 'rgba(34,211,238,0.15)',
border: '1px solid rgba(34,211,238,0.4)',
borderRadius: 6,
color: '#22d3ee',
fontSize: 10,
fontFamily: 'monospace',
padding: '6px 16px',
cursor: 'pointer',
letterSpacing: '0.15em',
fontWeight: 'bold',
}}
>
📋 COPY
</button>
<a
href={imgUrl}
target="_blank"
rel="noopener noreferrer"
style={{
background: 'rgba(16,185,129,0.15)',
border: '1px solid rgba(16,185,129,0.4)',
borderRadius: 6,
color: '#10b981',
fontSize: 10,
fontFamily: 'monospace',
padding: '6px 16px',
cursor: 'pointer',
textDecoration: 'none',
letterSpacing: '0.15em',
fontWeight: 'bold',
}}
>
OPEN FULL RES
</a>
</div>
)}
</>
) : (
<div style={{ padding: '40px 16px', fontSize: 11, color: 'rgba(147,197,253,0.5)', fontFamily: 'monospace', textAlign: 'center' }}>
No clear imagery in last 30 days
</div>
)}
</div>
{regionDossier.sentinel2.found ? (
<>
{/* Metadata row */}
<div className="flex items-center justify-between px-3 py-1.5 text-[9px] font-mono border-b border-blue-900/40">
<span className="text-blue-300">{regionDossier.sentinel2.platform}</span>
<span className="text-cyan-400 font-bold">{regionDossier.sentinel2.datetime?.slice(0, 10)}</span>
<span className="text-blue-300">{regionDossier.sentinel2.cloud_cover?.toFixed(0)}% cloud</span>
</div>
{/* Thumbnail */}
{regionDossier.sentinel2.thumbnail_url ? (
<a href={regionDossier.sentinel2.fullres_url || regionDossier.sentinel2.thumbnail_url} target="_blank" rel="noopener noreferrer">
<img
src={regionDossier.sentinel2.thumbnail_url}
alt="Sentinel-2 scene"
className="w-full block hover:brightness-110 transition-all cursor-pointer"
style={{ maxHeight: 220, objectFit: 'cover' }}
/>
</a>
) : (
<div className="px-3 py-4 text-[9px] text-blue-300/50 font-mono text-center">Scene found no preview available</div>
)}
{/* Footer */}
<div className="px-3 py-1 bg-blue-950/40 text-[7px] text-blue-400/50 font-mono tracking-widest text-center">
CLICK IMAGE TO OPEN FULL RESOLUTION
</div>
</>
) : (
<div className="px-3 py-4 text-[9px] text-blue-300/50 font-mono text-center">
No clear imagery in last 30 days
</div>
)}
</div>
</Popup>
)}
);
})()}
{/* MEASUREMENT LINES */}
{measurePoints && measurePoints.length >= 2 && (