Friday, August 13, 2010

Using more than 8 waypoints in Google maps

If you have ever tried to use the Google Maps API, you will probably have noticed that Google places a limit of 8 waypoints for the free version of the API, and a limit of 23 waypoints for a premiere account. For those of you who want to circumvent this limitation, I have written a class that can be used in place of Google's DirectionsService and DirectionsRenderer classes. Although, my class uses Google's DirectionsService class, it does things a bit differently.

It works by splitting apart a list of waypoints into groups of 10 or less, finding the path between those points, and then appending all the results into a single PolyLine. Since the DirectionsService class returns a DirectionsResult class, we can maintain and amend all the data that DirectionsRenderer has access to, and this can be used to customize the way the information is presented to the user.

The majority of this functionality is done by this function:

/**
 * Calculate path between destinations.
 *
 * @param {Array} destinations The list of destinations
 *    to visit.
 * @param {google.maps.DirectionsTravelMode} selectedMode The type of traveling: car, bike, or walking
 * @param {bool} hwy Whether to avoid highways
 * @param {bool} toll Whether to avoid tolled roads
 * @param {bool} onlyCurrent If using multiple routes, do we want to show all of them or just the current
 * @param {string} units The distance units to use, either "km" or "mi"
 */
DirectionsRoute.prototype.route = function(destinations, selectedMode, hwy, toll, onlyCurrent, units) {
     this.directionsDisplay.reset();
 
     // Add all destinations as markers.
     var places = new Array();
     for(var i=0; i < destinations.length; i++) {
          this.process_location_(destinations, i, places);
     }
 
     // Determine unit system.
     var unitSystem = google.maps.DirectionsUnitSystem.IMPERIAL;
     if(units == "km")
          unitSystem = google.maps.DirectionsUnitSystem.METRIC;

     // Loop through all destinations in groups of 10, and find route to display.
     for(var idx1=0; idx1 < destinations.length-1; idx1+=9)
     {
          // Setup options.
          var idx2 = Math.min(idx1+9, destinations.length-1);
          var request = {
               avoidHighways: hwy,
               avoidTolls: toll,
               origin: destinations[idx1].location,
               destination: destinations[idx2].location,
               travelMode: google.maps.DirectionsTravelMode[selectedMode],
               unitSystem: unitSystem,
               waypoints: destinations.slice(idx1+1, idx2)
          };
  
          // Determine path and display results.
          this.directionsService.route(request, function (response, status) {
               if (status == google.maps.DirectionsStatus.OK)
                    this.directionsDisplay.parse(response, units);
               });
     }
}
This function uses a few auxiliary functions, but the bulk of the logic is in here. First, the code goes through each destination and places a marker on the map. The, the main for loop goes through each destination in groups of 10 (because we can have a maximum of 8 waypoints, plus the origin and end) and finds the path between each set. For each route that is found, we then pass the DirectionsResults that is returned to a new function to combine and display the results:
/**
 * Generates boxes for a given route and distance
 *
 * @param {google.maps.DirectionsResult} response The result of calculating
 *    directions through the destinations.
 */
DirectionsDisplay.prototype.parse = function (response, units) {
     var routes = response.routes;
 
     // Loop through all routes and append
     for(var rte in routes)
     {
          var legs = routes[rte].legs;
          this.add_leg_(routes[rte].overview_path);
  
          for(var leg in legs)
          {
               var steps = legs[leg].steps;
   
               // Compute overall distance and time for the trip.
               this.overallDistance += legs[leg].distance.value;
               this.overallTime += legs[leg].duration.value;
          }
     }

     // Set zoom and center of map to fit all paths, and display directions.
     this.fit_route_();
     this.create_stepbystep_(response, units);
}
This function simply goes through each route and leg of each DirectionsResult and creates a PolyLine and appends it to an array, this.legs. It also calculates the overall distance/duration of the trip by summing together each piece as it goes. Finally, it fits the display window around all of the routes, and displays the step-by-step directions for the entire trip. We are then given freedom to customize the function create_stepbystep_() to do as we please and incorporate any features we wish. So far, it simply displays each step in a table, but ultimately I plan to add mouse events to interact with the map, and also make things a little more visually appealing. The entire javascript file can be found here. And, a sample page that uses the classes can be found here.

Next post I will outline my process of adding a right-click menu to the map. I will also add some other basic functionality, such as reordering the destinations and centering the map around the routes.

11 comments:

  1. Absolute legend. I require this sort of functionality to create snapped routes which may consist of any number of waypoints. Thanks very much for making my life easier!

    ReplyDelete
  2. Is there any way to use getJSON to pre-load the waypoints?

    ReplyDelete
  3. This is incredible! i never thought this way.... And this code helped me a lot! Thanks man!

    ReplyDelete
  4. Hi, I am not good a JS. Can you tell me how to use your function if I have many static waypoints (30+) with lat and lng that I would like to draw a route on the google map? I don't even need to show markers on the map.

    Thank you.

    ReplyDelete
  5. Please how can I use these function..?

    ReplyDelete
  6. Hi,
    I am well for js. but i dont know for how will use this functions. Please immediate reply

    ReplyDelete
  7. Hello is there a way to calculate the best route ?

    thank you

    ReplyDelete
  8. It's not accurate !! when zooming the marker is in the right place but the route is far from the marker.. can you fix it plz

    ReplyDelete
  9. How can make editable the Direction line?

    Pls... Help ME!

    ReplyDelete
  10. Thanks for usefull information.

    ReplyDelete