import { useRef, useEffect, useState } from 'react' import { MapContainer, TileLayer, Marker, Popup, Polyline, Rectangle, useMap, useMapEvents } from 'react-leaflet' import L from 'leaflet' import 'leaflet/dist/leaflet.css' import 'leaflet-polylinedecorator' import { useTranslation } from 'react-i18next' import DrawControl from './DrawControl' import MapPoint from './MapPoint' import MiniMap from './MiniMap' import { defaultIcon } from './mapIcons' import { TILE_URLS } from './types' import type { FlightPoint, CalculatedPointInfo, MapRectangle, ActionMode, MovingPointInfo } from './types' interface MapEventsProps { points: FlightPoint[] handlePolylineClick: (e: L.LeafletMouseEvent) => void containerRef: React.RefObject onMapMove: (center: L.LatLng) => void } function MapEvents({ points, handlePolylineClick, containerRef, onMapMove }: MapEventsProps) { const map = useMap() const polylineRef = useRef(null) const arrowRef = useRef(null) useEffect(() => { const handler = () => onMapMove(map.getCenter()) map.on('moveend', handler) return () => { map.off('moveend', handler) } }, [map, onMapMove]) useEffect(() => { if (polylineRef.current) map.removeLayer(polylineRef.current) if (arrowRef.current) map.removeLayer(arrowRef.current) if (points.length > 1) { const positions: L.LatLngTuple[] = points.map(p => [p.position.lat, p.position.lng]) polylineRef.current = L.polyline(positions, { color: '#228be6', weight: 6, opacity: 0.7, lineJoin: 'round' }).addTo(map) arrowRef.current = L.polylineDecorator(polylineRef.current, { patterns: [{ offset: '10%', repeat: '40%', symbol: L.Symbol.arrowHead({ pixelSize: 12, pathOptions: { fillOpacity: 1, weight: 0, color: '#228be6' } }) }], }).addTo(map) polylineRef.current.on('click', handlePolylineClick) } const observer = new ResizeObserver(() => map.invalidateSize()) if (containerRef.current) observer.observe(containerRef.current) return () => { if (polylineRef.current) { map.removeLayer(polylineRef.current); polylineRef.current = null } if (arrowRef.current) { map.removeLayer(arrowRef.current); arrowRef.current = null } observer.disconnect() } }, [map, points, handlePolylineClick, containerRef]) return null } function SetView({ center }: { center: L.LatLngExpression }) { const map = useMap() useEffect(() => { map.setView(center) }, [center, map]) return null } interface Props { points: FlightPoint[] calculatedPointInfo: CalculatedPointInfo[] currentPosition: { lat: number; lng: number } rectangles: MapRectangle[] setRectangles: React.Dispatch> rectangleColor: string actionMode: ActionMode onAddPoint: (lat: number, lng: number) => void onUpdatePoint: (index: number, position: { lat: number; lng: number }) => void onRemovePoint: (id: string) => void onAltitudeChange: (index: number, altitude: number) => void onMetaChange: (index: number, meta: string[]) => void onPolylineClick: (e: L.LeafletMouseEvent) => void onPositionChange: (pos: { lat: number; lng: number }) => void onMapMove: (center: L.LatLng) => void } export default function FlightMap({ points, currentPosition, rectangles, setRectangles, rectangleColor, actionMode, onAddPoint, onUpdatePoint, onRemovePoint, onAltitudeChange, onMetaChange, onPolylineClick, onPositionChange, onMapMove, }: Props) { const { t } = useTranslation() const containerRef = useRef(null) const [mapType, setMapType] = useState<'classic' | 'satellite'>('satellite') const [movingPoint, setMovingPoint] = useState(null) const [draggablePoints, setDraggablePoints] = useState(points) const polylineClickRef = useRef(false) useEffect(() => { setDraggablePoints(points) }, [points]) function ClickHandler() { useMapEvents({ click(e) { if (actionMode === 'points') { if (!polylineClickRef.current) onAddPoint(e.latlng.lat, e.latlng.lng) polylineClickRef.current = false } }, }) return null } const handlePolylineClick = (e: L.LeafletMouseEvent) => { if (actionMode === 'points') { polylineClickRef.current = true onPolylineClick(e) } } const handleDrag = (index: number, pos: { lat: number; lng: number }) => { const updated = [...draggablePoints] updated[index] = { ...updated[index], position: pos } setDraggablePoints(updated) } return (
OSM' : 'Satellite'} /> {movingPoint && } {draggablePoints.map((point, index) => ( onUpdatePoint(i, pos)} onAltitudeChange={onAltitudeChange} onMetaChange={onMetaChange} onRemove={onRemovePoint} onMoving={setMovingPoint} /> ))} {draggablePoints.length > 1 && ( )} {currentPosition && ( onPositionChange((e.target as L.Marker).getLatLng()) }}> {t('flights.planner.currentLocation')} )} {rectangles.map(rect => ( ))} {(actionMode === 'workArea' || actionMode === 'prohibitedArea') && (
Click and drag on the map to draw a {actionMode === 'workArea' ? 'work area' : 'no-go zone'}
)}
) }