diff --git a/examples/directions/README.md b/examples/directions/README.md index 8de64737..43875d63 100644 --- a/examples/directions/README.md +++ b/examples/directions/README.md @@ -1,19 +1,14 @@ -# Google Maps Directions API Example +# Google Maps Routes API Example ![image](https://raw.githubusercontent.com/visgl/react-google-maps/main/website/static/images/examples/directions.jpg) -This is an example which shows how to use `useMapsLibrary` to load the `routes` library, and then use `DirectionsService` and `DirectionsRenderer` to find and display a route on a map. +This is an example which shows how to use `useMapsLibrary` to load the `routes` library, and then use the modern `Route` class to compute and render routes on a map. -It allows the user to choose alternative routes, updating the route being rendered on the map. - -Users can also drag the markers around the map to change the route. The route is updated in real-time. +It utilizes the modern client-side `Route.computeRoutes()` method combined with custom-styled React `` rendering, completely avoiding legacy services and CORS restrictions. > [!IMPORTANT] > -> This example is only compatible with the -> Directions API Legacy Service. Using this Services requires enabling the -> API on your Google Cloud project by following the direct links: -> [Directions API (Legacy)][gcp-directions-api] +> This example uses the new [Routes API (Recommended)][gcp-routes-api] which is the modern and current way to calculate directions. If you are using the [Directions API (Legacy)][gcp-directions-api] Service, consider switching to this implementation of Routes API. ## Google Maps Platform API Key @@ -47,3 +42,4 @@ The regular `npm start` task is only used for the standalone versions of the exa [get-api-key]: https://developers.google.com/maps/documentation/javascript/get-api-key [gcp-directions-api]: https://console.cloud.google.com/apis/library/directions-backend.googleapis.com +[gcp-routes-api]: https://console.cloud.google.com/apis/library/routes.googleapis.com diff --git a/examples/directions/src/app.tsx b/examples/directions/src/app.tsx index 7f551002..e3227e69 100644 --- a/examples/directions/src/app.tsx +++ b/examples/directions/src/app.tsx @@ -1,11 +1,12 @@ -import React, {useEffect, useState} from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import {createRoot} from 'react-dom/client'; import { APIProvider, Map, useMapsLibrary, - useMap + useMap, + AdvancedMarker } from '@vis.gl/react-google-maps'; import ControlPanel from './control-panel'; @@ -15,104 +16,122 @@ const API_KEY = const App = () => ( - + ); -function Directions() { +interface RouteDisplayProps { + origin: string; + destination: string; + travelMode: 'DRIVING' | 'WALKING' | 'BICYCLING' | 'TWO_WHEELER' | 'TRANSIT'; +} + +interface RouteDetails { + distanceMeters: number; + durationMillis: number; + startCoords?: google.maps.LatLngLiteral; + endCoords?: google.maps.LatLngLiteral; + steps: Array<{ + instructions: string; + distanceMeters: number; + durationMillis: number; + maneuver?: string; + }>; +} + +export function RouteDisplay({ + origin, + destination, + travelMode, +}: RouteDisplayProps) { const map = useMap(); - const routesLibrary = useMapsLibrary('routes'); - 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]; - - // Initialize directions service and renderer - useEffect(() => { - if (!routesLibrary || !map) return; - setDirectionsService(new routesLibrary.DirectionsService()); - setDirectionsRenderer( - new routesLibrary.DirectionsRenderer({ - draggable: true, // Only necessary for draggable markers - map - }) - ); - }, [routesLibrary, map]); + const routesLib = useMapsLibrary('routes'); + const polylinesRef = useRef([]); + const [routeDetails, setRouteDetails] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); - // Add the following useEffect to make markers draggable useEffect(() => { - if (!directionsRenderer) return; - - // Add the listener to update routes when directions change - const listener = directionsRenderer.addListener( - 'directions_changed', - () => { - const result = directionsRenderer.getDirections(); - if (result) { - setRoutes(result.routes); + if (!routesLib || !map || !origin || !destination) return; + + // Clean up previous route lines + polylinesRef.current.forEach(p => p.setMap(null)); + polylinesRef.current = []; + setError(null); + setLoading(true); + + const request = { + origin: origin, + destination: destination, + travelMode: travelMode, + fields: ['path', 'distanceMeters', 'durationMillis', 'viewport', 'legs'], + }; + + // Access the modern client-side Route.computeRoutes class service + (routesLib.Route as any).computeRoutes(request) + .then(({ routes }: { routes: any[] }) => { + setLoading(false); + if (!routes || routes.length === 0) { + setError('No route found.'); + return; } - } - ); - return () => google.maps.event.removeListener(listener); - }, [directionsRenderer]); + const primaryRoute = routes[0]; + + // Render polylines dynamically using modern Route.createPolylines() + const newPolylines = primaryRoute.createPolylines(); + newPolylines.forEach((polyline: google.maps.Polyline) => { + polyline.setOptions({ + strokeColor: '#3b82f6', // Stunning visual Tailwind Blue 500 + strokeOpacity: 0.85, + strokeWeight: 6, + }); + polyline.setMap(map); + }); + polylinesRef.current = newPolylines; + + if (primaryRoute.viewport) { + map.fitBounds(primaryRoute.viewport); + } - // Use directions service - useEffect(() => { - if (!directionsService || !directionsRenderer) return; - - directionsService - .route({ - origin: '100 Front St, Toronto ON', - destination: '500 College St, Toronto ON', - travelMode: google.maps.TravelMode.DRIVING, - provideRouteAlternatives: true + const details: RouteDetails = { + distanceMeters: primaryRoute.distanceMeters ?? 0, + durationMillis: primaryRoute.durationMillis ?? 0, + steps: [], + }; + setRouteDetails(details); }) - .then(response => { - directionsRenderer.setDirections(response); - setRoutes(response.routes); + .catch((err: any) => { + setLoading(false); + console.error('Error computing routes:', err); + setError(err.message || 'Failed to compute route.'); }); - return () => directionsRenderer.setMap(null); - }, [directionsService, directionsRenderer]); - - // Update direction route - useEffect(() => { - if (!directionsRenderer) return; - directionsRenderer.setRouteIndex(routeIndex); - }, [routeIndex, directionsRenderer]); + return () => { + polylinesRef.current.forEach(p => p.setMap(null)); + polylinesRef.current = []; + }; + }, [routesLib, map, origin, destination, travelMode]); - if (!leg) return null; + if (loading) return
Calculating route...
; + if (error) return
{error}
; + if (!routeDetails) return null; return ( -
-

{selected.summary}

-

- {leg.start_address.split(',')[0]} to {leg.end_address.split(',')[0]} -

-

Distance: {leg.distance?.text}

-

Duration: {leg.duration?.text}

- -

Other Routes

-
    - {routes.map((route, index) => ( -
  • - -
  • - ))} -
+
+

Toronto Route Summary

+

Distance: {(routeDetails.distanceMeters / 1000).toFixed(2)} km

+

Duration: {(routeDetails.durationMillis / 60000).toFixed(0)} mins

); } diff --git a/examples/directions/src/control-panel.tsx b/examples/directions/src/control-panel.tsx index 5b3a75db..60fa8940 100644 --- a/examples/directions/src/control-panel.tsx +++ b/examples/directions/src/control-panel.tsx @@ -4,23 +4,28 @@ import './control-panel.css'; const GCP_DIRECTIONS_API = 'https://console.cloud.google.com/apis/library/directions-backend.googleapis.com'; +const GCP_ROUTES_API = + 'https://console.cloud.google.com/apis/library/routes.googleapis.com'; + function ControlPanel() { return (
-

Directions

+

Routes API

- Loading the routes library to render directions on the map using - DirectionsService and DirectionsRenderer. + Loading the routes library to compute and render routes on the map using + the modern Route.computeRoutes service.

- Important: This example is only compatible with the - Directions API Legacy Service. Using this Services requires enabling the - API on your Google Cloud project by following the direct links:{' '} + Important: This example uses the new {' '} + + Routes API (Recommended) + {' '}, + the modern and current way to calculate directions. If you are using the{' '} Directions API (Legacy) - - . + {' '} + Service, switch to this implementation of Routes API.

diff --git a/website/static/images/examples/directions.jpg b/website/static/images/examples/directions.jpg index dfa152ad..461c63ff 100644 Binary files a/website/static/images/examples/directions.jpg and b/website/static/images/examples/directions.jpg differ