Younger hackers are hard to classify. They're probably just as diverse as the old hackers are. We're all over the map. - Larry Wall

In Part 1 I showed you how to get started with the MapQuest Platform 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. Part 3 showed you how to incorporate basic geocoding. In this installment I'll discuss more advanced geocoding topics - including handling multiple matches and specifying geocode search options.

Multiple Matches

The version of the Map It! widget developed in Part 3 added the ability to plot a point of interest on the map. In the Java application that was developed to return the coordinates of an address, only the first match is returned. What if the user enters a street address without the house number? Geocoding would actually return multiple results. We need to change the getLocation method developed in Part 3 to return each match. On a search without a house number for example, the geocoding process would return points for each range of addresses. Listing 1 shows how the getLocation method has been modified:

Listing 1

  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) {
        // loop through each result
        for(int i=0; i < geocodeResults.getSize(); i++) {
              geoAddress = (GeoAddress)geocodeResults.getAt(i);
          // append the coordinates and information about that
          // matching location
          result = result +
           String.valueOf(new Double(geoAddress.getLatLng().getLatitude())) +
           "|" + String.valueOf(new
           Double(geoAddress.getLatLng().getLongitude())) + "|" +
             geoAddress.getStreet() + "|" + geoAddress.getCity()  + "|" +
               geoAddress.getState() + "|" + geoAddress.getCountry() + "|" +
             geoAddress.getPostalCode() + "\n";
        }
       }
        }
        catch (Exception e)
      {
      // anything wrong - print an error message
      return "ERROR";
      };
      // if the address is not found - report NOT FOUND
        return "NOT FOUND";
     }

For each match that is returned by geocoding, the result string is appended with the coordinates, and street, city, state, zip, and country of that location. Each parameter is separated by | for easy parsing in JavaScript.

Geocoding Options

The API documentation indicates that the default geocoding options will typically be adequate for most developers. However, there are a few options you can specify that ma be of interest. These include:

  • MatchType - A constant that corresponds to the granularity of the desired match.
  • QualityType - The minimum confidence necessary for a match.
  • MaxMatches - the maximum number of matchers to return.

These options are explained in detail in Chapter 3 of the Java API Developer Guide.

To specify an option you need to create a GeocodeOptions object, then specify the MatchType, QualityType, or MaxMatches properties. (The constants for each option are specified in Chapter 3). As shown in Listing 2, the GeoCodeOptions object is passed as a parameter to the geocode method.

Listing 2

// create a geocode options object
GeocodeOptions geocodeOptions = new GeocodeOptions();

// return only exact matches
geocodeOptions.setQualityType(QualityType.EXACT);

// create the options collection object
// then add the option to the options collection
GeocodeOptionsCollection geocodeOptionsCollection =
  new GeocodeOptionsCollection();
geocodeOptionsCollection.add(geocodeOptions);
try {
  // perform the search
  Client.geocode(originAddress, geocodeResults, geocodeOptionsCollection);
}

Selecting the Best Match

Now that multiple locations are returned for a search, those results need to be displayed to the user in a combo box, as shown in Figure 1.

Figure 1

When the users selects an address from the combo box that point is displayed on the map. To do this we need to change the onAddressSearch JavaScript function introduced in Part 3, as follows:

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 = "";

       $(lblErrorMsg).innerText = "";
         $(multMatches).style.visibility = "hidden";
       $(lblMultMatch).style.visibility = "hidden";

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

       var result = widget.system('java -classpath .:mq.jar GetLocations
         -street "' + street + '" -city "' + city + '" -state "' + state + '"
         -zip "' + zip + '" -country "' +country + '"',null).outputString;

       // any errors?
       if(result == "ERROR") {
        $(lblErrorMsg).innerText = "Error! Unknown Address Format!";
       // address not found?
       } else if (result == "NOT FOUND") {
        $(lblErrorMsg).innerText = "Address Was Not Found!";
       } else {

       // *** CHANGED TO SUPPORT MULTIPLE LOCATION ***

       // split on return - a line is printed for each individual result
       var eachresult = result.split('\n');

       // just one result returned? - then just plot the point
       if(eachresult.length == 1) {
          // 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);
       } else {
          // clear out existing items
          $(multMatches).options.length = 0;
          for(var i=0; i < eachresult.length; i++) {
            // parse each returned location
            var location = eachresult[i].split('|');
            // 7 items on the result line?
            if(location.length == 7) {
              var locationtext = location[2] + "," + location[3] +
                "," + location[4] + "," + location[5] + "," + location[6];
              var objNewOption = document.createElement("OPTION");
              $(multMatches).options.add(objNewOption);
              // add the location text
              objNewOption.text = locationtext;
              // add the coordinates as a | separate string to the value...
              objNewOption.value = location[0] + '|' + location[1];
            }
          }
          // show the label and combo box...
          $(multMatches).style.visibility = "visible";
          $(lblMultMatch).style.visibility = "visible";
        }
       }
        }
    }
  }

A separate line is returned for each matching location. The text is displayed for each option, and the value is set to the coordinates. When the user selects an item, the coordinates value is retrieved and the point is plotted on the map.

Conclusion

In Part 5 I'll discuss enhancements to the Map It! widget, including setting the default zoom level when adding an API, specifying point of interest icons, and the default map type. For your reference, here are some references to the MapQuest Platform: