Fix: Ortsnamen-Layer, InfoBox bei Klick, globale Flugabdeckung

Ortsnamen: Esri World Boundaries als zuschaltbarer Imagery-Layer.
InfoBox: CSS-Ausblendung entfernt, Dark-Theme Styling.
Flugverkehr: Batch-Groesse 3, Pause 3s, zufaellige Reihenfolge —
alle Regionen bekommen Daten statt nur Europa.
Dieser Commit ist enthalten in:
Claude Dev
2026-03-24 11:44:13 +01:00
Ursprung 785c9b1e9e
Commit cbb6596513
3 geänderte Dateien mit 49 neuen und 8 gelöschten Zeilen

Datei anzeigen

@@ -27,10 +27,12 @@ _lock = asyncio.Lock()
_task = None
import random
async def _fetch_all():
"""Holt Flugdaten fuer alle Stuetzpunkte."""
now = time.time()
if _cache["data"] and now - _cache["ts"] < 25:
if _cache["data"] and now - _cache["ts"] < 45:
return _cache["data"]
async with _lock:
@@ -39,9 +41,11 @@ async def _fetch_all():
seen = {}
errors = 0
grid = list(_GRID)
random.shuffle(grid)
async with httpx.AsyncClient(timeout=10) as client:
for i in range(0, len(_GRID), 10):
batch = _GRID[i:i+10]
for i in range(0, len(grid), 3):
batch = grid[i:i+3]
tasks = [client.get(f"https://api.airplanes.live/v2/point/{lat:.2f}/{lon:.2f}/250")
for lat, lon in batch]
results = await asyncio.gather(*tasks, return_exceptions=True)
@@ -56,8 +60,8 @@ async def _fetch_all():
seen[h] = ac
except Exception:
errors += 1
if i + 10 < len(_GRID):
await asyncio.sleep(0.2)
if i + 3 < len(grid):
await asyncio.sleep(3.0)
_cache["data"] = {"ac": list(seen.values()), "total": len(seen), "errors": errors}
_cache["ts"] = time.time()
@@ -73,7 +77,7 @@ async def _collector_loop():
await _fetch_all()
except Exception as e:
logger.warning(f"Flight collector error: {e}")
await asyncio.sleep(30)
await asyncio.sleep(60)
def start_flight_collector():

Datei anzeigen

@@ -19,7 +19,7 @@ html, body { height: 100%; overflow: hidden; background: var(--bg-primary); colo
.cesium-viewer-animationContainer,
.cesium-viewer-timelineContainer,
.cesium-viewer-fullscreenContainer,
.cesium-viewer-infoBoxContainer,
.cesium-viewer-geocoderContainer,
.cesium-viewer-bottom { display: none !important; }
.cesium-credit-logoContainer { opacity: 0.3; }
@@ -92,3 +92,23 @@ html, body { height: 100%; overflow: hidden; background: var(--bg-primary); colo
/* === Cesium InfoBox Override === */
.cesium-infoBox { background: var(--bg-panel) !important; border: 1px solid var(--border) !important; }
.cesium-infoBox-title { color: var(--accent) !important; font-family: var(--font-mono) !important; }
/* === InfoBox (Entity-Details bei Klick) === */
.cesium-infoBox {
background: var(--bg-panel) !important;
border: 1px solid var(--border) !important;
border-radius: 8px !important;
box-shadow: 0 8px 32px rgba(0,0,0,0.5) !important;
}
.cesium-infoBox-title {
color: var(--accent) !important;
font-family: var(--font-mono) !important;
font-size: 12px !important;
background: rgba(0,255,136,0.08) !important;
}
.cesium-infoBox-iframe {
background: var(--bg-primary) !important;
}
.cesium-selection-wrapper {
border-color: var(--accent) !important;
}

Datei anzeigen

@@ -5,6 +5,7 @@ const Globe = {
viewer: null,
layers: {},
_statsInterval: null,
_labelsLayer: null,
init() {
// Cesium Ion Token
@@ -82,6 +83,22 @@ const Globe = {
document.getElementById('bottom-stats').textContent = 'Globe initialisiert — Lade Daten...';
},
_toggleLabels(on) {
if (on && !this._labelsLayer) {
this._labelsLayer = this.viewer.imageryLayers.addImageryProvider(
new Cesium.UrlTemplateImageryProvider({
url: 'https://server.arcgisonline.com/ArcGIS/rest/services/Reference/World_Boundaries_and_Places/MapServer/tile/{z}/{y}/{x}',
maximumLevel: 18,
credit: 'Esri',
})
);
this._labelsLayer.alpha = 0.9;
} else if (!on && this._labelsLayer) {
this.viewer.imageryLayers.remove(this._labelsLayer);
this._labelsLayer = null;
}
},
_setupLayerToggles() {
var toggles = {
'layer-flights': function(on) { on ? FlightsLayer.start(Globe.viewer) : FlightsLayer.stop(); },
@@ -89,7 +106,7 @@ const Globe = {
'layer-quakes': function(on) { on ? QuakesLayer.start(Globe.viewer) : QuakesLayer.stop(); },
'layer-gdelt': function(on) { on ? GdeltLayer.start(Globe.viewer) : GdeltLayer.stop(); },
'layer-daynight': function(on) { Globe.viewer.scene.globe.enableLighting = on; },
'layer-labels': function(on) { /* Phase 2 */ },
'layer-labels': function(on) { Globe._toggleLabels(on); },
};
Object.keys(toggles).forEach(function(id) {
var cb = document.getElementById(id);