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.
Figure 1. The mashup application in action
Figure 2 shows the architecture of the tutorial demonstration when running on ajaxmashup.googlepages.com.
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.
- The user requests
ajaxGoogleMapsMashup.html
with the browser, which has the following structure:
+ An HTML button that invokes thegetLocationsAndMap()
function when clicked
+ An empty div tag with idlocations_div
, where the location list will be inserted
+ An empty div tag with idgoogle_map_div
, where the Google Map will be inserted
- The user clicks the button, and
getLocationsAndMap()
issues an XMLHttpRequest to the REST service (getD2DSites.html). - The REST service returns a list of locations in the form of JSON text, which is deserialized in
getLocationsAndMapCallback()
. -
getLocationsAndMapCallback()
inserts HTML for each location found in the returned list, which includes an anchor tag. - The user clicks on a location's anchor tag, which triggers a call to
showAddress()
and passes the location's street address. -
showAddress()
invokes JavaScript code from the Google API, which injects the proper map into thegoogle_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.