Compare commits
12 Commits
4d2a94ffea
...
aeec307909
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aeec307909 | ||
|
|
369f29a769 | ||
|
|
d166b2468d | ||
|
|
59c8680c23 | ||
|
|
1a046f8212 | ||
|
|
e35216daf5 | ||
|
|
91ad47166f | ||
|
|
3a9b436352 | ||
|
|
7b881e80c2 | ||
|
|
cc19a0a466 | ||
|
|
f200d0bb20 | ||
|
|
75a0ab000f |
@@ -23,4 +23,4 @@ NEXT_PUBLIC_USE_MOCKS=true
|
||||
# z.B. http://10.10.0.13/xyz/index.aspx -> basePath in config.json auf /xyz setzen
|
||||
# basePath wird jetzt in public/config.json gepflegt
|
||||
# App-Versionsnummer
|
||||
NEXT_PUBLIC_APP_VERSION=1.1.355
|
||||
NEXT_PUBLIC_APP_VERSION=1.1.369
|
||||
|
||||
@@ -24,4 +24,4 @@ NEXT_PUBLIC_USE_MOCKS=false
|
||||
# basePath wird jetzt in public/config.json gepflegt
|
||||
|
||||
# App-Versionsnummer
|
||||
NEXT_PUBLIC_APP_VERSION=1.1.355
|
||||
NEXT_PUBLIC_APP_VERSION=1.1.369
|
||||
|
||||
32
.gitattributes
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# Normalize line endings and mark binary files
|
||||
|
||||
# Default: let Git manage line endings
|
||||
* text=auto
|
||||
|
||||
# Source files use LF in repo
|
||||
*.js text eol=lf
|
||||
*.jsx text eol=lf
|
||||
*.ts text eol=lf
|
||||
*.tsx text eol=lf
|
||||
*.json text eol=lf
|
||||
*.css text eol=lf
|
||||
*.scss text eol=lf
|
||||
*.md text eol=lf
|
||||
*.yml text eol=lf
|
||||
*.yaml text eol=lf
|
||||
*.svg text eol=lf
|
||||
*.sh text eol=lf
|
||||
|
||||
# Windows scripts keep CRLF
|
||||
*.ps1 text eol=crlf
|
||||
*.bat text eol=crlf
|
||||
*.cmd text eol=crlf
|
||||
|
||||
# Images and binaries
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
*.gif binary
|
||||
*.webp binary
|
||||
*.ico binary
|
||||
*.pdf binary
|
||||
@@ -1,6 +1,3 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
echo "🔄 Version wird automatisch erhöht (bumpVersion.js)..."
|
||||
|
||||
# Version automatisch erhöhen
|
||||
|
||||
18
components/icons/material-symbols/AlarmIcon.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const AlarmIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="red"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M6 6.9L3.87 4.78l1.41-1.41L7.4 5.5zM13 1v3h-2V1zm7.13 3.78L18 6.9l-1.4-1.4l2.12-2.13zM4.5 10.5v2h-3v-2zm15 0h3v2h-3zM6 20h12a2 2 0 0 1 2 2H4a2 2 0 0 1 2-2m6-15a6 6 0 0 1 6 6v8H6v-8a6 6 0 0 1 6-6"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default AlarmIcon;
|
||||
18
components/icons/material-symbols/EditIcon.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const EditIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default EditIcon;
|
||||
18
components/icons/material-symbols/EditOffIcon.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const EditOffIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L18 21l-3-3m-12.728-.364A9 9 0 015.636 5.636m0 0L3 3l3 3m9.364 9.364L18 21M5.636 5.636L3 3"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default EditOffIcon;
|
||||
18
components/icons/material-symbols/ExpandIcon.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const ExpandIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default ExpandIcon;
|
||||
18
components/icons/material-symbols/InfoIcon.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const InfoIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default InfoIcon;
|
||||
19
components/icons/material-symbols/MapMarkerIcon.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const MapMarkerIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="rgb(0, 174, 239)"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 10.5a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25s-7.5-4.108-7.5-11.25a7.5 7.5 0 1115 0z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default MapMarkerIcon;
|
||||
18
components/icons/material-symbols/MenuIcon.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const MenuIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default MenuIcon;
|
||||
18
components/icons/material-symbols/SearchIcon.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const SearchIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default SearchIcon;
|
||||
@@ -6,7 +6,15 @@ 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 { Icon } from "@iconify/react";
|
||||
import EditIcon from "@/components/icons/material-symbols/EditIcon";
|
||||
import EditOffIcon from "@/components/icons/material-symbols/EditOffIcon";
|
||||
import SearchIcon from "@/components/icons/material-symbols/SearchIcon";
|
||||
import MenuIcon from "@/components/icons/material-symbols/MenuIcon";
|
||||
import InfoIcon from "@/components/icons/material-symbols/InfoIcon";
|
||||
import AlarmIcon from "@/components/icons/material-symbols/AlarmIcon";
|
||||
import MapMarkerIcon from "@/components/icons/material-symbols/MapMarkerIcon";
|
||||
import ExpandIcon from "@/components/icons/material-symbols/ExpandIcon";
|
||||
import PoiUpdateModal from "@/components/pois/poiUpdateModal/PoiUpdateModal.js";
|
||||
import { ToastContainer, toast } from "react-toastify";
|
||||
import plusRoundIcon from "../icons/devices/overlapping/PlusRoundIcon.js";
|
||||
@@ -26,6 +34,7 @@ import CoordinatePopup from "@/components/contextmenu/CoordinatePopup.js";
|
||||
import MapLayersControlPanel from "@/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js";
|
||||
import CoordinateInput from "@/components/uiWidgets/CoordinateInput.js";
|
||||
import VersionInfoModal from "@/components/uiWidgets/VersionInfoModal.js";
|
||||
import AreaDropdown from "@/components/uiWidgets/AreaDropdown";
|
||||
//----------Daten aus API--------------------
|
||||
import { fetchPoiDataService } from "@/services/database/pois/fetchPoiDataByIdService.js";
|
||||
import AddPOIModal from "@/components/pois/AddPOIModal.js";
|
||||
@@ -39,6 +48,8 @@ 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, setLayerVisibility } from "@/redux/slices/mapLayersSlice";
|
||||
import { setSelectedArea } from "@/redux/slices/selectedAreaSlice";
|
||||
import { incrementZoomTrigger } from "@/redux/slices/zoomTriggerSlice";
|
||||
import { setCurrentPoi } from "@/redux/slices/database/pois/currentPoiSlice.js";
|
||||
import { selectGisLines } from "@/redux/slices/database/polylines/gisLinesSlice";
|
||||
import { selectGisLinesStatus } from "@/redux/slices/webservice/gisLinesStatusSlice";
|
||||
@@ -129,6 +140,13 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
const { data: gisLinesStatusData, status: statusGisLinesStatus } = useSelector(
|
||||
selectGisLinesStatusFromWebservice
|
||||
);
|
||||
|
||||
// Alarm Status aus GisStationsStatusDistrict
|
||||
const gisStationsStatusDistrict = useSelector(state => state.gisStationsStatusDistrict.data);
|
||||
// Unterstützt sowohl Array-Shape (Statis[]) als auch Objekt mit Statis-Array
|
||||
const hasActiveAlarm = Array.isArray(gisStationsStatusDistrict)
|
||||
? gisStationsStatusDistrict.some(item => item?.Alarm === 1)
|
||||
: gisStationsStatusDistrict?.Statis?.some(item => item?.Alarm === 1) || false;
|
||||
const poiIconsData = useSelector(selectPoiIconsData);
|
||||
const poiIconsStatus = useSelector(selectPoiIconsStatus);
|
||||
const poiTypData = useSelector(selectPoiTypData);
|
||||
@@ -147,10 +165,39 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
const [showVersionInfoModal, setShowVersionInfoModal] = useState(false);
|
||||
const [poiTypMap, setPoiTypMap] = useState(new Map());
|
||||
const [showPopup, setShowPopup] = useState(false);
|
||||
const [showAreaDropdown, setShowAreaDropdown] = 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
|
||||
// Sichtbarkeit der App-Info-Karte (unten links)
|
||||
const [showAppInfoCard, setShowAppInfoCard] = useState(() => {
|
||||
try {
|
||||
const v = localStorage.getItem("showAppInfoCard");
|
||||
return v === null ? true : v === "true";
|
||||
} catch (_) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
// Sichtbarkeit des Layer-Kontrollpanels (oben rechts)
|
||||
const [showLayersPanel, setShowLayersPanel] = useState(() => {
|
||||
try {
|
||||
const v = localStorage.getItem("showLayersPanel");
|
||||
return v === null ? true : v === "true";
|
||||
} catch (_) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
// Base-Map Panel wurde entfernt
|
||||
// Sichtbarkeit der Koordinaten-Suche (Lupe)
|
||||
const [showCoordinateInput, setShowCoordinateInput] = useState(() => {
|
||||
try {
|
||||
const v = localStorage.getItem("showCoordinateInput");
|
||||
return v === null ? false : v === "true";
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Flag, ob Nutzer die Polyline-Checkbox manuell betätigt hat
|
||||
// Nutzer-Flag global auf window, damit auch Redux darauf zugreifen kann
|
||||
@@ -188,6 +235,14 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
const [popupCoordinates, setPopupCoordinates] = useState(null);
|
||||
const [popupVisible, setPopupVisible] = useState(false);
|
||||
const [poiData, setPoiData] = useState([]);
|
||||
// Edit mode state mirrors MapLayersControlPanel's behavior
|
||||
const [editMode, setEditMode] = useState(() => {
|
||||
try {
|
||||
return localStorage.getItem("editMode") === "true";
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const openVersionInfoModal = () => {
|
||||
setShowVersionInfoModal(true);
|
||||
@@ -209,6 +264,26 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Persistiere Sichtbarkeit der App-Info-Karte
|
||||
useEffect(() => {
|
||||
try {
|
||||
localStorage.setItem("showAppInfoCard", String(showAppInfoCard));
|
||||
} catch (_) {}
|
||||
}, [showAppInfoCard]);
|
||||
// Persistiere Sichtbarkeit des Layer-Panels
|
||||
useEffect(() => {
|
||||
try {
|
||||
localStorage.setItem("showLayersPanel", String(showLayersPanel));
|
||||
} catch (_) {}
|
||||
}, [showLayersPanel]);
|
||||
// Persist-Logik für Base-Map Panel entfernt
|
||||
// Persistiere Sichtbarkeit der Koordinaten-Suche
|
||||
useEffect(() => {
|
||||
try {
|
||||
localStorage.setItem("showCoordinateInput", String(showCoordinateInput));
|
||||
} catch (_) {}
|
||||
}, [showCoordinateInput]);
|
||||
|
||||
//--------------------------------------------
|
||||
|
||||
const handleCoordinatesSubmit = coords => {
|
||||
@@ -981,6 +1056,29 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
}, [GisSystemStatic, mapLayersVisibility, dispatch]);
|
||||
|
||||
//---------------------------------------------
|
||||
//--------------------------------------------
|
||||
// Expand handler (same behavior as MapLayersControlPanel expand icon)
|
||||
const handleExpandClick = () => {
|
||||
dispatch(setSelectedArea("Station wählen"));
|
||||
dispatch(incrementZoomTrigger());
|
||||
};
|
||||
|
||||
// Toggle edit mode (same logic as EditModeToggle component)
|
||||
const hasEditRight = Array.isArray(userRights)
|
||||
? userRights.includes?.(56) || userRights.some?.(r => r?.IdRight === 56)
|
||||
: false;
|
||||
const toggleEditMode = () => {
|
||||
if (!hasEditRight) return;
|
||||
const next = !editMode;
|
||||
setEditMode(next);
|
||||
try {
|
||||
localStorage.setItem("editMode", String(next));
|
||||
} catch (_) {}
|
||||
if (typeof window !== "undefined") {
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------
|
||||
return (
|
||||
<>
|
||||
@@ -1035,17 +1133,120 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{GisStationsStaticDistrict && GisStationsStaticDistrict.Points?.length > 0 && (
|
||||
{GisStationsStaticDistrict &&
|
||||
GisStationsStaticDistrict.Points?.length > 0 &&
|
||||
showLayersPanel &&
|
||||
!showAreaDropdown && (
|
||||
<MapLayersControlPanel
|
||||
className="z-50"
|
||||
handlePolylineCheckboxChange={handlePolylineCheckboxChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
<CoordinateInput onCoordinatesSubmit={handleCoordinatesSubmit} />
|
||||
{showCoordinateInput && <CoordinateInput onCoordinatesSubmit={handleCoordinatesSubmit} />}
|
||||
<div id="map" ref={mapRef} className="z-0" style={{ height: "100vh", width: "100vw" }}></div>
|
||||
{/* Top-right controls: layers, info, expand, edit, and base map stack */}
|
||||
<div className="absolute top-3 right-3 z-50 pointer-events-auto flex items-center gap-2">
|
||||
{/* Alarm-Icon - nur anzeigen wenn Alarm aktiv */}
|
||||
{hasActiveAlarm && (
|
||||
<button
|
||||
onClick={() => {}}
|
||||
aria-label="Alarm aktiv"
|
||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||
title="Alarm aktiv"
|
||||
>
|
||||
<AlarmIcon className="h-8 w-8 animate-pulse text-red-500" />
|
||||
</button>
|
||||
)}
|
||||
{/* Marker-Icon (line-md) */}
|
||||
<button
|
||||
onClick={() =>
|
||||
setShowAreaDropdown(v => {
|
||||
const next = !v;
|
||||
if (next) setShowLayersPanel(false); // Dropdown öffnen -> Panel schließen
|
||||
return next;
|
||||
})
|
||||
}
|
||||
aria-label="Marker"
|
||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||
title="Marker"
|
||||
>
|
||||
<MapMarkerIcon className="h-8 w-8" />
|
||||
</button>
|
||||
{showAreaDropdown && <AreaDropdown onClose={() => setShowAreaDropdown(false)} />}
|
||||
{/*Lupe: Koordinatensuche ein-/ausblenden */}
|
||||
<button
|
||||
onClick={() => setShowCoordinateInput(v => !v)}
|
||||
aria-label={
|
||||
showCoordinateInput ? "Koordinatensuche ausblenden" : "Koordinatensuche einblenden"
|
||||
}
|
||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||
title={
|
||||
showCoordinateInput ? "Koordinatensuche ausblenden" : "Koordinatensuche einblenden"
|
||||
}
|
||||
>
|
||||
<SearchIcon className="h-8 w-8" />
|
||||
</button>
|
||||
<button
|
||||
onClick={toggleEditMode}
|
||||
aria-label={editMode ? "Bearbeitungsmodus deaktivieren" : "Bearbeitungsmodus aktivieren"}
|
||||
className={`rounded-full shadow p-1 ${
|
||||
hasEditRight
|
||||
? "bg-white/90 hover:bg-white"
|
||||
: "bg-white/60 cursor-not-allowed opacity-50"
|
||||
}`}
|
||||
title={
|
||||
hasEditRight
|
||||
? editMode
|
||||
? "Bearbeitungsmodus deaktivieren"
|
||||
: "Bearbeitungsmodus aktivieren"
|
||||
: "Keine Bearbeitungsrechte"
|
||||
}
|
||||
disabled={!hasEditRight}
|
||||
>
|
||||
{editMode ? <EditOffIcon className="h-8 w-8" /> : <EditIcon className="h-8 w-8" />}
|
||||
</button>
|
||||
{/* Expand: Karte auf Standardansicht */}
|
||||
<button
|
||||
onClick={handleExpandClick}
|
||||
aria-label="Karte auf Standardansicht"
|
||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1 "
|
||||
title="Karte auf Standardansicht"
|
||||
>
|
||||
<ExpandIcon className="h-8 w-8" />
|
||||
</button>
|
||||
{/* Lupe: Koordinaten-Suche ein-/ausblenden */}
|
||||
<button
|
||||
onClick={() =>
|
||||
setShowLayersPanel(v => {
|
||||
const next = !v;
|
||||
if (next) setShowAreaDropdown(false); // Panel öffnen -> Dropdown schließen
|
||||
return next;
|
||||
})
|
||||
}
|
||||
aria-label={showLayersPanel ? "Layer-Panel ausblenden" : "Layer-Panel einblenden"}
|
||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||
title={showLayersPanel ? "Layer-Panel ausblenden" : "Layer-Panel einblenden"}
|
||||
>
|
||||
<MenuIcon className="h-8 w-8" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => setShowAppInfoCard(v => !v)}
|
||||
aria-label={showAppInfoCard ? "Info ausblenden" : "Info einblenden"}
|
||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||
title={showAppInfoCard ? "Info ausblenden" : "Info einblenden"}
|
||||
>
|
||||
<InfoIcon
|
||||
className="h-8 w-8 pr-1"
|
||||
title={showAppInfoCard ? "Info ausblenden" : "Info einblenden"}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
{/* BaseMapPanel entfernt */}
|
||||
<CoordinatePopup isOpen={isPopupOpen} coordinates={currentCoordinates} onClose={closePopup} />
|
||||
|
||||
{showAppInfoCard && (
|
||||
<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>
|
||||
@@ -1055,11 +1256,12 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={openVersionInfoModal}>
|
||||
<InformationCircleIcon className="text-blue-900 h-8 w-8 pr-1" title="Weitere Infos" />
|
||||
<InfoIcon className="h-8 w-8 pr-1" title="Weitere Infos" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<VersionInfoModal
|
||||
showVersionInfoModal={showVersionInfoModal}
|
||||
closeVersionInfoModal={closeVersionInfoModal}
|
||||
|
||||
79
components/uiWidgets/AreaDropdown.js
Normal file
@@ -0,0 +1,79 @@
|
||||
// /components/uiWidgets/AreaDropdown.js
|
||||
import React, { useEffect, useMemo } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { setSelectedArea } from "@/redux/slices/selectedAreaSlice";
|
||||
import { selectGisStationsStaticDistrict } from "@/redux/slices/webservice/gisStationsStaticDistrictSlice";
|
||||
import { selectGisSystemStatic } from "@/redux/slices/webservice/gisSystemStaticSlice";
|
||||
|
||||
/**
|
||||
* Kleines Dropdown zur Auswahl der Station (Area_Name),
|
||||
* nutzt dieselbe Datenquelle wie das MapLayersControlPanel.
|
||||
*/
|
||||
const AreaDropdown = ({ onClose }) => {
|
||||
const dispatch = useDispatch();
|
||||
const GisStationsStaticDistrict = useSelector(selectGisStationsStaticDistrict) || {};
|
||||
const GisSystemStatic = useSelector(selectGisSystemStatic) || [];
|
||||
|
||||
// Erlaubte Systeme: Allow === 1 und Map === 1
|
||||
const allowedSystems = useMemo(() => {
|
||||
return new Set(
|
||||
(Array.isArray(GisSystemStatic) ? GisSystemStatic : [])
|
||||
.filter(sys => sys.Allow === 1 && sys.Map === 1)
|
||||
.map(sys => sys.IdSystem)
|
||||
);
|
||||
}, [GisSystemStatic]);
|
||||
|
||||
// Uniqe Areas basierend auf Allowed Systems
|
||||
const areaOptions = useMemo(() => {
|
||||
const points = GisStationsStaticDistrict?.Points || [];
|
||||
const seen = new Set();
|
||||
const filtered = points.filter(p => {
|
||||
if (!p?.Area_Name) return false;
|
||||
if (!allowedSystems.has(p.System)) return false;
|
||||
if (seen.has(p.Area_Name)) return false;
|
||||
seen.add(p.Area_Name);
|
||||
return true;
|
||||
});
|
||||
return filtered.map(p => ({ label: p.Area_Name, value: p.IdLD }));
|
||||
}, [GisStationsStaticDistrict, allowedSystems]);
|
||||
|
||||
const handleChange = e => {
|
||||
const selectedIndex = e.target.options.selectedIndex;
|
||||
const label = e.target.options[selectedIndex].text;
|
||||
dispatch(setSelectedArea(label));
|
||||
onClose?.();
|
||||
};
|
||||
|
||||
// Schließe mit ESC
|
||||
useEffect(() => {
|
||||
const onKey = e => {
|
||||
if (e.key === "Escape") onClose?.();
|
||||
};
|
||||
window.addEventListener("keydown", onKey);
|
||||
return () => window.removeEventListener("keydown", onKey);
|
||||
}, [onClose]);
|
||||
|
||||
return (
|
||||
<div className="absolute top-16 right-3 z-[60]">
|
||||
<div className="bg-white rounded-md shadow-lg p-3 border border-gray-200 min-w-[220px]">
|
||||
<div className="text-sm font-semibold mb-2">Station wählen</div>
|
||||
<select
|
||||
onChange={handleChange}
|
||||
className="border p-2 rounded w-full"
|
||||
defaultValue="__default__"
|
||||
>
|
||||
<option value="__default__" disabled>
|
||||
Bitte wählen…
|
||||
</option>
|
||||
{areaOptions.map(opt => (
|
||||
<option key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AreaDropdown;
|
||||
@@ -4,7 +4,7 @@ import React, { useState } from "react";
|
||||
const CoordinateInput = ({ onCoordinatesSubmit }) => {
|
||||
const [coordinates, setCoordinates] = useState("");
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
const handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
if (onCoordinatesSubmit) {
|
||||
onCoordinatesSubmit(coordinates);
|
||||
@@ -12,9 +12,21 @@ const CoordinateInput = ({ onCoordinatesSubmit }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="fixed top-5 left-5 z-50 bg-white shadow-lg rounded-lg p-4 w-72">
|
||||
<input type="text" placeholder="Koordinaten eingeben (lat,lng)" value={coordinates} onChange={(e) => setCoordinates(e.target.value)} className="border p-2 rounded w-full mb-2" />
|
||||
<button type="submit" className="bg-blue-500 text-white p-2 rounded w-full hover:bg-blue-600">
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="fixed top-5 left-5 z-50 bg-white shadow-lg rounded-lg p-4 w-72"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Koordinaten eingeben (lat,lng)"
|
||||
value={coordinates}
|
||||
onChange={e => setCoordinates(e.target.value)}
|
||||
className="border p-2 rounded w-full mb-2"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-littwin-blue text-white p-2 rounded w-full hover:bg-blue-600"
|
||||
>
|
||||
Zu Marker zoomen
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@@ -31,7 +31,7 @@ const VersionInfoModal = ({ showVersionInfoModal, closeVersionInfoModal, APP_VER
|
||||
</p>
|
||||
<button
|
||||
onClick={closeVersionInfoModal}
|
||||
className="mt-4 bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-700 mx-auto block"
|
||||
className="mt-4 bg-littwin-blue text-white px-4 py-2 rounded mx-auto block"
|
||||
>
|
||||
Schließen
|
||||
</button>
|
||||
|
||||
163
components/uiWidgets/baseMapPanel/BaseMapPanel.js
Normal file
@@ -0,0 +1,163 @@
|
||||
// components/uiWidgets/baseMapPanel/BaseMapPanel.js
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import L from "leaflet";
|
||||
import { Icon } from "@iconify/react";
|
||||
|
||||
// Minimal, safe defaults (no API key required). You can extend via config later.
|
||||
const DEFAULT_BASE_LAYERS = [
|
||||
{
|
||||
id: "osm-standard",
|
||||
name: "Standard",
|
||||
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
attribution: "© OpenStreetMap contributors",
|
||||
minZoom: 0,
|
||||
maxZoom: 19,
|
||||
},
|
||||
{
|
||||
id: "osm-humanitarian",
|
||||
name: "Humanitarian",
|
||||
url: "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png",
|
||||
attribution: "© OpenStreetMap contributors, Humanitarian OpenStreetMap Team",
|
||||
minZoom: 0,
|
||||
maxZoom: 19,
|
||||
},
|
||||
{
|
||||
id: "cyclosm",
|
||||
name: "CyclOSM",
|
||||
url: "https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png",
|
||||
attribution: "© OpenStreetMap contributors, CyclOSM",
|
||||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
},
|
||||
{
|
||||
id: "carto-light",
|
||||
name: "Carto Light",
|
||||
url: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
|
||||
attribution: "© OpenStreetMap contributors, © CARTO",
|
||||
subdomains: "abcd",
|
||||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
},
|
||||
];
|
||||
|
||||
function getCurrentTileLayer(map) {
|
||||
let found = null;
|
||||
if (!map) return null;
|
||||
map.eachLayer(layer => {
|
||||
if (!found && layer instanceof L.TileLayer) {
|
||||
found = layer;
|
||||
}
|
||||
});
|
||||
return found;
|
||||
}
|
||||
|
||||
export default function BaseMapPanel({ map, onSelect, onClose, initialId }) {
|
||||
const [activeId, setActiveId] = useState(initialId || null);
|
||||
const layerCacheRef = useRef({});
|
||||
const bases = useMemo(() => {
|
||||
try {
|
||||
if (typeof window !== "undefined") {
|
||||
const cfg = window.__leafletConfig;
|
||||
if (cfg && cfg.tileSources) {
|
||||
return Object.entries(cfg.tileSources).map(([key, ts]) => ({
|
||||
id: key,
|
||||
name: ts.name || key,
|
||||
url: ts.url,
|
||||
attribution: ts.attribution || "© OpenStreetMap contributors",
|
||||
minZoom: ts.minZoom ?? cfg.minZoom ?? 0,
|
||||
maxZoom: ts.maxZoom ?? cfg.maxZoom ?? 19,
|
||||
subdomains: ts.subdomains,
|
||||
}));
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
return DEFAULT_BASE_LAYERS;
|
||||
}, []);
|
||||
|
||||
const applyBase = id => {
|
||||
if (!map) return;
|
||||
const base = bases.find(b => b.id === id) || bases[0];
|
||||
if (!base) return;
|
||||
|
||||
// Remove current tile layer
|
||||
const current = getCurrentTileLayer(map);
|
||||
if (current) {
|
||||
try {
|
||||
map.removeLayer(current);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// Get or create the new layer
|
||||
let nextLayer = layerCacheRef.current[id];
|
||||
if (!nextLayer) {
|
||||
nextLayer = L.tileLayer(base.url, {
|
||||
attribution: base.attribution,
|
||||
subdomains: base.subdomains || "abc",
|
||||
tileSize: 256,
|
||||
minZoom: base.minZoom ?? 0,
|
||||
maxZoom: base.maxZoom ?? 19,
|
||||
noWrap: true,
|
||||
// Ensure base tiles stay behind overlays
|
||||
zIndex: 1,
|
||||
});
|
||||
layerCacheRef.current[id] = nextLayer;
|
||||
}
|
||||
|
||||
nextLayer.addTo(map);
|
||||
try {
|
||||
if (typeof map.setMinZoom === "function") map.setMinZoom(base.minZoom ?? 0);
|
||||
if (typeof map.setMaxZoom === "function") map.setMaxZoom(base.maxZoom ?? 19);
|
||||
if (typeof window !== "undefined") {
|
||||
window.__tileSourceMinZoom = base.minZoom ?? 0;
|
||||
window.__tileSourceMaxZoom = base.maxZoom ?? 19;
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
setActiveId(id);
|
||||
try {
|
||||
localStorage.setItem("baseMapId", id);
|
||||
} catch (_) {}
|
||||
onSelect && onSelect(id);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const saved = (() => {
|
||||
try {
|
||||
return localStorage.getItem("baseMapId");
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
const targetId = initialId || saved || bases[0]?.id;
|
||||
if (targetId) {
|
||||
applyBase(targetId);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [map]);
|
||||
|
||||
return (
|
||||
<div className="absolute top-16 right-3 z-50 w-64 bg-white rounded-lg shadow-lg p-3">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h3 className="text-sm font-semibold">Map Layers</h3>
|
||||
<button onClick={onClose} aria-label="Schließen" title="Schließen">
|
||||
<Icon icon="material-symbols:close-rounded" className="h-5 w-5 text-gray-700" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
{bases.map(b => (
|
||||
<button
|
||||
key={b.id}
|
||||
onClick={() => applyBase(b.id)}
|
||||
className={`text-left rounded-md border p-2 hover:bg-gray-50 ${
|
||||
activeId === b.id ? "ring-2 ring-blue-500" : ""
|
||||
}`}
|
||||
title={b.name}
|
||||
>
|
||||
<div className="font-medium text-sm">{b.name}</div>
|
||||
<div className="text-[10px] text-gray-500 truncate">{b.url}</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -241,14 +241,47 @@ function MapLayersControlPanel({ handlePolylineCheckboxChange }) {
|
||||
}, [GisStationsStaticDistrict]);
|
||||
//---------------------------
|
||||
|
||||
// Polyline (Kabelstrecken) abhängig von TALAS (system-1)
|
||||
const onPolylineToggle = checked => {
|
||||
if (editMode) return;
|
||||
|
||||
// Wenn Nutzer Kabelstrecken einschaltet, aber TALAS aktuell ausgeblendet ist,
|
||||
// dann TALAS automatisch aktivieren (sofern erlaubt)
|
||||
const talasKey = "system-1";
|
||||
const talasVisible = !!mapLayersVisibility[talasKey];
|
||||
if (checked && isTalasAllowed && !talasVisible) {
|
||||
dispatch(setLayerVisibility({ layer: talasKey, visibility: true }));
|
||||
|
||||
// Persistiere Sichtbarkeit map/user-spezifisch
|
||||
const mapId2 = localStorage.getItem("currentMapId");
|
||||
const userId2 = localStorage.getItem("currentUserId");
|
||||
const mapStorageKey =
|
||||
mapId2 && userId2 ? `mapLayersVisibility_m${mapId2}_u${userId2}` : "mapLayersVisibility";
|
||||
localStorage.setItem(
|
||||
mapStorageKey,
|
||||
JSON.stringify({ ...mapLayersVisibility, [talasKey]: true })
|
||||
);
|
||||
|
||||
// Event feuern wie an anderer Stelle
|
||||
setTimeout(() => {
|
||||
const event = new Event("visibilityChanged");
|
||||
window.dispatchEvent(event);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// Sichtbarkeit der Kabelstrecken setzen
|
||||
handlePolylineCheckboxChange(checked);
|
||||
};
|
||||
|
||||
//---------------------------
|
||||
return (
|
||||
<div
|
||||
id="mainDataSheet"
|
||||
className="absolute top-3 right-3 w-1/6 min-w-[300px] max-w-[400px] z-10 bg-white p-2 rounded-lg shadow-lg"
|
||||
className="absolute top-16 right-3 w-1/6 min-w-[300px] max-w-[200px] z-10 bg-white p-2 rounded-lg shadow-lg"
|
||||
>
|
||||
<div className="flex flex-col gap-4 p-4">
|
||||
<div className="flex items-center justify-between space-x-2">
|
||||
{/*
|
||||
<select
|
||||
onChange={handleAreaChange}
|
||||
id="stationListing"
|
||||
@@ -256,13 +289,7 @@ function MapLayersControlPanel({ handlePolylineCheckboxChange }) {
|
||||
style={{ minWidth: "150px", maxWidth: "200px" }}
|
||||
>
|
||||
<option value="Station wählen">Station wählen</option>
|
||||
{/*
|
||||
...new Map(
|
||||
(GisStationsStaticDistrict.Points || [])
|
||||
.filter(p => !!p.Area_Name)
|
||||
.map(p => [p.Area_Name, p])
|
||||
).values(),
|
||||
*/}
|
||||
|
||||
{[
|
||||
...new Map(
|
||||
(GisStationsStaticDistrict.Points || [])
|
||||
@@ -275,6 +302,9 @@ function MapLayersControlPanel({ handlePolylineCheckboxChange }) {
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
*/}
|
||||
|
||||
{/*
|
||||
<div className="flex items-center space-x-2">
|
||||
<EditModeToggle />
|
||||
<img
|
||||
@@ -284,6 +314,7 @@ function MapLayersControlPanel({ handlePolylineCheckboxChange }) {
|
||||
onClick={handleIconClick}
|
||||
/>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
|
||||
{/* Checkboxen mit Untermenüs */}
|
||||
@@ -310,7 +341,7 @@ function MapLayersControlPanel({ handlePolylineCheckboxChange }) {
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={kabelstreckenVisible}
|
||||
onChange={e => handlePolylineCheckboxChange(e.target.checked)}
|
||||
onChange={e => onPolylineToggle(e.target.checked)}
|
||||
id="polyline-checkbox"
|
||||
disabled={!isTalasAllowed || editMode}
|
||||
/>
|
||||
|
||||
@@ -102,3 +102,4 @@ Station suchen
|
||||
Hamburger-Menü
|
||||
- [ ] TODO: Info Karte: VersionInfoModal.js Modal soll über einem Icon 'Info' oben rechts
|
||||
eingeblendet und ausgeblendet um mehr von der Karte zu sehen ℹ️ Info
|
||||
https://www.openstreetmap.org/#map=13/51.80097/9.33495&layers=P
|
||||
|
||||
@@ -22,4 +22,73 @@ Verzeichnisstruktur funktioniert.
|
||||
|
||||
---
|
||||
|
||||
## OSM‑basierte, „open“ Quellen
|
||||
|
||||
Diese sind datenrechtlich offen (ODbL bzw. Community-Lizenzen), aber das „kostenlos“ gilt nicht im
|
||||
Sinne unbegrenzter Tile‑Nutzung. Die Tile‑Server werden als Community‑Ressource bereitgestellt –
|
||||
bitte Policies respektieren.
|
||||
|
||||
- osm-standard (OpenStreetMap)
|
||||
- - Key: Nein
|
||||
- - Nutzung: Fair‑Use; für produktive/hohe Last eigenen Tile‑Server/Provider verwenden.
|
||||
- - Attribution: „© OpenStreetMap contributors“
|
||||
|
||||
- osm-humanitarian (HOT)
|
||||
- - Key: Nein
|
||||
- - Nutzung: Fair‑Use; für größere Last die Betreiber kontaktieren bzw. andere Infrastruktur nutzen.
|
||||
- - Attribution: „© OpenStreetMap contributors <br>
|
||||
-
|
||||
- Humanitarian OpenStreetMap Team“ cyclosm
|
||||
- - Key: Nein
|
||||
- - Nutzung: Fair‑Use (bereitgestellt u. a. über OSM France). Für höhere Last
|
||||
Unterstützung/Hostingoptionen prüfen.
|
||||
- - Attribution: „CyclOSM“ + „OpenStreetMap contributors“
|
||||
- Praxis‑Tipps Kleine bis mittlere Nutzung: Die oben genannten „keyless“ Quellen sind oft
|
||||
ausreichend, solange du Attribution setzt und Limits respektierst. Produktion/hohe Last: Nutze
|
||||
einen Anbieter mit Vertrag/Key (z. B. Thunderforest, Tracestrack, MapTiler, Mapbox) oder hoste
|
||||
Tiles selbst. Schlüssel im Client: Für Thunderforest/Tracestrack stehen die Keys im Frontend. Das
|
||||
ist üblich, aber der Key ist sichtbar. Wenn du ihn verbergen willst, richte einen kleinen
|
||||
Tile‑Proxy auf deinem Server ein, der den Key serverseitig anhängt und optional cached.
|
||||
Attribution: Dein BaseMapPanel setzt bereits Attributionsstrings aus config.json. Achte darauf,
|
||||
dass sie je Quelle korrekt sind.
|
||||
|
||||
---
|
||||
|
||||
Kurzantwort: Für kommerzielle Nutzung sind OSM‑Community‑Tile‑Server nicht geeignet. Nutze einen
|
||||
bezahlten Anbieter (z. B. Thunderforest, Tracestrack) oder hoste selbst. Attribution ist immer
|
||||
Pflicht.
|
||||
|
||||
Links und Hinweise je Layer/Provider:
|
||||
|
||||
OpenStreetMap Standard (osm-standard)
|
||||
|
||||
Lizenz/Daten: ODbL, Attribution Pflicht Tile-Server-Policy (keine Produktion/hohe Last):
|
||||
https://operations.osmfoundation.org/policies/tiles/ Urheberrecht/Attribution:
|
||||
https://www.openstreetmap.org/copyright HOT Humanitarian (osm-humanitarian)
|
||||
|
||||
Community-Server (OSM France); keine Produktion/hohe Last Info/Policy OSM France Tiles:
|
||||
https://tile.openstreetmap.fr/ HOT: https://www.hotosm.org/ CyclOSM (cyclosm)
|
||||
|
||||
Community-Server (OSM France); keine Produktion/hohe Last Projektseite: https://www.cyclosm.org/
|
||||
Hinweise/Policy (OSM France): https://tile.openstreetmap.fr/ Wiki:
|
||||
https://wiki.openstreetmap.org/wiki/CyclOSM Carto Light (carto-light / Positron)
|
||||
|
||||
Keylos nutzbar mit Attribution; Fair‑Use, für hohe Last über CARTO‑Pläne Basemaps:
|
||||
https://carto.com/basemaps/ Attribution: https://carto.com/attributions Pricing (Plattform):
|
||||
https://carto.com/pricing/ (bei großem Volumen Sales kontaktieren) Thunderforest (Cycle/Transport u.
|
||||
a.)
|
||||
|
||||
Kommerziell mit API‑Key; Pläne von Free bis Pro Pricing: https://www.thunderforest.com/pricing/
|
||||
Terms/Attribution: https://www.thunderforest.com/terms/ Tracestrack Topo
|
||||
|
||||
API‑Key erforderlich; kostenlose und bezahlte Pläne Übersicht/Pricing:
|
||||
https://www.tracestrack.com/en/maps/ Nutzungsbedingungen: https://www.tracestrack.com/en/terms/
|
||||
Empfehlung:
|
||||
|
||||
Produktion: Nimm Thunderforest oder Tracestrack (oder MapTiler: https://www.maptiler.com/pricing/)
|
||||
oder hoste Tiles selbst. Attribution in der Karte anzeigen (OSM + jeweiliger Anbieter). Soll ich
|
||||
diese Links samt klarer Hinweise kompakt in eure README.md einpflegen?
|
||||
|
||||
---
|
||||
|
||||
[Zurück zur Übersicht](../README.md)
|
||||
|
||||
26
package-lock.json
generated
@@ -1,16 +1,17 @@
|
||||
{
|
||||
"name": "nodemap",
|
||||
"version": "1.1.355",
|
||||
"version": "1.1.369",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "nodemap",
|
||||
"version": "1.1.355",
|
||||
"version": "1.1.369",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@heroicons/react": "^2.1.5",
|
||||
"@iconify/react": "^6.0.1",
|
||||
"@mui/icons-material": "^6.0.2",
|
||||
"@reduxjs/toolkit": "^2.5.1",
|
||||
"autoprefixer": "^10.4.19",
|
||||
@@ -357,6 +358,27 @@
|
||||
"react": ">= 16 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/@iconify/react": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@iconify/react/-/react-6.0.1.tgz",
|
||||
"integrity": "sha512-fCocnAfiGXjrA0u7KkS3W/OQHNp9LRFICudvOtxmS3Mf7U92aDhP50wyzRbobZli51zYt9ksZ9g0J7H586XvOQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@iconify/types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/cyberalien"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@iconify/types": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
|
||||
"integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "nodemap",
|
||||
"version": "1.1.355",
|
||||
"version": "1.1.369",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@heroicons/react": "^2.1.5",
|
||||
"@iconify/react": "^6.0.1",
|
||||
"@mui/icons-material": "^6.0.2",
|
||||
"@reduxjs/toolkit": "^2.5.1",
|
||||
"autoprefixer": "^10.4.19",
|
||||
|
||||
@@ -17,6 +17,61 @@
|
||||
"_comment": "OpenStreetMap Online-Kartenquelle über Server-Proxy (relativ)",
|
||||
"minZoom": 0,
|
||||
"maxZoom": 19
|
||||
},
|
||||
"osm-standard": {
|
||||
"name": "Standard",
|
||||
"url": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
"attribution": "© OpenStreetMap contributors",
|
||||
"subdomains": "abc",
|
||||
"minZoom": 0,
|
||||
"maxZoom": 19
|
||||
},
|
||||
"osm-humanitarian": {
|
||||
"name": "Humanitarian",
|
||||
"url": "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png",
|
||||
"attribution": "© OpenStreetMap contributors, Humanitarian OpenStreetMap Team",
|
||||
"subdomains": "abc",
|
||||
"minZoom": 0,
|
||||
"maxZoom": 19
|
||||
},
|
||||
"cyclosm": {
|
||||
"name": "CyclOSM",
|
||||
"url": "https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png",
|
||||
"attribution": "© OpenStreetMap contributors, CyclOSM",
|
||||
"subdomains": "abc",
|
||||
"minZoom": 0,
|
||||
"maxZoom": 20
|
||||
},
|
||||
"carto-light": {
|
||||
"name": "Carto Light",
|
||||
"url": "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
|
||||
"attribution": "© OpenStreetMap contributors, © CARTO",
|
||||
"subdomains": "abcd",
|
||||
"minZoom": 0,
|
||||
"maxZoom": 20
|
||||
},
|
||||
"thunderforest-cycle": {
|
||||
"name": "Cycle Map (Thunderforest)",
|
||||
"url": "https://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey=YOUR_THUNDERFOREST_API_KEY",
|
||||
"attribution": "© OpenStreetMap contributors, © Thunderforest",
|
||||
"subdomains": "abc",
|
||||
"minZoom": 0,
|
||||
"maxZoom": 22
|
||||
},
|
||||
"thunderforest-transport": {
|
||||
"name": "Transport Map (Thunderforest)",
|
||||
"url": "https://{s}.tile.thunderforest.com/transport/{z}/{x}/{y}.png?apikey=YOUR_THUNDERFOREST_API_KEY",
|
||||
"attribution": "© OpenStreetMap contributors, © Thunderforest",
|
||||
"subdomains": "abc",
|
||||
"minZoom": 0,
|
||||
"maxZoom": 22
|
||||
},
|
||||
"tracestrack-topo": {
|
||||
"name": "Tracestrack Topo",
|
||||
"url": "https://tile.tracestrack.com/topo__/{z}/{x}/{y}.webp?key=YOUR_TRACESTRACK_KEY",
|
||||
"attribution": "© OpenStreetMap contributors, © Tracestrack",
|
||||
"minZoom": 0,
|
||||
"maxZoom": 19
|
||||
}
|
||||
},
|
||||
"active": "osm",
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 349 B |
3
public/img/icons/material-symbols/alarm.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 396 B |
3
public/img/icons/material-symbols/edit-off.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L18 21l-3-3m-12.728-.364A9 9 0 015.636 5.636m0 0L3 3l3 3m9.364 9.364L18 21M5.636 5.636L3 3" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 351 B |
3
public/img/icons/material-symbols/edit.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 432 B |
3
public/img/icons/material-symbols/info.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 333 B |
4
public/img/icons/material-symbols/map-marker.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="rgb(0, 174, 239)" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25s-7.5-4.108-7.5-11.25a7.5 7.5 0 1115 0z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 373 B |
3
public/img/icons/material-symbols/menu.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 231 B |
3
public/img/icons/material-symbols/search.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 261 B |
@@ -1,6 +1,10 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}", "./hooks/**/*.{js,ts,jsx,tsx}"],
|
||||
content: [
|
||||
"./pages/**/*.{js,ts,jsx,tsx}",
|
||||
"./components/**/*.{js,ts,jsx,tsx}",
|
||||
"./hooks/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
zIndex: {
|
||||
@@ -10,6 +14,9 @@ module.exports = {
|
||||
90: "90",
|
||||
100: "100",
|
||||
},
|
||||
colors: {
|
||||
"littwin-blue": "#00aeef",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Eingang DE 01 kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50922,
|
||||
@@ -15,7 +16,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Eingang DE 31 kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50922,
|
||||
@@ -24,7 +26,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Eingang DE 17 kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50922,
|
||||
@@ -33,7 +36,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Eingang DE 05 kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50984,
|
||||
@@ -42,7 +46,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Eingang DE 20 kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50975,
|
||||
@@ -51,7 +56,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Eingang DE 32 kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50984,
|
||||
@@ -60,7 +66,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Eingang DE 01 kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50977,
|
||||
@@ -69,7 +76,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Station offline",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50977,
|
||||
@@ -78,7 +86,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Eingang DE 01 kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50975,
|
||||
@@ -87,7 +96,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Station offline",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50066,
|
||||
@@ -96,7 +106,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "CPL offline",
|
||||
"Feld": 5,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50011,
|
||||
@@ -105,7 +116,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "CPL offline",
|
||||
"Feld": 16,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50011,
|
||||
@@ -114,7 +126,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Wasserdruck aus",
|
||||
"Feld": 16,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50011,
|
||||
@@ -123,7 +136,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Ein",
|
||||
"Feld": 16,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50011,
|
||||
@@ -132,7 +146,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Digitaleingang 1 ON",
|
||||
"Feld": 16,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50000,
|
||||
@@ -141,7 +156,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Ein",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50922,
|
||||
@@ -150,7 +166,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Station offline",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50922,
|
||||
@@ -159,7 +176,8 @@
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Eingang DE 32 kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50975,
|
||||
@@ -168,7 +186,8 @@
|
||||
"Co": "#FFFF00",
|
||||
"Me": "KÜG 07: Überspannung kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50975,
|
||||
@@ -177,7 +196,8 @@
|
||||
"Co": "#FFFF00",
|
||||
"Me": "KÜG 08: Überspannung gehend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50977,
|
||||
@@ -186,7 +206,8 @@
|
||||
"Co": "#FFFF00",
|
||||
"Me": "KÜG 08: Überspannung gehend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50984,
|
||||
@@ -195,7 +216,8 @@
|
||||
"Co": "#FFFF00",
|
||||
"Me": "KÜG 08: Überspannung gehend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50922,
|
||||
@@ -204,7 +226,8 @@
|
||||
"Co": "#FFFF00",
|
||||
"Me": "Eingang DE 02 kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50922,
|
||||
@@ -213,7 +236,8 @@
|
||||
"Co": "#FFFF00",
|
||||
"Me": "KÜG 08: Überspannung gehend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50922,
|
||||
@@ -222,7 +246,8 @@
|
||||
"Co": "#FF9900",
|
||||
"Me": "Eingang DE 03 Test Karte kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50922,
|
||||
@@ -231,7 +256,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "KÜG 01: Aderbruch kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50922,
|
||||
@@ -240,7 +266,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "KÜG 02: Aderbruch kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50922,
|
||||
@@ -249,7 +276,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "KÜG 03: Aderbruch kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50000,
|
||||
@@ -258,7 +286,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "über 8V kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50984,
|
||||
@@ -267,7 +296,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "KÜG 05: Aderbruch kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50984,
|
||||
@@ -276,7 +306,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "KÜG 02: Isolationsminderung kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50984,
|
||||
@@ -285,7 +316,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "KÜG 06: Aderbruch kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50977,
|
||||
@@ -294,7 +326,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "KÜG 01: Isolationsminderung kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50977,
|
||||
@@ -303,7 +336,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "KÜG 06: Aderbruch kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50977,
|
||||
@@ -312,7 +346,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "KÜG 05: Aderbruch kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50976,
|
||||
@@ -321,7 +356,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "CPL offline",
|
||||
"Feld": 3,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50976,
|
||||
@@ -330,7 +366,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "KÜG 03: Isolationsminderung kommend",
|
||||
"Feld": 3,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50975,
|
||||
@@ -339,7 +376,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "KÜG 04: Isolationsminderung kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50975,
|
||||
@@ -348,7 +386,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "KÜG 02: Isolationsminderung kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50975,
|
||||
@@ -357,7 +396,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "KÜG 01: Isolationsminderung kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50001,
|
||||
@@ -366,7 +406,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "Sammelstörung kommend",
|
||||
"Feld": 5,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50975,
|
||||
@@ -375,7 +416,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "KÜG 06: Aderbruch kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50975,
|
||||
@@ -384,7 +426,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "KÜG 05: Aderbruch kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50963,
|
||||
@@ -393,7 +436,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "CPL offline",
|
||||
"Feld": 3,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50063,
|
||||
@@ -402,7 +446,8 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "Digitaleingang 1 EIN",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
},
|
||||
{
|
||||
"IdLD": 50000,
|
||||
@@ -411,6 +456,7 @@
|
||||
"Co": "#FF0000",
|
||||
"Me": "über 10V kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
"Icon": 0,
|
||||
"Alarm": 0
|
||||
}
|
||||
]
|
||||