iOS Reference Library Apple Developer
Search

Geocoding Location Data

Location data is usually returned as a pair of numerical values representing the latitude and longitude of the corresponding point on the globe. These coordinates offer a precise and easy way to specify location data in your code but they are not very intuitive for users. Instead of global coordinates, users are more likely to understand a location that is specified using information they are more familiar with such as street, city, state, and country information. For situations where you want to display a user friendly version of a location, you can use a geocoder object to obtain that information.

About Geocoder Objects

A geocoder object uses a network service to convert between latitude and longitude values and a user-friendly placemark, which is a collection of data such as the street, city, state, and country information. Many geocoding services allow you to perform conversions in either direction but iOS currently supports only reverse geocoding, which converts a latitude and longitude into a placemark. If you want to convert placemark information into a latitude and longitude—a process known as forward geocoding—you would need to find an appropriate third-party service to use.

Because geocoders rely on a network service, a live network connection must be present in order for a geocoding request to succeed. If a device is in Airplane mode or the network is currently not configured, the geocoder cannot connect to the service it needs and must therefore return an appropriate error.

Each application has a limited amount of geocoding capacity, so it is to your advantage to use geocoding requests sparingly. Here are some rules of thumb to apply when creating your requests:

Getting Placemark Information from the Reverse Geocoder

Reverse geocoding is done using the MKReverseGeocoder class of the Map Kit framework. This class uses a delegate-based approach for geocoding a single location. This means that you can use a single instance of the MKReverseGeocoder class only once. In addition, the Google terms of service require that the MKReverseGeocoder class be used in conjunction with a Google map.

To initiate a reverse geocoding request, create an instance of the MKReverseGeocoder class, assign an appropriate object to the delegate property, and call the start method. If the query completes successfully, your delegate’s reverseGeocoder:didFindPlacemark: method is called and passed an MKPlacemark object with the results. If there is a problem reverse geocoding the location, the reverseGeocoder:didFailWithError: method is called instead.

Listing 3-1 shows the code required to use a reverse geocoder. Upon successful completion of the geocoding operation, the code adds a button to the annotation view’s callout so that it can display the placemark information. Because the annotation is not automatically available to the delegate, the custom annotationForCoordinate: method is included to find the appropriate annotation object from the map view.

Listing 3-1  Geocoding a location using MKReverseGeocoder

@implementation MyGeocoderViewController (CustomGeocodingAdditions)
- (void)geocodeLocation:(CLLocation*)location forAnnotation:(MapLocation*)annotation
{
    MKReverseGeocoder* theGeocoder = [[MKReverseGeocoder alloc] initWithCoordinate:location.coordinate];
 
    theGeocoder.delegate = self;
    [theGeocoder start];
}
 
// Delegate methods
- (void)reverseGeocoder:(MKReverseGeocoder*)geocoder didFindPlacemark:(MKPlacemark*)place
{
    MapLocation*    theAnnotation = [map annotationForCoordinate:place.coordinate];
    if (!theAnnotation)
        return;
 
   // Associate the placemark with the annotation.
    theAnnotation.placemark = place;
 
    // Add a More Info button to the annotation's view.
    MKPinAnnotationView*  view = (MKPinAnnotationView*)[map viewForAnnotation:annotation];
    if (view && (view.rightCalloutAccessoryView == nil))
    {
        view.canShowCallout = YES;
        view.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    }
}
 
- (void)reverseGeocoder:(MKReverseGeocoder*)geocoder didFailWithError:(NSError*)error
{
    NSLog(@"Could not retrieve the specified place information.\n");
}
@end
 
@implementation MKMapView (GeocoderAdditions)
 
- (MapLocation*)annotationForCoordinate:(CLLocationCoordinate2D)coord
{
    // Iterate through the map view's list of coordinates
    // and return the first one whose coordinate matches
    // the specified value exactly.
    id<MKAnnotation> theObj = nil;
 
    for (id obj in [self annotations])
    {
        if (([obj isKindOfClass:[MapLocation class]]))
        {
            MapLocation* anObj = (MapLocation*)obj;
 
            if ((anObj.coordinate.latitude == coord.latitude) &&
                (anObj.coordinate.longitude == coord.longitude))
            {
                theObj = anObj;
                break;
            }
        }
    }
 
    return theObj;
}
@end



Last updated: 2010-05-20

Did this document help you? Yes It's good, but... Not helpful...