Getting Directions Using the Google Maps API
In Working with Geolocation and the Google Maps API you learned how to determine a user’s physical location and display it on a map. In this followup article, you’ll create a service that lets a user get directions from one address to another. To enhance the user experience, the service will also allow the user to automatically use their current position as the starting point.
This article assumes that the reader is familiar with the content from the previous post. With that said – let’s get started.
Exploring the Google Maps API Further
In the demo you’ll become reacquainted with your old friends Map
, LatLng
and Geocoder
. You’ll also make some new friends. The first one is google.maps.DirectionsService
, which calculates directions (or routes) between two or more locations. This class is very simple. Its constructor takes no parameters, and it has just one method, route()
, which calculates directions. This method accepts two parameters, a google.maps.DirectionsRequest
object and a callback function.
The google.maps.DirectionsRequest
object is used to set options that the route request must satisfy. The only required properties of this object are origin
, destination
, and travelMode
. The first two properties define the start and the end of the path, while travelMode
defines your mode of transportation. The possible values are bicycling, driving, walking, and transit (using the public transportation). One important thing to note is that origin
and destination
can use either a LatLng
instance or a string containing the address.
As I said, the request can also include several options like unitSystem
that ask to return the distances using a specific unit system. The possible values are metric (google.maps.UnitSystem.METRIC
) and imperial (google.maps.UnitSystem.IMPERIAL
). The default is chosen based on the country of origin. You can also specify a set of intermediate points to pass through using the waypoints
property. Additionally, you can constrain the directions. For example, you can ask for a route that doesn’t use highways, if possible, by setting the property avoidHighways
to true
. You can also try to avoid toll roads by setting the avoidTolls
property to true
.
The callback function of DirectionsService
returns two values, a google.maps.DirectionsResult
object and one of the possible values (actually properties) of the google.maps.DirectionsStatus
class. The former has just one property, routes
, that is an array of DirectionsRoute
and contains the information for every path calculated. The DirectionsStatus
represents the final state of the request, and can indicate success (DirectionsStatus.OK
), no results (DirectionsStatus.ZERO_RESULTS
), or an error (like DirectionsStatus.INVALID_REQUEST
or DirectionsStatus.REQUEST_DENIED
).
Another of our new friends is the google.maps.DirectionsRenderer
class. It renders directions retrieved in the form of a
DirectionsResult
object retrieved from the DirectionsService
. This class only contains getters and setters, so we won’t explore it further. The only remarkable thing is its constructor, which accepts a
google.maps.DirectionsRendererOptions
object that allows you to set several options. The key properties of the latter are directions
and map
, that set the routes to display (retrieved using DirectionsService
) and the map object used to show the routes.
Let’s Start Coding
Now that you’ve seen all of the new classes used in this article, it’s time to dive into the code. To enable the user to ask for a path from an origin to a destination, the first thing you need is a form. It’ll be very simple because it needs just two <input>
elements and a submit button. However, to enhance the user experience, the page will also offer the chance to automatically fill an <input>
with the user’s current position. To achieve this goal, I’ll put a link below each <input>
that once clicked, will retrieve the user’s address using Geolocation and the Google Maps API. The form to implement this feature is shown below.
<form id="calculate-route" name="calculate-route" action="#" method="get">
<label for="from">From:</label>
<input type="text" id="from" name="from" required="required" placeholder="An address" size="30" />
<a id="from-link" href="#">Get my position</a>
<br />
<label for="to">To:</label>
<input type="text" id="to" name="to" required="required" placeholder="Another address" size="30" />
<a id="to-link" href="#">Get my position</a>
<br />
<input type="submit" />
<input type="reset" />
</form>
Now we’ll move on to the business logic behind the demo. Unlike the first article, we’ll take advantage of jQuery to quickly select elements in the DOM and to attach handlers in a cross-browser manner. Since all of the work is done on the client-side, the first thing we need is to block the default behavior of the form, and run some additional JavaScript that we’ll cover later. To achieve this goal, we’ll attach a handler to the form’s submit
event. The handler uses the jQuery preventDefault()
method and then calls the calculateRoute()
function. The code that implements this is shown below.
$("#calculate-route").submit(function(event) {
event.preventDefault();
calculateRoute($("#from").val(), $("#to").val());
});
The most important code is contained in the calculateRoute()
function. The function will accept two parameters, from
and to
, that represent the origin and the destination address, respectively. The first step is to create the map
instance, as shown in the following code.
// Center initialized to Naples, Italy
var myOptions = {
zoom: 10,
center: new google.maps.LatLng(40.84, 14.25),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
// Draw the map
var mapObject = new google.maps.Map(document.getElementById("map"), myOptions);
Before we go any further, there are some considerations I’d like to discuss. As you have seen, I used a static position to initially center the map. As you learned in the previous article, the center
property is required. In this demo, you might be tempted to omit it because if the route request succeeds, the map is redrawn and centered accordingly. You shouldn’t do this, because if the request fails, you’ll see a gray-filled area. An alternative could be to initially center the map based on the user’s current position. This requires an additional geolocation lookup, so you might consider it a waste of resources.
Next, you need to create the DirectionsService
instance and the directionsRequest
object, as shown below. This demo only uses the unitSystem
option, but you can expand further.
var directionsService = new google.maps.DirectionsService();
var directionsRequest = {
origin: from,
destination: to,
travelMode: google.maps.DirectionsTravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC
};
The last step, is to use the route()
method to run the request. We must also write the callback function which uses the response to set and show the calculated path. In the callback function we’ll check if the request has been successful, in which case we’ll display the route, or not, in which case we’ll display an error. This is implemented by the code below.
directionsService.route(
directionsRequest,
function(response, status)
{
if (status == google.maps.DirectionsStatus.OK)
{
new google.maps.DirectionsRenderer({
map: mapObject,
directions: response
});
}
else
$("#error").append("Unable to retrieve your route<br />");
}
);
Putting it all Together
The previous section explained the key components of the demo. Now, it’s time to put those pieces together into the final result, which is shown below.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Find a route using Geolocation and Google Maps API</title>
<script src="http://maps.google.com/maps/api/js?sensor=true"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script>
function calculateRoute(from, to) {
// Center initialized to Naples, Italy
var myOptions = {
zoom: 10,
center: new google.maps.LatLng(40.84, 14.25),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
// Draw the map
var mapObject = new google.maps.Map(document.getElementById("map"), myOptions);
var directionsService = new google.maps.DirectionsService();
var directionsRequest = {
origin: from,
destination: to,
travelMode: google.maps.DirectionsTravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC
};
directionsService.route(
directionsRequest,
function(response, status)
{
if (status == google.maps.DirectionsStatus.OK)
{
new google.maps.DirectionsRenderer({
map: mapObject,
directions: response
});
}
else
$("#error").append("Unable to retrieve your route<br />");
}
);
}
$(document).ready(function() {
// If the browser supports the Geolocation API
if (typeof navigator.geolocation == "undefined") {
$("#error").text("Your browser doesn't support the Geolocation API");
return;
}
$("#from-link, #to-link").click(function(event) {
event.preventDefault();
var addressId = this.id.substring(0, this.id.indexOf("-"));
navigator.geolocation.getCurrentPosition(function(position) {
var geocoder = new google.maps.Geocoder();
geocoder.geocode({
"location": new google.maps.LatLng(position.coords.latitude, position.coords.longitude)
},
function(results, status) {
if (status == google.maps.GeocoderStatus.OK)
$("#" + addressId).val(results[0].formatted_address);
else
$("#error").append("Unable to retrieve your address<br />");
});
},
function(positionError){
$("#error").append("Error: " + positionError.message + "<br />");
},
{
enableHighAccuracy: true,
timeout: 10 * 1000 // 10 seconds
});
});
$("#calculate-route").submit(function(event) {
event.preventDefault();
calculateRoute($("#from").val(), $("#to").val());
});
});
</script>
<style type="text/css">
#map {
width: 500px;
height: 400px;
margin-top: 10px;
}
</style>
</head>
<body>
<h1>Calculate your route</h1>
<form id="calculate-route" name="calculate-route" action="#" method="get">
<label for="from">From:</label>
<input type="text" id="from" name="from" required="required" placeholder="An address" size="30" />
<a id="from-link" href="#">Get my position</a>
<br />
<label for="to">To:</label>
<input type="text" id="to" name="to" required="required" placeholder="Another address" size="30" />
<a id="to-link" href="#">Get my position</a>
<br />
<input type="submit" />
<input type="reset" />
</form>
<div id="map"></div>
<p id="error"></p>
</body>
</html>
Conclusion
This article introduced you to several new classes and properties from the Google Maps API. You used these classes to develop a basic service that allows a user to get directions from one address to another. In the next article, you’ll learn how to create a polyline to join several points on a map.