5 neue Layer: Militaerflug, Seekabel, Infrastruktur, ISS Tracker
MILITAERFLUGVERKEHR (adsb.lol /v2/mil): - ~254 Militaerflugzeuge weltweit in Echtzeit (rot) - Callsign-Labels bei Zoom (RCH=USAF, GAF=Luftwaffe etc.) - Klick zeigt: Callsign, Registration, Typ, Hoehe, Squawk - 20s Refresh SEEKABEL (TeleGeography): - Untersee-Glasfaserkabel als cyan-Linien auf dem Globus - ~500+ internationale Kabelverbindungen - Geopolitisch relevant (Sabotage, Abhoerung) INFRASTRUKTUR (OpenStreetMap Overpass): - 348 Kernkraftwerke weltweit (gelb mit Orange-Rand) - Militaerflughaefen (rot) - Labels bei Zoom (<500km) - 24h Cache (statische Daten) ISS TRACKER (Open-Notify API): - Echtzeit-Position der ISS (roter Punkt, 420km Hoehe) - 5s Refresh, prominentes Label - Klick zeigt Details Backend: data_military.py, data_infra.py (2 neue Dateien) Frontend: military.js, cables.js, infra.js, iss.js (4 neue Dateien)
Dieser Commit ist enthalten in:
49
src/data_military.py
Normale Datei
49
src/data_military.py
Normale Datei
@@ -0,0 +1,49 @@
|
||||
"""Militaerflugverkehr: adsb.lol /v2/mil Endpoint."""
|
||||
import asyncio, logging, time, httpx
|
||||
from fastapi import APIRouter
|
||||
|
||||
logger = logging.getLogger("globe.military")
|
||||
router = APIRouter()
|
||||
_cache = {"data": None, "ts": 0}
|
||||
_task = None
|
||||
|
||||
async def _fetch():
|
||||
now = time.time()
|
||||
if _cache["data"] and now - _cache["ts"] < 15:
|
||||
return _cache["data"]
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=15) as client:
|
||||
r = await client.get("https://api.adsb.lol/v2/mil")
|
||||
r.raise_for_status()
|
||||
ac = []
|
||||
for a in r.json().get("ac", []):
|
||||
if a.get("lat") and a.get("lon"):
|
||||
ac.append({
|
||||
"hex": a.get("hex",""), "flight": (a.get("flight") or "").strip(),
|
||||
"reg": a.get("r",""), "type": a.get("t",""),
|
||||
"lat": a["lat"], "lon": a["lon"],
|
||||
"alt_baro": a.get("alt_baro"), "gs": a.get("gs"),
|
||||
"track": a.get("track"), "squawk": a.get("squawk",""),
|
||||
"dbFlags": a.get("dbFlags", 0),
|
||||
})
|
||||
_cache["data"] = {"ac": ac, "total": len(ac)}
|
||||
_cache["ts"] = time.time()
|
||||
logger.info(f"Military: {len(ac)} Flugzeuge")
|
||||
except Exception as e:
|
||||
logger.warning(f"Military: {e}")
|
||||
return _cache["data"] or {"ac": [], "total": 0}
|
||||
|
||||
async def _loop():
|
||||
await asyncio.sleep(5)
|
||||
while True:
|
||||
await _fetch()
|
||||
await asyncio.sleep(20)
|
||||
|
||||
def start_mil_collector():
|
||||
global _task
|
||||
if _task is None or _task.done():
|
||||
_task = asyncio.create_task(_loop())
|
||||
|
||||
@router.get("/military")
|
||||
async def get_military():
|
||||
return await _fetch()
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren