Geocoding Tutorial – Getting physical address from IP address (using MaxMind)

Posted April 21st, 2008 by

Introduction

This tutorial will show you how to access MaxMind’s GeoIP web service, which allows you to turn an IP address into a physical address. In order to use the MaxMind GeoIP web service, you have to buy a license for it. However, the paid service is very responsive, and allows you to get lots of detailed information on IP addresses.

Given an IP address, this tutorial will show you how to get location information from it. You can then use this location information for many different things, like looking up weather there, or perhaps displaying a pushpin on google maps, etc. To learn more about geocoding and the Google Maps API, read this chapter in the Google Maps Hack book (on Safari Books Online).

I’ve written an existing tutorial and code that deals with using ippages.com service to get this GeoIP data.

Background Information

Locations on earth can be specified by a combination of longitude, latitude, and elevation. You can read all about it here on wikipedia. The following code is enough to identify a geographic location:

double latitude;
double longitude;

This is the bare minimum information that you will need, but you will be able to get by and interface with many location based services just with this data. How do you get this long/lat data? If your app has access to a GPS device, then it can read the GPS coordinates from it (NMEA standard) – the GPS receiver sends lat/long information every few seconds via a serial port, USB, or Bluetooth connection to your laptop or mobile device, and you can then use that data. Alternatively, if you know the IP address where your desktop, mobile, or web based app is running, then you might be able to get geographic information out of it. There are issues with this and it’s not always reliable, and these issues are explained here.

MaxMind Service

You can learn more about the MaxMind service subscriptions here. Here’s more information on their service offering. Basically, you construct a URI and send it to their service via HTTP GET request, and it will send you back a comma separated value with the physical location, and other information associated with the given IP address. The input parameters for the GET request include the IP address you’re interested in getting more information on, and the license key. I’ve created a set of Java classes that make it trivial to use the MaxMind service and get a Java object back that you can query to get all the information provided by the MaxMind web service. As of the writing of this tutorial, Java code is not provided on MaxMind’s website to access their excellent service, which is why I wrote this tutorial.

Source code

You can download the source code to access the MaxMind GeoIP service here.

The main class that you have to use in order to access this MaxMind GeoIP service is LocationLookup class in the Provider.MaxMind_GeoIPLookup package. This is a static class that has to be provided a license key in order for it to work, otherwise it will just produce an error when it connects to the MaxMind service itself. You can get a license here.

Usage Example

Here’s a usage example:

public static void main(String[] args) throws IOException, LookupException {

  // make sure to set a valid license key!
  setLicenseKey("valid key here");
  IpAddressLookupData addressLookup = performLookup("24.24.24.24");
  System.out.println(Utils.mapToString(addressLookup.getData()));

}

private static class LookupException extends Exception {
  public LookupException(String s) {
    super(s);
  }
}

LocationLookup class

Once you’ve made the call to performLookup() after you’ve called setLicenseKey(), and passed the IP address you’re interested in, you will get back an IpAddressLookupData, which if all goes will will contain all the information you requested from MaxMind. If there are problems, a LookupException is thrown, and it’s getMessage() String contains the error code from MaxMind’s service. It’s all very easy to use.

For your reference, here’s the whole class:

/**
 * AddressLookup is a simple class that uses MaxMind's GeoIPLookup service to resolve an IP address to a
 * street address, etc. More details are provided here:
 * <a href="http://www.maxmind.com/app/web_services_guide#cityisporg">MaxMind site</a>.
 *
 * @author Nazmul Idris
 * @version 1.0n
 * @since Mar 23, 2008, 2:51:57 PM
 */
