diff --git a/src/data_flights.py b/src/data_flights.py index 1721ffb..c79666e 100644 --- a/src/data_flights.py +++ b/src/data_flights.py @@ -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(): diff --git a/static/css/globe.css b/static/css/globe.css index d130923..241fb6a 100644 --- a/static/css/globe.css +++ b/static/css/globe.css @@ -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; +} diff --git a/static/js/app.js b/static/js/app.js index 39e7f53..b5c78a2 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -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);