GEOINT Flugverkehr: Kein Flackern, stabiler
- Atomarer Layer-Swap statt clearLayers (Marker verschwinden nie) - Einzelner API-Call (radius=250nm) statt 4 Grid-Calls (weniger Rate-Limits) - Refresh-Interval 15s -> 30s (weniger API-Last) - moveend-Debounce 1.2s -> 2s (ruhigeres Verhalten beim Navigieren) - Backend Cache-TTL 10s -> 20s, Koordinaten auf 0.5-Grad-Raster gerundet
Dieser Commit ist enthalten in:
@@ -45,8 +45,8 @@ async def get_flights(
|
|||||||
_user: dict = Depends(get_current_user),
|
_user: dict = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
"""Proxy fuer airplanes.live API. 10s Cache, max 300 Aircraft."""
|
"""Proxy fuer airplanes.live API. 10s Cache, max 300 Aircraft."""
|
||||||
cache_key = f"flights:{round(lat, 1)}:{round(lon, 1)}:{radius}"
|
cache_key = f"flights:{round(lat*2)/2:.1f}:{round(lon*2)/2:.1f}:{radius}"
|
||||||
cached = _get_cached(cache_key, ttl=10)
|
cached = _get_cached(cache_key, ttl=20)
|
||||||
if cached:
|
if cached:
|
||||||
return cached
|
return cached
|
||||||
|
|
||||||
|
|||||||
@@ -189,11 +189,11 @@ const GEOINT = {
|
|||||||
this._flightLayer = L.layerGroup().addTo(map);
|
this._flightLayer = L.layerGroup().addTo(map);
|
||||||
var self = this;
|
var self = this;
|
||||||
this._fetchFlights(map);
|
this._fetchFlights(map);
|
||||||
this._flightInterval = setInterval(function() { self._fetchFlights(map); }, 15000);
|
this._flightInterval = setInterval(function() { self._fetchFlights(map); }, 30000);
|
||||||
// Bei Kartenbewegung neu laden
|
// Bei Kartenbewegung neu laden
|
||||||
this._moveHandler = function() {
|
this._moveHandler = function() {
|
||||||
clearTimeout(self._moveDebounce);
|
clearTimeout(self._moveDebounce);
|
||||||
self._moveDebounce = setTimeout(function() { self._fetchFlights(map); }, 1200);
|
self._moveDebounce = setTimeout(function() { self._fetchFlights(map); }, 2000);
|
||||||
};
|
};
|
||||||
map.on('moveend', this._moveHandler);
|
map.on('moveend', this._moveHandler);
|
||||||
},
|
},
|
||||||
@@ -207,32 +207,19 @@ const GEOINT = {
|
|||||||
_fetchFlights(map) {
|
_fetchFlights(map) {
|
||||||
if (this._flightFetching || !map || map.getZoom() < 3) return;
|
if (this._flightFetching || !map || map.getZoom() < 3) return;
|
||||||
this._flightFetching = true;
|
this._flightFetching = true;
|
||||||
var bounds = map.getBounds();
|
var center = map.getCenter();
|
||||||
var self = this;
|
var self = this;
|
||||||
var token = localStorage.getItem('osint_token') || '';
|
var token = localStorage.getItem('osint_token') || '';
|
||||||
var headers = token ? { 'Authorization': 'Bearer ' + token } : {};
|
var headers = token ? { 'Authorization': 'Bearer ' + token } : {};
|
||||||
|
|
||||||
// Grid-Punkte berechnen fuer vollstaendige Abdeckung
|
fetch('/api/geoint/flights?lat=' + center.lat.toFixed(2) + '&lon=' + center.lng.toFixed(2) + '&radius=250', { headers: headers })
|
||||||
var points = this._calcGridPoints(bounds);
|
.then(function(r) { return r.ok ? r.json() : { ac: [] }; })
|
||||||
|
.then(function(data) {
|
||||||
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;
|
if (!self._flightLayer) return;
|
||||||
self._flightLayer.clearLayers();
|
// Neue Marker in temporaerem Layer bauen, dann atomar swappen
|
||||||
// Alle Ergebnisse zusammenfuehren, Duplikate per hex entfernen
|
var newLayer = L.layerGroup();
|
||||||
var seen = {};
|
var ac = data.ac || data.aircraft || [];
|
||||||
var allAc = [];
|
ac.slice(0, 500).forEach(function(a) {
|
||||||
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;
|
if (!a.lat || !a.lon) return;
|
||||||
var heading = a.track || a.true_heading || 0;
|
var heading = a.track || a.true_heading || 0;
|
||||||
var icon = L.divIcon({
|
var icon = L.divIcon({
|
||||||
@@ -255,8 +242,13 @@ const GEOINT = {
|
|||||||
'<br><span class="geoint-popup-key">SPD</span> ' + (typeof spd === 'number' ? Math.round(spd) + ' kts' : spd) +
|
'<br><span class="geoint-popup-key">SPD</span> ' + (typeof spd === 'number' ? Math.round(spd) + ' kts' : spd) +
|
||||||
'<br><span class="geoint-popup-key">HDG</span> ' + Math.round(heading) + '°' +
|
'<br><span class="geoint-popup-key">HDG</span> ' + Math.round(heading) + '°' +
|
||||||
'</div>';
|
'</div>';
|
||||||
L.marker([a.lat, a.lon], { icon: icon }).bindPopup(popup, { className: 'geoint-leaflet-popup' }).addTo(self._flightLayer);
|
L.marker([a.lat, a.lon], { icon: icon }).bindPopup(popup, { className: 'geoint-leaflet-popup' }).addTo(newLayer);
|
||||||
});
|
});
|
||||||
|
// Atomar swappen: alte Marker entfernen, neue hinzufuegen
|
||||||
|
if (self._map && self._flightLayer) {
|
||||||
|
self._map.removeLayer(self._flightLayer);
|
||||||
|
self._flightLayer = newLayer.addTo(self._map);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(function(e) { if (typeof DEV_MODE !== 'undefined' && DEV_MODE) console.warn('GEOINT flights:', e); })
|
.catch(function(e) { if (typeof DEV_MODE !== 'undefined' && DEV_MODE) console.warn('GEOINT flights:', e); })
|
||||||
.finally(function() { self._flightFetching = false; });
|
.finally(function() { self._flightFetching = false; });
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren