Hacking Maps with the Google Maps API
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.
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:
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 |
To develop a Google map application, you need to go through the following five steps:
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.
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.
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.
|
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>
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.)
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.)
|
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.
)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 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!
XML.com Copyright © 1998-2006 O'Reilly Media, Inc.