42

I have an application that needs to have a similar search feature like the Apple "Maps" application (included with iPhone, iPod Touch and iPad).

The feature in question should not be a hard thing to do, but I'm really clueless about how to input a Street Address in the search bar, and then obtaining coordinates for that address or something that can help me to actually move the map and center in that place.

I mean, what do I have to query, does Apple provide an "address searching API method" ? or I need to use the google maps API directly ?

I would love to hear how should it be done.

Goles
  • 11,599
  • 22
  • 79
  • 140

5 Answers5

39

Ok, to answer my own question:

As was mentioned before, the best thing to do is to use the Google Maps API, it supports a lot of formats but for several reasons I chose to go with JSON.

So here are the steps to perform a JSON query to Google Maps and obtain the coordinate of the query. Note that not all the correct validations are done, this is only a Proof of concept.

1) Download a JSON framework/library for the iPhone, there are several, I chose to go with this one, it's very good and seems an active project, plus several comercial applications seem to be using it. So add it to your project ( instructions here ).

2) To query Google Maps for an address we need to build a request URL like this: http://maps.google.com/maps/geo?q=Paris+France

This url, will return a JSON object for the query "Paris+France".

3) Code:

//Method to handle the UISearchBar "Search", 
- (void) searchBarSearchButtonClicked:(UISearchBar *)theSearchBar 
{
    //Perform the JSON query.
    [self searchCoordinatesForAddress:[searchBar text]];

    //Hide the keyboard.
    [searchBar resignFirstResponder];
}

After we handle the UISearchBar search, we must make the request to Google Maps:

- (void) searchCoordinatesForAddress:(NSString *)inAddress
{
    //Build the string to Query Google Maps.
    NSMutableString *urlString = [NSMutableString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@?output=json",inAddress];

    //Replace Spaces with a '+' character.
    [urlString setString:[urlString stringByReplacingOccurrencesOfString:@" " withString:@"+"]];

    //Create NSURL string from a formate URL string.
    NSURL *url = [NSURL URLWithString:urlString];

    //Setup and start an async download.
    //Note that we should test for reachability!.
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

    [connection release];
    [request release];
}

We must of course then handle the response of the GoogleMaps server ( Note: a lot of validations missing)

//It's called when the results of [[NSURLConnection alloc] initWithRequest:request delegate:self] come back.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{   
    //The string received from google's servers
    NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    //JSON Framework magic to obtain a dictionary from the jsonString.
    NSDictionary *results = [jsonString JSONValue];

    //Now we need to obtain our coordinates
    NSArray *placemark  = [results objectForKey:@"Placemark"];
    NSArray *coordinates = [[placemark objectAtIndex:0] valueForKeyPath:@"Point.coordinates"];

    //I put my coordinates in my array.
    double longitude = [[coordinates objectAtIndex:0] doubleValue];
    double latitude = [[coordinates objectAtIndex:1] doubleValue];

    //Debug.
    //NSLog(@"Latitude - Longitude: %f %f", latitude, longitude);

    //I zoom my map to the area in question.
    [self zoomMapAndCenterAtLatitude:latitude andLongitude:longitude];

    [jsonString release];
}

Finally the function to zoom my map, which should by now be a trivial thing.

- (void) zoomMapAndCenterAtLatitude:(double) latitude andLongitude:(double) longitude
{
    MKCoordinateRegion region;
    region.center.latitude  = latitude;
    region.center.longitude = longitude;

    //Set Zoom level using Span
    MKCoordinateSpan span;
    span.latitudeDelta  = .005;
    span.longitudeDelta = .005;
    region.span = span;

    //Move the map and zoom
    [mapView setRegion:region animated:YES];
}

Hope this helps someone because the JSON part was a real pain to figure out, the library is not very well documented in my opinion, still it's very good.

EDIT:

Modified one method name to "searchCoordinatesForAddress:" because of @Leo question. I have to say that this method is good as a proof of concept but if you plan to download big JSON files , you will have to append to a NSMutableData object to hold all the query to the google server. ( remember that HTTP queries come by pieces . )

