From 6f4c5ab3a6bed66b688f952faf5a39a1f6013783 Mon Sep 17 00:00:00 2001 From: Claude Dev Date: Tue, 24 Mar 2026 12:22:21 +0100 Subject: [PATCH] Ladebalken bei Layer-Aktivierung + Ortsnamen-Rendering verbessert - Animierter Ladebalken unter jedem Layer-Toggle bei Datenabruf - Status-Text (Lade Daten.../Fehler beim Laden) - Fetch-Wrapper: nur 401 redirected zum Login, nicht 403 - Ortsnamen: minimumLevel, tileWidth/Height, LINEAR Texture-Filter fuer konsistente Schriftgroessen beim Laden --- static/css/globe.css | 37 +++++++++++++++++++++++++++++++++++++ static/index.html | 10 +++++++++- static/js/app.js | 7 ++++++- static/js/layers/flights.js | 7 ++++++- static/js/layers/gdelt.js | 4 ++++ static/js/layers/quakes.js | 7 ++++++- static/js/layers/ships.js | 7 ++++++- 7 files changed, 74 insertions(+), 5 deletions(-) diff --git a/static/css/globe.css b/static/css/globe.css index 241fb6a..04a6e7d 100644 --- a/static/css/globe.css +++ b/static/css/globe.css @@ -112,3 +112,40 @@ html, body { height: 100%; overflow: hidden; background: var(--bg-primary); colo .cesium-selection-wrapper { border-color: var(--accent) !important; } + + +/* === Loading Indicator === */ +.layer-loading { + display: none; + height: 2px; + background: var(--accent); + position: relative; + overflow: hidden; + border-radius: 1px; + margin: 4px 0 0; +} +.layer-loading.active { + display: block; +} +.layer-loading::after { + content: ''; + position: absolute; + left: -40%; + width: 40%; + height: 100%; + background: linear-gradient(90deg, transparent, #00ff88, transparent); + animation: layer-loading-slide 1s ease-in-out infinite; +} +@keyframes layer-loading-slide { + 0% { left: -40%; } + 100% { left: 100%; } +} +.layer-status { + font-size: 9px; + color: var(--text-dim); + margin-top: 2px; + display: none; +} +.layer-status.active { + display: block; +} diff --git a/static/index.html b/static/index.html index c08aae6..d588f46 100644 --- a/static/index.html +++ b/static/index.html @@ -21,7 +21,7 @@ opts.headers['Authorization'] = 'Bearer ' + localStorage.getItem('globe_token'); } return _origFetch(url, opts).then(function(r) { - if (r.status === 401 || r.status === 403) { localStorage.removeItem('globe_token'); window.location.href = '/login'; } + if (r.status === 401) { localStorage.removeItem('globe_token'); window.location.href = '/login'; } return r; }); }; @@ -52,24 +52,32 @@ Flugverkehr - +
+
+
+
+
+
+
+
diff --git a/static/js/app.js b/static/js/app.js index cc81909..6c81153 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -89,11 +89,16 @@ const Globe = { 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}', + minimumLevel: 1, maximumLevel: 18, + tileWidth: 256, + tileHeight: 256, credit: 'Esri', }) ); - this._labelsLayer.alpha = 0.9; + this._labelsLayer.alpha = 0.95; + this._labelsLayer.minificationFilter = Cesium.TextureMinificationFilter.LINEAR; + this._labelsLayer.magnificationFilter = Cesium.TextureMagnificationFilter.LINEAR; } else if (!on && this._labelsLayer) { this.viewer.imageryLayers.remove(this._labelsLayer); this._labelsLayer = null; diff --git a/static/js/layers/flights.js b/static/js/layers/flights.js index 25f3078..3474c0b 100644 --- a/static/js/layers/flights.js +++ b/static/js/layers/flights.js @@ -28,6 +28,10 @@ const FlightsLayer = { _fetch() { var self = this; + var loadEl = document.getElementById('loading-flights'); + var statusEl = document.getElementById('status-flights'); + if (loadEl) loadEl.classList.add('active'); + if (statusEl) { statusEl.textContent = 'Lade Daten...'; statusEl.classList.add('active'); } fetch('/api/flights') .then(function(r) { return r.json(); }) .then(function(data) { @@ -72,6 +76,7 @@ const FlightsLayer = { }); }); }) - .catch(function(e) { console.warn('Flights fetch error:', e); }); + .catch(function(e) { console.warn('Flights fetch error:', e); if (statusEl) { statusEl.textContent = 'Fehler beim Laden'; } }) + .finally(function() { if (loadEl) loadEl.classList.remove('active'); setTimeout(function() { if (statusEl) statusEl.classList.remove('active'); }, 3000); }); }, }; diff --git a/static/js/layers/gdelt.js b/static/js/layers/gdelt.js index 70cebbc..0966625 100644 --- a/static/js/layers/gdelt.js +++ b/static/js/layers/gdelt.js @@ -28,6 +28,10 @@ const GdeltLayer = { _fetch() { var self = this; + var loadEl = document.getElementById('loading-gdelt'); + var statusEl = document.getElementById('status-gdelt'); + if (loadEl) loadEl.classList.add('active'); + if (statusEl) { statusEl.textContent = 'Lade Daten...'; statusEl.classList.add('active'); } fetch('/api/gdelt') .then(function(r) { return r.json(); }) .then(function(data) { diff --git a/static/js/layers/quakes.js b/static/js/layers/quakes.js index 8e2f72e..e3f186b 100644 --- a/static/js/layers/quakes.js +++ b/static/js/layers/quakes.js @@ -28,6 +28,10 @@ const QuakesLayer = { _fetch() { var self = this; + var loadEl = document.getElementById('loading-quakes'); + var statusEl = document.getElementById('status-quakes'); + if (loadEl) loadEl.classList.add('active'); + if (statusEl) { statusEl.textContent = 'Lade Daten...'; statusEl.classList.add('active'); } fetch('/api/quakes') .then(function(r) { return r.json(); }) .then(function(data) { @@ -66,6 +70,7 @@ const QuakesLayer = { }); }); }) - .catch(function(e) { console.warn('Quakes fetch error:', e); }); + .catch(function(e) { console.warn('Quakes fetch error:', e); if (statusEl) { statusEl.textContent = 'Fehler beim Laden'; } }) + .finally(function() { if (loadEl) loadEl.classList.remove('active'); setTimeout(function() { if (statusEl) statusEl.classList.remove('active'); }, 3000); }); }, }; diff --git a/static/js/layers/ships.js b/static/js/layers/ships.js index 9d4d59b..bc482f3 100644 --- a/static/js/layers/ships.js +++ b/static/js/layers/ships.js @@ -28,6 +28,10 @@ const ShipsLayer = { _fetch() { var self = this; + var loadEl = document.getElementById('loading-ships'); + var statusEl = document.getElementById('status-ships'); + if (loadEl) loadEl.classList.add('active'); + if (statusEl) { statusEl.textContent = 'Lade Daten...'; statusEl.classList.add('active'); } fetch('/api/ships') .then(function(r) { return r.json(); }) .then(function(data) { @@ -72,6 +76,7 @@ const ShipsLayer = { }); }); }) - .catch(function(e) { console.warn('Ships fetch error:', e); }); + .catch(function(e) { console.warn('Ships fetch error:', e); if (statusEl) { statusEl.textContent = 'Fehler beim Laden'; } }) + .finally(function() { if (loadEl) loadEl.classList.remove('active'); setTimeout(function() { if (statusEl) statusEl.classList.remove('active'); }, 3000); }); }, };