Thursday, August 12, 2010

Google APIs

Okay, after looking through some information on Google APIs, I came across this wonderful resource:

http://code.google.com/apis/ajax/playground/

It is an interactive code development environment that allows you to write web scripts and see the output on the fly! There is also some example code for various Google APIs, and a built-in Firebug Lite debugger. I have been messing around with it for a few days or so, and decided on an initial project that I want to complete. It involves the Google Maps API, and my plan is to allow route calculation with the ability to look up restaurants, fuel stations, hotels, rental car agencies, weather, etc. along the trip path. I can't tell you how many times I've planned a trip and needed to search in various cities along the path for hotels and rental car agencies, not to mention look up the weather and find the cheapest gas stations. This, to me, seems to be a major feature lacking in Google Maps.

I have also been looking through the Google Maps API:

http://code.google.com/apis/maps/documentation/javascript/reference.html

to try to figure out where to start since I've never used this particular API before. After looking through the samples provided on the page, I finally figured out how to create a simple web application to show a map and allow me to search for directions from one place to another. This is actually pretty simple using the following code (hosted here):

var map;
var directionsDisplay = new google.maps.DirectionsRenderer();
var directionsService = new google.maps.DirectionsService();
var waypts = new Array();

function initialize() {
   // Setup the map.
   var chicago = new google.maps.LatLng(41.850033, -87.6500523);
   var myOptions = {
        zoom: 7,
        mapTypeId: google.maps.MapTypeId.ROADMAP,
        center: chicago
   }
   map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
 
   // Initialize the directions renderer.
   directionsDisplay.setMap(map);
   directionsDisplay.setPanel(document.getElementById("directionsPanel"));

   // Place our initial locations into the waypoints list, and calculate route.
   placeMarker("Chicago, IL");
   placeMarker("St. Louis, MO");

   // Setup listener to add a new location for each map click.
   google.maps.event.addListener(map, 'click', function(event) {placeMarker(event.latLng);});
}

This initialize() function is used to setup the page elements. It initializes the map and directions services, and then adds a couple default destinations (Chicago and St. Louis) to our list of destinations before calculating the route. It then registers a callback to append a new destination to the end of the list when the map is clicked. The "directionsPanel" div is an empty div on the bottom-right of the page used to print out the step-by-step directions.

The callback code to add a new destination is as follows:

// Add a new waypoint at the specified location, and recalculate path.
function placeMarker(location) {
   waypts.push({
        location:location,
        stopover:true});
    
   calcRoute();
}

It is fairly simple in its operation. The one thing that should be noted is that Google uses the DirectionsWaypoint class for waypoints, and so we must add an object containing a location and whether it is a stopover (whether it is an actual destination between the origin and ending point).

Then, finally, the code to plot the directions is:

// Calculate directions between a set of waypoints.
function calcRoute() {
   // Get which method of travel is to be used.
   var selectedMode = document.getElementById("mode").value
   // Calculate directions and display results.
   var request = {
        origin:waypts[0].location,
        destination:waypts[waypts.length-1].location,
        travelMode: google.maps.DirectionsTravelMode[selectedMode],
        waypoints: waypts.slice(1,waypts.length-1)
   };
   directionsService.route(request, function(response, status) {
        if (status == google.maps.DirectionsStatus.OK) {
             directionsDisplay.setDirections(response);
        }
   });

   // Print waypoint information into the waypoint panel in the top-right.
   var waypointPanel  = document.getElementById("waypointsPanel");
   waypointPanel.innerHTML = "";
   for(var x=0; x < waypts.length; x++) {
        waypointPanel.innerHTML += "<b> WP" + (x+1) + ": </b>" + waypts[x].location + "<br>";
   }
}

This code sends a request to the Google directions service to find a path between the waypoints, prints the results to the map and the "directionsPanel" div, and then prints out each waypoint to see on the "waypointsPanel" div.

However, I quickly discovered that Google limits the free API to only allow 8 waypoints to be sent to the DirectionsService (which is Google's class for finding a path through a set of waypoints) for route fitting. Premiere customers can have up to 23 waypoints, but either way I didn't like the limitation and wanted to find the path between an unlimited number of waypoints. I thought of many possible solutions, but the most effective seemed to be to rewrite a couple of Google's classes to break apart the waypoints into groups of 8, send the query to Google's DirectionsService, and then append them all into a single PolyLine.

Of course, after rewriting this code, I will probably have to rewrite the code for DirectionsRenderer (which is Google's class for displaying the step-by-step directions for a given route) because I am unsure of where the limitations are placed. My guess would be that the DirectionsService contains the limitation because that is where most of the work is being done, but I didn't want to waste too much time trying to link together all of the DirectionsResult's that are returned from DirectionsService (and if they both contain the limitation, then I would have wasted a lot of time). Mainly, I didn't know what fields I would have to update in order to allow the DirectionsRenderer to not yell at me when it tried to do its work. And, besides, if I write my own version of the class, I can then customize the output to however I want. All of the pertinent information is contained in the DirectionsResult's that will be returned from each query to the DirectionsService, so it should be a simple matter of displaying the results in a visually-appealing way.

Anyway, my next post will detail my progress.

1 comment:

  1. Hello there
    The example is really very helpful to us, we are trying to draw a route using your code but when we sending the more points with its latitude and longitude directly using placemarker function, it gives us the OVER_QUERY_LIMIT. Please suggest how can we resolve this.

    ReplyDelete