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.
Dieser Commit ist enthalten in:
Claude Dev
2026-03-24 10:17:33 +01:00
Ursprung 8d5eb91383
Commit db5aa965bd
2 geänderte Dateien mit 70 neuen und 15 gelöschten Zeilen

Datei anzeigen

@@ -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;
}

Datei anzeigen

@@ -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
// -----------------------------------------------------------------------