"use client" import { useState, useEffect, useRef } from "react" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { MapPin, Loader2, AlertCircle, Play, Route } from "lucide-react" declare global { interface Window { L: any } } interface LocationData { latitude: number longitude: number accuracy: number } export function LocationMap() { const [location, setLocation] = useState(null) const [startLocation, setStartLocation] = useState(null) const [distance, setDistance] = useState(null) const [isTracking, setIsTracking] = useState(false) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [mapLoaded, setMapLoaded] = useState(false) const mapRef = useRef(null) const mapInstanceRef = useRef(null) const startMarkerRef = useRef(null) const currentMarkerRef = useRef(null) const routeLineRef = useRef(null) const watchIdRef = useRef(null) const calculateDistance = (lat1: number, lon1: number, lat2: number, lon2: number): number => { const R = 6371 // Earth's radius in kilometers const dLat = ((lat2 - lat1) * Math.PI) / 180 const dLon = ((lon2 - lon1) * Math.PI) / 180 const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2) const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) const calculatedDistance = R * c * 1000 // Convert to meters const MIN_DISTANCE_THRESHOLD = 5 // meters return calculatedDistance >= MIN_DISTANCE_THRESHOLD ? calculatedDistance : 0 } useEffect(() => { const loadLeaflet = async () => { if (typeof window !== "undefined" && !window.L) { const link = document.createElement("link") link.rel = "stylesheet" link.href = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" document.head.appendChild(link) const script = document.createElement("script") script.src = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" script.onload = () => setMapLoaded(true) document.head.appendChild(script) } else if (window.L) { setMapLoaded(true) } } loadLeaflet() }, []) useEffect(() => { return () => { if (watchIdRef.current !== null) { navigator.geolocation.clearWatch(watchIdRef.current) } } }, []) useEffect(() => { if (mapLoaded && mapRef.current && (location || startLocation)) { const L = window.L if (!mapInstanceRef.current) { const initialLocation = startLocation || location const map = L.map(mapRef.current).setView([initialLocation!.latitude, initialLocation!.longitude], 15) L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { attribution: "© OpenStreetMap contributors", }).addTo(map) mapInstanceRef.current = map } const map = mapInstanceRef.current if (startLocation && !startMarkerRef.current) { const startMarker = L.marker([startLocation.latitude, startLocation.longitude], { icon: L.divIcon({ html: '
S
', className: "custom-div-icon", iconSize: [24, 24], iconAnchor: [12, 12], }), }).addTo(map) startMarker.bindPopup(`
🚀 Start Location
Lat: ${startLocation.latitude.toFixed(6)}
Lng: ${startLocation.longitude.toFixed(6)}
`) startMarkerRef.current = startMarker } if (location) { if (currentMarkerRef.current) { map.removeLayer(currentMarkerRef.current) } const currentMarker = L.marker([location.latitude, location.longitude], { icon: L.divIcon({ html: '
📍
', className: "custom-div-icon", iconSize: [24, 24], iconAnchor: [12, 12], }), }).addTo(map) currentMarker.bindPopup(`
📍 Current Location
Lat: ${location.latitude.toFixed(6)}
Lng: ${location.longitude.toFixed(6)}
Accuracy: ${location.accuracy.toFixed(0)}m
`) currentMarkerRef.current = currentMarker L.circle([location.latitude, location.longitude], { radius: location.accuracy, fillColor: "#3b82f6", fillOpacity: 0.15, color: "#3b82f6", weight: 2, dashArray: "5, 5", }).addTo(map) } if (startLocation && location) { if (routeLineRef.current) { map.removeLayer(routeLineRef.current) } const routeLine = L.polyline( [ [startLocation.latitude, startLocation.longitude], [location.latitude, location.longitude], ], { color: "#ef4444", weight: 4, opacity: 0.8, dashArray: "10, 5", }, ).addTo(map) routeLineRef.current = routeLine const group = L.featureGroup([startMarkerRef.current, currentMarkerRef.current]) map.fitBounds(group.getBounds().pad(0.2)) const dist = calculateDistance( startLocation.latitude, startLocation.longitude, location.latitude, location.longitude, ) setDistance(dist) } } }, [location, startLocation, mapLoaded]) const startTracking = () => { if (!navigator.geolocation) { setError("Geolocation is not supported by this browser.") return } setLoading(true) setError(null) setIsTracking(true) navigator.geolocation.getCurrentPosition( (position) => { const { latitude, longitude, accuracy } = position.coords setStartLocation({ latitude, longitude, accuracy }) setLoading(false) watchIdRef.current = navigator.geolocation.watchPosition( (position) => { const { latitude, longitude, accuracy } = position.coords setLocation({ latitude, longitude, accuracy }) }, (error) => { let errorMessage = "Unable to retrieve your location." switch (error.code) { case error.PERMISSION_DENIED: errorMessage = "Location access denied. Please enable location permissions in your browser settings." break case error.POSITION_UNAVAILABLE: errorMessage = "Location information is unavailable. Please check your GPS/location services." break case error.TIMEOUT: errorMessage = "Location request timed out. Please try again or check your connection." break } setError(errorMessage) }, { enableHighAccuracy: true, timeout: 30000, // Increased to 30 seconds maximumAge: 10000, // Allow cached location up to 10 seconds }, ) }, (error) => { let errorMessage = "Unable to retrieve your location." switch (error.code) { case error.PERMISSION_DENIED: errorMessage = "Location access denied. Please enable location permissions in your browser settings." break case error.POSITION_UNAVAILABLE: errorMessage = "Location information is unavailable. Please check your GPS/location services." break case error.TIMEOUT: errorMessage = "Location request timed out. Please try again or check your connection." break } setError(errorMessage) setLoading(false) setIsTracking(false) }, { enableHighAccuracy: true, timeout: 30000, // Increased to 30 seconds maximumAge: 30000, // Allow cached location up to 30 seconds for initial position }, ) } const resetTracking = () => { if (watchIdRef.current !== null) { navigator.geolocation.clearWatch(watchIdRef.current) watchIdRef.current = null } setStartLocation(null) setLocation(null) setDistance(null) setIsTracking(false) setError(null) if (mapInstanceRef.current) { mapInstanceRef.current.remove() mapInstanceRef.current = null startMarkerRef.current = null currentMarkerRef.current = null routeLineRef.current = null } } return (
Location Distance Tracker Track distance between your start location and current position
{!isTracking ? ( ) : ( )}
{error && (
{error}
)} {isTracking && distance !== null && (
Distance Traveled
{distance.toFixed(1)} meters
{(distance / 1000).toFixed(3)} kilometers
)} {isTracking && (
Auto-updating location...
)} {(startLocation || location) && (
{startLocation && (
Start Location
Latitude
{startLocation.latitude.toFixed(6)}
Longitude
{startLocation.longitude.toFixed(6)}
)} {location && (
Current Location
Latitude
{location.latitude.toFixed(6)}
Longitude
{location.longitude.toFixed(6)}
Accuracy
{location.accuracy.toFixed(0)}m
)}
)}
{(startLocation || location) && ( Interactive Map {startLocation && location ? "Green marker shows start location, blue marker shows current location with route line" : startLocation ? "Green marker shows your start location" : "Blue marker shows your current location"}
{!mapLoaded && (startLocation || location) && (

Loading map...

)} )}
) }