Files
nodeMap/utils/devices/createAndSetDevices.js
ISA f7f7122620 feat: Marker-Cleanup zur Vermeidung von Memory Leaks implementiert
- cleanupMarkers() Utility in /utils/common/cleanupMarkers.js erstellt
- Marker-Cleanup in MapComponent.js vor createAndSetDevices() integriert
- createAndSetDevices.js von Cleanup-Verantwortung befreit (reine Erzeugung)
- setupPOIs.js erweitert um cleanupMarkers() vor Layer-Neuerstellung
- poiUtils.js und markerUtils.js angepasst: cleanupMarkers() ersetzt .remove()
- Memory Leaks durch verwaiste Tooltips, Events und Marker behoben
- Grundlage für wiederverwendbare Marker-Cleanup-Logik für POIs, Geräte, Linien geschaffen
2025-06-06 10:21:56 +02:00

176 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// /utils/devices/createAndSetDevices.js
import L from "leaflet";
import "leaflet.smooth_marker_bouncing";
import { store } from "@/redux/store.js";
import { setSelectedDevice } from "@/redux/slices/selectedDeviceSlice.js";
import { selectGisStationsStaticDistrict } from "@/redux/slices/webservice/gisStationsStaticDistrictSlice.js";
import { selectGisStationsStatusDistrict } from "@/redux/slices/webservice/gisStationsStatusDistrictSlice.js";
import { selectGisStationsMeasurements } from "@/redux/slices/webservice/gisStationsMeasurementsSlice.js";
import { addContextMenuToMarker } from "@/utils/contextMenuUtils";
import { cleanupMarkers } from "@/utils/common/cleanupMarkers";
const determinePriority = (iconPath, priorityConfig) => {
for (let priority of priorityConfig) {
if (iconPath.includes(priority.name.toLowerCase())) {
return priority.level;
}
}
return 5;
};
export const createAndSetDevices = async (
systemId,
setMarkersFunction,
GisSystemStatic,
priorityConfig,
measurements,
oms // 🔁 OMS für Spiderfy hinzugefügt
) => {
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
try {
const state = store.getState();
const staticDistrictData = selectGisStationsStaticDistrict(state);
const statusDistrictData = selectGisStationsStatusDistrict(state);
const measurementData = measurements ?? selectGisStationsMeasurements(state);
if (!staticDistrictData?.Points?.length || !statusDistrictData?.length) return;
const statisMap = new Map(statusDistrictData.map(s => [s.IdLD, s]));
const measurementsMap = new Map();
measurementData?.forEach(m => {
if (!measurementsMap.has(m.IdLD)) {
measurementsMap.set(m.IdLD, m);
}
});
const activeStations = staticDistrictData.Points.filter(
station => station.System === systemId && station.Active === 1
);
const markersData = activeStations.map(station => {
const statis = statisMap.get(station.IdLD);
const messung = measurementsMap.get(station.IdLD);
const iconPath = statis
? `img/icons/${statis.Na}-marker-icon-${station.Icon}.png`
: `img/icons/marker-icon-${station.Icon}.png`;
const priority = determinePriority(iconPath, priorityConfig);
const zIndexOffset = 100 * (5 - priority);
const marker = L.marker([station.X, station.Y], {
icon: L.icon({
iconUrl: iconPath,
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
}),
areaName: station.Area_Name,
link: station.Link,
zIndexOffset,
deviceName: station.Device,
idDevice: station.IdLD,
});
// ✅ Popup
let popupContent = `
<div class="bg-white rounded-lg">
<span class="text-lg font-semibold text-gray-900">${station.LD_Name}</span>
<span class="text-md font-bold text-gray-800">${station.Device}</span><br>
<span class="text-gray-800"><strong>${station.Area_Short}</strong> (${
station.Area_Name
})</span><br>
<span class="text-gray-800"><strong>${station.Location_Short}</strong> (${
station.Location_Name
})</span>
<div class="mt-2">
${statusDistrictData
.filter(status => status.IdLD === station.IdLD)
.reverse()
.map(
status => `
<div class="flex items-center my-1">
<div class="w-2 h-2 mr-2 inline-block rounded-full" style="background-color: ${status.Co};"></div>
${status.Me}&nbsp;<span style="color: ${status.Co};">(${status.Na})</span>
</div>`
)
.join("")}
</div>
</div>`;
marker.bindPopup(popupContent);
// ✅ Tooltip (nur für System 11)
if (station.System === 11 && messung) {
const gmaMap = new Map();
measurementData?.forEach(m => {
if (!gmaMap.has(m.IdLD)) {
gmaMap.set(m.IdLD, {});
}
const mapEntry = gmaMap.get(m.IdLD);
mapEntry[m.Na] = m.Val; // z.B. mapEntry["FBT"] = 6
});
const gmaValues = gmaMap.get(station.IdLD) ?? {};
const lt = gmaValues["LT"] ?? "-";
const fbt = gmaValues["FBT"] ?? "-";
const gt = gmaValues["GT"] ?? "-";
const rlf = gmaValues["RLF"] ?? "-";
const gmaTooltipHtml = `
<div class="p-0 rounded-lg bg-white bg-opacity-90 tooltip-content">
<div class="font-bold text-sm text-black">${station.Area_Name}</div>
<div class="font-bold text-xxs text-blue-700">LT: ${lt} °C</div>
<div class="font-bold text-xxs text-red-700">FBT: ${fbt} °C</div>
<div class="font-bold text-xxs text-yellow-500">GT: ${gt} °C</div>
<div class="font-bold text-xxs text-green-700">RLF: ${rlf} %</div>
</div>
`;
marker.bindTooltip(gmaTooltipHtml, {
permanent: true,
direction: "right",
offset: [50, -20],
interactive: true,
className: "leaflet-tooltip gma-tooltip",
});
}
marker.on("mouseover", () => {
store.dispatch(
setSelectedDevice({
id: station.IdLD,
name: station.Device,
area: station.Area_Name,
})
);
marker.openPopup();
});
marker.on("contextmenu", () => {
store.dispatch(
setSelectedDevice({
id: station.IdLD,
name: station.Device,
area: station.Area_Name,
})
);
});
addContextMenuToMarker(marker);
// ✅ OverlappingMarkerSpiderfier hinzufügen
if (oms) {
oms.addMarker(marker);
}
return marker;
});
setMarkersFunction(markersData);
} catch (error) {
console.error("❌ Fehler in createAndSetDevices.js:", error.message, error.stack);
}
};