mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-07-04 11:27:57 +02:00
v0.9.5: The Voltron Update — modular architecture, stable IDs, parallelized boot
- Parallelized startup (60s → 15s) via ThreadPoolExecutor - Adaptive polling engine with ETag caching (no more bbox interrupts) - useCallback optimization for interpolation functions - Sliding LAYERS/INTEL edge panels replace bulky Record Panel - Modular fetcher architecture (flights, geo, infrastructure, financial, earth_observation) - Stable entity IDs for GDELT & News popups (PR #63, credit @csysp) - Admin auth (X-Admin-Key), rate limiting (slowapi), auto-updater - Docker Swarm secrets support, env_check.py validation - 85+ vitest tests, CI pipeline, geoJSON builder extraction - Server-side viewport bbox filtering reduces payloads 80%+ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Former-commit-id: f2883150b5bc78ebc139d89cc966a76f7d7c0408
This commit is contained in:
@@ -0,0 +1,423 @@
|
||||
// ─── Pure GeoJSON builder functions ─────────────────────────────────────────
|
||||
// Extracted from MaplibreViewer to reduce component size and enable unit testing.
|
||||
// Each function takes data arrays + optional helpers and returns a GeoJSON FeatureCollection or null.
|
||||
|
||||
import type { Earthquake, GPSJammingZone, FireHotspot, InternetOutage, DataCenter, GDELTIncident, LiveUAmapIncident, CCTVCamera, KiwiSDR, FrontlineGeoJSON, UAV, Satellite, Ship, ActiveLayers } from "@/types/dashboard";
|
||||
import { classifyAircraft } from "@/utils/aircraftClassification";
|
||||
import { MISSION_COLORS, MISSION_ICON_MAP } from "@/components/map/icons/SatelliteIcons";
|
||||
|
||||
type FC = GeoJSON.FeatureCollection | null;
|
||||
type InViewFilter = (lat: number, lng: number) => boolean;
|
||||
|
||||
// ─── Earthquakes ────────────────────────────────────────────────────────────
|
||||
|
||||
export function buildEarthquakesGeoJSON(earthquakes?: Earthquake[]): FC {
|
||||
if (!earthquakes?.length) return null;
|
||||
return {
|
||||
type: 'FeatureCollection',
|
||||
features: earthquakes.map((eq, i) => {
|
||||
if (eq.lat == null || eq.lng == null) return null;
|
||||
return {
|
||||
type: 'Feature' as const,
|
||||
properties: {
|
||||
id: i,
|
||||
type: 'earthquake',
|
||||
name: `[M${eq.mag}]\n${eq.place || 'Unknown Location'}`,
|
||||
title: eq.title,
|
||||
},
|
||||
geometry: { type: 'Point' as const, coordinates: [eq.lng, eq.lat] }
|
||||
};
|
||||
}).filter(Boolean) as GeoJSON.Feature[]
|
||||
};
|
||||
}
|
||||
|
||||
// ─── GPS Jamming Zones ──────────────────────────────────────────────────────
|
||||
|
||||
export function buildJammingGeoJSON(zones?: GPSJammingZone[]): FC {
|
||||
if (!zones?.length) return null;
|
||||
return {
|
||||
type: 'FeatureCollection',
|
||||
features: zones.map((zone, i) => {
|
||||
const halfDeg = 0.5;
|
||||
return {
|
||||
type: 'Feature' as const,
|
||||
properties: {
|
||||
id: i,
|
||||
severity: zone.severity,
|
||||
ratio: zone.ratio,
|
||||
degraded: zone.degraded,
|
||||
total: zone.total,
|
||||
opacity: zone.severity === 'high' ? 0.45 : zone.severity === 'medium' ? 0.3 : 0.18
|
||||
},
|
||||
geometry: {
|
||||
type: 'Polygon' as const,
|
||||
coordinates: [[
|
||||
[zone.lng - halfDeg, zone.lat - halfDeg],
|
||||
[zone.lng + halfDeg, zone.lat - halfDeg],
|
||||
[zone.lng + halfDeg, zone.lat + halfDeg],
|
||||
[zone.lng - halfDeg, zone.lat + halfDeg],
|
||||
[zone.lng - halfDeg, zone.lat - halfDeg]
|
||||
]]
|
||||
}
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
// ─── CCTV Cameras ──────────────────────────────────────────────────────────
|
||||
|
||||
export function buildCctvGeoJSON(cameras?: CCTVCamera[], inView?: InViewFilter): FC {
|
||||
if (!cameras?.length) return null;
|
||||
return {
|
||||
type: 'FeatureCollection' as const,
|
||||
features: cameras.filter(c => c.lat != null && c.lon != null && (!inView || inView(c.lat, c.lon))).map((c, i) => ({
|
||||
type: 'Feature' as const,
|
||||
properties: {
|
||||
id: c.id || i,
|
||||
type: 'cctv',
|
||||
name: c.direction_facing || 'Camera',
|
||||
source_agency: c.source_agency || 'Unknown',
|
||||
media_url: c.media_url || '',
|
||||
media_type: c.media_type || 'image'
|
||||
},
|
||||
geometry: { type: 'Point' as const, coordinates: [c.lon, c.lat] }
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
// ─── KiwiSDR Receivers ─────────────────────────────────────────────────────
|
||||
|
||||
export function buildKiwisdrGeoJSON(receivers?: KiwiSDR[], inView?: InViewFilter): FC {
|
||||
if (!receivers?.length) return null;
|
||||
return {
|
||||
type: 'FeatureCollection' as const,
|
||||
features: receivers.filter(k => k.lat != null && k.lon != null && (!inView || inView(k.lat, k.lon))).map((k, i) => ({
|
||||
type: 'Feature' as const,
|
||||
properties: {
|
||||
id: i,
|
||||
type: 'kiwisdr',
|
||||
name: k.name || 'Unknown SDR',
|
||||
url: k.url || '',
|
||||
users: k.users || 0,
|
||||
users_max: k.users_max || 0,
|
||||
bands: k.bands || '',
|
||||
antenna: k.antenna || '',
|
||||
location: k.location || '',
|
||||
lat: k.lat,
|
||||
lon: k.lon,
|
||||
},
|
||||
geometry: { type: 'Point' as const, coordinates: [k.lon, k.lat] }
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
// ─── NASA FIRMS Fires ───────────────────────────────────────────────────────
|
||||
|
||||
export function buildFirmsGeoJSON(fires?: FireHotspot[]): FC {
|
||||
if (!fires?.length) return null;
|
||||
return {
|
||||
type: 'FeatureCollection',
|
||||
features: fires.map((f, i) => {
|
||||
const frp = f.frp || 0;
|
||||
const iconId = frp >= 100 ? 'fire-darkred' : frp >= 20 ? 'fire-red' : frp >= 5 ? 'fire-orange' : 'fire-yellow';
|
||||
return {
|
||||
type: 'Feature' as const,
|
||||
properties: {
|
||||
id: i,
|
||||
type: 'firms_fire',
|
||||
name: `Fire ${frp.toFixed(1)} MW`,
|
||||
frp,
|
||||
iconId,
|
||||
brightness: f.brightness || 0,
|
||||
confidence: f.confidence || '',
|
||||
daynight: f.daynight === 'D' ? 'Day' : 'Night',
|
||||
acq_date: f.acq_date || '',
|
||||
acq_time: f.acq_time || '',
|
||||
},
|
||||
geometry: { type: 'Point' as const, coordinates: [f.lng, f.lat] }
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Internet Outages ───────────────────────────────────────────────────────
|
||||
|
||||
export function buildInternetOutagesGeoJSON(outages?: InternetOutage[]): FC {
|
||||
if (!outages?.length) return null;
|
||||
return {
|
||||
type: 'FeatureCollection',
|
||||
features: outages.map((o) => {
|
||||
if (o.lat == null || o.lng == null) return null;
|
||||
const severity = o.severity || 0;
|
||||
const region = o.region_name || o.region_code || '?';
|
||||
const country = o.country_name || o.country_code || '';
|
||||
const label = `${region}, ${country}`;
|
||||
const detail = `${label}\n${severity}% drop · ${o.datasource || 'IODA'}`;
|
||||
return {
|
||||
type: 'Feature' as const,
|
||||
properties: {
|
||||
id: o.region_code || region,
|
||||
type: 'internet_outage',
|
||||
name: label,
|
||||
country,
|
||||
region,
|
||||
level: o.level,
|
||||
severity,
|
||||
datasource: o.datasource || '',
|
||||
detail,
|
||||
},
|
||||
geometry: { type: 'Point' as const, coordinates: [o.lng, o.lat] }
|
||||
};
|
||||
}).filter(Boolean) as GeoJSON.Feature[]
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Data Centers ───────────────────────────────────────────────────────────
|
||||
|
||||
export function buildDataCentersGeoJSON(datacenters?: DataCenter[]): FC {
|
||||
if (!datacenters?.length) return null;
|
||||
return {
|
||||
type: 'FeatureCollection',
|
||||
features: datacenters.map((dc, i) => ({
|
||||
type: 'Feature' as const,
|
||||
properties: {
|
||||
id: `dc-${i}`,
|
||||
type: 'datacenter',
|
||||
name: dc.name || 'Unknown',
|
||||
company: dc.company || '',
|
||||
street: dc.street || '',
|
||||
city: dc.city || '',
|
||||
country: dc.country || '',
|
||||
zip: dc.zip || '',
|
||||
},
|
||||
geometry: { type: 'Point' as const, coordinates: [dc.lng, dc.lat] }
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
// ─── GDELT Incidents ────────────────────────────────────────────────────────
|
||||
|
||||
export function buildGdeltGeoJSON(gdelt?: GDELTIncident[], inView?: InViewFilter): FC {
|
||||
if (!gdelt?.length) return null;
|
||||
return {
|
||||
type: 'FeatureCollection',
|
||||
features: gdelt.map((g) => {
|
||||
if (!g.geometry || !g.geometry.coordinates) return null;
|
||||
const [gLng, gLat] = g.geometry.coordinates;
|
||||
if (inView && !inView(gLat, gLng)) return null;
|
||||
return {
|
||||
type: 'Feature' as const,
|
||||
properties: { id: g.properties?.name || String(g.geometry.coordinates), type: 'gdelt', title: g.properties?.name || '' },
|
||||
geometry: g.geometry
|
||||
};
|
||||
}).filter(Boolean) as GeoJSON.Feature[]
|
||||
};
|
||||
}
|
||||
|
||||
// ─── LiveUAMap Incidents ────────────────────────────────────────────────────
|
||||
|
||||
export function buildLiveuaGeoJSON(incidents?: LiveUAmapIncident[], inView?: InViewFilter): FC {
|
||||
if (!incidents?.length) return null;
|
||||
return {
|
||||
type: 'FeatureCollection',
|
||||
features: incidents.map((incident) => {
|
||||
if (incident.lat == null || incident.lng == null) return null;
|
||||
if (inView && !inView(incident.lat, incident.lng)) return null;
|
||||
const isViolent = /bomb|missil|strike|attack|kill|destroy|fire|shoot|expl|raid/i.test(incident.title || "");
|
||||
return {
|
||||
type: 'Feature' as const,
|
||||
properties: {
|
||||
id: incident.id,
|
||||
type: 'liveuamap',
|
||||
title: incident.title || '',
|
||||
iconId: isViolent ? 'icon-liveua-red' : 'icon-liveua-yellow',
|
||||
},
|
||||
geometry: { type: 'Point' as const, coordinates: [incident.lng, incident.lat] }
|
||||
};
|
||||
}).filter(Boolean) as GeoJSON.Feature[]
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Ukraine Frontline ──────────────────────────────────────────────────────
|
||||
|
||||
export function buildFrontlineGeoJSON(frontlines?: FrontlineGeoJSON | null): FC {
|
||||
if (!frontlines?.features?.length) return null;
|
||||
return frontlines;
|
||||
}
|
||||
|
||||
// ─── Parameterized Flight Layer ─────────────────────────────────────────────
|
||||
// Deduplicates commercial / private / jets / military flight GeoJSON builders.
|
||||
|
||||
export interface FlightLayerConfig {
|
||||
colorMap: Record<string, string>;
|
||||
groundedMap: Record<string, string>;
|
||||
typeLabel: string;
|
||||
idPrefix: string;
|
||||
/** For military flights: special icon overrides by military_type */
|
||||
milSpecialMap?: Record<string, string>;
|
||||
/** If true, prefer true_track over heading for rotation (commercial flights) */
|
||||
useTrackHeading?: boolean;
|
||||
}
|
||||
|
||||
export function buildFlightLayerGeoJSON(
|
||||
flights: any[] | undefined,
|
||||
config: FlightLayerConfig,
|
||||
helpers: {
|
||||
interpFlight: (f: any) => [number, number];
|
||||
inView: InViewFilter;
|
||||
trackedIcaoSet: Set<string>;
|
||||
}
|
||||
): FC {
|
||||
if (!flights?.length) return null;
|
||||
const { colorMap, groundedMap, typeLabel, idPrefix, milSpecialMap, useTrackHeading } = config;
|
||||
const { interpFlight, inView, trackedIcaoSet } = helpers;
|
||||
return {
|
||||
type: 'FeatureCollection',
|
||||
features: flights.map((f: any, i: number) => {
|
||||
if (f.lat == null || f.lng == null) return null;
|
||||
if (!inView(f.lat, f.lng)) return null;
|
||||
if (f.icao24 && trackedIcaoSet.has(f.icao24.toLowerCase())) return null;
|
||||
const acType = classifyAircraft(f.model, f.aircraft_category);
|
||||
const grounded = f.alt != null && f.alt <= 100;
|
||||
|
||||
let iconId: string;
|
||||
if (milSpecialMap) {
|
||||
const milType = f.military_type || 'default';
|
||||
iconId = milSpecialMap[milType] || '';
|
||||
if (!iconId) {
|
||||
iconId = grounded ? groundedMap[acType] : colorMap[acType];
|
||||
} else if (grounded) {
|
||||
iconId = groundedMap[acType];
|
||||
}
|
||||
} else {
|
||||
iconId = grounded ? groundedMap[acType] : colorMap[acType];
|
||||
}
|
||||
|
||||
const rotation = useTrackHeading ? (f.true_track || f.heading || 0) : (f.heading || 0);
|
||||
const [iLng, iLat] = interpFlight(f);
|
||||
return {
|
||||
type: 'Feature' as const,
|
||||
properties: { id: f.icao24 || f.callsign || `${idPrefix}${i}`, type: typeLabel, callsign: f.callsign || f.icao24, rotation, iconId },
|
||||
geometry: { type: 'Point' as const, coordinates: [iLng, iLat] }
|
||||
};
|
||||
}).filter(Boolean) as GeoJSON.Feature[]
|
||||
};
|
||||
}
|
||||
|
||||
// ─── UAVs / Drones ──────────────────────────────────────────────────────────
|
||||
|
||||
export function buildUavGeoJSON(uavs?: UAV[], inView?: InViewFilter): FC {
|
||||
if (!uavs?.length) return null;
|
||||
return {
|
||||
type: 'FeatureCollection',
|
||||
features: uavs.map((uav, i) => {
|
||||
if (uav.lat == null || uav.lng == null) return null;
|
||||
if (inView && !inView(uav.lat, uav.lng)) return null;
|
||||
return {
|
||||
type: 'Feature' as const,
|
||||
properties: {
|
||||
id: (uav as any).id || `uav-${i}`,
|
||||
type: 'uav',
|
||||
callsign: uav.callsign,
|
||||
rotation: uav.heading || 0,
|
||||
iconId: 'svgDrone',
|
||||
name: uav.aircraft_model || uav.callsign,
|
||||
country: uav.country || '',
|
||||
uav_type: uav.uav_type || '',
|
||||
alt: uav.alt || 0,
|
||||
wiki: uav.wiki || '',
|
||||
speed_knots: uav.speed_knots || 0,
|
||||
icao24: uav.icao24 || '',
|
||||
registration: uav.registration || '',
|
||||
squawk: uav.squawk || '',
|
||||
},
|
||||
geometry: { type: 'Point' as const, coordinates: [uav.lng, uav.lat] }
|
||||
};
|
||||
}).filter(Boolean) as GeoJSON.Feature[]
|
||||
};
|
||||
}
|
||||
// ─── Satellites ─────────────────────────────────────────────────────────────
|
||||
|
||||
export function buildSatellitesGeoJSON(
|
||||
satellites: Satellite[] | undefined,
|
||||
inView: InViewFilter,
|
||||
interpSat: (s: Satellite) => [number, number]
|
||||
): FC {
|
||||
if (!satellites?.length) return null;
|
||||
return {
|
||||
type: 'FeatureCollection',
|
||||
features: satellites
|
||||
.filter((s) => s.lat != null && s.lng != null && inView(s.lat, s.lng))
|
||||
.map((s, i) => ({
|
||||
type: 'Feature' as const,
|
||||
properties: {
|
||||
id: s.id || i, type: 'satellite', name: s.name, mission: s.mission || 'general',
|
||||
sat_type: s.sat_type || 'Satellite', country: s.country || '', alt_km: s.alt_km || 0,
|
||||
wiki: s.wiki || '', color: MISSION_COLORS[s.mission] || '#aaaaaa',
|
||||
iconId: MISSION_ICON_MAP[s.mission] || 'sat-gen'
|
||||
},
|
||||
geometry: { type: 'Point' as const, coordinates: interpSat(s) }
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Ships (non-carrier) ────────────────────────────────────────────────────
|
||||
|
||||
export function buildShipsGeoJSON(
|
||||
ships: Ship[] | undefined,
|
||||
activeLayers: ActiveLayers,
|
||||
inView: InViewFilter,
|
||||
interpShip: (s: Ship) => [number, number]
|
||||
): FC {
|
||||
if (!(activeLayers.ships_military || activeLayers.ships_cargo || activeLayers.ships_civilian || activeLayers.ships_passenger || activeLayers.ships_tracked_yachts) || !ships) return null;
|
||||
return {
|
||||
type: 'FeatureCollection',
|
||||
features: ships.map((s, i) => {
|
||||
if (s.lat == null || s.lng == null) return null;
|
||||
if (!inView(s.lat, s.lng)) return null;
|
||||
const isTrackedYacht = !!s.yacht_alert;
|
||||
const isMilitary = s.type === 'carrier' || s.type === 'military_vessel';
|
||||
const isCargo = s.type === 'tanker' || s.type === 'cargo';
|
||||
const isPassenger = s.type === 'passenger';
|
||||
|
||||
if (s.type === 'carrier') return null; // Handled by buildCarriersGeoJSON
|
||||
|
||||
if (isTrackedYacht) {
|
||||
if (activeLayers?.ships_tracked_yachts === false) return null;
|
||||
} else if (isMilitary && activeLayers?.ships_military === false) return null;
|
||||
else if (isCargo && activeLayers?.ships_cargo === false) return null;
|
||||
else if (isPassenger && activeLayers?.ships_passenger === false) return null;
|
||||
else if (!isMilitary && !isCargo && !isPassenger && activeLayers?.ships_civilian === false) return null;
|
||||
|
||||
let iconId = 'svgShipBlue';
|
||||
if (isTrackedYacht) iconId = 'svgShipPink';
|
||||
else if (isCargo) iconId = 'svgShipRed';
|
||||
else if (s.type === 'yacht' || isPassenger) iconId = 'svgShipWhite';
|
||||
else if (isMilitary) iconId = 'svgShipYellow';
|
||||
|
||||
const [iLng, iLat] = interpShip(s);
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties: { id: s.mmsi || s.name || `ship-${i}`, type: 'ship', name: s.name, rotation: s.heading || 0, iconId },
|
||||
geometry: { type: 'Point', coordinates: [iLng, iLat] }
|
||||
};
|
||||
}).filter(Boolean) as GeoJSON.Feature[]
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Carriers ───────────────────────────────────────────────────────────────
|
||||
|
||||
export function buildCarriersGeoJSON(ships: Ship[] | undefined): FC {
|
||||
if (!ships?.length) return null;
|
||||
return {
|
||||
type: 'FeatureCollection',
|
||||
features: ships.map((s, i) => {
|
||||
if (s.type !== 'carrier' || s.lat == null || s.lng == null) return null;
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties: { id: s.mmsi || s.name || `carrier-${i}`, type: 'ship', name: s.name, rotation: s.heading || 0, iconId: 'svgCarrier' },
|
||||
geometry: { type: 'Point', coordinates: [s.lng, s.lat] }
|
||||
};
|
||||
}).filter(Boolean) as GeoJSON.Feature[]
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user