Somewhere there is a map of how it can be done. - Ben Stein

In Part 1 I showed you how to get started with the MapQuest Advantage API by getting a developer key. In Part 2 I put that key to use by providing access to a basic map in the Map It! widget. In this installment I'll show you how to incorporate basic geocoding.

About Geocoding

Geocoding is the process of converting an address into latitude and longitude coordinates that uniquely identify a location, and you can use to plot on a map. Applications using the MapQuest Platform can calculate the latitude and longitude of:

  • Street addresses and intersections, the highest accuracy geocoding methods.
  • Street blocks, including the nearest block to an invalid house number.
  • Postal codes, including ZIP, ZIP+2, and ZIP+4 codes.
  • City centers.
  • US state and Canadian province centers.
  • Country centers.
  • Centers of other administrative areas that are used internationally.

The Map It! application will allow users to enter an address in one of the following forms:

  • street address, zip
  • street address, city, state
  • street address, city, state, zip
  • street address, city, state, zip, country

Preparing an Address for Geocoding

As shown in Figure 1, the user enters the address in the search text box in one of the acceptable forms.

Figure 1

The address needs to be separated into its constituent parts for geocoding. Since the user enters a comma separated string, the following JavaScript function is called on every key press. Once a return is entered (key code 13) the address is parsed.

