Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+131
View File
@@ -0,0 +1,131 @@
import { useEffect, useMemo, useState, FC } from 'react';
import { MapContainer, TileLayer, Marker, Polyline, Polygon, Popup } from 'react-leaflet';
import L from 'leaflet';
import icon from 'leaflet/dist/images/marker-icon.png';
import iconShadow from 'leaflet/dist/images/marker-shadow.png';
import { GeoJson } from '../types/geojson';
import 'leaflet/dist/leaflet.css';
import '../styles/map.css';
import { getAvgLatLng } from '../api/replay';
import { getIcon } from '../utils/markerSymbol';
// Standard-Icon für Leaflet
const DefaultIcon = L.icon({
iconUrl: icon,
shadowUrl: iconShadow,
iconSize: [25, 41],
iconAnchor: [12.5, 41],
});
L.Marker.prototype.options.icon = DefaultIcon;
interface ReplayIdProps {
geoJsons: GeoJson[];
replayId: string;
}
const StaticMap: FC<ReplayIdProps> = ({ geoJsons, replayId }) => {
const [avgLatLng, setAvgLatLng] = useState<L.LatLng | null>(null);
const [isLoading, setIsLoading] = useState(true);
const iconCache = useMemo(() => new Map<string, L.Icon>(), []);
useEffect(() => {
const fetchAvgLatLng = async () => {
try {
const avgLatLngResult = await getAvgLatLng(replayId);
if (Array.isArray(avgLatLngResult) && avgLatLngResult.length > 0 && typeof avgLatLngResult[0].lat === 'number' && typeof avgLatLngResult[0].lng === 'number') {
setAvgLatLng(new L.LatLng(avgLatLngResult[0].lat, avgLatLngResult[0].lng));
} else {
throw new Error('Invalid LatLng object');
}
setIsLoading(false);
} catch (error) {
console.error('Fehler beim Abrufen von avgLatLng:', error);
setIsLoading(false);
}
};
fetchAvgLatLng();
}, [replayId]);
const tileLayer = useMemo(
() => <TileLayer url="https://tile.openstreetmap.de/{z}/{x}/{y}.png" />,
[]
);
const icons = useMemo(() => {
return geoJsons.map((marker) => {
const { id, sidic } = marker.properties;
const key = `${id}-${sidic}`;
// Prüfe, ob das Icon im Cache vorhanden ist
if (!iconCache.has(key)) {
iconCache.set(key, getIcon(marker));
}
return iconCache.get(key)!; // Das "!" ist hier sicher, da wir garantiert vorher das Icon hinzufügen
});
}, [geoJsons, iconCache]);
const renderGeoJson = useMemo(() => {
return geoJsons.map((marker, index) => {
const renderProperties = () => {
return Object.entries(marker.properties).map(([key, value]) => (
<p key={key}><strong>{key}:</strong> {value}</p>
));
};
switch (marker.geometry.type) {
case 'Point':
return (
<Marker
key={index}
position={marker.geometry.coordinates as L.LatLngExpression}
icon={icons[index]}
>
<Popup>
{renderProperties()}
</Popup>
</Marker>
);
case 'LineString':
return (
<Polyline
key={index}
positions={marker.geometry.coordinates as L.LatLngExpression[]}
/>
);
case 'Polygon':
return (
<Polygon
key={index}
positions={marker.geometry.coordinates as L.LatLngExpression[][]}
/>
);
default:
console.warn(`Unsupported geometry type: ${marker.geometry.type}`);
return null;
}
});
}, [geoJsons, icons]);
if (isLoading || !avgLatLng) {
return (
<div className="flex h-80vh items-center justify-center">
Loading...
</div>
);
}
return (
<MapContainer center={avgLatLng} zoom={13}>
{tileLayer}
{renderGeoJson}
</MapContainer>
);
};
export default StaticMap;