From db5aa965bd69400a97abe67787bcb9ccdccdee20 Mon Sep 17 00:00:00 2001 From: Claude Dev Date: Tue, 24 Mar 2026 10:17:33 +0100 Subject: [PATCH] GEOINT Flugverkehr: Lesbarkeit + vollstaendige Abdeckung Popup: Dunkler Hintergrund (rgba(11,17,33,0.95)) mit gruener Border, weisse Schrift statt grau, groesser (12/13px). Taktischer Look. Abdeckung: Grid-basiertes Multi-Point-Fetching statt einzelnem Mittelpunkt. Kartenbereich wird in bis zu 2x2 Zellen unterteilt, pro Zelle ein API-Call. Duplikate per hex-ID gefiltert. Deckt jetzt den gesamten sichtbaren Bereich ab, auch bei weitem Zoom oder Breitbild-Viewports. --- src/static/css/geoint.css | 25 +++++++++++++--- src/static/js/geoint.js | 60 ++++++++++++++++++++++++++++++++------- 2 files changed, 70 insertions(+), 15 deletions(-) diff --git a/src/static/css/geoint.css b/src/static/css/geoint.css index d828c03..bbe08b7 100644 --- a/src/static/css/geoint.css +++ b/src/static/css/geoint.css @@ -238,21 +238,38 @@ } /* --- Popup-Styling fuer GEOINT-Layer --- */ +/* Dunkler Popup-Hintergrund fuer GEOINT-Layer */ +.geoint-leaflet-popup .leaflet-popup-content-wrapper { + background: rgba(11, 17, 33, 0.95); + border: 1px solid rgba(0, 255, 136, 0.25); + border-radius: 6px; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5); +} +.geoint-leaflet-popup .leaflet-popup-tip { + background: rgba(11, 17, 33, 0.95); +} +.geoint-leaflet-popup .leaflet-popup-close-button { + color: rgba(255, 255, 255, 0.6); +} +.geoint-leaflet-popup .leaflet-popup-close-button:hover { + color: #00ff88; +} .geoint-popup { font-family: var(--font-mono, 'Courier New', monospace); - font-size: 11px; - line-height: 1.5; - color: #e0e0e0; + font-size: 12px; + line-height: 1.6; + color: #ffffff; } .geoint-popup strong { color: #00ff88; + font-size: 13px; } .geoint-popup .geoint-popup-row { display: flex; gap: 8px; } .geoint-popup .geoint-popup-key { - color: rgba(255, 255, 255, 0.5); + color: rgba(255, 255, 255, 0.55); min-width: 40px; } diff --git a/src/static/js/geoint.js b/src/static/js/geoint.js index ccd32ca..b5e6a20 100644 --- a/src/static/js/geoint.js +++ b/src/static/js/geoint.js @@ -207,23 +207,32 @@ const GEOINT = { _fetchFlights(map) { if (this._flightFetching || !map || map.getZoom() < 3) return; this._flightFetching = true; - var center = map.getCenter(); var bounds = map.getBounds(); - // Radius in nm (grob: 1 Grad Lat ≈ 60nm) - var latDiff = Math.abs(bounds.getNorth() - bounds.getSouth()) / 2; - var radius = Math.min(Math.round(latDiff * 60), 250); var self = this; var token = localStorage.getItem('osint_token') || ''; + var headers = token ? { 'Authorization': 'Bearer ' + token } : {}; - fetch('/api/geoint/flights?lat=' + center.lat.toFixed(4) + '&lon=' + center.lng.toFixed(4) + '&radius=' + radius, { - headers: token ? { 'Authorization': 'Bearer ' + token } : {} - }) - .then(function(r) { return r.ok ? r.json() : Promise.reject(r.status); }) - .then(function(data) { + // Grid-Punkte berechnen fuer vollstaendige Abdeckung + var points = this._calcGridPoints(bounds); + + Promise.all(points.map(function(p) { + return fetch('/api/geoint/flights?lat=' + p.lat.toFixed(4) + '&lon=' + p.lon.toFixed(4) + '&radius=' + p.radius, { headers: headers }) + .then(function(r) { return r.ok ? r.json() : { ac: [] }; }) + .catch(function() { return { ac: [] }; }); + })) + .then(function(results) { if (!self._flightLayer) return; self._flightLayer.clearLayers(); - var ac = data.ac || data.aircraft || []; - ac.slice(0, 300).forEach(function(a) { + // Alle Ergebnisse zusammenfuehren, Duplikate per hex entfernen + var seen = {}; + var allAc = []; + results.forEach(function(data) { + (data.ac || data.aircraft || []).forEach(function(a) { + var key = a.hex || (a.lat + ',' + a.lon); + if (!seen[key]) { seen[key] = true; allAc.push(a); } + }); + }); + allAc.slice(0, 500).forEach(function(a) { if (!a.lat || !a.lon) return; var heading = a.track || a.true_heading || 0; var icon = L.divIcon({ @@ -550,6 +559,35 @@ const GEOINT = { } }, + + // Berechnet Grid-Punkte fuer vollstaendige Kartenabdeckung + _calcGridPoints(bounds) { + var n = bounds.getNorth(), s = bounds.getSouth(); + var e = bounds.getEast(), w = bounds.getWest(); + var latSpan = Math.abs(n - s); + var lonSpan = Math.abs(e - w); + // Ein Punkt deckt ~250nm (~4.2 Grad) ab + var step = 3.5; // Grad pro Grid-Zelle (etwas Overlap) + var latSteps = Math.max(1, Math.ceil(latSpan / step)); + var lonSteps = Math.max(1, Math.ceil(lonSpan / step)); + // Maximal 4 Punkte (2x2 Grid) um API-Last zu begrenzen + if (latSteps > 2) latSteps = 2; + if (lonSteps > 2) lonSteps = 2; + var points = []; + var latStep = latSpan / latSteps; + var lonStep = lonSpan / lonSteps; + for (var i = 0; i < latSteps; i++) { + for (var j = 0; j < lonSteps; j++) { + points.push({ + lat: s + latStep * (i + 0.5), + lon: w + lonStep * (j + 0.5), + radius: Math.min(Math.round(Math.max(latStep, lonStep) / 2 * 60), 250) + }); + } + } + return points; + }, + // ----------------------------------------------------------------------- // Cleanup // -----------------------------------------------------------------------