Getting Directions Using the Google Maps API

Aurelio De Rosa
Share

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.