mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 14:58:07 -05:00
262 lines
6.9 KiB
JavaScript
262 lines
6.9 KiB
JavaScript
import { createPolylinesLayer } from "./polylines";
|
|
import { createLiveMarker } from "./marker_factory";
|
|
|
|
/**
|
|
* LiveMapHandler - Manages real-time GPS point streaming and live map updates
|
|
*
|
|
* This class handles the memory-efficient live mode functionality that was
|
|
* previously causing memory leaks in the main maps controller.
|
|
*
|
|
* Features:
|
|
* - Incremental marker addition (no layer recreation)
|
|
* - Bounded data structures (prevents memory leaks)
|
|
* - Efficient polyline segment updates
|
|
* - Smart last marker tracking
|
|
*/
|
|
export class LiveMapHandler {
|
|
constructor(map, layers, options = {}) {
|
|
this.map = map;
|
|
this.markersLayer = layers.markersLayer;
|
|
this.polylinesLayer = layers.polylinesLayer;
|
|
this.heatmapLayer = layers.heatmapLayer;
|
|
this.fogOverlay = layers.fogOverlay;
|
|
|
|
// Data arrays - can be initialized with existing data
|
|
this.markers = options.existingMarkers || [];
|
|
this.markersArray = options.existingMarkersArray || [];
|
|
this.heatmapMarkers = options.existingHeatmapMarkers || [];
|
|
|
|
// Configuration options
|
|
this.maxPoints = options.maxPoints || 1000;
|
|
this.routeOpacity = options.routeOpacity || 1;
|
|
this.timezone = options.timezone || 'UTC';
|
|
this.distanceUnit = options.distanceUnit || 'km';
|
|
this.userSettings = options.userSettings || {};
|
|
this.clearFogRadius = options.clearFogRadius || 100;
|
|
this.fogLineThreshold = options.fogLineThreshold || 10;
|
|
|
|
// State tracking
|
|
this.isEnabled = false;
|
|
this.lastMarkerRef = null;
|
|
|
|
// Bind methods
|
|
this.appendPoint = this.appendPoint.bind(this);
|
|
this.enable = this.enable.bind(this);
|
|
this.disable = this.disable.bind(this);
|
|
}
|
|
|
|
/**
|
|
* Enable live mode
|
|
*/
|
|
enable() {
|
|
this.isEnabled = true;
|
|
console.log('Live map mode enabled');
|
|
}
|
|
|
|
/**
|
|
* Disable live mode and cleanup
|
|
*/
|
|
disable() {
|
|
this.isEnabled = false;
|
|
this._cleanup();
|
|
console.log('Live map mode disabled');
|
|
}
|
|
|
|
/**
|
|
* Check if live mode is currently enabled
|
|
*/
|
|
get enabled() {
|
|
return this.isEnabled;
|
|
}
|
|
|
|
/**
|
|
* Append a new GPS point to the live map (memory-efficient implementation)
|
|
*
|
|
* @param {Array} data - Point data [lat, lng, battery, altitude, timestamp, velocity, id, country]
|
|
*/
|
|
appendPoint(data) {
|
|
if (!this.isEnabled) {
|
|
console.warn('LiveMapHandler: appendPoint called but live mode is not enabled');
|
|
return;
|
|
}
|
|
|
|
// Parse the received point data
|
|
const newPoint = data;
|
|
|
|
// Add the new point to the markers array
|
|
this.markers.push(newPoint);
|
|
|
|
// Implement bounded markers array (keep only last maxPoints in live mode)
|
|
this._enforcePointLimits();
|
|
|
|
// Create and add new marker incrementally
|
|
const newMarker = this._createMarker(newPoint);
|
|
this.markersArray.push(newMarker);
|
|
this.markersLayer.addLayer(newMarker);
|
|
|
|
// Update heatmap with bounds
|
|
this._updateHeatmap(newPoint);
|
|
|
|
// Update polylines incrementally
|
|
this._updatePolylines(newPoint);
|
|
|
|
// Pan map to new location
|
|
this.map.setView([newPoint[0], newPoint[1]], 16);
|
|
|
|
// Update fog of war if enabled
|
|
this._updateFogOfWar();
|
|
|
|
// Update the last marker efficiently
|
|
this._updateLastMarker();
|
|
}
|
|
|
|
/**
|
|
* Get current statistics about the live map state
|
|
*/
|
|
getStats() {
|
|
return {
|
|
totalPoints: this.markers.length,
|
|
visibleMarkers: this.markersArray.length,
|
|
heatmapPoints: this.heatmapMarkers.length,
|
|
isEnabled: this.isEnabled,
|
|
maxPoints: this.maxPoints
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Update configuration options
|
|
*/
|
|
updateOptions(newOptions) {
|
|
Object.assign(this, newOptions);
|
|
}
|
|
|
|
/**
|
|
* Clear all live mode data
|
|
*/
|
|
clear() {
|
|
// Clear data arrays
|
|
this.markers = [];
|
|
this.markersArray = [];
|
|
this.heatmapMarkers = [];
|
|
|
|
// Clear map layers
|
|
this.markersLayer.clearLayers();
|
|
this.polylinesLayer.clearLayers();
|
|
this.heatmapLayer.setLatLngs([]);
|
|
|
|
// Clear last marker reference
|
|
if (this.lastMarkerRef) {
|
|
this.map.removeLayer(this.lastMarkerRef);
|
|
this.lastMarkerRef = null;
|
|
}
|
|
}
|
|
|
|
// Private helper methods
|
|
|
|
/**
|
|
* Enforce point limits to prevent memory leaks
|
|
* @private
|
|
*/
|
|
_enforcePointLimits() {
|
|
if (this.markers.length > this.maxPoints) {
|
|
this.markers.shift(); // Remove oldest point
|
|
|
|
// Also remove corresponding marker from display
|
|
if (this.markersArray.length > this.maxPoints) {
|
|
const oldMarker = this.markersArray.shift();
|
|
this.markersLayer.removeLayer(oldMarker);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new marker using the shared factory (memory-efficient for live streaming)
|
|
* @private
|
|
*/
|
|
_createMarker(point) {
|
|
return createLiveMarker(point);
|
|
}
|
|
|
|
/**
|
|
* Update heatmap with bounded data
|
|
* @private
|
|
*/
|
|
_updateHeatmap(point) {
|
|
this.heatmapMarkers.push([point[0], point[1], 0.2]);
|
|
|
|
// Keep heatmap bounded
|
|
if (this.heatmapMarkers.length > this.maxPoints) {
|
|
this.heatmapMarkers.shift(); // Remove oldest point
|
|
}
|
|
|
|
this.heatmapLayer.setLatLngs(this.heatmapMarkers);
|
|
}
|
|
|
|
/**
|
|
* Update polylines incrementally (only add new segments)
|
|
* @private
|
|
*/
|
|
_updatePolylines(newPoint) {
|
|
// Only update polylines if we have more than one point
|
|
if (this.markers.length > 1) {
|
|
const prevPoint = this.markers[this.markers.length - 2];
|
|
const newSegment = L.polyline([
|
|
[prevPoint[0], prevPoint[1]],
|
|
[newPoint[0], newPoint[1]]
|
|
], {
|
|
color: this.routeOpacity > 0 ? '#3388ff' : 'transparent',
|
|
weight: 3,
|
|
opacity: this.routeOpacity
|
|
});
|
|
|
|
// Add only the new segment instead of recreating all polylines
|
|
this.polylinesLayer.addLayer(newSegment);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update fog of war if enabled
|
|
* @private
|
|
*/
|
|
_updateFogOfWar() {
|
|
if (this.map.hasLayer(this.fogOverlay)) {
|
|
// This would need to be implemented based on the existing fog logic
|
|
// For now, we'll just log that it needs updating
|
|
console.log('LiveMapHandler: Fog of war update needed');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the last marker efficiently using direct reference tracking
|
|
* @private
|
|
*/
|
|
_updateLastMarker() {
|
|
// Remove previous last marker
|
|
if (this.lastMarkerRef) {
|
|
this.map.removeLayer(this.lastMarkerRef);
|
|
}
|
|
|
|
// Add new last marker and store reference
|
|
if (this.markers.length > 0) {
|
|
const lastPoint = this.markers[this.markers.length - 1];
|
|
const lastMarker = L.marker([lastPoint[0], lastPoint[1]]);
|
|
this.lastMarkerRef = lastMarker.addTo(this.map);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cleanup resources when disabling live mode
|
|
* @private
|
|
*/
|
|
_cleanup() {
|
|
// Remove last marker
|
|
if (this.lastMarkerRef) {
|
|
this.map.removeLayer(this.lastMarkerRef);
|
|
this.lastMarkerRef = null;
|
|
}
|
|
|
|
// Note: We don't clear the data arrays here as the user might want to keep
|
|
// the points visible after disabling live mode. Use clear() for that.
|
|
}
|
|
}
|