chemistree apps

ios development

Forward Geocoding

by Andy Riordan

In iOS 5, Apple introduced a great new feature: forward geocoding. Forward here is as opposed to reverse geocoding, which has been present since MapKit was introduced. Where reverse geocoding takes a longitude/latitude pair and converts it to city, state, zip code and so on, forward geocoding takes a city, state, zip code kind of thing and turns it into a lat/long.

First try

Here’s a simple snippet to try forward geocoding on iOS 5. Make sure you have CoreLocation.framework in your Frameworks section, then include the header:

#import <CoreLocation/CoreLocation.h>

Then, to test it out:

CLGeocoder *geocoder = [[CLGeocoder alloc] init];

CLGeocodeCompletionHandler completionHandler = 
^(NSArray *placemarks, NSError *error)
{
    if ([placemarks count] > 0)
    {
        CLPlacemark *placemark = [placemarks objectAtIndex:0];
        
        NSLog(@"Found placemark: %@", placemark);
    }
};

[geocoder geocodeAddressString:@"Outer Mongolia" completionHandler:completionHandler];

Note: For brevity, this snippet leaks geocoder if not run on ARC. The sample code provided releases it correctly.

If you run it in the iOS 5 simulator (or device), it will work great. Turns out Outer Mongolia is really a place:

Forward Geocoding[1902:f803] Found placemark: Outer Mongolia, Mongolia @ <+47.00000000,+103.00000000> +/- 100.00m, region (identifier <+47.00019850,+103.00094600> radius 129.34) <+47.00019850,+103.00094600> radius 129.34m

Warnings

It’s great that we can specify location information with such free syntax. However, with that great power comes great responsibility. Let’s say you have a perfectly normal-sounding US zip code, 38720. That zip code belongs to Duncan, MS. However, when we run it through Core Location, we get this:

Forward Geocoding[7123:f803] Found placemark: 38720 Kankaanpaa, Finland @ <+61.74309935,+22.56331008> +/- 100.00m, region (identifier <+61.74316400,+22.57415800> radius 9380.53) <+61.74316400,+22.57415800> radius 9380.53m

That’s in Finland, folks. Not quite what we were looking for. However, we do get an array of placemarks back - perhaps it’s somewhere in the array, just not the first object?

CLGeocoder *geocoder = [[CLGeocoder alloc] init];

CLGeocodeCompletionHandler completionHandler = 
^(NSArray *placemarks, NSError *error)
{
    for (CLPlacemark *placemark in placemarks)
    {
        NSLog(@"Found placemark: %@", placemark);
    }
};

[geocoder geocodeAddressString:@"38720" completionHandler:completionHandler];
Forward Geocoding[8216:f803] Found placemark: 38720 Kankaanpaa, Finland @ <+61.74309935,+22.56331008> +/- 100.00m, region (identifier <+61.74316400,+22.57415800> radius 9380.53) <+61.74316400,+22.57415800> radius 9380.53m

Forward Geocoding[8216:f803] Found placemark: Duncan, MS  38720, United States @ <+34.11106550,-90.82042082> +/- 100.00m, region (identifier <+34.10980250,-90.82122800> radius 15440.36) <+34.10980250,-90.82122800> radius 15440.36m

Forward Geocoding[8216:f803] Found placemark: 38720 San Andrés y Sauces (Canary Islands), Spain @ <+28.77990090,-17.79729925> +/- 100.00m, region (identifier <+28.78246300,-17.79785150> radius 6970.58) <+28.78246300,-17.79785150> radius 6970.58m

Okay, so it found it after all - it’s the second item in the array, not the first. We can just filter out the non-US results and we’ll be fine. However, why not let Core Location do that for us? Instead, let’s be more specific and use 38720 United States:

Forward Geocoding[7214:f803] Found placemark: Duncan, MS  38720, United States @ <+34.11106550,-90.82042082> +/- 100.00m, region (identifier <+34.10980250,-90.82122800> radius 15440.36) <+34.10980250,-90.82122800> radius 15440.36m

Ahh, there we go. The moral here is to be careful and specify as much information as you have. I learned that one the hard way.

Legacy OS versions

Unfortunately, when you run this code on iOS 4, you get no output. As mentioned above, forward geocoding is an iOS 5 feature. If you need it on iOS versions earlier than 5, there are a few options, including:

  1. Third-party geocoding services
  2. In-storage geocoding database

Conclusion

Where might this be useful? Beyond the obvious uses, like allowing a user to input a zip code or city/state name and having it come back with locations when your server only supports lat/long searching, you can now display city details rather than just “Current Location”.

Go forth and geocode!