746 lines
30 KiB
JavaScript
746 lines
30 KiB
JavaScript
// components/mainComponent/MapComponent.js
|
|
import React, { useEffect, useRef, useState, useCallback } from "react";
|
|
import L from "leaflet";
|
|
import "leaflet/dist/leaflet.css";
|
|
import "leaflet-contextmenu/dist/leaflet.contextmenu.css";
|
|
import "leaflet-contextmenu";
|
|
import "leaflet.smooth_marker_bouncing";
|
|
import "react-toastify/dist/ReactToastify.css";
|
|
import { InformationCircleIcon } from "@heroicons/react/20/solid";
|
|
import PoiUpdateModal from "../pois/PoiUpdateModal.js";
|
|
import { ToastContainer, toast } from "react-toastify";
|
|
import plusRoundIcon from "../icons/devices/overlapping/PlusRoundIcon.js";
|
|
import { restoreMapSettings, checkOverlappingMarkers } from "../../utils/mapUtils.js";
|
|
import { APP_VERSION } from "../../config/appVersion.js";
|
|
import addItemsToMapContextMenu from "../contextmenu/useMapContextMenu.js";
|
|
import useAreaMarkersLayer from "../../hooks/layers/useAreaMarkersLayer.js";
|
|
import { setupPolylines } from "../../utils/polylines/setupPolylines.js";
|
|
import { setupPOIs } from "../../utils/setupPOIs.js";
|
|
import useLineData from "../../hooks/useLineData.js";
|
|
import { useMapComponentState } from "../../hooks/useMapComponentState.js";
|
|
import CoordinatePopup from "../contextmenu/CoordinatePopup.js";
|
|
//----------Ui Widgets----------------
|
|
import MapLayersControlPanel from "../uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js";
|
|
import CoordinateInput from "../uiWidgets/CoordinateInput.js";
|
|
import VersionInfoModal from "../uiWidgets/VersionInfoModal.js";
|
|
//----------Daten aus API--------------------
|
|
import { fetchPoiDataService } from "../../services/database/pois/fetchPoiDataByIdService.js";
|
|
import AddPOIModal from "../pois/AddPOIModal.js";
|
|
import { enablePolylineEvents, disablePolylineEvents } from "../../utils/polylines/eventHandlers";
|
|
//----------MapComponent.js hooks--------------------
|
|
import useInitializeMap from "./hooks/useInitializeMap";
|
|
//-------------------Redux--------------------
|
|
import { useSelector, useDispatch } from "react-redux";
|
|
//-------------------Redux-Slices--------------------
|
|
import { setSelectedPoi } from "../../redux/slices/database/pois/selectedPoiSlice.js";
|
|
import { setDisabled } from "../../redux/slices/database/polylines/polylineEventsDisabledSlice.js";
|
|
import { setMapId, setUserId } from "../../redux/slices/urlParameterSlice";
|
|
import { selectMapLayersState } from "../../redux/slices/mapLayersSlice";
|
|
import { setCurrentPoi } from "../../redux/slices/database/pois/currentPoiSlice.js";
|
|
import { selectGisLines } from "../../redux/slices/database/polylines/gisLinesSlice";
|
|
import { selectGisLinesStatus } from "../../redux/slices/webservice/gisLinesStatusSlice";
|
|
import { selectPoiTypData, selectPoiTypStatus } from "../../redux/slices/database/pois/poiTypSlice";
|
|
import { selectPriorityConfig } from "../../redux/slices/database/priorityConfigSlice.js";
|
|
import { selectPoiIconsData, selectPoiIconsStatus } from "../../redux/slices/database/pois/poiIconsDataSlice";
|
|
import { selectGisLinesStatusFromWebservice } from "../../redux/slices/webservice/gisLinesStatusSlice";
|
|
import { selectGisUserRightsFromWebservice } from "../../redux/slices/webservice/userRightsSlice";
|
|
import { updateCountdown, closePolylineContextMenu } from "../../redux/slices/database/polylines/polylineContextMenuSlice.js";
|
|
import { selectPolylineVisible, setPolylineVisible } from "../../redux/slices/database/polylines/polylineLayerVisibleSlice.js";
|
|
import { selectGisStationsStaticDistrict } from "../../redux/slices/webservice/gisStationsStaticDistrictSlice.js";
|
|
import { selectGisSystemStatic, setGisSystemStatic } from "../../redux/slices/webservice/gisSystemStaticSlice.js";
|
|
//-----------Redux-Thunks-------------------
|
|
import { fetchGisStationsMeasurementsThunk } from "../../redux/thunks/webservice/fetchGisStationsMeasurementsThunk";
|
|
import { fetchGisSystemStaticThunk } from "../../redux/thunks/webservice/fetchGisSystemStaticThunk";
|
|
import { fetchGisStationsStaticDistrictThunk } from "../../redux/thunks/webservice/fetchGisStationsStaticDistrictThunk";
|
|
import { fetchGisStationsStatusDistrictThunk } from "../../redux/thunks/webservice/fetchGisStationsStatusDistrictThunk";
|
|
import { fetchLocationDevicesThunk } from "../../redux/thunks/database/fetchLocationDevicesThunk";
|
|
import { fetchPriorityConfigThunk } from "../../redux/thunks/database/fetchPriorityConfigThunk.js";
|
|
import { fetchGisLinesThunk } from "../../redux/thunks/database/polylines/fetchGisLinesThunk.js";
|
|
import { fetchGisLinesStatusThunk } from "../../redux/thunks/webservice/fetchGisLinesStatusThunk";
|
|
import { fetchUserRightsThunk } from "../../redux/thunks/webservice/fetchUserRightsThunk";
|
|
import { fetchPoiIconsDataThunk } from "../../redux/thunks/database/pois/fetchPoiIconsDataThunk.js";
|
|
import { fetchPoiTypThunk } from "../../redux/thunks/database/pois/fetchPoiTypThunk.js";
|
|
import { updateAreaThunk } from "../../redux/thunks/database/area/updateAreaThunk";
|
|
|
|
import useDynamicDeviceLayers from "../../hooks/layers/useDynamicDeviceLayers";
|
|
//-----------------------------------------------------------------------------------------------------
|
|
const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|
//-------------------------------
|
|
const dispatch = useDispatch();
|
|
|
|
const countdown = useSelector((state) => state.polylineContextMenu.countdown);
|
|
const countdownActive = useSelector((state) => state.polylineContextMenu.countdownActive);
|
|
const isPolylineContextMenuOpen = useSelector((state) => state.polylineContextMenu.isOpen);
|
|
const polylineVisible = useSelector(selectPolylineVisible);
|
|
|
|
const isPoiTypLoaded = useSelector((state) => state.poiTypes.status === "succeeded");
|
|
const statusMeasurements = useSelector((state) => state.gisStationsMeasurements.status);
|
|
const statusSystem = useSelector((state) => state.gisSystemStatic.status);
|
|
const statusStaticDistrict = useSelector((state) => state.gisStationsStaticDistrict.status);
|
|
const statusStatusDistrict = useSelector((state) => state.gisStationsStatusDistrict.status);
|
|
const priorityConfig = useSelector(selectPriorityConfig);
|
|
const poiLayerVisible = useSelector((state) => state.poiLayerVisible.visible);
|
|
const zoomTrigger = useSelector((state) => state.zoomTrigger.trigger);
|
|
const poiReadTrigger = useSelector((state) => state.poiReadFromDbTrigger.trigger);
|
|
const GisStationsStaticDistrict = useSelector(selectGisStationsStaticDistrict);
|
|
const GisSystemStatic = useSelector(selectGisSystemStatic);
|
|
const gisSystemStaticStatus = useSelector((state) => state.gisSystemStatic.status);
|
|
const polylineEventsDisabled = useSelector((state) => state.polylineEventsDisabled.disabled);
|
|
const mapLayersVisibility = useSelector(selectMapLayersState) || {};
|
|
const selectedArea = useSelector((state) => state.selectedArea.area);
|
|
const linesData = useSelector((state) => state.gisLinesFromDatabase.data);
|
|
const gisLinesStatus = useSelector((state) => state.gisLinesStatusFromWebservice.status);
|
|
const { data: gisLinesStatusData, status: statusGisLinesStatus } = useSelector(selectGisLinesStatusFromWebservice);
|
|
const poiIconsData = useSelector(selectPoiIconsData);
|
|
const poiIconsStatus = useSelector(selectPoiIconsStatus);
|
|
const poiTypData = useSelector(selectPoiTypData);
|
|
const poiTypStatus = useSelector((state) => state.poiTypes.status);
|
|
//const poiTypStatus = useSelector(selectPoiTypStatus);
|
|
|
|
//-------------------------------
|
|
const { deviceName, setDeviceName } = useMapComponentState();
|
|
const [locationDeviceData, setLocationDeviceData] = useState([]);
|
|
const [menuItemAdded, setMenuItemAdded] = useState(false);
|
|
const [isPopupOpen, setIsPopupOpen] = useState(false);
|
|
const closePopup = () => setIsPopupOpen(false);
|
|
const [currentCoordinates, setCurrentCoordinates] = useState("");
|
|
const [showPoiUpdateModal, setShowPoiUpdateModal] = useState(false);
|
|
const [currentPoiData, setCurrentPoiData] = useState(null);
|
|
const [showVersionInfoModal, setShowVersionInfoModal] = useState(false);
|
|
const [poiTypMap, setPoiTypMap] = useState(new Map());
|
|
const [showPopup, setShowPopup] = useState(false);
|
|
const poiLayerRef = useRef(null); // Referenz auf die Layer-Gruppe für Datenbank-Marker
|
|
const mapRef = useRef(null); // Referenz auf das DIV-Element der Karte
|
|
const [map, setMap] = useState(null); // Zustand der Karteninstanz
|
|
const [oms, setOms] = useState(null); // State für OMS-Instanz
|
|
|
|
//-----userRights----------------
|
|
const isRightsLoaded = useSelector((state) => state.gisUserRightsFromWebservice.status === "succeeded");
|
|
const userRights = useSelector(selectGisUserRightsFromWebservice);
|
|
const hasRights = userRights.includes(56);
|
|
//-----------------------------
|
|
const openPopupWithCoordinates = (e) => {
|
|
const coordinates = `${e.latlng.lat.toFixed(5)}, ${e.latlng.lng.toFixed(5)}`;
|
|
setCurrentCoordinates(coordinates);
|
|
setIsPopupOpen(true);
|
|
setPopupCoordinates(e.latlng);
|
|
setPopupVisible(true);
|
|
};
|
|
|
|
// Konstanten für die URLs
|
|
|
|
//console.log("priorityConfig in MapComponent1: ", priorityConfig);
|
|
//-----------------------------------------
|
|
const [linePositions, setLinePositions] = useState([]);
|
|
const { lineColors, tooltipContents } = useLineData();
|
|
const [polylines, setPolylines] = useState([]);
|
|
const [markers, setMarkers] = useState([]);
|
|
const [newPoint, setNewPoint] = useState(null);
|
|
const [newCoords, setNewCoords] = useState(null);
|
|
const [tempMarker, setTempMarker] = useState(null);
|
|
const [showPoiModal, setShowPoiModal] = useState(false);
|
|
const [showCoordinatesModal, setShowCoordinatesModal] = useState(false);
|
|
const [popupCoordinates, setPopupCoordinates] = useState(null);
|
|
const [popupVisible, setPopupVisible] = useState(false);
|
|
const [poiData, setPoiData] = useState([]);
|
|
|
|
const openVersionInfoModal = () => {
|
|
setShowVersionInfoModal(true);
|
|
};
|
|
const closeVersionInfoModal = () => {
|
|
setShowVersionInfoModal(false);
|
|
};
|
|
const [currentZoom, setCurrentZoom] = useState(() => {
|
|
const storedZoom = localStorage.getItem("mapZoom");
|
|
return storedZoom ? parseInt(storedZoom, 10) : 12;
|
|
});
|
|
const [currentCenter, setCurrentCenter] = useState(() => {
|
|
const storedCenter = localStorage.getItem("mapCenter");
|
|
try {
|
|
return storedCenter ? JSON.parse(storedCenter) : [53.111111, 8.4625];
|
|
} catch (e) {
|
|
console.error("Error parsing stored map center:", e);
|
|
return [53.111111, 8.4625];
|
|
}
|
|
});
|
|
|
|
//--------------------------------------------
|
|
|
|
const handleCoordinatesSubmit = (coords) => {
|
|
const [lat, lng] = coords.split(",").map(Number);
|
|
if (map && lat && lng) {
|
|
map.flyTo([lat, lng], 12); // Zentriere die Karte auf die Koordinaten
|
|
}
|
|
};
|
|
//-----------------------------Map Initialisierung----------------
|
|
useInitializeMap(map, mapRef, setMap, setOms, setMenuItemAdded, addItemsToMapContextMenu, hasRights, (value) => dispatch(setDisabled(value)));
|
|
|
|
//-------------------------React Hooks--------------------------------
|
|
useEffect(() => {
|
|
if (linesData && Array.isArray(linesData)) {
|
|
const transformed = linesData.map((item) => ({
|
|
coordinates: item.points.map((point) => [point.x, point.y]),
|
|
idModul: item.idModul,
|
|
idLD: item.idLD,
|
|
}));
|
|
setLinePositions(transformed);
|
|
}
|
|
}, [linesData]);
|
|
//--------------------------------------------
|
|
useEffect(() => {
|
|
dispatch(fetchPoiIconsDataThunk());
|
|
dispatch(fetchPoiTypThunk());
|
|
}, [dispatch]);
|
|
|
|
//--------------------------------------------
|
|
// POIs auf die Karte zeichnen
|
|
useEffect(() => {
|
|
if (map && !poiLayerRef.current) {
|
|
poiLayerRef.current = new L.LayerGroup().addTo(map);
|
|
}
|
|
|
|
return () => {
|
|
if (map && poiLayerRef.current) {
|
|
map.removeLayer(poiLayerRef.current);
|
|
poiLayerRef.current = new L.LayerGroup().addTo(map);
|
|
}
|
|
locations.forEach((location) => {});
|
|
};
|
|
//console.log("trigger in MapComponent.js:", poiReadTrigger);
|
|
}, [map, locations, poiReadTrigger]);
|
|
|
|
//--------------------------------------------
|
|
// POIs auf die Karte zeichnen
|
|
useEffect(() => {
|
|
if (poiData.length === 0) return;
|
|
|
|
setupPOIs(
|
|
map,
|
|
locations,
|
|
poiData,
|
|
poiTypMap,
|
|
userRights,
|
|
poiLayerRef,
|
|
setSelectedPoi,
|
|
setLocationDeviceData,
|
|
setDeviceName,
|
|
setCurrentPoi,
|
|
poiLayerVisible,
|
|
fetchPoiDataService,
|
|
toast,
|
|
setShowPoiUpdateModal,
|
|
setCurrentPoiData,
|
|
deviceName,
|
|
dispatch
|
|
);
|
|
}, [map, locations, onLocationUpdate, poiReadTrigger, isPoiTypLoaded, userRights, poiLayerVisible, poiData, poiTypMap, dispatch]);
|
|
|
|
//---------------------------------------------
|
|
//console.log("priorityConfig in MapComponent2: ", priorityConfig);
|
|
useEffect(() => {
|
|
if (map) {
|
|
}
|
|
}, [map, GisSystemStatic, priorityConfig]);
|
|
//--------------------------------------------
|
|
useEffect(() => {
|
|
if (map) {
|
|
var x = 51.41321407879154;
|
|
var y = 7.739617925303934;
|
|
var zoom = 7;
|
|
|
|
if (map && map.flyTo) {
|
|
map.flyTo([x, y], zoom);
|
|
} else {
|
|
console.error("Map object is not ready or does not have flyTo method");
|
|
}
|
|
}
|
|
}, [map, zoomTrigger]);
|
|
//--------------------------------------------
|
|
|
|
//-----------------------------------------------------------------
|
|
//Tooltip an mouse position anzeigen für die Linien
|
|
useEffect(() => {
|
|
if (!map) return;
|
|
|
|
// Entferne alte Marker und Polylinien
|
|
markers.forEach((marker) => marker.remove());
|
|
polylines.forEach((polyline) => polyline.remove());
|
|
|
|
// Setze neue Marker und Polylinien mit den aktuellen Daten
|
|
const { markers: newMarkers, polylines: newPolylines } = setupPolylines(
|
|
map,
|
|
linePositions,
|
|
lineColors,
|
|
tooltipContents,
|
|
setNewCoords,
|
|
tempMarker,
|
|
currentZoom,
|
|
currentCenter,
|
|
polylineVisible // kommt aus Redux
|
|
);
|
|
|
|
newPolylines.forEach((polyline, index) => {
|
|
//console.log("polyline: ", polyline);
|
|
const tooltipContent = tooltipContents[`${linePositions[index].idLD}-${linePositions[index].idModul}`] || "Die Linie ist noch nicht in Webservice vorhanden oder bekommt keine Daten";
|
|
|
|
polyline.bindTooltip(tooltipContent, {
|
|
permanent: false,
|
|
direction: "auto",
|
|
sticky: true,
|
|
offset: [20, 0],
|
|
pane: "tooltipPane",
|
|
});
|
|
|
|
polyline.on("mouseover", (e) => {
|
|
const tooltip = polyline.getTooltip();
|
|
if (tooltip) {
|
|
const mousePos = e.containerPoint;
|
|
const mapSize = map.getSize();
|
|
|
|
let direction = "right";
|
|
|
|
if (mousePos.x > mapSize.x - 100) {
|
|
direction = "left";
|
|
} else if (mousePos.x < 100) {
|
|
direction = "right";
|
|
}
|
|
|
|
if (mousePos.y > mapSize.y - 100) {
|
|
direction = "top";
|
|
} else if (mousePos.y < 100) {
|
|
direction = "bottom";
|
|
}
|
|
|
|
tooltip.options.direction = direction;
|
|
polyline.openTooltip(e.latlng);
|
|
}
|
|
});
|
|
|
|
polyline.on("mouseout", () => {
|
|
polyline.closeTooltip();
|
|
});
|
|
});
|
|
|
|
setMarkers(newMarkers);
|
|
setPolylines(newPolylines);
|
|
}, [map, linePositions, lineColors, tooltipContents, newPoint, newCoords, tempMarker, polylineVisible]);
|
|
|
|
//--------------------------------------------
|
|
|
|
//Test in useEffect
|
|
useEffect(() => {
|
|
if (map) {
|
|
console.log("🗺️ Map-Einstellungen werden wiederhergestellt...");
|
|
restoreMapSettings(map);
|
|
}
|
|
}, [map]);
|
|
//--------------------------------------------
|
|
useEffect(() => {
|
|
if (map) {
|
|
console.log("map in MapComponent: ", map);
|
|
const handleMapMoveEnd = (event) => {
|
|
const newCenter = map.getCenter();
|
|
const newZoom = map.getZoom();
|
|
|
|
setCurrentCenter([newCenter.lat, newCenter.lng]);
|
|
setCurrentZoom(newZoom);
|
|
|
|
localStorage.setItem("mapCenter", JSON.stringify([newCenter.lat, newCenter.lng]));
|
|
localStorage.setItem("mapZoom", newZoom);
|
|
};
|
|
|
|
map.on("moveend", handleMapMoveEnd);
|
|
map.on("zoomend", handleMapMoveEnd);
|
|
|
|
return () => {
|
|
map.off("moveend", handleMapMoveEnd);
|
|
map.off("zoomend", handleMapMoveEnd);
|
|
};
|
|
}
|
|
}, [map]);
|
|
//--------------------------------------------
|
|
// Area in DataSheet ->dropdownmenu
|
|
useEffect(() => {
|
|
//console.log("🔍 GisStationsStaticDistrict Inhalt:", GisStationsStaticDistrict);
|
|
|
|
// Sicherstellen, dass `Points` existiert und ein Array ist
|
|
const points = GisStationsStaticDistrict?.Points;
|
|
|
|
if (selectedArea && map) {
|
|
const station = points.find((s) => s.Area_Name === selectedArea);
|
|
|
|
if (station) {
|
|
console.log("📌 Gefundene Station:", station);
|
|
map.flyTo([station.X, station.Y], 14);
|
|
} else {
|
|
console.warn("⚠️ Keine passende Station für die Area gefunden:", selectedArea);
|
|
}
|
|
}
|
|
}, [selectedArea, map, GisStationsStaticDistrict]);
|
|
|
|
//-------------------------------------
|
|
useEffect(() => {
|
|
if (zoomTrigger && map) {
|
|
map.flyTo([51.41321407879154, 7.739617925303934], 7);
|
|
}
|
|
}, [zoomTrigger, map]);
|
|
//--------------------------------------------
|
|
useEffect(() => {
|
|
if (map && poiLayerRef.current && isPoiTypLoaded && !menuItemAdded && isRightsLoaded) {
|
|
addItemsToMapContextMenu(map, menuItemAdded, setMenuItemAdded, hasRights, setShowPopup, setPopupCoordinates);
|
|
}
|
|
}, [map, poiLayerRef, isPoiTypLoaded, menuItemAdded, hasRights, isRightsLoaded]);
|
|
//--------------------------------------------
|
|
// rote Marker ganz oben wenn überlappen sind
|
|
|
|
//--------------------------------------------
|
|
|
|
useEffect(() => {
|
|
if (map) {
|
|
if (polylineEventsDisabled) {
|
|
disablePolylineEvents(window.polylines);
|
|
} else {
|
|
enablePolylineEvents(window.polylines, window.lineColors);
|
|
}
|
|
}
|
|
}, [map, polylineEventsDisabled]);
|
|
//--------------------------------------------
|
|
useEffect(() => {
|
|
if (map) {
|
|
console.log("6- Karteninstanz (map) wurde jetzt erfolgreich initialisiert");
|
|
}
|
|
}, [map]);
|
|
//--------------------------------------------
|
|
useEffect(() => {
|
|
const initializeContextMenu = () => {
|
|
if (map) {
|
|
map.whenReady(() => {
|
|
setTimeout(() => {
|
|
if (map.contextmenu) {
|
|
//console.log("Contextmenu ist vorhanden");
|
|
} else {
|
|
console.warn("Contextmenu ist nicht verfügbar.");
|
|
}
|
|
}, 500);
|
|
});
|
|
}
|
|
};
|
|
|
|
initializeContextMenu();
|
|
}, [map]);
|
|
|
|
//---------------------------------------
|
|
// Initialisiere Leaflet-Karte
|
|
// Rufe useAreaMarkersLayer direkt auf
|
|
const urlParams = new URLSearchParams(window.location.search); // URL-Parameter parsen
|
|
const mValue = urlParams.get("m"); // Wert von "m" holen
|
|
const hostname = window.location.hostname; // Dynamischer Hostname
|
|
const port = 3000; // Definiere den gewünschten Port
|
|
const areaUrl = `http://${hostname}:${port}/api/talas_v5_DB/area/readArea?m=${mValue}`; // Dynamischer Hostname und Port
|
|
|
|
// Areas-Marker basierend auf dynamischer URL laden
|
|
const handleLocationUpdate = async (idLocation, idMap, newCoords) => {
|
|
try {
|
|
await dispatch(updateAreaThunk({ idLocation, idMap, newCoords })).unwrap();
|
|
console.log("Koordinaten erfolgreich aktualisiert:", result);
|
|
} catch (error) {
|
|
console.error("Fehler beim Aktualisieren der Location:", error);
|
|
}
|
|
};
|
|
|
|
// Areas-Marker basierend auf dynamischer URL laden
|
|
const areaMarkers = useAreaMarkersLayer(map, oms, areaUrl, handleLocationUpdate);
|
|
/*
|
|
Areamarker werden jetzt nur angezeigt, wenn der editMode aktiviert ist.
|
|
Marker werden bei deaktiviertem editMode aus der Karte entfernt.
|
|
Dynamische Überwachung von Änderungen im editMode über localStorage und Event Listener implementiert.
|
|
Dragging für Marker im editMode aktiviert und Z-Index angepasst.
|
|
*/
|
|
useEffect(() => {
|
|
const editMode = localStorage.getItem("editMode") === "true";
|
|
|
|
// Prüfe, ob der editMode deaktiviert ist
|
|
if (!editMode) {
|
|
// Entferne alle Marker aus der Karte
|
|
if (!map) return; // Sicherstellen, dass map existiert
|
|
|
|
areaMarkers.forEach((marker) => {
|
|
if (map.hasLayer(marker)) {
|
|
map.removeLayer(marker);
|
|
}
|
|
});
|
|
} else {
|
|
// Wenn editMode aktiviert ist, füge die Marker hinzu und aktiviere Dragging
|
|
areaMarkers.forEach((marker) => {
|
|
if (!map.hasLayer(marker)) {
|
|
marker.addTo(map); // Layer hinzufügen
|
|
}
|
|
marker.dragging.enable();
|
|
marker.setZIndexOffset(1000); // Marker nach oben setzen
|
|
});
|
|
}
|
|
}, [areaMarkers, map]);
|
|
|
|
//----------------------------------
|
|
const { markerStates, layerRefs } = useDynamicDeviceLayers(map, GisSystemStatic, mapLayersVisibility, priorityConfig, oms);
|
|
useEffect(() => {
|
|
const handleVisibilityChange = () => {
|
|
if (!map) return;
|
|
|
|
const allMarkers = Object.values(markerStates).filter(Array.isArray).flat();
|
|
|
|
checkOverlappingMarkers(map, allMarkers, plusRoundIcon, oms);
|
|
};
|
|
|
|
window.addEventListener("visibilityChanged", handleVisibilityChange);
|
|
return () => {
|
|
window.removeEventListener("visibilityChanged", handleVisibilityChange);
|
|
};
|
|
}, [map, markerStates]);
|
|
|
|
//---------------------------------------
|
|
useEffect(() => {
|
|
if (map) {
|
|
// initGeocoderFeature(map); // Geocoder-Feature initialisieren, kann von .env.local ausgeschaltet werden
|
|
}
|
|
}, [map]);
|
|
|
|
//--------------------------------------------
|
|
|
|
useEffect(() => {
|
|
if (map && !menuItemAdded) {
|
|
addItemsToMapContextMenu(
|
|
map,
|
|
menuItemAdded,
|
|
setMenuItemAdded,
|
|
setShowCoordinatesModal,
|
|
setShowPoiModal,
|
|
setPopupCoordinates,
|
|
openPopupWithCoordinates // Diese Funktion wird jetzt übergeben!
|
|
);
|
|
}
|
|
}, [map, menuItemAdded]);
|
|
//--------------------------------------------
|
|
useEffect(() => {
|
|
dispatch(fetchUserRightsThunk());
|
|
}, [dispatch]);
|
|
|
|
//--------------------------------------------
|
|
// Beim ersten Client-Render den Wert aus localStorage laden
|
|
useEffect(() => {
|
|
const storedPolylineVisible = localStorage.getItem("polylineVisible") === "true";
|
|
dispatch(setPolylineVisible(storedPolylineVisible));
|
|
}, [dispatch]);
|
|
//--------------------------------------------
|
|
useEffect(() => {
|
|
if (statusStaticDistrict === "idle") {
|
|
dispatch(fetchGisStationsStaticDistrictThunk());
|
|
}
|
|
}, [statusStaticDistrict, dispatch]);
|
|
|
|
useEffect(() => {
|
|
if (statusStatusDistrict === "idle") {
|
|
dispatch(fetchGisStationsStatusDistrictThunk());
|
|
}
|
|
}, [statusStatusDistrict, dispatch]);
|
|
|
|
useEffect(() => {
|
|
if (statusMeasurements === "idle") {
|
|
dispatch(fetchGisStationsMeasurementsThunk());
|
|
}
|
|
}, [statusMeasurements, dispatch]);
|
|
|
|
useEffect(() => {
|
|
if (statusSystem === "idle") {
|
|
dispatch(fetchGisSystemStaticThunk());
|
|
}
|
|
}, [statusSystem, dispatch]);
|
|
|
|
useEffect(() => {
|
|
dispatch(fetchLocationDevicesThunk());
|
|
}, [dispatch]);
|
|
|
|
//---------------------------------------------------------------
|
|
useEffect(() => {
|
|
const params = new URL(window.location.href).searchParams;
|
|
dispatch(setMapId(params.get("m")));
|
|
dispatch(setUserId(params.get("u")));
|
|
}, [dispatch]);
|
|
//---------------------------------------------------------------
|
|
useEffect(() => {
|
|
dispatch(fetchPriorityConfigThunk());
|
|
}, [dispatch]);
|
|
//--------------------------------------------------------
|
|
useEffect(() => {
|
|
if (gisLinesStatus === "idle") {
|
|
dispatch(fetchGisLinesThunk());
|
|
}
|
|
}, [gisLinesStatus, dispatch]);
|
|
//--------------------------------------------------------
|
|
useEffect(() => {
|
|
dispatch(fetchGisLinesStatusThunk());
|
|
}, [dispatch]);
|
|
//---------------------------------------------------------
|
|
const rights = useSelector((state) => state.gisUserRightsFromWebservice.rights);
|
|
useEffect(() => {
|
|
dispatch(fetchUserRightsThunk());
|
|
}, [dispatch]);
|
|
//----------------------------------------------------
|
|
useEffect(() => {
|
|
if (poiTypStatus === "idle") {
|
|
dispatch(fetchPoiTypThunk());
|
|
}
|
|
}, [poiTypStatus, dispatch]);
|
|
|
|
//--------------------------------------
|
|
useEffect(() => {
|
|
if (isPolylineContextMenuOpen && countdownActive) {
|
|
//console.log("🔄 Starte Redux-Countdown für Kontextmenü!");
|
|
|
|
const interval = setInterval(() => {
|
|
dispatch(updateCountdown());
|
|
|
|
// console.log(`⏳ Redux Countdown: ${countdown} Sekunden`);
|
|
|
|
if (countdown <= 2) {
|
|
console.log("🚀 Kontextmenü wird wegen Countdown < 2 geschlossen.");
|
|
dispatch(closePolylineContextMenu());
|
|
|
|
if (window.map?.contextmenu) {
|
|
window.map.contextmenu.hide();
|
|
}
|
|
|
|
clearInterval(interval);
|
|
}
|
|
}, 1000);
|
|
|
|
return () => {
|
|
clearInterval(interval);
|
|
};
|
|
}
|
|
}, [isPolylineContextMenuOpen, countdown, countdownActive, dispatch, window.map]);
|
|
//----------------------------------
|
|
// map in window setzen für mehr Debugging
|
|
useEffect(() => {
|
|
if (map) {
|
|
window.map = map;
|
|
console.log("✅ window.map wurde gesetzt:", window.map);
|
|
}
|
|
}, [map]);
|
|
|
|
//---------------------------------------
|
|
// **Fehlerbehandlung für `contextmenu`**
|
|
// damit den Fehler mit contextmenu nicht angezeigt wird und überspringt wird und die Seite neu geladen wird
|
|
useEffect(() => {
|
|
window.onerror = function (message, source, lineno, colno, error) {
|
|
if (message.includes("Cannot read properties of null (reading 'contextmenu')")) {
|
|
console.warn("⚠️ Fehler mit `contextmenu` erkannt - Neuladen der Seite.");
|
|
setTimeout(() => {
|
|
window.location.reload();
|
|
}, 0); // **Seite nach Sekunde neu laden**
|
|
return true; // **Fehler unterdrücken, damit React ihn nicht anzeigt**
|
|
}
|
|
};
|
|
|
|
return () => {
|
|
window.onerror = null; // **Fehlerbehandlung entfernen, wenn Komponente unmounted wird**
|
|
};
|
|
}, []);
|
|
//------------------------------------------------
|
|
useEffect(() => {
|
|
if (poiTypStatus === "succeeded" && Array.isArray(poiTypData)) {
|
|
const map = new Map();
|
|
poiTypData.forEach((item) => map.set(item.idPoiTyp, item.name));
|
|
setPoiTypMap(map);
|
|
}
|
|
}, [poiTypData, poiTypStatus]);
|
|
//------------------------------------------------
|
|
useEffect(() => {
|
|
if (poiIconsStatus === "succeeded") {
|
|
setPoiData(poiIconsData);
|
|
}
|
|
}, [poiIconsData, poiIconsStatus]);
|
|
//-----------------------------------------------------------------
|
|
//-----------------------------------------------------------------
|
|
useEffect(() => {
|
|
if (!map) return;
|
|
|
|
const editMode = localStorage.getItem("editMode") === "true";
|
|
|
|
Object.entries(markerStates).forEach(([systemName, markers]) => {
|
|
const isVisible = mapLayersVisibility[systemName];
|
|
markers.forEach((marker) => {
|
|
const hasLayer = map.hasLayer(marker);
|
|
if (editMode || !isVisible) {
|
|
if (hasLayer) map.removeLayer(marker);
|
|
} else {
|
|
if (!hasLayer) marker.addTo(map);
|
|
}
|
|
});
|
|
});
|
|
|
|
// optional für alle zusammen
|
|
const allMarkers = Object.values(markerStates)
|
|
.filter((entry) => Array.isArray(entry))
|
|
.flat();
|
|
|
|
checkOverlappingMarkers(map, allMarkers, plusRoundIcon);
|
|
}, [map, markerStates, mapLayersVisibility]);
|
|
//---------------------------------------------
|
|
// 🧠 Optional für Debugging für überlappende Markers
|
|
useEffect(() => {
|
|
if (oms) {
|
|
console.log("📌 OMS ready:", oms);
|
|
window.oms = oms; // Für Debugging global
|
|
}
|
|
}, [oms]);
|
|
//---------------------------------------------
|
|
//--------------------------------------------
|
|
return (
|
|
<>
|
|
{/* Zeigt das POI-Modal, wenn `showPoiModal` true ist */}
|
|
{showPoiModal && <AddPOIModal latlng={popupCoordinates} onClose={() => setShowPoiModal(false)} />}
|
|
<ToastContainer />
|
|
<div>{showPoiUpdateModal && <PoiUpdateModal onClose={() => setShowPoiUpdateModal(false)} poiData={currentPoiData} onSubmit={() => {}} latlng={popupCoordinates} />}</div>
|
|
|
|
<div>
|
|
{showPopup && (
|
|
<div className="fixed inset-0 bg-black bg-opacity-10 flex justify-center items-center z-[1000]" onClick={closePopup}>
|
|
<div className="relative bg-white p-6 rounded-lg shadow-lg" onClick={(e) => e.stopPropagation()}>
|
|
<button onClick={closePopup} className="absolute top-0 right-0 mt-2 mr-2 p-1 text-gray-700 hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-gray-600" aria-label="Close">
|
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{GisStationsStaticDistrict && GisStationsStaticDistrict.Points?.length > 0 && <MapLayersControlPanel className="z-50" />}
|
|
|
|
<CoordinateInput onCoordinatesSubmit={handleCoordinatesSubmit} />
|
|
<div id="map" ref={mapRef} className="z-0" style={{ height: "100vh", width: "100vw" }}></div>
|
|
<CoordinatePopup isOpen={isPopupOpen} coordinates={currentCoordinates} onClose={closePopup} />
|
|
|
|
<div className="absolute bottom-3 left-3 w-72 p-4 bg-white rounded-lg shadow-md z-50">
|
|
<div className="flex justify-between items-center">
|
|
<div>
|
|
<span className="text-black text-lg font-semibold"> TALAS.Map </span>
|
|
<br />
|
|
<span className="text-black text-lg">Version {APP_VERSION}</span>
|
|
</div>
|
|
<div>
|
|
<button onClick={openVersionInfoModal}>
|
|
<InformationCircleIcon className="text-blue-900 h-8 w-8 pr-1" title="Weitere Infos" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<VersionInfoModal showVersionInfoModal={showVersionInfoModal} closeVersionInfoModal={closeVersionInfoModal} APP_VERSION={APP_VERSION} />
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default MapComponent;
|