fixed wrong env var, added base maps

This commit is contained in:
PxlLoewe
2025-06-01 09:00:59 -07:00
parent cded757b9f
commit e73f19d9e8
11 changed files with 113651 additions and 24 deletions

View File

@@ -1,7 +1,272 @@
"use client";
import { usePannelStore } from "_store/pannelStore";
import { useEffect } from "react";
import { TileLayer, useMap } from "react-leaflet";
import { Control, Icon, LatLngExpression } from "leaflet";
import { useEffect, useState } from "react";
import {
LayerGroup,
LayersControl,
TileLayer,
useMap,
WMSTileLayer,
GeoJSON,
Circle,
useMapEvent,
FeatureGroup,
Marker,
Tooltip,
} from "react-leaflet";
// @ts-ignore
import type { FeatureCollection, Geometry } from "geojson";
import L from "leaflet";
import LEITSTELLENBERECHE from "./_geojson/Leitstellen_VAR.json";
import { createCustomMarker } from "_components/map/_components/createCustomMarker";
import { Station } from "@repo/db";
import { useQuery } from "@tanstack/react-query";
import { getStationsAPI } from "_querys/stations";
const RadioAreaLayer = () => {
const getColor = (randint: number) => {
switch (randint) {
case 1:
return "#2b6eff";
case 2:
return "#233ee5";
case 3:
return "#7BA5FF";
case 4:
return "#5087FF";
default:
return "#7f7f7f";
}
};
return (
<GeoJSON
data={LEITSTELLENBERECHE as FeatureCollection<Geometry>}
style={(feature) => {
if (!feature || !feature.properties) return {}; // Early return if feature or its properties are undefined
return {
color: getColor(feature.properties.randint),
weight: 1.5,
className: "no-pointer",
};
}}
onEachFeature={(feature, layer) => {
if (feature && feature.properties && feature.properties.name) {
layer.bindTooltip(
new L.Tooltip({
content: feature.properties.name,
direction: "top",
sticky: true,
}),
);
}
}}
/>
);
};
const StationsLayer = ({ attribution }: { attribution: Control.Attribution }) => {
const { data: stations } = useQuery({
queryKey: ["stations"],
queryFn: () => getStationsAPI(),
});
const [selectedStations, setSelectedStations] = useState<Station["id"][]>([]);
const [stationsWithIcon, setStationsWithIcon] = useState<(Station & { icon?: string })[]>([]); // Zustand für die Stationen mit Icon
const attributionText = "";
const resetSelection = () => {
setSelectedStations([]);
};
useMapEvent("click", () => {
resetSelection();
});
const handleMarkerClick = (stationId: number) => {
if (selectedStations.includes(stationId)) {
setSelectedStations((prevStations) => prevStations.filter((s) => s !== stationId));
} else {
setSelectedStations((prevStations) => [...prevStations, stationId]);
}
};
useEffect(() => {
// Erstelle die Icons für alle Stationen
const fetchIcons = async () => {
if (!stations) return;
const urls = await Promise.all(
stations.map(async (station) => {
return createCustomMarker(station.operator);
}),
);
setStationsWithIcon(stations.map((station, index) => ({ ...station, icon: urls[index] })));
};
fetchIcons();
}, [stations]);
return (
<FeatureGroup>
{stationsWithIcon.map((station) => {
const coordinates: LatLngExpression = [station.latitude, station.longitude];
const typeLabel = station.bosUse.charAt(0).toUpperCase();
return (
<Marker
key={`marker-${station.id}`}
position={coordinates}
icon={
new Icon({
iconUrl: station.icon,
iconSize: [30, 30],
iconAnchor: [15, 15],
tooltipAnchor: [0, 20],
className: station.hideRangeRings ? "no-pointer" : "pointer",
})
}
eventHandlers={{
click: () => {
if (!station.hideRangeRings) handleMarkerClick(station.id);
},
add: () => attribution.addAttribution(attributionText),
remove: () => attribution.removeAttribution(attributionText),
}}
>
<Tooltip direction="top" sticky>
<div style={{ textAlign: "center" }}>
<strong>{station.bosCallsign}</strong>
<small style={{ fontWeight: "bold", fontSize: "0.7em" }}>{` (${typeLabel})`}</small>
<br />
<small>
{[
station.hasWinch ? "W" : null,
station.is24h ? "24h" : null,
station.hasNvg ? "N" : null,
]
.filter(Boolean)
.join(", ")}
</small>
</div>
</Tooltip>
</Marker>
);
})}
{selectedStations.map((stationId) => {
const station = stations?.find((s) => s.id === stationId);
if (!station) return null;
const center: LatLngExpression = [station.latitude, station.longitude];
return (
<div key={`marker-${stationId}`}>
<Circle
center={center}
radius={(station.aircraftSpeed * 1000) / 6}
color="#0e0ecf"
fillOpacity={0}
weight={2}
/>
<Circle
center={center}
radius={(station.aircraftSpeed * 1000) / 3}
color="navy"
fillOpacity={0}
weight={2}
/>
{(station.bosUse === "SECONDARY" || station.bosUse === "DUAL_USE") && (
<Circle
center={center}
radius={station.aircraftSpeed * 1000}
color="maroon"
fillOpacity={0}
weight={1}
dashArray="40,30"
/>
)}
</div>
);
})}
</FeatureGroup>
);
};
const EsriSatellite = ({ attribution }: { attribution: Control.Attribution }) => {
const accessToken = process.env.NEXT_PUBLIC_ESRI_ACCESS_TOKEN || "";
const attributionText = "Sources: Esri, TomTom, Garmin, FAO, NOAA, USGS";
return (
<>
{/* Satellite Imagery Layer; API KEY PROVIDED BY VAR0002 */}
<TileLayer
eventHandlers={{
add: () => attribution.addAttribution(attributionText),
remove: () => attribution.removeAttribution(attributionText),
}}
url={`https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}?token=${accessToken}`}
tileSize={256}
/>
</>
);
};
const StrassentexteEsri = () => {
return (
<WMSTileLayer
url="https://tiledbasemaps.arcgis.com/arcgis/rest/services/Reference/World_Transportation/MapServer/tile/{z}/{y}/{x}"
format="image/png"
transparent
/>
);
};
const OpenAIP = ({ attribution }: { attribution: Control.Attribution }) => {
const accessToken = process.env.NEXT_PUBLIC_OPENAIP_ACCESS;
const attributionText = '© <a href="https://www.openaip.net" target="_blank">OpenAIP</a>';
return (
<TileLayer
eventHandlers={{
add: () => attribution.addAttribution(attributionText),
remove: () => attribution.removeAttribution(attributionText),
}}
url={`https://api.tiles.openaip.net/api/data/openaip/{z}/{x}/{y}.png?apiKey=${accessToken}`}
/>
);
};
const NiederschlagOverlay = ({ attribution }: { attribution: Control.Attribution }) => {
let tileLayer: L.TileLayer | null = null;
useEffect(() => {
if (tileLayer) {
tileLayer.bringToFront();
}
}, [tileLayer]);
return (
<WMSTileLayer
ref={(layer) => {
if (layer) {
tileLayer = layer;
}
}}
eventHandlers={{
add: () => attribution.addAttribution("Quelle: Deutscher Wetterdienst"),
remove: () => attribution.removeAttribution("Quelle: Deutscher Wetterdienst"),
}}
url="https://maps.dwd.de/geoserver/wms?"
format="image/png"
layers="dwd:Niederschlagsradar"
transparent
opacity={0.7}
/>
);
};
export const BaseMaps = () => {
const map = useMap();
@@ -14,16 +279,39 @@ export const BaseMaps = () => {
}, [isPannelOpen]);
return (
<>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
className="invert-100 grayscale"
/>
</>
<LayersControl position="topleft">
<LayersControl.Overlay name={"Funknetzbereiche"}>
<RadioAreaLayer />
</LayersControl.Overlay>
<LayersControl.Overlay name={"Niederschlag"}>
<NiederschlagOverlay attribution={map.attributionControl} />
</LayersControl.Overlay>
<LayersControl.Overlay name={"LRZs"}>
<StationsLayer attribution={map.attributionControl} />
</LayersControl.Overlay>
<LayersControl.Overlay name={"OpenAIP"}>
<OpenAIP attribution={map.attributionControl} />
</LayersControl.Overlay>
<LayersControl.BaseLayer name="OpenStreetMap" checked>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
</LayersControl.BaseLayer>
<LayersControl.BaseLayer name="OpenStreetMap Dark">
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
className="invert-100 grayscale"
/>
</LayersControl.BaseLayer>
<LayersControl.BaseLayer name="ESRI Satellite">
<LayerGroup>
<EsriSatellite attribution={map.attributionControl} />
<StrassentexteEsri />
</LayerGroup>
</LayersControl.BaseLayer>
</LayersControl>
);
};