directions
Never
import { useEffect, useState } from "react"; import { useMapsLibrary, useMap, Marker } from "@vis.gl/react-google-maps"; import directionIcon from "../../assets/img/icon_Nav.svg"; import HouseIcon from "../../assets/img/icon_House.svg"; import wareHouseIcon from "../../assets/img/icon_Warehouse.svg"; import {reverseGeocode} from "../../common/utils"; function Directions({ origin, destination, handleEta, handleAddress, handleDestinationTime, markerLat, markerLng, refreshData }) { const map = useMap(); const routesLibrary = useMapsLibrary("routes"); const mapsLibrary = useMapsLibrary("maps"); const [directionsService, setDirectionsService] = useState(); const [directionsRenderer, setDirectionsRenderer] = useState(); const [routes, setRoutes] = useState([]); const [routeIndex, setRouteIndex] = useState(0); const selected = routes[routeIndex]; const leg = selected?.legs[0]; const google = window.google; const [markerPosition, setMarkerPosition] = useState(null); const [currentIndex, setCurrentIndex] = useState(0); const [trigger, setTrigger] = useState(0); const [bearingAngle, setBearingAngle] = useState(0); const [address, setAddress] = useState(''); const [reverseGeocodeData, setReverseGeocodeData] = useState(null); const [intervals, setIntervals] = useState({ lastEtaUpdate: '', nextEtaUpdate: new Date().getTime(), lastRouteUpdate: '', nextRouteUpdate: new Date().getTime() }); const time = new Date().getTime(); // Initialize directions service and renderer useEffect(() => { if (!routesLibrary || !map) return; setDirectionsService(new routesLibrary.DirectionsService()); setDirectionsRenderer( new routesLibrary.DirectionsRenderer({ map, suppressMarkers: true }) ); }, [routesLibrary, map]); // Use directions service useEffect(() => { if (!directionsService || !directionsRenderer) return; directionsService .route({ origin: origin || "100 Front St, Toronto ON", destination: destination || "500 College St, Toronto ON", travelMode: google.maps.TravelMode.DRIVING, provideRouteAlternatives: false, }) .then((response) => { directionsRenderer.setDirections(response); setRoutes(response.routes); }); return () => directionsRenderer.setMap(null); }, [directionsService, directionsRenderer]); // console.log("routes", routes); // Update direction route useEffect(() => { if (!directionsRenderer) return; directionsRenderer.setRouteIndex(routeIndex); }, [routeIndex, directionsRenderer]); const calculateETA = (start, end) => { const request = { origin: start, destination: end, travelMode:'DRIVING', }; directionsService?.route(request, (response, status) => { if (status === 'OK') { const route = response?.routes[0]; const duration = route?.legs[0].duration.text; handleEta(duration); } else { console.error('Directions request failed:', status); } }); }; const getReverseGeocode = async (lat, lng) => { setReverseGeocodeData(await reverseGeocode(lat, lng)); }; const etaCalculation = () => { const pos = { lat: routes[routeIndex]?.overview_path[currentIndex]?.lat(), lng: routes[routeIndex]?.overview_path[currentIndex]?.lng(), }; console.log("pos inside eta", pos); const nextPos = { lat: routes[routeIndex]?.overview_path[ routes[routeIndex]?.overview_path.length - 1 ].lat(), lng: routes[routeIndex]?.overview_path[ routes[routeIndex]?.overview_path.length - 1 ].lng(), }; console.log("nextpos inside eta", nextPos); if(pos.lat !== undefined) { getReverseGeocode(pos.lat, pos.lng); handleAddress(reverseGeocodeData); calculateETA(pos, nextPos); setIntervals(interval => {return { ...interval, ['nextEtaUpdate']: interval.nextEtaUpdate + 60000, ['lastEtaUpdate']: interval.nextEtaUpdate }}); } }; // Move the marker useEffect(() => { // let interval; let destinationReached = false; const interval = 150000; // const condition = trigger % interval; const time = new Date().getTime(); if((routes[routeIndex] && intervals.lastEtaUpdate == '') || (time >= intervals.nextEtaUpdate)) { etaCalculation(); } if (routes[routeIndex] && (time >= intervals.nextRouteUpdate)) { refreshData(); let origin = { lat: routes[routeIndex]?.overview_path[currentIndex]?.lat(), lng: routes[routeIndex]?.overview_path[currentIndex]?.lng(), }; let destination = { lat: routes[routeIndex]?.overview_path[currentIndex + 5]?.lat(), lng: routes[routeIndex]?.overview_path[currentIndex + 5]?.lng(), // lat: routes[routeIndex].overview_path[ // routes[routeIndex].overview_path.length - 1 // ].lat(), // lng: routes[routeIndex].overview_path[ // routes[routeIndex].overview_path.length - 1 // ].lng(), }; let headingAngle = calculateHeading(origin, destination); setBearingAngle(headingAngle); console.log("currentIndex",currentIndex); if (currentIndex < routes[routeIndex].overview_path.length - 1) { console.log("inside if CurrentIndex", currentIndex); const pos = { lat: markerLat, lng: markerLng // lat: routes[routeIndex].overview_path[currentIndex].lat(), // lng: routes[routeIndex].overview_path[currentIndex].lng(), }; setMarkerPosition(pos); console.log("markerPosition", markerPosition); // const nextPos = { // lat: routes[routeIndex].overview_path[ // routes[routeIndex].overview_path.length - 1 // ].lat(), // lng: routes[routeIndex].overview_path[ // routes[routeIndex].overview_path.length - 1 // ].lng(), // }; routes[routeIndex].overview_path.forEach((obj, index) => { const lat = obj.lat(); const lng = obj.lng(); // if(markerLat == lat && markerLng == lng) { // setCurrentIndex(index); // } }); setCurrentIndex((prevIndex) => prevIndex + 1); } else { destinationReached = true; } const coveredCoordinates = []; const uncoveredCoordinates = []; routes[routeIndex].overview_path.forEach((obj, index) => { if (index <= currentIndex) { const lat = obj.lat(); const lng = obj.lng(); coveredCoordinates.push({ lat: lat, lng: lng }); } }); routes[routeIndex].overview_path.forEach((obj, index) => { if (index > currentIndex) { const lat = obj.lat(); const lng = obj.lng(); uncoveredCoordinates.push({ lat: lat, lng: lng }); } }); const coveredPath = new mapsLibrary.Polyline({ path: coveredCoordinates, geodesic: true, strokeColor: "#a4a4a4", //#a4a4a4 strokeOpacity: 2.0, strokeWeight: 8, zIndex: 1000, }); const uncoveredPath = new mapsLibrary.Polyline({ path: uncoveredCoordinates, geodesic: true, strokeColor: "#0f53ff", strokeOpacity: 2.0, strokeWeight: 8, }); coveredPath.setMap(map); uncoveredPath.setMap(map); setIntervals(interval => {return { ...interval, ['nextRouteUpdate']: interval.nextRouteUpdate + interval, ['lastRouteUpdate']: interval.nextRouteUpdate }}); } if (!destinationReached) { setTrigger(trigger + 1); } else { etaCalculation(); handleDestinationTime(time); } }, [trigger]); useEffect(() => { setTrigger(trigger + 1); }, []); const calculateHeading = (cord1, cord2) => { if (cord2) { const lat1 = cord1.lat; const lng1 = cord1.lng; const lat2 = cord2.lat; const lng2 = cord2.lng; const y = Math.sin(lng2 - lng1) * Math.cos(lat2); const x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lng2 - lng1); const θ = Math.atan2(y, x); const brng = ((θ * 180) / Math.PI + 360) % 360; return brng; } return 0; }; const RotateIcon = function (options) { this.options = options || {}; this.rImg = options.img || new Image(); this.rImg.src = this.rImg.src || this.options.url || ""; this.options.width = this.options.width || this.rImg.width || 52; this.options.height = this.options.height || this.rImg.height || 60; const canvas = document.createElement("canvas"); canvas.width = this.options.width; canvas.height = this.options.height; this.context = canvas.getContext("2d"); this.canvas = canvas; }; RotateIcon.makeIcon = function (url) { return new RotateIcon({ url: url, }); }; RotateIcon.prototype.setRotation = function (options) { const canvas = this.context, angle = options.deg ? (options.deg * Math.PI) / 180 : options.rad, centerX = this.options.width / 2, centerY = this.options.height / 2; canvas.clearRect(0, 0, this.options.width, this.options.height); canvas.save(); canvas.translate(centerX, centerY); canvas.rotate(angle); canvas.translate(-centerX, -centerY); canvas.drawImage(this.rImg, 0, 0); canvas.restore(); return this; }; RotateIcon.prototype.getUrl = function () { return this.canvas.toDataURL("image/svg"); }; useEffect(() => { // console.log("bearingAngle", bearingAngle); }, [bearingAngle]); if (!leg) return null; // console.log("markerLat", markerLat); // console.log(" markerLng", markerLng); return ( <div className="directions"> <h2>{selected.summary}</h2> <p> {leg.start_address.split(",")[0]} to {leg.end_address.split(",")[0]} </p> <p>Distance: {leg.distance?.text}</p> <p>Duration: {leg.duration?.text}</p> <h2>Other Routes</h2> <ul> {routes.map((route, index) => ( <li key={route.summary}> <button onClick={() => setRouteIndex(index)}> {route.summary} </button> </li> ))} </ul> <Marker position={{ lat: routes[routeIndex].legs[0].start_location.lat(), lng: routes[routeIndex].legs[0].start_location.lng(), }} icon={{ url: wareHouseIcon, scaledSize: new window.google.maps.Size(24, 24), anchor: new window.google.maps.Point(20, 20), }} /> <Marker position={{ lat: routes[routeIndex].legs[0].end_location.lat(), lng: routes[routeIndex].legs[0].end_location.lng(), }} icon={{ url: HouseIcon, scaledSize: new window.google.maps.Size(24, 24), anchor: new window.google.maps.Point(20, 20), }} /> {markerPosition && ( <Marker position={{ lat: markerPosition.lat, lng: markerPosition.lng, }} icon={{ url: RotateIcon.makeIcon(directionIcon) .setRotation({ deg: bearingAngle }) .getUrl(), scaledSize: new window.google.maps.Size(60, 60), anchor: new window.google.maps.Point(20, 20), }} /> )} </div> ); } export default Directions;