GEOINT Performance-Fix: Canvas-Renderer statt DOM-Marker

Vorher: 18.000+ DOM-Elemente (divIcon) -> Browser-Absturz
Jetzt: Canvas-basierte circleMarker (L.canvas Renderer)
- Flugzeuge: max 400 im sichtbaren Bereich, gruene Punkte
- Schiffe: max 500 im sichtbaren Bereich, blaue Punkte
- Canvas rendert tausende Marker als ein einzelnes HTML-Element
- Popups weiterhin per Klick verfuegbar
Dieser Commit ist enthalten in:
Claude Dev
2026-03-24 10:31:14 +01:00
Ursprung 9825f4df48
Commit b248c7e039

Datei anzeigen

@@ -53,6 +53,7 @@ const GEOINT = {
if (cb2) cb2.checked = enabled; if (cb2) cb2.checked = enabled;
if (enabled) { if (enabled) {
if (!this._canvasRenderer) this._canvasRenderer = L.canvas({ padding: 0.5 });
this._applySatelliteTiles(map); this._applySatelliteTiles(map);
this._createSubControl(map); this._createSubControl(map);
this._restoreSublayers(map); this._restoreSublayers(map);
@@ -202,7 +203,7 @@ const GEOINT = {
self._moveDebounce = setTimeout(function() { self._moveDebounce = setTimeout(function() {
self._renderFlights(map); self._renderFlights(map);
self._renderShips(map); self._renderShips(map);
}, 300); }, 500);
}; };
map.on('moveend', this._moveHandler); map.on('moveend', this._moveHandler);
}, },
@@ -256,18 +257,11 @@ const GEOINT = {
if (!map || !this._flightLayer || !this._flightsData) return; if (!map || !this._flightLayer || !this._flightsData) return;
var newLayer = L.layerGroup(); var newLayer = L.layerGroup();
var bounds = map.getBounds(); var bounds = map.getBounds();
this._flightsData.forEach(function(a) { var count = 0;
if (!a.lat || !a.lon || !bounds.contains([a.lat, a.lon])) return; for (var i = 0; i < this._flightsData.length && count < 400; i++) {
var heading = a.track || a.true_heading || 0; var a = this._flightsData[i];
var icon = L.divIcon({ if (!a.lat || !a.lon || !bounds.contains([a.lat, a.lon])) continue;
className: '', count++;
html: '<div class="geoint-aircraft" style="transform:rotate(' + heading + 'deg)">' +
'<svg viewBox="0 0 24 24" fill="#00ff88" stroke="#004422" stroke-width="1">' +
'<path d="M12 2L8 10h-4l2 4-2 4h4l4 4 4-4h4l-2-4 2-4h-4z"/>' +
'</svg></div>',
iconSize: [14, 14],
iconAnchor: [7, 7],
});
var callsign = (a.flight || a.callsign || a.hex || '???').trim(); var callsign = (a.flight || a.callsign || a.hex || '???').trim();
var alt = a.alt_baro || a.altitude || '?'; var alt = a.alt_baro || a.altitude || '?';
var spd = a.gs || a.ground_speed || '?'; var spd = a.gs || a.ground_speed || '?';
@@ -277,10 +271,12 @@ const GEOINT = {
(typ ? ' <span style="opacity:0.6">(' + typ + ')</span>' : '') + (typ ? ' <span style="opacity:0.6">(' + typ + ')</span>' : '') +
'<br><span class="geoint-popup-key">ALT</span> ' + (typeof alt === 'number' ? alt.toLocaleString() + ' ft' : alt) + '<br><span class="geoint-popup-key">ALT</span> ' + (typeof alt === 'number' ? alt.toLocaleString() + ' ft' : alt) +
'<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) + '\u00b0' +
'</div>'; '</div>';
L.marker([a.lat, a.lon], { icon: icon }).bindPopup(popup, { className: 'geoint-leaflet-popup' }).addTo(newLayer); L.circleMarker([a.lat, a.lon], {
}); radius: 3, fillColor: '#00ff88', color: '#004422',
fillOpacity: 0.9, weight: 1, renderer: this._canvasRenderer
}).bindPopup(popup, { className: 'geoint-leaflet-popup' }).addTo(newLayer);
}
if (this._map) { if (this._map) {
this._map.removeLayer(this._flightLayer); this._map.removeLayer(this._flightLayer);
this._flightLayer = newLayer.addTo(this._map); this._flightLayer = newLayer.addTo(this._map);
@@ -345,30 +341,26 @@ const GEOINT = {
if (!map || !this._shipsLayer || !this._shipsData) return; if (!map || !this._shipsLayer || !this._shipsData) return;
var newLayer = L.layerGroup(); var newLayer = L.layerGroup();
var bounds = map.getBounds(); var bounds = map.getBounds();
this._shipsData.forEach(function(s) { var count = 0;
if (!s.lat || !s.lon || !bounds.contains([s.lat, s.lon])) return; for (var i = 0; i < this._shipsData.length && count < 500; i++) {
var heading = s.heading || s.cog || 0; var s = this._shipsData[i];
if (!s.lat || !s.lon || !bounds.contains([s.lat, s.lon])) continue;
count++;
var sog = s.sog || 0; var sog = s.sog || 0;
var icon = L.divIcon({ var color = sog > 0.5 ? '#4499ff' : '#556688';
className: '',
html: '<div class="geoint-ship" style="transform:rotate(' + heading + 'deg)">' +
'<svg viewBox="0 0 24 24" width="10" height="10">' +
'<path d="M12 2l-4 8h-3l3 12h8l3-12h-3z" fill="' + (sog > 0.5 ? '#4499ff' : '#666688') + '" stroke="#223355" stroke-width="1"/>' +
'</svg></div>',
iconSize: [10, 10],
iconAnchor: [5, 5],
});
var mmsi = s.mmsi || '?'; var mmsi = s.mmsi || '?';
var navLabels = {0:'Motorbetrieb', 1:'Vor Anker', 2:'Nicht steuerbar', 3:'Eingeschraenkt', 5:'Festgemacht', 7:'Fischfang', 8:'Unter Segel'}; var navLabels = {0:'Motorbetrieb', 1:'Vor Anker', 2:'Nicht steuerbar', 3:'Eingeschraenkt', 5:'Festgemacht', 7:'Fischfang', 8:'Unter Segel'};
var navText = navLabels[s.navStat] || 'Status ' + s.navStat; var navText = navLabels[s.navStat] || 'Status ' + s.navStat;
var popup = '<div class="geoint-popup">' + var popup = '<div class="geoint-popup">' +
'<strong>MMSI ' + mmsi + '</strong>' + '<strong>MMSI ' + mmsi + '</strong>' +
'<br><span class="geoint-popup-key">SOG</span> ' + sog.toFixed(1) + ' kn' + '<br><span class="geoint-popup-key">SOG</span> ' + sog.toFixed(1) + ' kn' +
'<br><span class="geoint-popup-key">COG</span> ' + Math.round(s.cog || 0) + '\u00b0' +
'<br><span class="geoint-popup-key">NAV</span> ' + navText + '<br><span class="geoint-popup-key">NAV</span> ' + navText +
'</div>'; '</div>';
L.marker([s.lat, s.lon], { icon: icon }).bindPopup(popup, { className: 'geoint-leaflet-popup' }).addTo(newLayer); L.circleMarker([s.lat, s.lon], {
}); radius: 2.5, fillColor: color, color: '#223355',
fillOpacity: 0.85, weight: 0.5, renderer: this._canvasRenderer
}).bindPopup(popup, { className: 'geoint-leaflet-popup' }).addTo(newLayer);
}
if (this._map) { if (this._map) {
this._map.removeLayer(this._shipsLayer); this._map.removeLayer(this._shipsLayer);
this._shipsLayer = newLayer.addTo(this._map); this._shipsLayer = newLayer.addTo(this._map);