Previous Page
Next Page

Hack 29. Use the Google Maps API Request Object

The Google Maps API comes with its own request object for making HTTP requests from JavaScript code.

This hack initially displays a Google Map based on a user's preferences. These include the latitude/longitude at which the map should be centered, and the zoom level or magnification of the map when it is first displayed on the web page. An application typically obtains user-specific properties by reading a cookie, a small piece of data saved on a user's hard drive, or having a user sign in. This hack skips this opening step in order to focus on the gist of the hack's technology: obtaining user preferences from a server component to control a Google Map display.

"Send Cookie Values to a Server Program" [Hack #38] discusses reading cookies in an Ajax application.


Personal Googling

This hack displays a 500-by-300-pixel U.S. map on a web page, which also shows the user's preferred coordinates for centering the map and preferred zoom level (a two-digit number from the highest zoom level of 1 to around 18). A zoom level of 18, for instance, shows the continents and oceans, whereas a zoom level of 1 displays a town's streets.

As mentioned previously, when the user requests this web page, the application can either obtain the user's username from a previously generated cookie, or ask the user to sign in and fetch the preferences from a database. However, we are not going to show that step (even though it is important in a real-world application) because we surmise that the reader is more interested in the API's Ajax-related objects and the map-display code.

Here is the HTML for the hack:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v=
"urn:schemas-microsoft-com:vml">
<head>
<script src="http://maps.google.com/maps?file=api&v=1&key=ABQIAAAANJd_
PEMs2vnU_f0RhwHhZhQ6pfwiB1eVXKVVHswEcdvw4p5NixS195EO7O7VmH483DMz0QiZbIlbIf" 
type="text/javascript"></script>
<script src="js/hacks4_1a.js" type="text/javascript"></script>
<title>View Map</title>
</head>
<body>
<h3>Your Google Map</h3>
<div id="map" style="width: 500px; height: 300px"></div>
<h4>Your specifications</h4>
<form action="javascript:void%200">
<p>
Latitude: <input type="text" name="_latitude" size="20" maxlength="20" />
</p>
<p>
Longitude: <input type="text" name="_longitude" size="20" maxlength="20" />
</p>
<p>
Zoom level: <input type="text" name="_zoomLevel" size="2" maxlength="2" />
</p>
</form>
</body>
</html>

This code imports the Google API library with the first script tag. This tag allows the application to use Google Maps API objects such as GMap and GXmlHttp (which represents the request object). The script src attribute includes the developer key, as described in "Get Access to the Google Maps API" [Hack #28]. Another script tag imports into the page a hacks4_1a.js JavaScript code file, which contains the custom code for our application.

Google Maps requires a separate developer key for every URL directory containing Google Mapsrelated web pages. For example, I have a developer key that covers every web page in the http://www.parkerriver.com/ajaxhacks/ directory. It is extremely easy to generate a developer key at http://www.google.com/apis/maps/signup.html.


The map itself is displayed within a div tag that has an id of map. When the browser loads the page, the code first checks the compatibility of the browser using a Google global function, GBrowserIsCompatible( ). If this function returns TRue, the application calls a function named googleAjax( ). The window.onload event handler and googleAjax( ) appear inside the hacks4_1a.js file. googleAjax( ) queries a server for the user's specific preferences of a user by passing along the username ("bwperry," in this case). The application then uses the properties fetched by googleAjax( ) to display and zoom in on a map. Here is the code from hacks4_1a.js:

var map = null;
window.onload = function(  ){
    if(GBrowserIsCompatible(  )){
        googleAjax('http://www.parkerriver.com/s/gmap?user=bwperry');
    } else { alert('Your browser is not compatible with Google Maps!');}
};
function createMap(lat,lng,zoomLevel){
    map = new GMap(document.getElementById("map"));
    GEvent.addListener(map, 'click', function(overlay, point) {
        document.forms[0]._longitude.value=point.x;
        document.forms[0]._latitude.value=point.y;
        map.addOverlay(new GMarker(point));

    });
    map.addControl(new GLargeMapControl(  ));
    map.addControl(new GMapTypeControl(  ));
    if(lat != null && lat.length != 0 && lng != null && lng.
            length != 0 && zoomLevel != null && zoomLevel.length != 0){
        map.centerAndZoom(new GPoint(lng, lat), zoomLevel);
    } else {
        //center on roughly middle of USA 
        map.centerAndZoom(new GPoint(-97.20703, 40.580584), 14);
    }
}

function googleAjax(url){

    var request = GXmlHttp.create(  );
    request.open("GET", url, true);
    request.onreadystatechange = function(  ) {
        if (request.readyState == 4) {
            if (request.status == 200) {
                var resp = request.responseXML;
                var rootNode = resp.documentElement;
                var zoom = rootNode.getElementsByTagName("zoomLevel")[0];
                var latLng = rootNode.
                getElementsByTagName("centerCoords")[0];
                var coordArr = latLng.firstChild.nodeValue.split(" ");
                var zoomLevel=zoom.firstChild.nodeValue;
                createMap(coordArr[0],coordArr[1],zoomLevel);
                alert(coordArr[0]+" "+coordArr[1]+" "+zoomLevel);
                document.forms[0]._latitude.value=coordArr[0];
                document.forms[0]._longitude.value=coordArr[1];
                document.forms[0]._zoomLevel.value=zoomLevel;
            } else {
                alert(
                        "The application had a problem communicating with "+
                        "the server. Please try again.");
            }//inner if
        }//outer if
    }//end function
    request.send(null);

}

It will probably help you visualize the application's purpose if I show you the map inside a browser window, before digging into the code. The page loads the map and displays the user's preferred coordinates and zoom level in text fields beneath it. Figure 4-1 shows the page displayed in a browser.

Figure 4-1. Google Map centered on MA with zoom level 10


Map Objects

Take a gander at the googleAjax( ) function and its creation of an object that makes HTTP requests:

function googleAjax(url){
    var request = GXmlHttp.create(  );
    request.open("GET", url, true);
    request.onreadystatechange = function(  ) {
        if (request.readyState == 4) {
            if (request.status == 200) {
                var resp = request.responseXML;
                var rootNode = resp.documentElement;
                var zoom = rootNode.getElementsByTagName("zoomLevel")[0];
                var latLng = rootNode.
                getElementsByTagName("centerCoords")[0];
                var coordArr = latLng.firstChild.nodeValue.split(" ");
                var zoomLevel=zoom.firstChild.nodeValue;
                createMap(coordArr[0],coordArr[1],zoomLevel);
                document.forms[0]._latitude.value=coordArr[0];
                document.forms[0]._longitude.value=coordArr[1];
                document.forms[0]._zoomLevel.value=zoomLevel;
            } else {
                alert(
                        "The application had a problem communicating with "+
                        "the server. Please try again.");
            }//inner if
        }//outer if 
    }//end function
    request.send(null);
}

Remember all the code that created a request object in "Detect Browser Compatibility with the Request Object" [Hack #1] and "Use Your Own Library for XMLHttpRequest" [Hack #3]? All that's necessary with the Google Maps API is var request = GXmlHttp.create( ). You then call the open( ) and send( ) methods and point to a function that will be your onreadystatechange event handler, just as you would with a request object that you created with your own code.

The onreadystatechange event handler specifies a JavaScript function that the code uses to handle an HTTP response. In Ajax, the request object queries a server, which typically sends back a response. You can have the event handler refer to a function literal (as in this code) or to the name of a function (without the ( ) characters) that you have defined elsewhere in the code (see "Detect Browser Compatibility with the Request Object" [Hack #1]).


This code fetches an XML document from the server that contains the user's map preferences:

var resp = request.responseXML;

The returned XML data might look like this:

<mapSetup>
<centerCoords>42.057450220246 -71.64184570312</centerCoords>
<zoomLevel>10</zoomLevel>
</mapSetup>

Remember that you are getting this XML information from the server. The data is specific to each user and can be stored in a database. This information represents the user's preferred latitude and longitude for the center point of the map, as well as the preferred zoom level.

In Google Maps, latitude is measured in the range 90 degrees north of the equator to 90 degrees south of the equator. Longitude is measured in a range of 180 degrees east of the Greenwich Meridian to 180 degrees west of the Greenwich Meridian.


The code then uses Document Object Model programming to pull the text values out of the XML document and use them for map display:

var rootNode = resp.documentElement;
var zoom = rootNode.getElementsByTagName("zoomLevel")[0];
var latLng = rootNode.getElementsByTagName("centerCoords")[0];
var coordArr = latLng.firstChild.nodeValue.split(" ");
var zoomLevel = zoom.firstChild.nodeValue;
createMap(coordArr[0],coordArr[1],zoomLevel);
document.forms[0]._latitude.value=coordArr[0];
document.forms[0]._longitude.value=coordArr[1];
document.forms[0]._zoomLevel.value=zoomLevel;

The root node is the top-level XML element, such as mapSetup. The DOM Document object has a documentElement property that returns a reference to this element. The code then stores references to the elements that hold the data on the coordinates for the center point of the map and the zoom level in variables named latlng and zoom, respectively.

How do you get the values of elements using DOM? The latlng variable, for instance, is of a Node type. The Node has a property named firstChild, which (phew!) returns the text node contained by the XML element. The code gets the text value of this Node using the Node's nodeValue property.

The centerCoords element contains both latitude and longitude values, separated by a space character. Thus, calling the string split( ) method returns an array that contains the latitude as the first array member and the longitude as the second member.

You can redesign the server component to return the latitude and longitude in separate XML elements.


Creating Your Own Map

The createMap( ) function uses Google Maps objects to generate a map for the web page. The code calls this function with the user's preferred latitude, longitude, and zoom level as parameters, as in:

createMap(coordArr[0],coordArr[1],zoomLevel)

The first two parameters are array members, which is why the code uses, for example, the coordArr[0] syntax.


Unlike in other Ajax libraries, when using the Google Maps API, the developer does not have to deal with the basics of XMLHttpRequest. However, if your application requires more control over the request object, you can initiate a setup like the one described in [Hack #3], which imports its own JavaScript file that handles HTTP requests.


Previous Page
Next Page