// components/MapComponent.js import React, { useEffect, useRef, useState } from "react"; import L, { marker } from "leaflet"; import "leaflet/dist/leaflet.css"; import "leaflet-contextmenu/dist/leaflet.contextmenu.css"; import "leaflet-contextmenu"; import * as config from "../config/config.js"; import dynamic from "next/dynamic"; import "leaflet.smooth_marker_bouncing"; import OverlappingMarkerSpiderfier from "overlapping-marker-spiderfier-leaflet"; import DataSheet from "../components/DataSheet"; import { useRecoilState } from "recoil"; import { gisStationsStaticDistrictState } from "../features/gisStationState"; import { gisSystemStaticState } from "../features/gisSystemState"; const MapComponent = ({ locations, onLocationUpdate }) => { const mapRef = useRef(null); // Referenz auf das DIV-Element der Karte const [map, setMap] = useState(null); // Zustand der Karteninstanz const [online, setOnline] = useState(navigator.onLine); // Zustand der Internetverbindung const [GisStationsStaticDistrict, setGisStationsStaticDistrict] = useRecoilState(gisStationsStaticDistrictState); const [GisStationsStatusDistrict, setGisStationsStatusDistrict] = useState( [] ); // Zustand für Statusdaten const [GisStationsMeasurements, setGisStationsMeasurements] = useState([]); // Zustand für Messdaten const [GisSystemStatic, setGisSystemStatic] = useRecoilState(gisSystemStaticState); // Zustand für Systemdaten const [DataIcons, setDataIcons] = useState([]); // Zustand für Icon-Daten // Konstanten für die URLs const mapGisStationsStaticDistrictUrl = config.mapGisStationsStaticDistrictUrl; const mapGisStationsStatusDistrictUrl = config.mapGisStationsStatusDistrictUrl; const mapGisStationsMeasurementsUrl = config.mapGisStationsMeasurementsUrl; const mapGisSystemStaticUrl = config.mapGisSystemStaticUrl; const mapDataIconUrl = config.mapDataIconUrl; const [oms, setOms] = useState(null); // State für OMS-Instanz // Funktion zum Aktualisieren der Position in der Datenbank const updateLocationInDatabase = async (id, newLatitude, newLongitude) => { const response = await fetch("/api/updateLocation", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id, latitude: newLatitude, longitude: newLongitude, }), }); if (response.ok) { //schreib die neue Kooridnaten in die Console //akuellisiere die Position in der Datenbank mit den neuen Koordinaten mit updateLocation mit SQL Anweisung UPDATE } else { console.error("Fehler beim Aktualisieren der Position"); } }; // API-Daten laden für GisStationsStaticDistrict //http://10.10.0.13/talas5/ClientData/WebServiceMap.asmx/GisStationsStaticDistrict?idMap=10&idUser=485 useEffect(() => { const fetchData = async () => { try { const response = await fetch(mapGisStationsStaticDistrictUrl); const jsonResponse = await response.json(); // Prüfen, ob die Antwort das erwartete Format hat und Daten enthält if (jsonResponse && jsonResponse.Points) { setGisStationsStaticDistrict(jsonResponse.Points); // Direkter Zugriff auf 'Points' } else { console.error( 'Erwartete Daten im "Points"-Array nicht gefunden', jsonResponse ); setGisStationsStaticDistrict([]); } } catch (error) { console.error("Fehler beim Laden der Daten 1: ", error); setGisStationsStaticDistrict([]); } }; fetchData(); }, []); // Dependency-Array ist leer, um den Effekt nur beim Mount auszuführen //GisStationsStaticDistrict Daten laden useEffect(() => { const fetchData = async () => { try { const response = await fetch(mapGisStationsStaticDistrictUrl); const jsonResponse = await response.json(); // Prüfen, ob die Antwort das erwartete Format hat und Daten enthält if (jsonResponse && jsonResponse.Points) { setGisStationsStaticDistrict(jsonResponse.Points); // Direkter Zugriff auf 'Points' } else { console.error( 'Erwartete Daten im "Points"-Array nicht gefunden', jsonResponse ); setGisStationsStaticDistrict([]); } } catch (error) { console.error("Fehler beim Laden der Daten 1: ", error); setGisStationsStaticDistrict([]); } }; fetchData(); }, []); // Dependency-Array ist leer, um den Effekt nur beim Mount auszuführen //------------------------------------------ //GisStationsStatusDistrict Daten laden useEffect(() => { const fetchData = async () => { try { const response = await fetch(mapGisStationsStatusDistrictUrl); const jsonResponse = await response.json(); // Prüfen, ob die Antwort das erwartete Format hat und Daten enthält if (jsonResponse && jsonResponse.Statis) { setGisStationsStatusDistrict(jsonResponse.Statis); // Direkter Zugriff auf 'Statis' } else { console.error( 'Erwartete Daten im "Statis"-Array nicht gefunden', jsonResponse ); setGisStationsStatusDistrict([]); } } catch (error) { console.error("Fehler beim Laden der Daten 2: ", error); setGisStationsStatusDistrict([]); } }; fetchData(); }, []); // Dependency-Array ist leer, um den Effekt nur beim Mount auszuführen //------------------------------------------ //GisStationsMeasurements Daten laden useEffect(() => { const fetchData = async () => { try { const response = await fetch(mapGisStationsMeasurementsUrl); const jsonResponse = await response.json(); // Prüfen, ob die Antwort das erwartete Format hat und Daten enthält if (jsonResponse && jsonResponse.Statis) { setGisStationsMeasurements(jsonResponse.Statis); // Direkter Zugriff auf 'Statis' } else { console.error( 'Erwartete Daten im "Statis"-Array nicht gefunden', jsonResponse ); setGisStationsMeasurements([]); } } catch (error) { console.error("Fehler beim Laden der Daten 3: ", error); setGisStationsMeasurements([]); } }; fetchData(); }, []); // Dependency-Array ist leer, um den Effekt nur beim Mount auszuführen //------------------------------------------ //GisSystemStatic Daten laden useEffect(() => { const fetchData = async () => { try { const response = await fetch(mapGisSystemStaticUrl); const jsonResponse = await response.json(); // Prüfen, ob die Antwort das erwartete Format hat und Daten enthält if (jsonResponse && jsonResponse.Systems) { setGisSystemStatic(jsonResponse.Systems); // Direkter Zugriff auf 'Systems' console.log("GisSystemStatic:", jsonResponse.Systems); } else { console.error( 'Erwartete Daten im "Systems"-Array nicht gefunden', jsonResponse ); setGisSystemStatic([]); } } catch (error) { console.error("Fehler beim Laden der Daten 4: ", error); setGisSystemStatic([]); } }; fetchData(); }, []); // Dependency-Array ist leer, um den Effekt nur beim Mount auszuführen //------------------------------------------ const offlineTileLayer = "../TileMap/mapTiles/{z}/{x}/{y}.png"; const onlineTileLayer = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"; // Create map layers const TALAS = new L.layerGroup(); const ECI = new L.layerGroup(); const ULAF = new L.layerGroup(); const GSMModem = new L.layerGroup(); const CiscoRouter = new L.layerGroup(); const WAGO = new L.layerGroup(); const Siemens = new L.layerGroup(); const OTDR = new L.layerGroup(); const WDM = new L.layerGroup(); const GMA = new L.layerGroup(); const Sonstige = new L.layerGroup(); const TALASICL = new L.layerGroup(); let newMap = []; useEffect(() => { if (typeof window !== "undefined") { console.log("Window height from config:", config.windowHeight); } }, []); // Funktionen zur Überwachung der Internetverbindung const checkInternet = () => { fetch("https://tile.openstreetmap.org/1/1/1.png", { method: "HEAD" }) .then((response) => setOnline(response.ok)) .catch(() => setOnline(false)); }; // Initialisierung der karte und hinzuügen der Layers useEffect(() => { if (mapRef.current && !map) { newMap = L.map(mapRef.current, { center: [53.111111, 8.4625], zoom: 8, layers: [ TALAS, ECI, ULAF, GSMModem, CiscoRouter, WAGO, Siemens, OTDR, WDM, GMA, Sonstige, TALASICL, ], zoomControl: false, // Deaktiviere die Standard-Zoomsteuerung contextmenu: true, contextmenuItems: [ { text: "Station hinzufügen", callback: showAddStationPopup }, { text: "Station öffnen (Tab)", icon: "img/screen_new.png", callback: newLink, }, { text: "Station öffnen", icon: "img/screen_same.png", callback: sameLink, }, { text: "Koordinaten", icon: "img/screen_same.png", callback: lata, }, "-", // Divider { text: "Reinzoomen", callback: zoomIn }, { text: "Rauszoomen", callback: zoomOut }, { text: "Hier zentrieren", callback: centerHere }, ], }); L.tileLayer(online ? onlineTileLayer : offlineTileLayer, { attribution: '© OpenStreetMap contributors', }).addTo(newMap); const newOms = new window.OverlappingMarkerSpiderfier(newMap, { nearbyDistance: 20, //Radius um einen Marker, innerhalb dessen andere Marker gruppiert werden in Pixel }); setMap(newMap); setOms(newOms); } }, [mapRef, map]); // Abhängigkeiten prüfen // Marker hinzufügen von lokale MySQL Datenbank und nicht von APIs /* useEffect(() => { // Remove old markers if (map) { map.eachLayer((layer) => { if (layer instanceof L.Marker) { map.removeLayer(layer); } }); // Add new markers locations.forEach((location) => { const { latitude, longitude } = parsePoint(location.position); const marker = L.marker([latitude, longitude], { icon: L.icon({ iconUrl: "/location.svg", iconSize: [34, 34], iconAnchor: [17, 34], popupAnchor: [0, -34], }), draggable: true, id: location.idPoi, }); marker.bindPopup( `
${location.description || "Unbekannt"}
Type: ${location.idPoiTyp || "N/A"}
Lat: ${latitude.toFixed(5)}, Lng: ${longitude.toFixed(5)}
`, { permanent: false, closeButton: true } ); marker.bindTooltip( `
${location.description || "Unbekannt"}
Type: ${location.idPoiTyp || "N/A"}
Lat: ${latitude.toFixed(5)}, Lng: ${longitude.toFixed(5)}
`, { permanent: false, direction: "top", offset: [0, -30] } ); marker.on("dragend", function (e) { const newLat = e.target.getLatLng().lat; const newLng = e.target.getLatLng().lng; const markerId = e.target.options.id; updateLocationInDatabase(markerId, newLat, newLng).then(() => { onLocationUpdate(markerId, newLat, newLng); }); }); marker.addTo(map); }); } }, [map, locations, onLocationUpdate]); */ //------------------------------------------ function parsePoint(pointString) { const match = pointString.match( /POINT\s*\((\d+(\.\d+)?)\s+(\d+(\.\d+)?)\)/ ); if (match) { return { longitude: parseFloat(match[1]), latitude: parseFloat(match[3]), // Achtung: Index 3 für die zweite Koordinate, wegen der Gruppe (\.\d+)? }; } else { // Handle the error or return a default/fallback value console.error("Invalid POINT format:", pointString); return null; // Oder eine sinnvolle Standardantwort } } //---------------------------------- //-----Kontextmenu---------------- const newLink = (e) => { try { if (!e.relatedTarget || !e.relatedTarget.options) { throw new Error("relatedTarget or options not defined"); } alert("Neues Fenster: " + e.relatedTarget.options.test); window .open(`../devices/${e.relatedTarget.options.test}`, "_blank") .focus(); } catch (error) { console.error("Failed in newLink function:", error); } }; const sameLink = (e) => { alert(e.relatedTarget.options.test); window .open("../devices/" + e.relatedTarget.options.test, "_parent") .focus(); }; const lata = (e) => { alert("Breitengrad: " + e.latlng.lat); }; const zoomIn = (e) => { newMap.flyTo(e.latlng, 12); }; const zoomOut = (e) => { fly(); }; const centerHere = (e) => { newMap.panTo(e.latlng); }; const showCoordinates = (e) => { alert("Breitengrad: " + e.latlng.lat + "\nLängengrad: " + e.latlng.lng); }; const showData = (e) => {}; const showTalas = (e) => { map.addLayer(TALAS); loadData(); }; const hideTalas = (e) => { map.removeLayer(TALAS); loadData(); }; const showGSM = (e) => { map.addLayer(GMA); loadData(); }; const hideGSM = (e) => { map.removeLayer(GMA); loadData(); }; //-----Kontextmenu----ende------------ // Ensure this function is only called when map is initialized and available const showAddStationPopup = (e) => { if (!newMap) { return; } const popupContent = L.DomUtil.create("div"); popupContent.innerHTML = `
`; L.popup().setLatLng(e.latlng).setContent(popupContent).openOn(newMap); // Attach event listener here L.DomEvent.on(popupContent, "submit", handleSubmit); }; // Funktion zum Hinzufügen eines neuen Standorts async function handleSubmit(event) { event.preventDefault(); const form = event.target; const data = { name: form.name.value, type: form.type.value, latitude: form.lat.value, longitude: form.lng.value, }; try { const response = await fetch("/api/addLocation", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(data), }); const result = await response.json(); if (response.ok) { alert("Standort erfolgreich hinzugefügt!"); form.reset(); // Formular zurücksetzen // Hier könntest du weitere Aktionen durchführen, wie das Schließen des Popups oder das Aktualisieren der Marker auf der Karte } else { throw new Error( result.error || "Ein unbekannter Fehler ist aufgetreten." ); } } catch (error) { console.error("Fehler beim Hinzufügen des Standorts:", error); alert(error.message); } } function fly(stationValue) { var x = 51.41321407879154; var y = 7.739617925303934; var zoom = 7; newMap.flyTo([x, y], zoom); } function getIconPath(status, iconNumber, marker) { let path = status ? `TileMap/img/icons/${status}-marker-icon-${iconNumber}.png` : `TileMap/img/icons/marker-icon-${iconNumber}.png`; // Wenn der Pfad das Wort "critical" oder "major" enthält, dann den Marker bouncing options setzen if ( path.includes("critical") || path.includes("major") || path.includes("minor") || path.includes("system") ) { // Setze Bouncing-Optionen marker.setBouncingOptions({ bounceHeight: 15, // Höhe des Bounces contractHeight: 12, // Höhe des Einzugs beim Landen bounceSpeed: 52, // Geschwindigkeit des Bounces contractSpeed: 52, // Geschwindigkeit des Einzugs shadowAngle: null, // Standard-Schattenwinkel }); // Check if the icon path includes 'critical' if (path.includes("critical")) { // marker.bounce(3); } } return path; } //------------------------------------------ //\talas5\TileMap\img\icons\icon1.png // minor-marker-icon-23.png // Marker hinzufügen für GisStationsStaticDistrict /* useEffect(() => { // Stellen Sie sicher, dass sowohl `map` als auch `oms` initialisiert sind if (!map || !oms) { console.error("Map or OMS is not initialized"); return; } // Alte Marker entfernen, indem alle Marker, die durch OMS verwaltet werden, gelöscht werden oms.clearMarkers(); map.eachLayer((layer) => { if (layer instanceof L.Marker) { map.removeLayer(layer); } }); // Neue Marker für jede Station hinzufügen GisStationsStaticDistrict.forEach((station) => { // Filter für Statusobjekte dieser spezifischen Station const matchingStatuses = GisStationsStatusDistrict.filter( (status) => status.IdLD === station.IdLD ); const marker = L.marker([station.X, station.Y], { icon: L.icon({ iconUrl: "default-icon.png", // Default, wird geändert iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowSize: [41, 41], }), }).addTo(map); // Popup beim Überfahren mit der Maus öffnen marker.on("mouseover", function (e) { this.openPopup(); }); // Popup schließen, wenn die Maus den Marker verlässt marker.on("mouseout", function (e) { this.closePopup(); }); // String-Zusammenstellung für das Popup-Infofenster let statusInfo = matchingStatuses .reverse() .map( (status) => `
${status.Me} (${status.Na})
` ) .join(""); // Bestimmen des Icons basierend auf dem Status let iconPath = getIconPath( matchingStatuses[0]?.Na || "", station.Icon, marker ); marker.setIcon( L.icon({ iconUrl: iconPath, iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowSize: [41, 41], }) ); // Check if the icon path includes 'critical' and initiate bouncing if ( iconPath.includes("critical") || iconPath.includes("major") || iconPath.includes("minor") || iconPath.includes("system") ) { marker.setBouncingOptions({ bounceHeight: 15, contractHeight: 12, bounceSpeed: 52, contractSpeed: 52, shadowAngle: null, }); marker.bounce(3); } // Prüfen, ob der Name der Station "GMA Littwin (TEST)" entspricht if (station.LD_Name === "GMA Littwin (TEST)") { marker.bindTooltip( `
${station.Area_Name}
`, { permanent: true, direction: "right", opacity: 1, // Tooltip immer sichtbar offset: L.point({ x: 10, y: 0 }), } ).addTo(GMA); } else { marker.bindTooltip( `
${station.LD_Name}
`, { permanent: false, direction: "right", opacity: 0, offset: L.point({ x: 10, y: 0 }), } ).addTo(GMA); } // Marker zu OMS und der Karte hinzufügen oms.addMarker(marker); marker.addTo(map).bindPopup(` ${station.LD_Name}
${station.Device}
${station.Area_Short} (${station.Area_Name})
${station.Location_Short} (${station.Location_Name})
${statusInfo}
`); let LocID = station.IdLocation; }); }, [map, oms, GisStationsStaticDistrict, GisStationsStatusDistrict]); //------------------------------------------ */ //------------------------------------------ */ //useEffect zusammen von MySQL Daten bank und von APIs useEffect(() => { const fetchData = async () => { try { const responses = await Promise.all([ fetch(mapGisStationsStaticDistrictUrl), fetch(mapGisStationsStatusDistrictUrl), // Andere relevante API-Anfragen ]); const data = await Promise.all(responses.map((res) => res.json())); if (data[0] && data[0].Points) { setGisStationsStaticDistrict(data[0].Points); } else { console.error("Daten für GisStationsStaticDistrict nicht gefunden"); setGisStationsStaticDistrict([]); } if (data[1] && data[1].Statis) { setGisStationsStatusDistrict(data[1].Statis); } else { console.error("Daten für GisStationsStatusDistrict nicht gefunden"); setGisStationsStatusDistrict([]); } // Weitere Datenverarbeitung... } catch (error) { console.error("Fehler beim Laden der Daten: ", error); // Setzen Sie Zustände auf leere Arrays oder Fehlerzustände } }; fetchData(); }, []); useEffect(() => { if (!map) return; map.eachLayer((layer) => { if (layer instanceof L.Marker) { map.removeLayer(layer); } }); // Marker für lokale MySQL-Daten locations.forEach((location) => { const { latitude, longitude } = parsePoint(location.position); const marker = L.marker([latitude, longitude], { icon: L.icon({ iconUrl: "/location.svg", iconSize: [34, 34], iconAnchor: [17, 34], popupAnchor: [0, -34], }), draggable: true, id: location.idPoi, }); marker.bindPopup( `
${location.description || "Unbekannt"}
Type: ${location.idPoiTyp || "N/A"}
Lat: ${latitude.toFixed(5)}, Lng: ${longitude.toFixed(5)}
`, { permanent: false, closeButton: true } ); marker.bindTooltip( `
${location.description || "Unbekannt"}
Type: ${location.idPoiTyp || "N/A"}
Lat: ${latitude.toFixed(5)}, Lng: ${longitude.toFixed(5)}
`, { permanent: false, direction: "top", offset: [0, -30] } ); marker.on("dragend", function (e) { const newLat = e.target.getLatLng().lat; const newLng = e.target.getLatLng().lng; const markerId = e.target.options.id; updateLocationInDatabase(markerId, newLat, newLng).then(() => { onLocationUpdate(markerId, newLat, newLng); }); }); marker.addTo(map); }); // Marker für GisStationsStaticDistrict GisStationsStaticDistrict.forEach((station) => { // Filter für Statusobjekte dieser spezifischen Station const matchingStatuses = GisStationsStatusDistrict.filter( (status) => status.IdLD === station.IdLD ); const marker = L.marker([station.X, station.Y], { icon: L.icon({ iconUrl: "default-icon.png", // Default, wird geändert iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowSize: [41, 41], }), }).addTo(map); // Popup beim Überfahren mit der Maus öffnen marker.on("mouseover", function (e) { this.openPopup(); }); // Popup schließen, wenn die Maus den Marker verlässt marker.on("mouseout", function (e) { this.closePopup(); }); // String-Zusammenstellung für das Popup-Infofenster let statusInfo = matchingStatuses .reverse() .map( (status) => `
${status.Me} (${status.Na})
` ) .join(""); // Bestimmen des Icons basierend auf dem Status let iconPath = getIconPath( matchingStatuses[0]?.Na || "", station.Icon, marker ); marker.setIcon( L.icon({ iconUrl: iconPath, iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowSize: [41, 41], }) ); // Check if the icon path includes 'critical' and initiate bouncing if ( iconPath.includes("critical") || iconPath.includes("major") || iconPath.includes("minor") || iconPath.includes("system") ) { marker.setBouncingOptions({ bounceHeight: 15, contractHeight: 12, bounceSpeed: 52, contractSpeed: 52, shadowAngle: null, }); marker.bounce(3); } // Prüfen, ob der Name der Station "GMA Littwin (TEST)" entspricht if (station.LD_Name === "GMA Littwin (TEST)") { marker .bindTooltip( `
${station.Area_Name}
`, { permanent: true, direction: "right", opacity: 1, // Tooltip immer sichtbar offset: L.point({ x: 10, y: 0 }), } ) .addTo(GMA); } else { marker .bindTooltip( `
${station.LD_Name}
`, { permanent: false, direction: "right", opacity: 0, offset: L.point({ x: 10, y: 0 }), } ) .addTo(GMA); } // Marker zu OMS und der Karte hinzufügen oms.addMarker(marker); marker.addTo(map).bindPopup(` ${station.LD_Name}
${station.Device}
${station.Area_Short} (${station.Area_Name})
${station.Location_Short} (${station.Location_Name})
${statusInfo}
`); let LocID = station.IdLocation; }); }, [map, locations, GisStationsStaticDistrict]); // Fügen Sie weitere Abhängigkeiten hinzu, falls erforderlich //------------------------------------------ */ let uniqueIdLDsData = []; let Tooltip = []; for (let index = 0; index < uniqueIdLDsData.length; index++) { let element = uniqueIdLDsData[index].split(","); let lat = element[0]; let lng = element[1]; let ID = element[2]; let IdLD = element[3]; let LocID = element[4]; Tooltip[LocID] = L.marker([lat, lng], { icon: invisibleMarker }) .bindTooltip( '
' + '
---
' + '
---
' + '
---
' + '
---
' + '
---
' + "
", { permanent: true, direction: "right", opacity: 0, offset: L.point({ x: 10, y: 0 }), } ) .openTooltip() .addTo(GMA); } //------------------------------------------ return ( <>
); }; export default MapComponent;