Forward geocoding in iOS5 is pretty simple, for early stages of iOS (by now <iOS5) we usually use MKReverseGeoder class. But in this article we would like to focus about the idea to get addresses or GPS coodinates from a CLLocation object or from a NSString for minor versions of iOS.

To achieve our goal we will use categories. It is easy to think about a category like a kind of methods that we add to a system class, i.e: we can add our own methods to NSString class for new functionalities such as URL encoding or format a string as we want. But today we will use categories to geocode.

Also we can do categories to create private methods inside our classes, but by now this is not our business.

To use this code you should add to our project: CoreLocation.framework also we would use JSONKit

In our demo project we have a group called Additions, in many cases developers use a special notation on files to create categories (systemClass+Additions), lets create a category, go File -> New -> New File and in Cocoa Touch section you must select Objective-C category. Click next button.

We will name this addition as JSON and category will be on NSString, we do the same for category Geocodereverse and class CLLocation.

Also we have to include JSONKit available at: https://github.com/johnezang/JSONKit

Then we will use a google service that give us a JSON output, we can use this service to get coordination from string address and vice versa, documentation about this service is fully available (http://code.google.com/intl/es-ES/apis/maps/documentation/webservices/).

We can obtain street address: http://maps.googleapis.com/maps/api/geocode/json?latlng=LATITUDE,LONGITUDE&language=LANGUAGE&sensor=false

We can obtain coodinates from street address by using: http://maps.googleapis.com/maps/api/geocode/json?address=ADDRESS&sensor=false

In both cases outputs are JSON response, so it is vere easy to parse, as you will know…let’s have a stub of code for both cases:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@implementation NSString (JSON)

- (CLLocation *)newGeocodeAddress
{
    CLLocation *location = nil;

    NSString *gUrl = [NSString stringWithFormat:@"http://maps.googleapis.com/maps/api/geocode/json?address=%@&amp;sensor=false", self];
    gUrl = [gUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    NSString *infoData = [[[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:gUrl]
                                                         encoding:NSUTF8StringEncoding
                                                            error:nil] autorelease];

    if ((infoData == nil) ||
        ([infoData isEqualToString:@"[]"]))
    {
        return location;
    }
    else
    {

        NSDictionary *jsonObject = [infoData objectFromJSONString];

        if (jsonObject == nil)
            return nil;

        NSDictionary *dict = [(NSDictionary *)jsonObject objectForKey:@"results"];

        for (id key in dict)
        {
            NSDictionary *value = [[key objectForKey:@"geometry"] valueForKey:@"location"];

            location = [[CLLocation alloc] initWithLatitude:[[value valueForKey:@"lat"] doubleValue]
                                                  longitude:[[value valueForKey:@"lng"] doubleValue]];
            break;
        }
    }  

    return location;
}

@end

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@implementation CLLocation (Geocodereverse)

- (NSString *)forwardGeocodeWithLanguage:(NSString *)language
{
    NSString *gUrl = [NSString stringWithFormat:@"http://maps.googleapis.com/maps/api/geocode/json?latlng=%.10f,%.10f&amp;language=%@&amp;sensor=false", self.coordinate.latitude, self.coordinate.longitude, language];

    gUrl = [gUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    NSString *infoData = [[[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:gUrl]
                                                         encoding:NSUTF8StringEncoding
                                                            error:nil] autorelease];

    NSString *value = @"";

    if ((infoData == nil) ||
        ([infoData isEqualToString:@"[]"]))
    {
        return value;
    }
    else
    {
        NSDictionary *jsonObject = [infoData objectFromJSONString];

        if (jsonObject == nil)
            return @"";

        NSDictionary *dict = [(NSDictionary *)jsonObject objectForKey:@"results"];

        for (id key in dict)
        {
            value = [NSString stringWithFormat:@"%@", [key objectForKey:@"formatted_address"]];
            break;
        }
    }  

    return value;
}

@end

This code is very easy to understand, even for a non-pro developer.

if we would like to use both categories we should import both files (CLLocation+Geocodereverse.h and CLLocation+Geocodereverse.h), we can use them as if:


1
2
3
4
5
6
7
8
9
10
NSString *address = @"1 Infinite Loop, Cupertino, California, USA";

CLLocation *coordinates = [address newGeocodeAddress];

NSLog(@"Coordinates - Latitude : %.10f, Longitude : %.10f", coordinates.coordinate.latitude, coordinates.coordinate.longitude);

NSString *currentLanguage = [[[[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"] objectAtIndex:0] lowercaseString];
NSString *newAddress = [coordinates forwardGeocodeWithLanguage:currentLanguage];

NSLog(@"New address: %@", newAddress);

This tutorial is a handy tip for all of us that still work for non iOS5 devices and want things quite easier ;)

You can download full code here. Enjoy.

Enjoyed this post?

Subscribe to our RSS Feed or Follow us on twitter to keep up to date with the latest from iOS-Blog. Remember, Sharing is caring so please click one of the following options:

Tags: , , , ,