Files
intelligence-terminal/apis/sources/opensky.mjs
calesthio a8682c50d0 Add zoom-aware symbology, fix globe popovers, expand geo coverage (#34)
* Add zoom-aware symbology, fix globe popovers, expand geo coverage

Map rendering improvements based on user feedback:
- Globe markers now scale with camera altitude (onZoom hook)
- Priority-based visibility culls low-priority markers at world view
- Globe popovers use getScreenCoords for accurate positioning
- Flat map labels hidden at low zoom, revealed progressively
- Default globe altitude lowered from 2.5 to 1.8 for better fill
- Americas region zoom tightened to CONUS focus

Geographic coverage expansion:
- 4 new OpenSky air theaters: Caribbean, Gulf of Guinea, Cape Route, Horn of Africa
- Flight corridors now span Americas and Africa
- NOAA alerts extract centroid lat/lon from GeoJSON geometry
- EPA RadNet stations geocoded with hardcoded coords for 10 US cities
- ISS + Tiangong positions estimated from TLE orbital elements
- GDELT geoEvents() now called in briefing for mapped event points
- New legend entries: Weather Alert, EPA RadNet, Space Station, GDELT Event

* Fix null-safe coordinate checks and remove injected data blob

- Use `!= null` instead of truthy checks for lat/lon in noaa.mjs
  and inject.mjs so valid 0-coordinates (equator/prime meridian)
  are not silently dropped
- Reset jarvis.html `let D` back to null placeholder so generated
  runtime data is not part of the PR diff

* Remove re-injected data blob from jarvis.html

Reset let D back to null — previous commit was correct but
inject.mjs build verification re-injected the payload.
2026-03-17 19:34:08 -07:00

101 lines
3.8 KiB
JavaScript

// OpenSky Network — Real-time flight tracking
// Free for research. 4,000 API credits/day (no auth), 8,000 with account.
// Tracks all aircraft with ADS-B transponders including many military.
import { safeFetch } from '../utils/fetch.mjs';
const BASE = 'https://opensky-network.org/api';
// Get all current flights (global state vector)
export async function getAllFlights() {
return safeFetch(`${BASE}/states/all`, { timeout: 30000 });
}
// Get flights in a bounding box (lat/lon)
export async function getFlightsInArea(lamin, lomin, lamax, lomax) {
const params = new URLSearchParams({
lamin: String(lamin),
lomin: String(lomin),
lamax: String(lamax),
lomax: String(lomax),
});
return safeFetch(`${BASE}/states/all?${params}`, { timeout: 20000 });
}
// Get flights by specific aircraft (ICAO24 hex codes)
export async function getFlightsByIcao(icao24List) {
const icao = Array.isArray(icao24List) ? icao24List : [icao24List];
const params = icao.map(i => `icao24=${i}`).join('&');
return safeFetch(`${BASE}/states/all?${params}`, { timeout: 20000 });
}
// Get departures from an airport in a time range
export async function getDepartures(airportIcao, begin, end) {
const params = new URLSearchParams({
airport: airportIcao,
begin: String(Math.floor(begin / 1000)),
end: String(Math.floor(end / 1000)),
});
return safeFetch(`${BASE}/flights/departure?${params}`);
}
// Get arrivals at an airport
export async function getArrivals(airportIcao, begin, end) {
const params = new URLSearchParams({
airport: airportIcao,
begin: String(Math.floor(begin / 1000)),
end: String(Math.floor(end / 1000)),
});
return safeFetch(`${BASE}/flights/arrival?${params}`);
}
// Key hotspot regions for monitoring
const HOTSPOTS = {
middleEast: { lamin: 12, lomin: 30, lamax: 42, lomax: 65, label: 'Middle East' },
taiwan: { lamin: 20, lomin: 115, lamax: 28, lomax: 125, label: 'Taiwan Strait' },
ukraine: { lamin: 44, lomin: 22, lamax: 53, lomax: 41, label: 'Ukraine Region' },
baltics: { lamin: 53, lomin: 19, lamax: 60, lomax: 29, label: 'Baltic Region' },
southChinaSea: { lamin: 5, lomin: 105, lamax: 23, lomax: 122, label: 'South China Sea' },
koreanPeninsula: { lamin: 33, lomin: 124, lamax: 43, lomax: 132, label: 'Korean Peninsula' },
caribbean: { lamin: 18, lomin: -90, lamax: 30, lomax: -72, label: 'Caribbean' },
gulfOfGuinea: { lamin: -2, lomin: -5, lamax: 8, lomax: 10, label: 'Gulf of Guinea' },
capeRoute: { lamin: -38, lomin: 12, lamax: -28, lomax: 24, label: 'Cape Route' },
hornOfAfrica: { lamin: 5, lomin: 40, lamax: 15, lomax: 55, label: 'Horn of Africa' },
};
// Briefing — check hotspot regions for flight activity
export async function briefing() {
const hotspotEntries = Object.entries(HOTSPOTS);
const results = await Promise.all(
hotspotEntries.map(async ([key, box]) => {
const data = await getFlightsInArea(box.lamin, box.lomin, box.lamax, box.lomax);
const states = data?.states || [];
return {
region: box.label,
key,
totalAircraft: states.length,
// states format: [icao24, callsign, origin_country, ...]
byCountry: states.reduce((acc, s) => {
const country = s[2] || 'Unknown';
acc[country] = (acc[country] || 0) + 1;
return acc;
}, {}),
// Flag potentially interesting (military often have no callsign or specific patterns)
noCallsign: states.filter(s => !s[1]?.trim()).length,
highAltitude: states.filter(s => s[7] && s[7] > 12000).length, // >12km altitude
};
})
);
return {
source: 'OpenSky',
timestamp: new Date().toISOString(),
hotspots: results,
};
}
if (process.argv[1]?.endsWith('opensky.mjs')) {
const data = await briefing();
console.log(JSON.stringify(data, null, 2));
}