public class LocationLookup {

//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// data
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
public static final String MaxMindUri = "http://geoip1.maxmind.com/f";
public static String License = "";
public static final String IpAddressUriKey = "i";
public static final String LicenseUriKey = "l";

//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// methods
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
public static void setLicenseKey(String lic) {
  License = lic;
}

public static IpAddressLookupData performLookup(String ipaddress)
    throws IllegalArgumentException, IOException, LookupException
{
  Validate.notNull(ipaddress, "ipaddress can not be null");

  HashMap<String, String> map = new HashMap<String, String>();
  map.put(LicenseUriKey, License);
  map.put(IpAddressUriKey, ipaddress);
  String uri = Utils.buildUri(MaxMindUri, map);

  System.out.println(uri);

  IpAddressLookupData data = _getResponse(uri);

  if (data.containsError()) {
    throw new LookupException("IP address=" + ipaddress + ", error or warning= " + data.getError());
  }
  else {
    return data;
  }
}

/** use HttpClient to make a HTTP request to the uri and get the response as a string */
private static IpAddressLookupData _getResponse(String uri) throws IOException {
  Validate.notEmpty(uri, "uri can not be empty or null");

  HttpClient client = new HttpClient();
  GetMethod get = new GetMethod(uri);

  client.executeMethod(get);

  String respStr = get.getResponseBodyAsString();

  return new IpAddressLookupData(respStr);
}

//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// self test method
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
public static void main(String[] args) throws IOException, LookupException {

  // make sure to set a valid license key!
  setLicenseKey("valid key here");
  IpAddressLookupData addressLookup = performLookup("24.24.24.24");
  System.out.println(Utils.mapToString(addressLookup.getData()));

}

private static class LookupException extends Exception {
  public LookupException(String s) {
    super(s);
  }
}

}//end class LocationLookup

IpAddressLookupData class

If you open the provide GeoIPLookup.ipr IDEA project file in IDEA 7, you will find multiple run configurations that you can test out. Here’s the output from the "MaxMind LocationLookup" run configuration (that just runs the LocationLookup’s main() method):

http://geoip1.maxmind.com/f?l=<YOUR LICENSE KEY>&i=24.24.24.24

{number of entries=10}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    [1] Organization=
         Road Runner
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    [2] PostalCode=
         10011
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    [3] City=
         New York
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    [4] MetropolitanCode=
         501
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    [5] Longitude=
         -74.001801
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    [6] ISP=
         Road Runner
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    [7] Latitude=
         40.742100
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    [8] AreaCode=
         212
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    [9] RegionCode=
         NY
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    [10] CountryCode=
         US
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The IpAddressLookupData that gets returned on successful completion of this method call, contains a HashMap with key/value pairs containing information that is retrieved from MaxMind. You can access this HashMap by calling the getData() method.

Error codes and LookupException

A full listing of the error codes and the keys that are retrieved can be found here:

//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// constants
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
public enum Keys {
  CountryCode,
  RegionCode,
  City,
  PostalCode,
  Latitude,
  Longitude,
  MetropolitanCode,
  AreaCode,
  ISP,
  Organization,
  ErrorCode
}

public enum ErrorCodeType {
  //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  // Warnings
  //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  WarningIpNotFound("IP_NOT_FOUND"),
  WarningCountryNotFound("COUNTRY_NOT_FOUND"),
  WarningCityNotFound("CITY_NOT_FOUND"),
  WarningCityRequired("CITY_REQUIRED"),
  WarningPostalCodeRequired("POSTAL_CODE_REQUIRED"),
  WarningPostalCodeNotFound("POSTAL_CODE_NOT_FOUND"),

  //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  // Fatal Errors
  //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  ErrorInvalidLicenseKey("INVALID_LICENSE_KEY"),
  ErrorMaxRequestsPerLicense("MAX_REQUESTS_PER_LICENSE"),
  ErrorIpRequired("IP_REQUIRED"),
  ErrorLicenseRequired("LICENSE_REQUIRED"),
  ErrorCountryRequired("COUNTRY_REQUIRED"),
  ErrorMaxRequestsReached("MAX_REQUESTS_REACHED");

  ErrorCodeType(String msg) { errMsg = msg; }
  public String errMsg;
  public String getMsg() { return errMsg; }
  /**
   * if there is no match, then returns null
   *
   * @return might be null
   */
  public static ErrorCodeType match(String key) {
    Validate.notEmpty(key, "key can not be empty or null");

    for (ErrorCodeType type : values()) {
      if (type.getMsg().equals(key)) return type;
    }

    return null;
  }
}

How the API works

You can look at how this service is implemented to understand what’s going on under the covers. Basically a URI is constructed with the input parameter (IP address) that you provide, and the valid license key. An HTTP GET method is created (using Apache HttpClient library) that is executed, and the resulting response is parsed as CSV data. This parsed data is provided to you as a HashMap. If an error was returned, then an exception is thrown with the error message/code.

Comments and feedback

To share your thoughts on this tutorial, please click here.


Comments are closed.