Goles
  • 11,599
  • 22
  • 79
  • 140
  • google geocoding api version is updated. http://code.google.com/apis/maps/documentation/geocoding/ – seapy Mar 24 '11 at 18:14
  • Hi, but you don't solve a situation when there's more than one result on given address if I am reading your code right. – JakubM Nov 30 '10 at 19:34
  • Yeah, it shouldn't be hard to solve given this solution though. – Goles Dec 01 '10 at 15:31
  • 3
    May I suggest using stringByAddingPercentEscapesUsingEncoding: method instead of replacing white spaces by "+"? :) – JakubKnejzlik Jul 10 '12 at 14:28
  • Yeah, that would be better I guess, did this a while ago. :) – Goles Jul 11 '12 at 18:11
  • 2
    one recommendation: use `http://maps.google.com/maps/geo?q=%@&output=csv` ( output in CSV mode ) to avoid parsing JSON. (no need to rely on JSON Framework) – Raptor Oct 31 '12 at 10:01
  • 1
    Well, technically for iOS 5+ JSON parsing is part of Cocoa-Touch :) so it's not that terrible either. – Goles Oct 31 '12 at 14:57
2

Swift version, adapted for iOS 9:

let geocoder = CLGeocoder()
geocoder.geocodeAddressString(addressString) { (placemarks, error) in

    if let center = (placemarks?.first?.region as? CLCircularRegion)?.center {

        let region = MKCoordinateRegion(center: center, span: MKCoordinateSpanMake(0.02, 0.02))
        self.mapView.setRegion(region, animated: true)
    }
}

based on user1466453's answer.

Eric
  • 16,003
  • 15
  • 87
  • 139
2

If anyone else is having the same issue, heres the link: https://github.com/stig/json-framework/ scroll down to Project renamed to SBJson

Also, here is the code for getting all the data before your app uses it. Note the delegate method 'did receive data' as it appends the mutable data object with the downloaded data.

I JUST USED MR GANDOS searchCoodinatesMETHOD AS IT IS AS IT WORKS WELL

- (void) searchCoordinatesForAddress:(NSString *)inAddress
{
    //Build the string to Query Google Maps.
    NSMutableString *urlString = [NSMutableString stringWithFormat:@"http://maps.googleapis.com/maps/api/geocode/json?address=%@&sensor=false",inAddress];

    //Replace Spaces with a '+' character.
    [urlString setString:[urlString stringByReplacingOccurrencesOfString:@" " withString:@"+"]];

    //Create NSURL string from a formate URL string.
    NSURL *url = [NSURL URLWithString:urlString];

    //Setup and start an async download.
    //Note that we should test for reachability!.
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];

    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

    [connection release];
    [request release];
}

// STEP ONE // THIS ONE IS IMPORTANT AS IT CREATES THE MUTABLE DATA OBJECT AS SOON AS A RESPONSE IS RECEIVED

-(void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
    if (receivedGeoData) 
    {
        [receivedGeoData release];
        receivedGeoData = nil;
        receivedGeoData = [[NSMutableData alloc] init];
    }
    else
    {
        receivedGeoData = [[NSMutableData alloc] init];
    }

}

/// STEP TWO // THIS ONE IS IMPORTANT AS IT APPENDS THE DATA OBJECT WITH THE DATA

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{   
    [receivedGeoData appendData:data]; 
}

// STEP THREE...... // NOW THAT YOU HAVE ALL THE DATA MAKE USE OF IT

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSString *jsonResult = [[NSString alloc] initWithData:receivedGeoData encoding:NSUTF8StringEncoding];
    NSError *theError = NULL;
    dictionary = [NSMutableDictionary dictionaryWithJSONString:jsonResult error:&theError];

    NSLog(@"%@",dictionary);

    int numberOfSites = [[dictionary objectForKey:@"results"] count];
    NSLog(@"count is %d ",numberOfSites);      
}

-(void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
{
    // Handle the error properly
}
P.J.Radadiya
  • 1,493
  • 1
  • 12
  • 21
timv
  • 3,346
  • 4
  • 34
  • 43
2

This link helps you if you search a region.

NSMutableString *urlString = [NSMutableString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@?output=json",inAddress];

If you want to search a street this is the corect link

NSMutableString *urlString = [NSMutableString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@&output=json",inAddress];

Notice that the 2nd ? should be &.

Sparkup
  • 3,686
  • 2
  • 36
  • 50
Vali
  • 21
  • 1
  • it took me a while to spot the difference between this two string, but the "&" instead of "?" was extremly helpfull, thanks for that! – pmk Feb 25 '13 at 12:32
1

You can use Google's API service to get lat/long coords from a textual search string. Be sure to pass the user's current location so the results are relevant. Read the answers to this question: Search and display business locations on MKMapView

Community
  • 1
  • 1
William Denniss
  • 16,089
  • 7
  • 81
  • 124