Neue Features: Satelliten-Orbits, Naturkatastrophen, Visual Modes

SATELLITEN (CelesTrak TLE):
- Raumstationen (ISS), GPS, Galileo, Wetter, Erdbeobachtung, Starlink
- Echtzeit-Positionsberechnung aus Kepler-Elementen (2s Update)
- Orbitbahnen als leuchtende Linien (Stationen, GPS, Galileo)
- Farbkodiert nach Gruppe (rot=Station, orange=GPS, blau=Galileo)

NATURKATASTROPHEN (NASA EONET):
- Waldbraende, Vulkane, Stuerme, Fluten, Erdrutsche, Eis
- Farbige Punkte mit Emoji-Labels
- Klick zeigt Details und Quellen

VISUAL MODES:
- STD: Standard-Ansicht
- NVG: Nachtsicht (gruener Monochrom-Filter)
- FLIR: Thermal-Ansicht (invertiert, Infrarot-Look)
- CRT: Retro-Monitor (Scanlines, Vignette)

4 neue Dateien: satellites.js, disasters.js, visualmodes.js,
data_satellites.py, data_disasters.py
Dieser Commit ist enthalten in:
Claude Dev
2026-03-24 12:50:04 +01:00
Ursprung 1c462c3041
Commit 9b4ea4cd28
9 geänderte Dateien mit 493 neuen und 0 gelöschten Zeilen

30
src/data_disasters.py Normale Datei
Datei anzeigen

@@ -0,0 +1,30 @@
"""Naturkatastrophen: NASA EONET (Earth Observatory Natural Event Tracker)."""
import logging
import time
import httpx
from fastapi import APIRouter
logger = logging.getLogger("globe.disasters")
router = APIRouter()
_cache: dict = {"data": None, "ts": 0}
@router.get("/disasters")
async def get_disasters():
now = time.time()
if _cache["data"] and now - _cache["ts"] < 600: # 10min Cache
return _cache["data"]
try:
async with httpx.AsyncClient(timeout=12) as client:
r = await client.get("https://eonet.gsfc.nasa.gov/api/v3/events?status=open&limit=100")
r.raise_for_status()
data = r.json()
_cache["data"] = data
_cache["ts"] = time.time()
logger.info(f"Disasters: {len(data.get('events', []))} aktive Ereignisse")
return data
except Exception as e:
logger.warning(f"NASA EONET Fehler: {e}")
return _cache["data"] or {"events": []}

69
src/data_satellites.py Normale Datei
Datei anzeigen

@@ -0,0 +1,69 @@
"""Satelliten-Daten: CelesTrak TLE Orbital Elements."""
import logging
import time
import httpx
from fastapi import APIRouter
logger = logging.getLogger("globe.satellites")
router = APIRouter()
_cache: dict = {"data": None, "ts": 0}
# Wichtigste Satellitengruppen (nicht alle 14.000)
_GROUPS = [
("stations", "Raumstationen (ISS etc.)"),
("gps-ops", "GPS Navigationssatelliten"),
("galileo", "Galileo Navigation"),
("weather", "Wettersatelliten"),
("resource", "Erdbeobachtung"),
("starlink", "Starlink (Auswahl)"),
("active", None), # Fallback: alle aktiven, wird gefiltert
]
@router.get("/satellites")
async def get_satellites():
now = time.time()
if _cache["data"] and now - _cache["ts"] < 3600: # 1h Cache
return _cache["data"]
all_sats = []
try:
async with httpx.AsyncClient(timeout=15) as client:
for group, label in _GROUPS:
if group == "active":
continue # Zu gross, skip
url = f"https://celestrak.org/NORAD/elements/gp.php?GROUP={group}&FORMAT=json"
try:
r = await client.get(url)
if r.status_code == 200:
sats = r.json()
# Starlink: nur 200 nehmen (sind tausende)
if group == "starlink":
sats = sats[:200]
for s in sats:
all_sats.append({
"name": s.get("OBJECT_NAME", "?"),
"id": s.get("NORAD_CAT_ID"),
"group": group,
"epoch": s.get("EPOCH"),
"meanMotion": s.get("MEAN_MOTION"),
"eccentricity": s.get("ECCENTRICITY"),
"inclination": s.get("INCLINATION"),
"raOfAscNode": s.get("RA_OF_ASC_NODE"),
"argOfPericenter": s.get("ARG_OF_PERICENTER"),
"meanAnomaly": s.get("MEAN_ANOMALY"),
"bstar": s.get("BSTAR"),
"meanMotionDot": s.get("MEAN_MOTION_DOT"),
})
except Exception as e:
logger.warning(f"CelesTrak {group}: {e}")
except Exception as e:
logger.warning(f"CelesTrak Fehler: {e}")
return _cache["data"] or {"satellites": [], "total": 0}
_cache["data"] = {"satellites": all_sats, "total": len(all_sats)}
_cache["ts"] = time.time()
logger.info(f"Satellites: {len(all_sats)} geladen")
return _cache["data"]

Datei anzeigen

@@ -31,12 +31,16 @@ from data_flights import router as flights_router, start_flight_collector
from data_ships import router as ships_router, start_ais_collector
from data_quakes import router as quakes_router
from data_gdelt import router as gdelt_router
from data_satellites import router as satellites_router
from data_disasters import router as disasters_router
# Alle Daten-APIs hinter Auth
app.include_router(flights_router, prefix="/api", dependencies=[Depends(get_current_user)])
app.include_router(ships_router, prefix="/api", dependencies=[Depends(get_current_user)])
app.include_router(quakes_router, prefix="/api", dependencies=[Depends(get_current_user)])
app.include_router(gdelt_router, prefix="/api", dependencies=[Depends(get_current_user)])
app.include_router(satellites_router, prefix="/api", dependencies=[Depends(get_current_user)])
app.include_router(disasters_router, prefix="/api", dependencies=[Depends(get_current_user)])
# --- Static files ---
static_dir = Path(__file__).parent.parent / "static"