From 9b4ea4cd2816b359af953d299555b8c9f625ce0c Mon Sep 17 00:00:00 2001 From: Claude Dev Date: Tue, 24 Mar 2026 12:50:04 +0100 Subject: [PATCH] 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 --- src/data_disasters.py | 30 +++++++ src/data_satellites.py | 69 ++++++++++++++++ src/main.py | 4 + static/css/globe.css | 81 ++++++++++++++++++ static/index.html | 31 +++++++ static/js/app.js | 9 ++ static/js/layers/disasters.js | 86 +++++++++++++++++++ static/js/layers/satellites.js | 142 ++++++++++++++++++++++++++++++++ static/js/layers/visualmodes.js | 41 +++++++++ 9 files changed, 493 insertions(+) create mode 100644 src/data_disasters.py create mode 100644 src/data_satellites.py create mode 100644 static/js/layers/disasters.js create mode 100644 static/js/layers/satellites.js create mode 100644 static/js/layers/visualmodes.js diff --git a/src/data_disasters.py b/src/data_disasters.py new file mode 100644 index 0000000..ede16a7 --- /dev/null +++ b/src/data_disasters.py @@ -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": []} diff --git a/src/data_satellites.py b/src/data_satellites.py new file mode 100644 index 0000000..4a8ae7d --- /dev/null +++ b/src/data_satellites.py @@ -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"] diff --git a/src/main.py b/src/main.py index 35488f9..7af32d3 100644 --- a/src/main.py +++ b/src/main.py @@ -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" diff --git a/static/css/globe.css b/static/css/globe.css index 04a6e7d..beae127 100644 --- a/static/css/globe.css +++ b/static/css/globe.css @@ -149,3 +149,84 @@ html, body { height: 100%; overflow: hidden; background: var(--bg-primary); colo .layer-status.active { display: block; } + + +/* === Neue Layer-Dots === */ +.dot-satellites { background: #ff4444; } +.dot-disasters { background: #ff8800; } + +/* === Visual Mode Buttons === */ +.vmode-btn { + padding: 4px 8px; + font-family: var(--font-mono); + font-size: 9px; + font-weight: 700; + letter-spacing: 1px; + background: rgba(255,255,255,0.05); + border: 1px solid var(--border); + border-radius: 3px; + color: var(--text-dim); + cursor: pointer; + transition: all 0.2s; +} +.vmode-btn:hover { border-color: var(--accent); color: var(--text); } +.vmode-btn.active { background: rgba(0,255,136,0.15); border-color: var(--accent); color: var(--accent); } + +/* === Visual Overlay (CRT Scanlines) === */ +.visual-overlay { + position: fixed; + inset: 0; + z-index: 50; + pointer-events: none; +} +.vmode-crt-overlay { + background: repeating-linear-gradient( + 0deg, + transparent, + transparent 2px, + rgba(0, 0, 0, 0.08) 2px, + rgba(0, 0, 0, 0.08) 4px + ); + box-shadow: inset 0 0 120px rgba(0, 0, 0, 0.3); +} + +/* === Night Vision Mode === */ +.vmode-nvg { + filter: saturate(0) brightness(1.2) contrast(1.1); +} +.vmode-nvg .cesium-viewer { mix-blend-mode: normal; } +.vmode-nvg::after { + content: ''; + position: absolute; + inset: 0; + background: rgba(0, 255, 0, 0.08); + pointer-events: none; + z-index: 40; + mix-blend-mode: multiply; +} + +/* === FLIR Thermal Mode === */ +.vmode-flir { + filter: saturate(0) invert(1) contrast(1.3) brightness(0.9); +} +.vmode-flir::after { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(135deg, rgba(255,100,0,0.05), rgba(255,0,100,0.05)); + pointer-events: none; + z-index: 40; +} + +/* === CRT Mode === */ +.vmode-crt { + filter: contrast(1.1) brightness(0.95); +} +.vmode-crt::after { + content: ''; + position: absolute; + inset: 0; + background: radial-gradient(ellipse at center, transparent 60%, rgba(0,0,0,0.4) 100%); + pointer-events: none; + z-index: 40; +} diff --git a/static/index.html b/static/index.html index 2bd61ea..274f8e9 100644 --- a/static/index.html +++ b/static/index.html @@ -78,6 +78,22 @@
+ +
+
+ +
+
@@ -93,6 +109,17 @@
+
+
+
ANSICHT
+
+ + + + +
+
+
LAT: --  LON: --
@@ -100,6 +127,7 @@
+