Geocoding

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]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! Maps

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:

  • Specify only city=Berkeley to get several results corresponding to the cities that go by the name of Berkeley.[202]

  • Use the output=php option to get serialized PHP.[203]

  • Enter a nonexistent street address for a given city.[204]

Geocoder.us

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:

http://geocoder.us/help/

Let’s calculate the latitude and longitude for the Apress office with different aspects of the Geocoder.us service:

Notice the use of the W3 Basic Geo encoding for the latitude and longitude in the response.

Google Geocoder

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.

REST Interface

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]Caution

Even though the XML output does include a </Placemark> element, which is valid KML, the </Response> element in the output precludes the output from being valid KML.

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]}}]}
            

JavaScript Interface

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:

  1. Open the simple Google map from the previous section.[209]

  2. 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]

  3. 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);
       }
      } 
    );
                      
  4. 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); 
      } 
    } );
                      
  5. 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]

  1. Open the basic Virtual Earth example.[211]

  2. 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);
    }
                   
  3. 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]Note

    The last line with the many null parameters might look surprising, but it is an officially recommended invocation from http://msdn2.microsoft.com/en-us/library/bb545008.aspx.

  4. 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>
                   

Geocoding Non-­U.S. Addresses

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:



[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/.