A common task in using online maps is to geocode addresses—that is, converting street addresses to the corresponding latitude and longitude. In the following sections, I will walk through the basics of geocoding in Yahoo!, Google, Geocoder.us, and Virtual Earth maps, which is enough to get you started. For the following examples, I use the address of Apress, which is 2855 Telegraph Ave., Berkeley, CA.
Caution | |
---|---|
There are subtleties that I won’t go in detail about: the precision and accuracy of the APIs, dealing with ambiguities in the addresses, and which geocoder is best for a given geographic location. |
Yahoo! provides a REST geocoding method here:
http://developer.yahoo.com/maps/rest/V1/geocode.html
whose base URL is http://api.local.yahoo.com/MapsService/V1/geocode
and whose
parameters include the appid
to identify your application and two ways of
identifying the address:
A combination of street
, city
, state
,
and zip
:
http://api.local.yahoo.com/MapsService/V1/geocode?appid={app-id}&street=2855+Telegraph+Ave.&city=Berkeley&state=CA
location
, which is free text that consists of one string that holds a
combination of street
, city
, state
, and
zip
. The location
string has priority for determining
the placement of this:
http://api.local.yahoo.com/MapsService/V1/geocode?appid={app-id}&location=2855+Telegraph+Ave.%2C+Berkeley%2C+CA
When you use these two methods to geocode the location of the Apress office, you get
the same result. Using street
, city
, and state
returns this:
<?xml version="1.0"?> <ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:yahoo:maps" xsi:schemaLocation="urn:yahoo:maps http://api.local.yahoo.com/MapsService/V1/GeocodeResponse.xsd"> <Result precision="address"> <Latitude>37.858377</Latitude> <Longitude>-122.259171</Longitude> <Address>2855 TELEGRAPH AVE</Address> <City>BERKELEY</City> <State>CA</State> <Zip>94705-1128</Zip> <Country>US</Country> </Result> </ResultSet>
Note the following characteristics of the output:
You get the same results with the two methods in this case.
You can compare the output address and the input address to make sure the geocoder is interpreting the address the way you think it should be.
The default output format is XML when no output
parameter is
specified.
You get the latitude and longitude in the <Latitude>
and
<Longitude>
elements, respectively.
A good way to see how the API behaves is to try various parameters. See what happens in the following cases:
Geocoder.us provides a free gecoding service for U.S. addresses. Refer to Chapter 7 for a detailed discussion of the REST and SOAP interfaces to the Geocoder.us API, which is documented here:
Let’s calculate the latitude and longitude for the Apress office with different aspects of the Geocoder.us service:
The Geocoder.us user interface, invoked with this:
http://geocoder.us/demo.cgi?address=2855+Telegraph+Ave.%2C+Berkeley%2C+CA
shows that the latitude and longitude of the address is the following:
(37.858276, -122.260070)
The CSV interface, invoked with this:
http://rpc.geocoder.us/service/csv?address=2855+Telegraph+Ave.%2C+Berkeley%2C+CA
returns the following:
37.858276,-122.260070,2855 Telegraph Ave,Berkeley,CA,94705
The REST interface, through this:
http://geocoder.us/service/rest/?address=2855+Telegraph+Ave.%2C+Berkeley%2C+CA
returns the following:
<?xml version="1.0"?> <rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <geo:Point rdf:nodeID="aid33483656"> <dc:description>2855 Telegraph Ave, Berkeley CA 94705</dc:description> <geo:long>-122.260070</geo:long> <geo:lat>37.858276</geo:lat> </geo:Point> </rdf:RDF>
Notice the use of the W3 Basic Geo encoding for the latitude and longitude in the response.
The Google Geocoder provides two interfaces: a REST interface and a JavaScript-accessible interface:
http://www.google.com/apis/maps/documentation/#Geocoding_Examples
I’ll cover each in turn.
We’ll first look at the REST method, whose base URL is http://maps.google.com/maps/geo
and whose parameters are as
follows:
q
is the address to geocode.
key
is your API key.[205]
output
is the format of the output—one of xml
,
kml
, csv
, or json
.
Let’s look at some example output. The xml
and kml
output for 2855 Telegraph Ave., Berkeley, CA, produces the same body (listed next)
but different Content-Type
headers (“text/xml” and
“application/vnd.google-earth.kml+xml”), respectively:[206]
<?xml version="1.0" encoding="UTF-8"?> <kml xmlns="http://earth.google.com/kml/2.0"> <Response> <name>2855 Telegraph Ave., Berkeley, CA.</name> <Status> <code>200</code> <request>geocode</request> </Status> <Placemark id="p1"> <address>2855 Telegraph Ave, Berkeley, CA 94705, USA</address> <AddressDetails Accuracy="8" xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"> <Country> <CountryNameCode>US</CountryNameCode> <AdministrativeArea> <AdministrativeAreaName>CA</AdministrativeAreaName> <SubAdministrativeArea> <SubAdministrativeAreaName>Alameda</SubAdministrativeAreaName> <Locality> <LocalityName>Berkeley</LocalityName> <Thoroughfare> <ThoroughfareName>2855 Telegraph Ave</ThoroughfareName> </Thoroughfare> <PostalCode> <PostalCodeNumber>94705</PostalCodeNumber> </PostalCode> </Locality> </SubAdministrativeArea> </AdministrativeArea> </Country> </AddressDetails> <Point> <coordinates>-122.259310,37.858517,0</coordinates> </Point> </Placemark> </Response> </kml>
Caution | |
---|---|
Even though the XML output does include a |
Here is the json
output for convenient use in JavaScript:[207]
{"name":"2855 Telegraph Ave., Berkeley, CA.","Status":{"code":200,"request": "geocode"},"Placemark":[{"id":"p1","address":"2855 Telegraph Ave, Berkeley, CA 94705, USA","AddressDetails":{"Country":{"CountryNameCode":"US", "AdministrativeArea":{"AdministrativeAreaName":"CA","SubAdministrativeArea": {"SubAdministrativeAreaName":"Alameda","Locality":{"LocalityName":"Berkeley", "Thoroughfare":{"ThoroughfareName":"2855 Telegraph Ave"},"PostalCode": {"PostalCodeNumber":"94705"}}}}},"Accuracy": 8},"Point":{"coordinates": [-122.259310,37.858517,0]}}]}
As for the JavaScript methods available in the Google mapping system, consult the
documentation provided by Google,[208]GClientGeocoder
object. You can use the JavaScript Shell to
see this object in action:
Open the simple Google map from the previous section.[209]
In the JavaScript Shell, set up the address and an instance of
GClientGeocoder
:
address = "2855 Telegraph Ave., Berkeley, CA"
2855 Telegraph Ave., Berkeley, CA
geocoder = new GClientGeocoder();
[object Object]
Compose a call to GClientGeocoder.getLatLng
, which takes an
address and a callback function, which in turn takes the point geocoded from
the address:
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); } } );
If you get rid of the whitespace in the function, you can easily invoke it in the JavaScript Shell:
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); } } );
You will then see that Google Maps adds an overlay marking 2855 Telegraph Ave., Berkeley, CA (as shown in Figure 13-1).
Virtual Earth provides geocoding functionality in the VEMap.Find
method
of version 5 of the Virtual Earth SDK,[210]
Open the basic Virtual Earth example.[211]
Let’s create a callback function that pops up an alert with the latitude/longitude of the found locations:
function onFoundResults (ShapeLayer,FindResult,Place,HasMore) { html = ""; for (x=0; x<Place.length; x++) { html = html + Place[x].LatLong + ""; } alert (html); }
In the JavaScript Shell, type the following:
address = "2855 Telegraph Ave., Berkeley, CA"
2855 Telegraph Ave., Berkeley, CA
function onFoundResults (ShapeLayer,FindResult,Place,HasMore) { html = ""; for (x=0; x<Place.length; x++) {html = html + Place[x].LatLong + "";} alert (html); } map.Find(null,address,null,null,null,null,null,null,null,null, onFoundResults);
Note | |
---|---|
The last line with the many |
You will see an alert that pops up the latitude and longitude of the address.
I packaged this logic in a simple example:[212]
<html> <head> <title>VE Map showing VEMap.Find (ve.map.find.html)</title> <meta ?http-?equiv="Content-Type" content="text/html; charset=utf-8"> <script src="http://dev.virtualearth.net/mapcontrol/v5/mapcontrol.js"></script> <script> var map = null; function onFoundResults (ShapeLayer,FindResult,Place,HasMore) { html = ""; //alert("Place: " + Place); for (x=0; x<Place.length; x++) { html = html + Place[x].LatLong + ""; } alert (html); } function GetMap() { map = new VEMap('myMap'); map.LoadMap(); address = "2855 Telegraph Ave., Berkeley, CA"; map.Find(null,address,null,null,null,null,null,null,null, null, onFoundResults); } </script> </head> <body onload="GetMap();"> <div id='myMap' style="position:relative; width:400px; height:400px;"></div> </body> </html>
As I have implied in my previous examples, the Google, Yahoo!, Microsoft, and Geocoder.us geocoders are built to handle American addresses. What if you want to geocode street-level addresses in other countries? It would be great to have a single geocoding API that would return a reliable latitude and longitude for an address from anywhere in the world. That service doesn’t currently exist. Here, however, are some starting points to geocoding addresses from around the world:
The Google geocoder can geocode addresses in countries listed here:
http://code.google.com/support/bin/answer.py?answer=62668&topic=12266
The list currently includes Austria, Australia, Belgium, Brazil, Canada, The Czech Republic, Denmark, Finland, France, Germany, Hong Kong, Hungary, India, Ireland, Italy, Japan, Luxembourg, The Netherlands, New Zealand, Poland, Portugal, Singapore, Spain, Sweden, Switzerland, Taiwan, the United Kingdom, and the United States. Note the caveat that the accuracy of the results can vary per country.
The best list I could find for Yahoo!’s coverage is here:
This blog entry lists the following countries in Western Europe as having “complete coverage”: Austria, Belgium, Denmark, Finland, Germany, Great Britain, Luxembourg, Netherlands, Ireland, Norway, Portugal, Spain, Sweden, Switzerland, France, and Italy.
Consult the following for lists of other geocoders:
http://groups.google.com/group/Google-Maps-API/web/resources-non-google-geocoders
[202] http://api.local.yahoo.com/MapsService/V1/geocode?appid={app-id}&city=Berkeley
[203] http://api.local.yahoo.com/MapsService/V1/geocode?appid={app-id}&location=350+5th+Ave,+New+York,+NY&output=php
[204] http://api.local.yahoo.com/MapsService/V1/geocode?appid={app-id}&location=350000+main+Street,+Berkeley,+CA
[205] You can get your key at http://www.google.com/apis/maps/signup.html. ABQIAAAAdjiS7YH6Pzk2Nrli02b5xxR10RG5t-vK3TwPKbpNUO2c5sYb4RTmySs_TEFzYvlZrCaYJKlmTzJ5lA is the key for http://examples.mashupguide.net/ch13/.
[206] http://maps.google.com/maps/geo?q=2855+Telegraph+Ave.%2C+Berkeley%2C+CA.&output=xml&key=?ABQIAAAAdjiS7YH6Pzk2Nrli02b5xxR10RG5t-vK3TwPKbpNUO2c5sYb4RTmySs_TEFzYvlZrCaYJKlmTzJ5lA