131 lines
4.3 KiB
TypeScript
Executable File
131 lines
4.3 KiB
TypeScript
Executable File
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; |