function onAddressSearch(event)
{
  // user hit a return?
    if(event.keyCode == 13) {
    if(!$(address).value.blank()) {

      var street = "";
      var city = "";
      var state = "";
      var zip = "";
      var country = "";

      // clear the error message area
       $(lblErrorMsg).innerText = "";

      // the split function splits a string into an array based on a
      // character delimeter...
         var splitAddr = $(address).value.split(',');

       // if we only get 2 elements assume address, zip
       if(splitAddr.length == 2) {
        street = splitAddr[0];
        zip = splitAddr[1];

      // for three elements assume address, city, state
       } else if(splitAddr.length == 3) {
        street = splitAddr[0];
        city = splitAddr[1];
        state = splitAddr[2];

      // for 4 elements assume address, city, state, zip
      } else if(splitAddr.length == 4) {
        street = splitAddr[0];
        city = splitAddr[1];
        state = splitAddr[2];
        zip = splitAddr[3];

      // for 5 elements assume address, city, state, zip, country
       } else if(splitAddr.length == 5) {
        street = splitAddr[0];
        city = splitAddr[1];
        state = splitAddr[2];
        zip = splitAddr[3];
        country = splitAddr[4];

       } else {
        // error! unknown address

        $(lblErrorMsg).innerText = "Error! Unknown Address Format!";
        return;
       }
.
.
.

If the address is blank or not in the proper format and error message is displayed below the search text box. Once the address string has been parsed, it's time to convert the address to a latitude and longitude.

The Need for a Proxy

To call the Geocoding routines from your web application you need to install a proxy. The JavaScript API documentation describes the purpose of the proxy as twofold: "Circumvent the XML cross domain security issue. A browser can currently only request XML from the same domain as the HTML page arrived from. In other words, you cannot serve a page that requests XML directly from us, it has to come from you, so the Proxy is therefore a routing mechanism to make that happen. Secondly, the Proxy also adds in your clientID and password on the way through, so that your authorization credentials are not exposed in any manner within the web browser." The JavaScript API provides proxies in Java, PHP 5, .Net, Ruby on Rails, and a client side Flash proxy. Refer to Chapter 3 in the Advantage API Javascript Documentation for instructions on installing the proxy.

Java Geocoding - Since a Proxy Doesn't Work for Dashboard Widgets

All of the proxies, including the client side Flash proxy, assume that your application is running on a web server. As I mentioned in Part 2 the Geocoding process presents a limitation with Dashboard widgets, as they don't run on a web server, so proxies won't work. Instead, we'll need to access Geocoding through a Java application that the widget can call. The Java application is simple, it takes an address in command line parameters, and prints the first matching coordinates. You'll need to download the MapQuest Platform: Java SDK from http://developer.mapquest.com/downloads. This will provide the mq.jar file, which is required to access the MapQuest functions from your Java application.

Getting the First Coordinate Match

The GetLocation Java program in shown in Listing 1. When run it searches the command line for parameters that specify each component of the address. These components are passed to the getLocation method. A new geocodeClient object is created. You need to specify the username and password you obtained when you signed up for MapQuest access, as I described in Part 1 (link back ). An Address object is created, and initialized with the address specified on the command line. Even if only some of the attributes are specified, the geocoding process will be able to locate the coordinates. If a result is found, a string is created that contains the first matching coordinates. The latitude and longitude is separated by a | character.

Listing 1

  public class GetLocation
  {
     public static void main (String[] args)
     {
        String street = "";
      String city = "";
      String state = "";
      String zip = "";
      String country = "";

      // parse the command line parameters
      if(args.length > 0) {
        for(int i=0; i < args.length; i+=2) {
          if(args[i].equalsIgnoreCase("-street")) {
            street = args[i+1];
          } else if(args[i].equalsIgnoreCase("-city")) {
            city = args[i+1];
          } else if(args[i].equalsIgnoreCase("-state")) {
            state = args[i+1];
          } else if(args[i].equalsIgnoreCase("-zip")) {
            zip = args[i+1];
          } else if(args[i].equalsIgnoreCase("-country")) {
            country = args[i+1];
          } else {
            System.out.printf("ERROR\n");
            return;
          }
        }
        // print the found coordinates
        System.out.printf("%s\n",getLocation(street,city,state,zip,country));
        return;
      } else {
        // if unable to parse the command line arguments
        // print ERROR
        System.out.printf("ERROR\n");
        return;
      }
     }//end main

     public static String getLocation(String street, String city, String state,
       String zip, String country)
     {
      String result;
        /*
        MapQuest.Exec is the MapQuest client object.
        All server requests, such as Geocode and Search,
        are part of the Exec object.
        */
        Exec geocodeClient = new Exec();

        geocodeClient.setClientId ("** YOUR CLIDENT ID **");
        geocodeClient.setPassword ("** YOUR PASSWORD **");
        geocodeClient.setServerName("geocode.dev.mapquest.com");

      // create a new address object
      Address originAddress = new Address();

      // create a new location collection to save the results
      LocationCollection geocodeResults = new LocationCollection();

      // save the parsed address
        originAddress.setStreet(street);
        originAddress.setCity(city);
        originAddress.setState(state);
        originAddress.setPostalCode(zip);
        originAddress.setCountry(country);

        try
        {
           // This is the first communication with the MapQuest server
           // Try converting the address to coordinates
           geocodeClient.geocode(originAddress,geocodeResults);

         // create a new GeoAddress
           GeoAddress geoAddress = new GeoAddress();

        // any results returned?
      if(geocodeResults.getSize() > 0) {
            // get the first result
            geoAddress = (GeoAddress)geocodeResults.getAt(0);

            // create a string to contain the latitude and longitutde
        result = String.valueOf(new Double(geoAddress.getLatLng().getLatitude())) + "|" + String.valueOf(new Double(geoAddress.getLatLng().getLongitude()));
        return result;
       }
        }
        catch (Exception e)
      {
      // anything wrong - print an error message
      return "ERROR";
      }

      // if the address is not found - report NOT FOUND
        return "NOT FOUND";
     }
  }//end class GetLocation

Plotting the Point of Interest (POI)

After parsing the entered address, the following code concludes the onAddressSearch function:

// call the GetLocation Java Program - note the mq.jar file must be included in
// classpath
var result = widget.system('java -classpath .:mq.jar GetLocation -street "'
  + street + '" -city "' + city + '" -state "' + state + '" -zip "'
  + zip + '" -country "' +country + '"',null).outputString;

// get the coordinates from the returned string
var coords = result.split('|');

// create a new point based on the coordinates
newCenter = new MQLatLng(parseFloat(coords[0]),parseFloat(coords[1]));

// create a point
myPoint = new MQPoi(newCenter);

// recenter the map on the point, the second parameter specifies the zoom level
myMap.setCenter(newCenter,10);

// add the point as a Point of Interest
myMap.addPoi(myPoint);

Once the coordinates are returned from the GetLocation Java program, a new MQLatLng object is created with the coordinates returned. A MQPoi (point of interest) object is created, then the map is centered on the new point, and an icon is added to mark it with the addPoi method of the MQTileMap object. The result of mapping a point in the Map It! widget is shown in Figure 1.

Figure 1

Conclusion

In Part 4 I'll discuss Geocoding quality and how to handle multiple returned coordinates. For your reference, here are some references to the MapQuest Platform: