import { useEffect, useState } from 'react'; import { useMap } from 'react-leaflet'; import L from 'leaflet'; interface CoordinateGridProps { visible: boolean; } /** * Coordinate grid overlay with adaptive spacing based on zoom level. * Drawn directly with Leaflet polylines — no extra dependencies. */ export default function CoordinateGrid({ visible }: CoordinateGridProps) { const map = useMap(); const [zoom, setZoom] = useState(map.getZoom()); useEffect(() => { const handleZoom = () => setZoom(map.getZoom()); map.on('zoomend', handleZoom); return () => { map.off('zoomend', handleZoom); }; }, [map]); useEffect(() => { if (!visible) return; // Adaptive grid spacing based on zoom let interval: number; if (zoom <= 7) interval = 1; else if (zoom <= 10) interval = 0.5; else if (zoom <= 13) interval = 0.1; else interval = 0.01; const bounds = map.getBounds(); const minLat = Math.floor(bounds.getSouth() / interval) * interval; const maxLat = Math.ceil(bounds.getNorth() / interval) * interval; const minLon = Math.floor(bounds.getWest() / interval) * interval; const maxLon = Math.ceil(bounds.getEast() / interval) * interval; const layerGroup = L.layerGroup(); // Latitude lines (horizontal) for (let lat = minLat; lat <= maxLat; lat += interval) { const line = L.polyline( [ [lat, minLon - 1], [lat, maxLon + 1], ], { color: '#666', weight: 0.7, opacity: 0.4, dashArray: '3,3' } ); layerGroup.addLayer(line); // Label const label = L.marker([lat, bounds.getWest() + 0.002], { icon: L.divIcon({ className: '', html: `${lat.toFixed(interval < 0.1 ? 2 : 1)}°`, iconAnchor: [0, 6], }), interactive: false, }); layerGroup.addLayer(label); } // Longitude lines (vertical) for (let lon = minLon; lon <= maxLon; lon += interval) { const line = L.polyline( [ [minLat - 1, lon], [maxLat + 1, lon], ], { color: '#666', weight: 0.7, opacity: 0.4, dashArray: '3,3' } ); layerGroup.addLayer(line); // Label const label = L.marker([bounds.getNorth() - 0.002, lon], { icon: L.divIcon({ className: '', html: `${lon.toFixed(interval < 0.1 ? 2 : 1)}°`, iconAnchor: [12, 12], }), interactive: false, }); layerGroup.addLayer(label); } layerGroup.addTo(map); return () => { map.removeLayer(layerGroup); }; }, [map, visible, zoom]); return null; }