Ajax-Powered Google Maps Mashup Tutorial

by Peter Laird
05/30/2007

Google Maps API

Both Google Maps and Yahoo! Maps (we will not discuss Yahoo! further) have created publicly accessible JavaScript APIs for embedding maps into arbitrary Web sites for free. This has created a cottage industry of mashup Web applications that use those APIs to plot everything from crime locations in Chicago to AP News stories mapped worldwide. The popular pattern, and the one I will build with this mashup, is to query a service for a set of addresses, and then map those addresses using Google Maps APIs. While this is straightforward to implement as you will see, there are two issues to be aware of.

First, the cross-domain security feature of browsers would seem to prevent a Google Maps mashup from being implemented in JavaScript in the browser. This is due to the fact that the user must navigate to the network domain that hosts the REST service that produces the list of addresses. Once in that network domain, the browser will not be allowed to make a request to http://maps.google.com. Google has implemented in its JavaScript library a workaround, overloading a resource request for a google.com script to inject its data into the browser.

The second issue is not a technical issue, but a business issue. Google owns the mapping data and API and reserves the right to define the terms of service. Currently, Google does throttle the free service at 50,000 requests per day per Web site. If this is an issue, a paid service option exists that does not have this limitation. To enforce the usage policy, each Web site has a unique API key that must be configured so that usage can be tracked. Getting the key is easy and free, and then it is configured in the script source block on the HTML page, as follows (key truncated for brevity):

<script 

  src="http://maps.google.com/maps?key=ABQIAAAAT"

  type="text/javascript">

</script>

Retrieving Locations from a REST Data Service

Now that your toolbox is stocked for development, it is time to start implementing the Ajax-powered Google Maps mashup. This section will walk through the construction of a REST service that serves up locations with addresses. In the next section we will wire up the REST service with Google Maps to create the mashup.

The first point to make about our REST service is that it will be simple. The intent of this tutorial and demonstration is to give you a starting point for building out more complicated mashups. Therefore, this Hello World mashup will be as basic as it can be to keep the sample clean of complications. To this end, the REST service is a static service: the locations are hard-coded into an HTML page. While this appears limiting, the overall approach could be easily replaced with a dynamic service. Developers schooled in JSP, PHP, or Ruby on Rails could replace the static HTML service with something that surfaces from a database, Web service, or something else.

The REST service is implemented in the sample in the getD2DSites.html. Take a look at that file, and you will see that it is just a serialized JavaScript array of location objects in JSON format:

