SPRINT 4 ‐ Routes - BlueJayBird11/UniJet GitHub Wiki

As a user, I have routes that are displayed connecting my current location to my destination(s).

The goal for this user story is to create routes that highlight the path the driver should take in order to pick the rider up and the path to deliver them to their destination. To do this, several steps must be taken.

First, you must create an account at mapbox.com and obtain an access token. Once completed, you must also install the Mapbox SDK using npm install @mapbox/mapbox-sdk and use necessary imports.

Next, we will initialize the state variables and set up the map component. State variables routeToDestination and routeToUser are used to hold the coordinates of the routes to the destination and from the driver's location to the user's location. The useEffect hook ensures that the code inside it runs only once when the component mounts. We will go over this more later.

const [routeToDestination, setRouteToDestination] = useState<[number, number][] | null>(null);
const [routeToUser, setRouteToUser] = useState<[number, number][] | null>(null);

useEffect(() => {
  // Fetch user's current location and routes on component mount
}, []);

Thirdly, we will use the fetchRoute function to make an asynchronous HTTP request to the Mapbox API to fetch route data between two sets of coordinates. It constructs the URL with start and end coordinates and the Mapbox access token. The route coordinates are extracted from the Mapbox API JSON response, and the routes are updated.

const fetchRoute = async (startLat: number, startLng: number, endLat: number, endLng: number, setRoute: React.Dispatch<React.SetStateAction<[number, number][] | null>>) => {
  // Construct URL for Mapbox API
  const url = `https://api.mapbox.com/directions/v5/mapbox/driving/${startLng},${startLat};${endLng},${endLat}?geometries=geojson&access_token=${mapboxAccessToken}`;
  
  try {
    // Fetch route data from Mapbox API
    const response = await fetch(url);
    const data = await response.json();
    // Extract route coordinates from response data
    const routeCoordinates = data.routes[0].geometry.coordinates;
    // Update route state with converted coordinates
    setRoute(routeCoordinates.map(([lng, lat]) => [lat, lng])); // Convert to Leaflet's format
  } catch (error) {
    console.error('Failed to fetch route:', error);
  }
};

Now, we will add to the useEffect hook that we touched on briefly earlier. navigator.geolocation.watchPosition is used to continuously monitor the user's position. When a new position is obtained, the position state is updated, and the fetchRoute function fetches routes to the destinations. A cleanup function is then returned to clear the geolocation watch when the component unomounts.

useEffect(() => {
  // Watch user's geolocation
  const watchId = navigator.geolocation.watchPosition(
    (position) => {
      const { latitude, longitude } = position.coords;
      setPosition([latitude, longitude]);
      // Fetch route to destination
      fetchRoute(latitude, longitude, placeholderLocation[0], placeholderLocation[1], setRouteToDestination);
      // Fetch reverse route (from driver's location to user's location)
      fetchRoute(driverLocation[0], driverLocation[1], latitude, longitude, setRouteToUser);
    },
    (error) => console.error('Error watching position:', error),
    { enableHighAccuracy: true, maximumAge: 0 }
  );

  return () => navigator.geolocation.clearWatch(watchId);
}, []);

Finally, we must display the lines of the defined routes. routeToDestination and routeToUser states are checked to make sure they are not null, then the corresponding polylines are rendered.

return (
  <div className="h-screen">
    <MapContainer style={{ width: '100%', height: '90.5%' }} center={position} zoom={13} scrollWheelZoom={true}>
      {/* OpenStreetMap Tile Layer */}
      <TileLayer
        attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />
      {/* Marker for user's current location */}
      <Marker position={position}>
        <Popup>You are here</Popup>
      </Marker>
      {/* Polyline for route to destination */}
      {routeToDestination && <Polyline positions={routeToDestination} color="blue" />}
      {/* Polyline for route from driver's location to user's location */}
      {routeToUser && <Polyline positions={routeToUser} color="red" />}
    </MapContainer>
  </div>
);

After all these steps have been taken, your map should look something like this:

image

The red route goes from the a placeholder driver location to the rider's location, and the blue route goes from the rider's location to the rider's placeholder destination.

⚠️ **GitHub.com Fallback** ⚠️