live tracker

                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 }) {
  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)

  // 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) => {
        console.log("response", response);
        directionsRenderer.setDirections(response);
        setRoutes(response.routes);
      });
    return () => directionsRenderer.setMap(null);
  }, [directionsService, directionsRenderer]);

  // 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);
        console.log("duration", duration);
       
      } else {
        console.error('Directions request failed:', status);
      }
    });
  };

  const getReverseGeocode = async (lat, lng) => {
    setReverseGeocodeData(await reverseGeocode(lat, lng));
  };

  // const handleGetAddress = () => {
  //   const geocoder = new window.google.maps.Geocoder();
  //       const latlng = { lat: parseFloat(markerPosition?.lat), lng: parseFloat(markerPosition?.lng) };
     
  //       geocoder.geocode({ location: latlng }, (results, status) => {

  //         console.log("results", results);
  //         console.log("status", status);
  //         if (status === 'OK') {
  //           if (results[0]) {
  //             setAddress(results[0]?.formatted_address);
  //           } else {
  //             setAddress('No results found');
  //           }
  //         } else {
  //           setAddress('Geocoder failed due to: ' + status);
  //         }
  //   });

  // }


 
  // useEffect(() => {
  //   let timer = setInterval(() => {
  //     const pos = {
  //       lat: routes[routeIndex]?.overview_path[currentIndex].lat(),
  //       lng: routes[routeIndex]?.overview_path[currentIndex].lng(),
  //     };
  //     // setMarkerPosition(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(),
  //     };      
      
  //     getReverseGeocode(pos.lat, pos.lng);
  //     console.log("reverseGeocodeData", reverseGeocodeData);    
   
  //     handleAddress(reverseGeocodeData);
  //     calculateETA(pos, nextPos);
  
  //   }, 2000);
  //   return () => {
  //   clearTimeout( timer);
  //   };
  // }, []);

  // Move the marker
  useEffect(() => {
    // let interval;
    let destinationReached = false;
    const interval = 10;
    const condition = trigger % interval;

    console.log("routes inside useeffect", routes);
    console.log("routeIndex inside useeffect", routeIndex);

    if (routes[routeIndex] && !condition) {
      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("I am here");
      console.log("currentIndex", currentIndex);
      // interval = setInterval(() => {
      if (currentIndex < routes[routeIndex].overview_path.length - 1) {
        console.log("inside interval");
        const pos = {
          lat: routes[routeIndex].overview_path[currentIndex].lat(),
          lng: routes[routeIndex].overview_path[currentIndex].lng(),
        };
        setMarkerPosition(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(),
        };      
        
        getReverseGeocode(pos.lat, pos.lng);
        console.log("reverseGeocodeData", reverseGeocodeData);
       
     
        handleAddress(reverseGeocodeData);
        calculateETA(pos, nextPos);
        setCurrentIndex((prevIndex) => prevIndex + 1);
        console.log("address", address);
      } else {
        console.log("I got cleared :-)");
        // clearInterval(interval);
        destinationReached = true;
      }
      // }, 10);
      console.log("MAP: ", map);
      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 });
        }
      });

      console.log("COVERED: ", coveredCoordinates);
      console.log("UNCOVERED: ", uncoveredCoordinates);
      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);
    }
    // return () => clearInterval(interval);
    if (!destinationReached) {
      setTrigger(trigger + 1);
    }
  }, [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;

  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: directionIcon,
          //     scaledSize: new window.google.maps.Size(40, 40),
          //     anchor: new window.google.maps.Point(20, 20),
          // }}
          icon={{
            // url: directionIcon,
            url: RotateIcon.makeIcon(directionIcon)
              .setRotation({ deg: bearingAngle })
              .getUrl(),
            scaledSize: new window.google.maps.Size(60, 60),
            anchor: new window.google.maps.Point(20, 20),
            // rotation: bearingAngle,
          }}
        />
      )}
    </div>
  );
}

export default Directions;

Raw Text