{"locations":   

{"location":[           

        {"id":  "WashingtonDC",                 

         "city": "Washington DC",                       

         "location": "Hilton Hotel, Tysons Corner",

         "address": "7920 Jones Branch Drive",

         "date": "May 2nd, 2007"                

        },

        {"id":  "NYC",                  

         "city": "New York City",                       

         "location": "Grand Hyatt New York",

         "address": "109 East 42nd Street, NY 10017",

         "date": "May 3rd, 2007"                

        },

        etc...

The client will use an XMLHttpRequest to retrieve the JSON object from the REST service. Once retrieved, the JavaScript code needs to deserialize the object, and then iterate through the array. Take a look at mapper.js to see the getLocationsAndMap and getLocationsAndMapCallback functions that accomplish this:

// Gets the current locations from the REST service

// on the server and writes out the HTML that 

// contains links for the map



function getLocationsAndMap() { 

 if (receiveReq.readyState == 4 || 

     receiveReq.readyState == 0) 

 {      

   // getD2DSites.html is a REST service

   // that returns the list of locations 

   // as JSON

  

   receiveReq.open("GET", 'getD2DSites.html',

                   true);               

   receiveReq.onreadystatechange = 

               getLocationsAndMapCallback;      

   receiveReq.send(null);       

 } // end  if   

} // end  function



function getLocationsAndMapCallback() {

 // state == 4 is when the response is complete

 if (receiveReq.readyState == 4) {              

  // Deserialize the JSON response (eval() command)

  // This creates an array of location objects. 

  var response = eval("("+request.responseText+")");

  // generate HTML listing the locations and update  

  //   the page DOM so the user will see the HTML

  var div = document.getElementById('loc_div');         

  div.innerHTML = '<p>Received ' + 

   response.locations.location.length+' results.';

 

  for(i=0;i < response.locations.location.length; i++) {

        var city = response.locations.location[i].city;

        var anchor = ''; // TODO: we will fix this later

        div.innerHTML += '<p><b>'+ city + '</b> ' + 

          anchor + loc + '</a><br/>' + addr + '</p>';

  } // end  for loop

 } // end   if (state == 4)

} // end   function

Note that the eval call will take the JSON and evaluate it, effectively constructing a JavaScript array that you can navigate. The for loop shows how to iterate through the locations within this array.

At this point you have accomplished the following:

  • Created a static REST service HTML file
  • Added a JSON payload to the HTML file
  • Wrote the code to reconstitute the JSON back into a JavaScript object using eval()
  • Wrote the code to loop through the locations array and manipulate the DOM with new HTML

Let's now look at how to show these locations on Google Maps.

Assembling the Mashup using REST and the Google Maps API

With the REST service implemented, it is time to build out the mashup. Figure 1 shows the mashup in its completed form. The user has clicked on the button to get the locations, and then clicked on the link for New York City. Notice how the Google map has been centered on the street address of the location.

Ajax Mashup

Figure 1. The mashup application in action

Figure 2 shows the architecture of the tutorial demonstration when running on ajaxmashup.googlepages.com.

Ajax Mashup Arch

Figure 2. The architecture of the demonstration application

The first step is to define the structure of the ajaxGoogleMashup.html mashup page. You need three major structural elements:

  • A button for the user to invoke the REST service you built above
  • A placeholder div tag to hold the results of the REST service
  • A placeholder div tag for the map

Look at the HTML fragment below to see this structure:

<table>

<tr>

<td valign="top">

    <div id="google_map_div" 

           style="width: 500px; height: 300px">

    </div>

</td>

<td valign="top">

<p id="getLocations_div" align="center">

        <>

        <form id="getLocationsForm">

        <input value="Get the Locations" 

           type="submit" 

          onclick="javascript:getLocationsAndMap();return false"

        />

        </form>

</p>

<p>

  <div id="locations_div">

  <>

  </div>

</p>

</td>

</tr>

</table>

Notice how when a user presses the button, it will raise an onclick event. This event is wired to the getLocationsAndMap() function listed above, which will invoke an XMLHttpRequest that targets the REST service. You've already seen the getLocationsAndMapCallback() function that then transforms the service response from the JSON text into HTML injected into the DOM.

Now at this point you need to mashup the addresses with Google Maps. First, the JavaScript code in getLocationsAndMapCallback() writes the HTML so that each location has an anchor tag that invokes a JavaScript function. The function is showAddress(), and the code passes the clicked location's address. The TODO comment in the code in the getLocationsAndMapCallback() function above is replaced with this code:

var anchor = '<a href="irrelevant" 

   onclick="javascript:showAddress(\''+

      response.locations.location[i].address+'\');'+

      ' return false">';

This creates an anchor tag for each location that when clicked will trigger the showAddress() function.

Next, the showAddress() function must be wired to the Google Maps API to actually render the passed address in the map. This is done using boilerplate code provide by Google. Note the GMap2 and geocoder objects in the code below; both are provided as part of the Google Maps API. This code will retrieve the map, and then update the google_map_div div tag in the HTML document.

function showAddress(address) {  

        var map = new GMap2(

           document.getElementById("google_map_div"));

        var geocoder = new GClientGeocoder();

        geocoder.getLatLng(address,    

                function(point) {      

                  if (!point) {        

                    alert(address + " not found");      

                  } else {        

                    map.setCenter(point, 13);        

                    var marker = new GMarker(point);        

                    map.addOverlay(marker);        

                    marker.openInfoWindowHtml(address);

                  }    

            }  

    );

}

Believe it or not, this completes the Ajax-powered Google Maps mashup! While I haven't shown the complete files in this tutorial, I have shown the important pieces. Check out the download for the complete code.

The Google Maps Mashup in Review

At this point, working directly with the example code will help you tie the pieces together. To guide you through the code, the list below highlights the important pieces that were covered above and how they work together.

  1. The user requests ajaxGoogleMapsMashup.html with the browser, which has the following structure:
    + An HTML button that invokes the getLocationsAndMap() function when clicked
    + An empty div tag with id locations_div, where the location list will be inserted
    + An empty div tag with id google_map_div, where the Google Map will be inserted
  2. The user clicks the button, and getLocationsAndMap() issues an XMLHttpRequest to the REST service (getD2DSites.html).
  3. The REST service returns a list of locations in the form of JSON text, which is deserialized in getLocationsAndMapCallback().
  4. getLocationsAndMapCallback() inserts HTML for each location found in the returned list, which includes an anchor tag.
  5. The user clicks on a location's anchor tag, which triggers a call to showAddress() and passes the location's street address.
  6. showAddress() invokes JavaScript code from the Google API, which injects the proper map into the google_map_div element.

Summary

As you can see, I combined HTML, JavaScript, XMLHttpRequest, the Google API, and JSON to create a working example of a Hello World mashup. While this can be overwhelming the first time through, you will quickly become comfortable with this collection of technologies. Web 2.0 is a major trend in Web application development, and this pattern of creating a mashup can be applied as you work on Web 2.0 projects.

References

  • Dev2Dev TechDays 2007, which is a seminar titled Mashup the Enterprise
  • An Introduction To Ajax by David Teare (Dev2Dev, 2005)
  • An Introduction to JSON by Daniel Rubio provides a great look at JSON (Dev2Dev, 2007)
  • Online hosted demo - shows the example covered by this tutorial

Peter Laird is the Managing Architect of the WebLogic Portal product.