Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions examples/directions/README.md
Original file line number Diff line number Diff line change
@@ -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 `createPolylines()` and `createWaypointAdvancedMarkers()` for rendering routes and markers on the map, 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

Expand Down Expand Up @@ -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
183 changes: 94 additions & 89 deletions examples/directions/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, {useEffect, useState} from 'react';
import React, {useEffect, useRef} from 'react';
import {createRoot} from 'react-dom/client';

import {
APIProvider,
Map,
useMapsLibrary,
useMap
useMap,
useMapsLibrary
} from '@vis.gl/react-google-maps';
import ControlPanel from './control-panel';

Expand All @@ -15,106 +15,111 @@ const API_KEY =
const App = () => (
<APIProvider apiKey={API_KEY}>
<Map
defaultCenter={{lat: 43.65, lng: -79.38}}
defaultZoom={9}
mapId={'bf51a910020fa25a'}
defaultCenter={{lat: -23.588363, lng: -46.658475}}
defaultZoom={15}
gestureHandling={'greedy'}
fullscreenControl={false}>
<Directions />
<Directions
origin="R. Dr. Diogo de Faria, 946, São Paulo"
destination="R. Domingos Fernandes, 588, São Paulo"
travelMode="DRIVING"
/>
</Map>
<ControlPanel />
</APIProvider>
);

function Directions() {
interface DirectionsProps {
origin: string;
destination: string;
travelMode?: google.maps.TravelModeString;
}

export function Directions({
origin,
destination,
travelMode = 'DRIVING'
}: DirectionsProps) {
const map = useMap();
const routesLibrary = useMapsLibrary('routes');
const [directionsService, setDirectionsService] =
useState<google.maps.DirectionsService>();
const [directionsRenderer, setDirectionsRenderer] =
useState<google.maps.DirectionsRenderer>();
const [routes, setRoutes] = useState<google.maps.DirectionsRoute[]>([]);
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');

// refs for the polylines and markers created with the Routes API
const polylinesRef = useRef<google.maps.Polyline[]>([]);
const markersRef = useRef<google.maps.marker.AdvancedMarkerElement[]>([]);

// 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);
}
}
);

return () => google.maps.event.removeListener(listener);
}, [directionsRenderer]);

// 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
if (!routesLib || !map || !origin || !destination) return;

// cancel async operations on unmount (no AbortSignal support in Routes API)
let isCancelled = false;

// Clean up previous polylines & markers
polylinesRef.current.forEach(p => p.setMap(null));
polylinesRef.current = [];
markersRef.current.forEach(m => {
m.map = null;
});
markersRef.current = [];

const request: google.maps.routes.ComputeRoutesRequest = {
origin,
destination,
travelMode,
fields: ['path', 'distanceMeters', 'durationMillis', 'viewport', 'legs']
};

routesLib.Route.computeRoutes(request)
.then(async ({routes}) => {
if (isCancelled) return;
if (!routes || routes.length === 0) return;

const route = routes[0];

// Render and append polylines
const newPolylines = route.createPolylines();
newPolylines.forEach(polyline => {
polyline.setOptions({
strokeColor: '#3b82f6',
strokeOpacity: 0.85,
strokeWeight: 6
});
polyline.setMap(map);
});
polylinesRef.current = newPolylines;

// Render waypoint advanced markers
const newMarkers = await route.createWaypointAdvancedMarkers();

if (isCancelled) return;

newMarkers.forEach(marker => {
marker.map = map;
});
markersRef.current = newMarkers;

if (route.viewport) map.fitBounds(route.viewport);
})
.then(response => {
directionsRenderer.setDirections(response);
setRoutes(response.routes);
.catch(err => {
if (isCancelled) return;
console.error('Error computing routes:', err);
});

return () => directionsRenderer.setMap(null);
}, [directionsService, directionsRenderer]);
return () => {
isCancelled = true;

// Update direction route
useEffect(() => {
if (!directionsRenderer) return;
directionsRenderer.setRouteIndex(routeIndex);
}, [routeIndex, directionsRenderer]);

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>
</div>
);
// clear polylines and markers
polylinesRef.current.forEach(p => p.setMap(null));
polylinesRef.current = [];

markersRef.current.forEach(marker => {
marker.map = null;
});
markersRef.current = [];
};
}, [routesLib, map, origin, destination, travelMode]);

return null;
}

export default App;
Expand Down
22 changes: 14 additions & 8 deletions examples/directions/src/control-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,29 @@ 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 (
<div className="control-panel">
<h3>Directions</h3>
<h3>Routes API</h3>
<p>
Loading the routes library to render directions on the map using
<code>DirectionsService</code> and <code>DirectionsRenderer</code>.
Loading the routes library to compute and render routes on the map using
the modern <code>Route.computeRoutes</code> service.
</p>

<p className={'note'}>
<strong>Important:</strong> 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:{' '}
<strong>Important:</strong> This example uses the new{' '}
<a target={'_new'} href={GCP_ROUTES_API}>
Routes API (Recommended)
</a>{' '}
, the modern and current way to calculate directions. If you are using
the{' '}
<a target={'_new'} href={GCP_DIRECTIONS_API}>
Directions API (Legacy)
</a>
.
</a>{' '}
Service, switch to this implementation of Routes API.
</p>

<div className="links">
Expand Down
Binary file modified website/static/images/examples/directions.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading