Menu

Hacking Maps with the Google Maps API

August 10, 2005

Hari Gottipati

Google didn't get around to releasing the documentation of its maps API until relatively recently, but many developers were experimenting with the service as soon as it went into public beta. I built the Irving, Texas (a suburb of Dallas) parks demo before the API was publicly documented. In addition to providing a useful service, Google also reinvigorated web user interface expectations, as has been widely noted. In particular, being able to fluidly drag and zoom maps from a web browser is an engrossing feature for the user.

Google Maps API

The Google Maps API is based on JavaScript, which makes life pretty easy, since you don't need to worry about installing software. You can directly run JavaScript in just about any web browser. Knowledge of XML and XSL helps to build a rich user interface, but it's not required. Google Maps currently supports Firefox 0.8+, Mozilla 1.4+, IE 5.5+, Safari 1.2+, Netscape 7.1+, Opera 7+, but E 5.0 is not supported. You can use the global method GBrowserIsCompatible() to check compatibility and show user-friendly error message for non compatible browsers.

There are several limitations to the Maps API:

  • It doesn't provide a geocoding service; that is, you have to provide it with longitude and latitude. This means you need to rely on third-party geocoding tools to get longitude and latitude for an address.
  • It doesn't include a routing or driving directions service.
  • The Google Maps API key is restricted to generate maps only for URLs (website or a local host). You need to have a web server even for development.
  • The key you receive is only valid for a single directory on your web site.
  • It can be used for commercial purposes, but it should be available to end users for free.
  • It can be used on a site that is password protected, but consumers should be able to sign up for a password without charge.
  • Usage is not necessarily restricted, but if your site is getting more than 50,000 hits per day, you'd better contact Google.

Yahoo also has a mapping API, with some significant differences. The Google Maps API requires long-and-lat, while Yahoo offers a geocoding facility.


Google maps API   Yahoo maps API
Accepts longitude and latitude only, no geocoding   Provides geocoding
Maps can be generated on, or for, any site   Maps will be generated only on a Yahoo site
JavaScript   XML (based on geoRSS 2.0) API
Ajax support   No Ajax support
Zoom in, Zoom out, move by mouse   Zoom in, Zoom out, move by clicking links
Can use for commercial purposes, but should be freely available to end user   Can use for commercial purposes, but should obtain written permission
Can add plain text or HTML or XML with XSL or show blowup of the map in a display window   Can add custom link, and/or custom image, or can display HTML in an IFRAME inside display window
Usage is not restricted up to a reasonable upper bound   No usage restriction
Currently in beta   Not beta, stable release

Developing a Map Application

To develop a Google map application, you need to go through the following five steps:

  1. Find a geocoder tool which gives the long-and-lat from an address.
  2. Identify the state or city or street you want to display by default (i.e., the starting point) and the default zoom level.
  3. Identify the locations you want to mark on the map.
  4. Decide what icon to represent the location on the map.
  5. Decide what information to display when the icon is clicked.

In order to demonstrate taking these five steps, we're going to work through the app I built, which maps the public parks in Irving, Texas.

1. Finding a Geocoder Tool

There are several free geocoder tools which provide the long-and-lat of an address. In order to integrate one of these services into a web app, you can use the Perl module Geo::Coder::US. As its name implies, it only works for US addresses. But it's free, and you can use Perl and JavaScript together in a web app. It's up to you to decide whether you need static data or dynamic data. But to keep things simple, we're going to use static data here. In fact, let's use geocoder.us/, which is based on Geo::Coder::US.

2. Identifying the Default Area

It's pretty easy to identify the default area, since it depends on what your mapping app is meant to accomplish. In our example, the default area that needs to be displayed is Irving, Texas. So we need to figure out the long-and-lat for Irving. Geocoder tools may not provide longitude and latitude based only on the city and state. You may need to provide a valid street address in the city; however, some tools will provide long-and-lat for a ZIP code. Once you get the right coordinates, you can zoom the map to display the whole city.

So we'll use the address 1698 Rochelle Blvd, Irving, TX, 75039, which has coordinates Longitude: -96.926791, Latitude: 32.863917. Now it's almost time to start using the Maps API; but first we have to get access.

Getting an API Key

Unfortunately the key is limited to the website's particular directory that you tell Google about, and you can only register the key to valid website addresses. Even though this is a JavaScript API, you cannot simply develop and test an app based on it without a web server. To get a key you need to provide the URL, and that key only works with requests from that URL. It will not even allow the requests from another directory of the same site. If you tell Google you want a key for http://www.gmap.example/mysite, your key is valid for http://www.gmap.example/mysite/, http://www.gmap.example/mysite/mypage.html, and http://www.gmap.example/mysite/page?arg=foo. But it's invalid for http://gmap.example/mysite/mypage.html, http://differenthost.gmap.example/mysite, and http://www.gmap.example/mysite/mysubdir/page.html.

Visit Google's sign-up page and get a key for your website. You need to have a Google account to get the key. The whole API is available through one JavaScript file (which may be using other JavaScript files). Include the following file in a SCRIPT tag with the key you generated (assume the key is "abc123"):

<script src="http://maps.google.com/maps?
  file=api&v=1&key=abc123" type="text/javascript"></script>
Creating Display Points on the Map

The GMap class represents the map, and it has the methods to center and to zoom the map. Also it lets you add the move and zoom controls on the map. With this class you can default the map to show a particular area -- in our case, the city of Irving. This map can be embedded in a div element and you can control the size of the map by setting the height and width of that div element:

<div id="map" style="width: 800px; height:
    500px;"></div>

Create the point with the long-and-lat of the Irving address we got from the geocoder:

var point = new GPoint(-96.926791,32.863917);

Next we create the instance of GMap by passing the div element we just made:

var map = new GMap(document.getElementById("map"));

We want the move and zoom controls, so let's add them too, but we have some choices. We can add the large pan/zoom control used on Google Maps (GLargeMapControl), the smaller pan/zoom control used on Google Local (GSmallMapControl), or the small zoom control, with no panning controls, used in the small map blowup windows (GSmallZoomControl).

Let's add one of these controls to our map:

map.addControl(new GLargeMapControl());

We also want to add the control which allows users to toggle between map and satellite view:

map.addControl(new GMapTypeControl());

Finally we set the default or initial starting point of the map, as well as zoom to the right level of magnification, which you can discover through trial-and-error. We use the point we created earlier.

Map.centerAndZoom(point, 6);

What have we done so far? We've made an 800 X 500 map, which will display Irving, Texas. (See Listing 1 for source code so far.)

3. Identifying the Overlay Locations

Once you have the map in place with a default area, it's time to identify the locations to display. If your app needs dynamic locations, then you need to write code to get them on the fly: take user input, convert to long-and-lat with a geocoder service, and so on. For example, HousingMaps gets the locations of properties for sale or rent from Craigslist on the fly, and converts and displays them. In our example, we want to identify city parks in Irving, Texas. The best place to find information about those parks is from the City of Irving's website. Once you have the park addresses in hand, you have to use that handy geocoder tool to get their longs-and-lats, as we already did earlier.

Let's get the long-and-lat for the first park, the Austin Recreation Center, which is at 825 East Union Bower Road, Irving, Texas 75061. The Geocoder returns -96.936574/32.822129.

Let's make another GPoint:

var point = new GPoint(parseFloat(-96.936574),parseFloat(32.822129));

Overlays are objects on the map that are tied to longitude and latitude coordinates, so they move when you drag/zoom the map around, and when you toggle between Map and Satellite modes. To overlay the location on the map you need to create a GMarker with longitude and latitude information. The marker is a type of map overlay that shows an icon at a single point on the map. The constructor takes the point at which it should be displayed and an optional instance of GIcon.

Let's create the marker with this point:

var marker = new GMarker(point);

Add the marker to the map in order to display this location on the map.

map.addOverlay(marker);

Repeat the process for all parks manually, or you can load the parks information from an XML file and use the GXmlHttp class (which creates cross-browser XMLHttpRequest instances) to load on startup.

Let's say you have all parks information in parks.xml and it looks like this:

<parks>
  <park><point lng="-96.936574" lat="32.822129"/></park>
  <park><point lng="-96.9330679" lat="32.871815"/></park>
</parks>

You can create an XMLHttpRequest via the GXmlHttp.create() method:

var request = GXmlHttp.create();

You can load parks.xml:

request.open("GET", "parks.xml", true);
request.onreadystatechange = function() {
  	if (request.readyState == 4) {
    	    var xmlDoc = request.responseXML;

Now let's extract the point elements out of the XML document:

var points = xmlDoc.documentElement.getElementsByTagName("point");

We can loop through the points to get the long-and-lat of the each point and create a GPoint:


for (var i = 0; i < points.length; i++) {
     var point = new GPoint(parseFloat(points[i].getgetAttribute("lng")),
          parseFloat(points[i].getAttribute("lat")));
} 

Now we create the marker and add it to the map inside the loop.

var marker = new GMarker(point);

map.addOverlay(marker);

(For complete source code for this step, see Listing2.)

4. Display Custom Icons for Map Overlays

Based on the requirements, you can decide what icon to show at each location. In our example, you can show a different icon for each park, or a different icon for parks that have pool facilities, or you can display the default icon for all locations. To display the default icon, there is nothing to do. The statement var marker = new GMarker(point) will create the marker with a default icon. If you decide to use the default icon for all locations, you can skip this step and directly go to the next step.

Again, add icon information in the same XML file and you can loop through the icon information to add different icons for different parks.
Let's rewrite our parks.xml file with icon information:

<parks>
    <park>
       <point lng="-96.936574" lat="32.822129"/> 
       <icon image="green.png" class="local"/>
    </park>
    <park>
       <point lng="-96.9330679" lat="32.871815"/> 
       <icon image="yellow.png" class="local"/>
    </park>
</parks>

Add the logic of parsing the icon information to the same section where you parsed the point information:

var iconImage = xmlDoc.documentElement.getElementsByTagName("icon");

Create the base icon with GIcon class and add the size, shadow, and shadow size:

var baseIcon = new GIcon();
baseIcon.shadow = "http://www.google.com/mapfiles/shadow50.png";
baseIcon.iconSize = new GSize(20, 34);
baseIcon.shadowSize = new GSize(37, 34);

Now create the Icon with base icon:

var icon = new GIcon(baseIcon);

Set the custom image you wanted to display to this icon:

Icon.image = iconImage[i].getAttribute("src");

In the previous step we created the marker with a default icon. Now we have a customized icon, so pass the icon to the marker constructor, along with a point:

var marker = new GMarker(point, icon);
Map.addOverlay(marker);

For complete source code for this step, see Listing 3.

5. Displaying Icon Information on Click

The last step is to display the content once a user clicks on the marker. In our example, if a user clicks on the marker, we'll show the park name, address, photo, and URL for more info.

The API provides several options for displaying content in an info window:

  • openInfoWindow(htmlElem): opens an info window with the given HTML content over this marker. htmlElem should be an HTML DOM element.
  • openInfoWindowHtml(htmlStr): takes an HTML string rather than an HTML DOM element
  • openInfoWindowXslt(xmlElem, xsltUri): takes an XML element and the URI of an XSLT document to produce the content of the info window. The first time a URI is given, it is retrieved with GXmlHttp and subsequently cached.

The API also provides support for different events: click, which is triggered when the user clicks on this marker; infowindowopen, which is triggered when the info window is opened above this marker; and infowindowclose, which is triggered when the info window above this marker is closed.

You can add dynamic elements to your application using event listeners. An object exports several named events, and your application can listen to those events using the static methods GEvent.addListener or GEvent.bind. The former takes a marker as its first argument, the event as the second argument, and a function as the third argument. In the function you can call one of the marker's info window methods.

This will display a blowup of the map over this marker. You can specify the zoom level, otherwise it uses a default zoom level of 1.

GEvent.addListener(marker, "click", function() {
          marker.showMapBlowup();
    });

These add plain text or, alternately, HTML:

GEvent.addListener(marker, "click", function() {
          marker.openInfoWindow("Park");
    });
GEvent.addListener(marker, "click", function() {
          marker.openInfoWindowHtml("<b>Park</b>");
    });

Let's add the final information about each park to our parks file:

<parks>
  <park><point lng="-96.936574" lat="32.822129"/> 
    <icon image="green.png" class="local" /> 
    <info>
      <FullName>Austin Recreation Center</FullName> 
      <url>http://www.ci.irving.tx.us/parks_and_recreation/
	     facilities/austin_recreation_center/index.asp</url>
      <address>825 East Union Bower Road</address> 
      <address2>Irving, Texas 75061</address2> 
      <phone>(972)721-2659</phone>
      <img>http://www.ci.irving.tx.us/parks_and_recreation/
	     facilities/austin_recreation_center/images/image7.gif</img>
    </info>
  </park>
  <park><point lng="-96.9330679" lat="32.871815"/> 
    <icon image="yellow.png" class="local" /> 
    <info>
      <FullName>Birds Fort Trail Park</FullName> 
      <url>http://www.ci.irving.tx.us/parks_and_recreation/
	     facilities/birds_fort_trail/index.asp</url>
      <address>5756 N. O'Connor Blvd</address> 
      <address2>Irving, Texas 75039</address2> 
      <phone>(972)721-2659</phone>
      <img>http://www.ci.irving.tx.us/parks_and_recreation/
	     facilities/birds_fort_trail/images/index2.jpg</img>
    </info>
  </park>
</parks>

Listing 4 contains the source of the XSLT we use to display the park information taken from the XML above.

Again, we parse the XML file for info elements inside the loop and add the event listener with an info element and parks.xsl file.

var info = xmlDoc.documentElement.getElementsByTagName("info");
GEvent.addListener(marker, "click", function() {
          marker.openInfoWindowXslt(info[i], "parks.xsl");
    });

(See Listing 5 for complete source code for this step.)

Now you've seen all steps involved in the process of developing a mapping application with Google Maps API. Happy map hacking!