feat: Add measurement tool with distance calculation and visual markers on the map
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
LayersControl,
|
||||
useMapEvents,
|
||||
useMap,
|
||||
Polyline,
|
||||
} from "react-leaflet";
|
||||
import L from "leaflet";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
@@ -84,6 +85,32 @@ const createColoredMarkerIcon = (color) => {
|
||||
} return null;
|
||||
};
|
||||
|
||||
// Create numbered measurement marker icons
|
||||
const createMeasurementMarkerIcon = (number) => {
|
||||
if (typeof window !== "undefined") {
|
||||
return new L.DivIcon({
|
||||
html: `<div style="
|
||||
background-color: #ef4444;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
border: 2px solid white;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||
">${number}</div>`,
|
||||
className: 'custom-measurement-marker',
|
||||
iconSize: [24, 24],
|
||||
iconAnchor: [12, 12],
|
||||
});
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Component to handle map events
|
||||
function MapEventHandler({ onViewChange }) {
|
||||
const map = useMapEvents({
|
||||
@@ -106,6 +133,19 @@ function MapEventHandler({ onViewChange }) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Component to handle measurement events
|
||||
function MeasurementHandler({ isMeasuring, onMeasurementClick, measurementPoints }) {
|
||||
const map = useMapEvents({
|
||||
click: (e) => {
|
||||
if (isMeasuring && onMeasurementClick) {
|
||||
onMeasurementClick(e.latlng);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Custom zoom control component that handles external events
|
||||
function CustomZoomHandler() {
|
||||
const map = useMap();
|
||||
@@ -143,6 +183,9 @@ export default function EnhancedLeafletMap({
|
||||
activeOverlays = [],
|
||||
onViewChange,
|
||||
showOverlays = true,
|
||||
isMeasuring = false,
|
||||
measurementPoints = [],
|
||||
onMeasurementClick,
|
||||
}) {
|
||||
useEffect(() => {
|
||||
fixLeafletIcons();
|
||||
@@ -157,6 +200,11 @@ export default function EnhancedLeafletMap({
|
||||
>
|
||||
<CustomZoomHandler />
|
||||
{onViewChange && <MapEventHandler onViewChange={onViewChange} />}
|
||||
{isMeasuring && <MeasurementHandler
|
||||
isMeasuring={isMeasuring}
|
||||
onMeasurementClick={onMeasurementClick}
|
||||
measurementPoints={measurementPoints}
|
||||
/>}
|
||||
|
||||
{showLayerControl ? (
|
||||
<LayersControl position="topright">
|
||||
@@ -255,6 +303,39 @@ export default function EnhancedLeafletMap({
|
||||
{marker.popup && <Popup>{marker.popup}</Popup>}
|
||||
</Marker>
|
||||
))}
|
||||
|
||||
{/* Measurement elements */}
|
||||
{isMeasuring && measurementPoints.length > 0 && (
|
||||
<>
|
||||
{/* Measurement line */}
|
||||
{measurementPoints.length > 1 && (
|
||||
<Polyline
|
||||
positions={measurementPoints}
|
||||
color="red"
|
||||
weight={3}
|
||||
opacity={0.8}
|
||||
dashArray="10, 10"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Measurement point markers */}
|
||||
{measurementPoints.map((point, index) => (
|
||||
<Marker
|
||||
key={`measurement-${index}`}
|
||||
position={point}
|
||||
icon={createMeasurementMarkerIcon(index + 1)}
|
||||
>
|
||||
<Popup>
|
||||
<div className="text-sm">
|
||||
<strong>Point {index + 1}</strong><br />
|
||||
Lat: {point.lat.toFixed(6)}<br />
|
||||
Lng: {point.lng.toFixed(6)}
|
||||
</div>
|
||||
</Popup>
|
||||
</Marker>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</MapContainer>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user