Code, Code, Revolution!
The iPhone (and now iPad) SDK supports reverse geocoding out of the box. Reverse geocoding means you have a coordinate and want to find out where you are located, for instance, the name of the street you are on. Forward geocoding means you know the name of a location and want to find the coordinates. For reasons not totally clear to me there’s no support for forward geocoding, I’ve read somewhere that it has something to do with license deals.
I’ve started working a little with the iPad simulator and with its large display it makes sense presenting information on a map view. Because I haven’t found a good example/API for forward geocoding I’ve decided to publish my own. There are a few players offering geocoding services, Yahoo, CloudeMade, Tele Atlas and of course Google. Yahoo and Google are both free but I’ve decided on using Google.
This sample iPad application contains a search bar and a large UIMapView. Search results are visualized on the map with a placemark and by clicking the placemark the map will zoom to the viewport returned from the geocoding service. When I started working with the geocoding service it was still in version two, this last week Google launched version three. Version two will be depricated of course but I had already written the parser for version two so I’ve included it as well in my sample. Of course the API works for iPhone as well. Because the API contains quite a lot of code you will find the code inside the sample project at the bottom of this page.
Using BSForwardGeocoder is pretty straight forward. Example below:
- (void) searchBarSearchButtonClicked:(UISearchBar *)theSearchBar {
NSLog(@"Searching for: %@", searchBar.text);
if(forwardGeocoder == nil)
{
forwardGeocoder = [[BSForwardGeocoder alloc] initWithDelegate:self];
}
// Forward geocode!
[forwardGeocoder findLocation:searchBar.text];
}
-(void)forwardGeocoderFoundLocation
{
if(forwardGeocoder.status == G_GEO_SUCCESS)
{
int searchResults = [forwardGeocoder.results count];
// Add placemarks for each result
for(int i = 0; i < searchResults; i++)
{
BSKmlResult *place = [forwardGeocoder.results objectAtIndex:i];
// Add a placemark on the map
CustomPlacemark *placemark = [[CustomPlacemark alloc] initWithRegion:place.coordinateRegion];
placemark.title = place.address;
[mapView addAnnotation:placemark];
NSArray *countryName = [place findAddressComponent:@"country"];
if([countryName count] > 0)
{
NSLog(@"Country: %@", ((BSAddressComponent*)[countryName objectAtIndex:0]).longName );
}
[countryName release];
}
if([forwardGeocoder.results count] == 1)
{
BSKmlResult *place = [forwardGeocoder.results objectAtIndex:0];
// Zoom into the location
[mapView setRegion:place.coordinateRegion animated:TRUE];
}
// Dismiss the keyboard
[searchBar resignFirstResponder];
}
}When search has executed the geocoder contains the result and a status code. The status code is from Google and to support both version 2 and 3 of the API there’s an enum containing response codes. If everything goes well the status should be “G_GEO_SUCCESS” and the “results” property will contain an array of BSKmlResult objects which contain the location information returned for the query. Here’s an example query for my home town Stockholm (Google geocoding service version 3) : http://maps.google.com/maps/api/geocode/xml?address=stockholm&sensor=false.
The details for the service can be found here: http://code.google.com/apis/maps/documentation/geocoding/.
There are multiple arguments you can pass to the search url, you should read the geocoding documentation and check the search url in the application before you implement this in your own app.
BSForwardGeocoder
#import <Foundation/Foundation.h>
#import "BSGoogleV2KmlParser.h"
#import "BSGoogleV3KmlParser.h"
// Enum for geocoding status responses
enum {
G_GEO_SUCCESS = 200,
G_GEO_BAD_REQUEST = 400,
G_GEO_SERVER_ERROR = 500,
G_GEO_MISSING_QUERY = 601,
G_GEO_UNKNOWN_ADDRESS = 602,
G_GEO_UNAVAILABLE_ADDRESS = 603,
G_GEO_UNKNOWN_DIRECTIONS = 604,
G_GEO_BAD_KEY = 610,
G_GEO_TOO_MANY_QUERIES = 620
};
@protocol BSForwardGeocoderDelegate <NSObject>
@required
-(void)forwardGeocoderFoundLocation;
@optional
-(void)forwardGeocoderError:(NSString *)errorMessage;
@end
@interface BSForwardGeocoder : NSObject {
NSString *searchQuery;
NSString *googleAPiKey;
int status;
NSArray *results;
id<BSForwardGeocoderDelegate> delegate;
}
-(id) initWithDelegate:(id<BSForwardGeocoderDelegate>)del;
-(void) findLocation:(NSString *)searchString;
@property (assign) id<BSForwardGeocoderDelegate> delegate;
@property (nonatomic, retain) NSString *searchQuery;
@property (nonatomic, readonly) int status;
@property (nonatomic, retain) NSArray *results;
@endBSKmlResult
The result class is the same for both version 2 and 3 of the service. A big difference in the returned information between the versions is the way address components are returned, in version three more information is returned and therefore I’ve created another class to store the address component information. The properties: countryNameCode, countryName, subAdministrativeAreaName and localityName are for version two only. For version three all address information is stored in the “addressComponents” array (contains BSAddressComponent objects). Because there is really no reason to use version 2 anymore you probably want to remove this code for your own application.
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#import "BSAddressComponent.h"
@interface BSKmlResult : NSObject {
NSString *address;
NSString *countryNameCode;
NSString *countryName;
NSString *subAdministrativeAreaName;
NSString *localityName;
float viewportSouthWestLat;
float viewportSouthWestLon;
float viewportNorthEastLat;
float viewportNorthEastLon;
float boundsSouthWestLat;
float boundsSouthWestLon;
float boundsNorthEastLat;
float boundsNorthEastLon;
float latitude;
float longitude;
float height;
NSInteger accuracy;
NSArray *addressComponents;
}
@property (nonatomic, retain) NSString *address;
@property (nonatomic, assign) NSInteger accuracy;
@property (nonatomic, retain) NSString *countryNameCode;
@property (nonatomic, retain) NSString *countryName;
@property (nonatomic, retain) NSString *subAdministrativeAreaName;
@property (nonatomic, retain) NSString *localityName;
@property (nonatomic, retain) NSArray *addressComponents;
@property (nonatomic, assign) float latitude;
@property (nonatomic, assign) float longitude;
@property (nonatomic, assign) float viewportSouthWestLat;
@property (nonatomic, assign) float viewportSouthWestLon;
@property (nonatomic, assign) float viewportNorthEastLat;
@property (nonatomic, assign) float viewportNorthEastLon;
@property (nonatomic, assign) float boundsSouthWestLat;
@property (nonatomic, assign) float boundsSouthWestLon;
@property (nonatomic, assign) float boundsNorthEastLat;
@property (nonatomic, assign) float boundsNorthEastLon;
@property (readonly) CLLocationCoordinate2D coordinate;
@property (readonly) MKCoordinateSpan coordinateSpan;
@property (readonly) MKCoordinateRegion coordinateRegion;
-(NSArray*)findAddressComponent:(NSString*)typeName;
@endTo make it somewhat simple to find address components I’ve added a method that will search for components for you. Using version 3 of the geocoding service you will get the country name using this code:
NSArray *countryName = [BSKmlResultPlace findAddressComponent:@"country"];
if([countryName count] > 0)
{
NSLog(@"Country: %@", ((BSAddressComponent*)[countryName objectAtIndex:0]).longName );
}
[countryName release];There are also properties to make the result simple to use with a MKMapView. The “coordinate” property returns a CLLocationCoordinate2D object, “coordinateSpan” calculates and returns a MKCoordinateSpan object for setting the map viewport. The “coordinateRegion” combines both coordinate and coordinateSpan returning a MKCoordinateRegion object that can be used to directly move your MKMapView to the right place.
#import <Foundation/Foundation.h>
@interface BSAddressComponent : NSObject {
NSString *longName;
NSString *shortName;
NSArray *types;
}
@property (nonatomic, retain) NSString *longName;
@property (nonatomic, retain) NSString *shortName;
@property (nonatomic, retain) NSArray *types;This class maps against what’s returned in version 3 of the geocoding service. It’s just a container class.
You will find the API inside this sample project. The code has now moved to GitHub, fork away!
BSForwardGeocoder @ GitHub
Happy geocoding!
With this blog I try to provide useful tips and solutions for programming .NET, Objective-C and more. My name is Björn Sållarp, and I love writing code.
It's now available on AppStore. It's free and open source. Read more about the app here: Swedish / English
Joao Prado Maia
March 15th, 2010 at 4:19 am
Bjorn,
The class looks really interesting, but shouldn’t the functionality be asynchronous?
–Joao
Sijo
March 15th, 2010 at 1:06 pm
nice example…thanks for sharing..
Björn Sållarp
March 15th, 2010 at 8:23 pm
@Joao,
You are right, asynch is much better. I’ve updated the post and sample with asynchronous geocoding. Thanks for your feedback!
// Björn
Armindo
March 17th, 2010 at 5:39 pm
Really great sample, it saves me a lot of time ! Thanks for sharing.
Hope google won’t change too many times their version.
Armindo
March 19th, 2010 at 3:59 pm
Dear Björn,
you have a NSString called “googleApiKey” that is never used. What should I do with it ?
in the source it is not allocated but released, that may cause a problem.
I have also a compiler warning using SDK 3.1.3 here :
if(parseError != nil)
{
if([delegate respondsToSelector:@selector(forwardGeocoderError:)])
{
[delegate performSelectorOnMainThread:@selector(forwardGeocoderError:) withObject:[parseError localizedDescription] waitUntilDone:NO];
}
}
else {
if([delegate respondsToSelector:@selector(forwardGeocoderFoundLocation)])
{
[delegate performSelectorOnMainThread:@selector(forwardGeocoderFoundLocation) withObject:nil waitUntilDone:NO];
}
}
Don’t know if it is ok on SDK 3.2.
on my iPhone device I get a lot of crahes when I close the MapViewController, but everything ok on Simulator
Again, I don’t know if it is related to SDK or not.
Regards
Armindo
Armindo
March 19th, 2010 at 5:00 pm
Dear Björn,
I can confirm you that the crashes come from the warning part.
I can also sometimes get the error in the simulator.
In fact after adding geocoding in one of my app, the main view is not the mapping view. And after shwing/closing a few time the view I have the error.
forwardGeocoder = [[BSForwardGeocoder alloc] initWithDelegate:self]; doesn’t pass a delegate , and it may create a problem later.
Here’s the kind of message I get :
*** NSInvocation: warning: object 0×5730060 of class ‘_NSZombie_GeocodedMapViewController’ does not implement methodSignatureForSelector: — trouble ahead
2010-03-19 16:39:56.747 MyApp[6350:207] *** NSInvocation: warning: object 0×5730060 of class ‘_NSZombie_GeocodedMapViewController’ does not implement doesNotRecognizeSelector: — abort
Hope this help
Armindo
Björn Sållarp
March 20th, 2010 at 10:26 am
@Armindo.
The google key is a leftover from when I started developing, Googles geocoding service doesn’t require a key anymore, not even version 2. You can just remove the google key bits.
On your problem, it’s very hard for me to know what the problem is in your code. are you sure your map view implement the delegate protocol? If you find the problem and it’s related to my code, please post a comment with details and I will update the sample.
Regards,
Björn
Rui Lopes
March 20th, 2010 at 7:24 pm
Hi,
It does not work on iphone 3.1.3.
Erro: performSelectorOnMainThread:withObject:waitUntilDone:’ not found in protocol(s)
Can you help me?
Rui.Lopes@me.com
Armindo
March 21st, 2010 at 10:00 am
Rui,
it’s not really an error but a warning, I have downloaded 3.2 and same problem. and this result in a unstable version. Maybe a casting solve solve the problem. Will look at it and post something if I solve the problem. I’m quite new to iPhone dev.
Regards
Björn Sållarp
March 21st, 2010 at 12:53 pm
Hey guys. I used some sunday time to update the sample code and remove the warnings. I’m sorry I missed the warnings in my first release but the warnings were not really causing problems with the application. I’ve also made some changes in memory management to remove the issued reported by running an analysis.
Oh yeah, I removed the google key bits as well.
// Björn
Armindo
March 21st, 2010 at 2:53 pm
Thanks Björn,
much much better now
Don’t forget to remove the google key in BSForwardGeocoder too.
Have a nice week-end.
Brendt
March 24th, 2010 at 10:16 am
Thanks heaps for this code Björn, it is exactly what I was looking for. Unfortunately I can only get it to run on iPhone Simulator 3.0 but no later SDKs. It runs with no errors or warnings and opening in the simulator but quits straight away.
Any ideas?
Björn Sållarp
March 25th, 2010 at 7:48 am
@Brendt
I used iPhone SDK 3.2 beta5. Does the sample app not run, or did you copy the code into your own project and that fails to run?
Ben
March 26th, 2010 at 2:31 am
Is there a quick adaptation that is needed to make this run on iPhone? I tried it without any modifications (other than setting the SDK to 3.1.3) and it immediately crashes with an invalid region error: ‘Invalid Region ‘
Thanks in advance for assistance. I am downloading the 3.2 beta 5 now to see if the problem is the simulator.
bh
Ben
March 26th, 2010 at 2:32 am
P.S. something edited out the substance of the error message I posted, so here it is with the angle brackets removed: ‘Invalid Region [center:+82.75106922, -90.00000000 span:+170.10225756, +360.00000000]‘
Armindo
March 28th, 2010 at 4:55 pm
Ben,
I run it on iPhone without problem…
nono
April 7th, 2010 at 12:00 pm
Cool job
Deeepak Patel
April 10th, 2010 at 10:25 am
thanks for the wonderful samples. I recently implement it in one of my iPad Apps and I am not able to tap on pin (custom placemark). Is this something I missing ? Also I did not get that How this find current location when app open in iPad. As per my knowledge iPad doesn’t have GPS function and I also didn’t see and corelocation use. Can you please explain me. I would very appreciate if you can help me.
Björn Sållarp
April 10th, 2010 at 11:00 am
@Deeepak
Does nothing happen at all when you tap the placemark? If you are zoomed out and tap a pin it should zoom in to that location. If you are already zoomed in you might not notice that anything happens. In the sample I log when a pin is tapped, check the sample and compare with your app and also check your gdb for log output.
I am not 100% sure how the positioning works in MKMap framework, but you can position a GPS-less device using WiFi depending on if a company called SkyHook scans the area you are in. It is also possible to position a person somewhat accurately from an IP, check any porn site and they will likely show you girls from somewhere close to where you live
. I do know the iPad supports WiFi (SkyHook) positioning because when I got to play around with an iPad at work yesterday it did position me at our old office that we moved out of two months ago.
You can find out if your area is scanned here: http://www.skyhookwireless.com/howitworks/coverage.php , so the bottom line is that you can’t rely on the iPad to find your correct location. I’m sure the 3G model will provide GPS like functionality either by real GPS or by cell tower triangulation.
Good luck with your app!
xiaoke
April 11th, 2010 at 9:02 am
it is what I just looking for. thanks
Deeepak Patel
April 14th, 2010 at 7:00 am
Hi,
Thanks for the response. Here is the explanation.
I load the app and search for the location california. It dropped the pin on Map at California. So now when I single tap on the pin placemark and it doesn’t do anything. I also checked the console for the log but it doesn’t have anything when tapped. If I double tap placemark then it zoom out the map. But I need to show annotation when single tap on the placemark.
I checked in the sample code as well and it also doesn’t do any thing when single tap on placemark.
Please explain me what should I need to do or if I am wrong at any part of the code. I would appreciate your reply.
Thanks & Regards,
Deepak Patel
Deeepak Patel
April 14th, 2010 at 7:02 am
In Addition, You wrote some code for placemark selected in following function.
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context;
But it never calls in the scope of the project whether I tapped on placemark.
Spanish Joe
April 16th, 2010 at 10:48 pm
I found an error in BSGoogleV3KmlParser.m in the name of an XML element
else if ([elementName isEqualToString:@"formattedAddress"])
should be
else if ([elementName isEqualToString:@"formatted_address"])
Thanks for the contribution!
ryan
April 18th, 2010 at 8:59 pm
Thank you very much for posting this. It’s of great help to us! Really appreciate it!
Tom
May 24th, 2010 at 2:46 pm
Thank you very much for this terrific piece of code, this is exactly what I was looking for! Very cool
Anna Billstrom
May 25th, 2010 at 11:23 pm
Thanks Björn, this is just what I was looking or this weekend- we are using historical data with no lat/long and need to do a lookup. Can’t wait to implement, and thanks so much for building.
Matteo
May 26th, 2010 at 2:37 pm
Thanks Bjorn, this is great!
I was looking to implement Google geocoding myself using the JSON-framework but you save a lot of time.
Thanks again and keep up the good job!
Stefan Klumpp
May 27th, 2010 at 11:52 am
Thanks a lot Björn for all the work and effort you put into this and providing it to the public. This saved me an incredible time of work.
The library works perfectly and the sample is very easy to understand.
Nino D'Aversa
May 28th, 2010 at 2:23 am
You sir are awesome!
Thanks for this, going to save me some time writing my own parser.
ND
Oscar
May 28th, 2010 at 10:49 pm
Hello!
Thank you very much for your code and article. I’ve been asking about if google’s geocoding service is free, and someone from google tell me that I have to pay for use.
Are you sure that google’s geocoding service is free?
SM
May 30th, 2010 at 7:38 pm
Thanks for the tutorial! Its a great help.
Björn Sållarp
May 30th, 2010 at 8:45 pm
First of all, I don’t take any responsibility for code posted on this blog. That being said, I havn’t investigated the legal issues using Googles forward geocoding API. They do provide it without the need for a licence key since version 3 and when used together with Google maps in the iphone i can’t see how it would be any different from using the forward geocoding API on the web using the javascript API, which is OK. On the other side, if Google was OK with it, why isn’t it already included in MapKit? It’s obvious that a lot of people want to forward geocode.
Because the API calls are coming from the iphone Google won’t be able to block the request coming from your app. But Google could still hunt you down if you publish your app on the appstore and they get really pissed off.
If you find out what Google has to say about it, please leave a message.
// Björn
noname
June 9th, 2010 at 10:06 am
This is awesome and helped my project a lot. Thank you very much!
Somebody
June 9th, 2010 at 10:12 pm
What’s happening here?
“_OBJC_CLASS_$_BSForwardGeocoder”, referenced from:
objc-class-ref-to-BSForwardGeocoder in RootViewController.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
Björn Sållarp
June 10th, 2010 at 8:45 am
@somebody. I have no idea, which version of Xcode are you using? The sample hasn’t been tested with the latest version release a day or so ago. However, a new xcode version shouldn’t break the sample.
Armindo
June 11th, 2010 at 10:26 am
Hi Björn,
When compiling for IOS 4 it raises a warning in BSGoogleV2KmlParser.h, warning: class ‘BSGoogleV2KmlParser’ does not implement the ‘NSXMLParserDelegate’ protocol.
Regards
Björn Sållarp
June 20th, 2010 at 2:19 pm
@Armindo,
Don’t worry about the V2-parser. You can exclude that from your project. Google released V3 quite a while ago so you should use the new API instead of the old.
Peter Nash
June 21st, 2010 at 12:36 pm
Hi Björn,
Great stuff and thanks for sharing. I thought about using Google’s JSON response instead of the XML parser as it’s quicker to download and parse – why did you choose XML?
Thanks
Peter
Greenflame
July 5th, 2010 at 1:31 am
Hey Björn!
Thanks so much for this tutorial, and for the sample code! This is exactly what I needed! Keep up the good work!
<3
Fauad Anwar
July 13th, 2010 at 8:00 am
Hi,
I am confused on topic about key.
I am developing an application which show current location and info near about my location.
Is it require for me to obtain key for it?
When i run application in simulator it point my location to some infinite loop place not my location. (I am using Interface builder to show my current location). Is this due to simulation and it will work properly on iPhone??
Also how can i get traffic info from Google and show it in my map?
If possible give reply or give some sample code.
(last time i have created a API key, it works for my app. that time. But same code not working today for me.) Giving me error invalid parameter where i called url with my key.
Thanks in Advance.
Björn Sållarp
July 17th, 2010 at 10:23 am
@Fauad,
The example in this post is only for forward geocoding, meaning, you know the name of a location and want the coordinates of that location. Google maps API v3 doesn’t require an API key but other services do, you probably want to use the new Places API (still in preview). Check out Googles services here:
http://code.google.com/intl/sv-SE/apis/maps/documentation/webservices/index.html
User location in the simulator only shows a somewhat correct location if you’re on a WLAN that has been scanned by Skyhook, otherwise your location is set to California USA. This will work on the iPhone.
You can find more information on traffic information here:
http://code.google.com/intl/en-US/apis/maps/documentation/javascript/overlays.html#TrafficLayer
I’m not sure if you can use it for the iphone.
Good luck with your project!
Gerald
July 22nd, 2010 at 7:35 pm
Thank you very much. This awesome.
Gerald
Gonso
July 29th, 2010 at 1:26 am
Great Implementation!
I’ve added this to a project and its working like a charm!
Hi quality work.
Gonso
Victor
August 13th, 2010 at 11:15 pm
This is great! I don’t think the BSKmlResult contains all the results it claims to, though (esp for version 3 of the API)… I’m too much of a newbie to figure it out myself, though.
If I override the description in BSKmlresult.m to see what’s coming out:
- (NSString *)description
{
NSString *descriptionString =
[[NSString alloc] initWithFormat:@”address %@ countryNameCode %@ countryName %@ subAdministrativeAreaName %@ localityName %@ viewportSouthWestLat %d viewportSouthWestLon %d viewportNorthEastLat %d viewportNorthEastLon %d boundsSouthWestLat %d boundsSouthWestLon %d boundsNorthEastLat %d boundsNElon %d latitude %d longitude %d height %d accuracy %d NSArray addressComponents %@”, address, countryNameCode, countryName, subAdministrativeAreaName, localityName, viewportSouthWestLat, viewportSouthWestLon, viewportNorthEastLat, viewportNorthEastLon, boundsSouthWestLat, boundsSouthWestLon, boundsNorthEastLat, boundsNorthEastLon, latitude, longitude, height, accuracy, addressComponents];
return [descriptionString autorelease];
}
then if I add a description after the results are obtained in viewController.m:
BSKmlResult *place = [forwardGeocoder.results objectAtIndex:i];
NSLog(@”subclassed description (messy): %@”,place);
Looking in the console: For version 2, I don’t get lat/lon/height/accuracy (in addition to the ones you list above as not being returned in ver 2). For version 3, I don’t seem to get lat/lon/height/accuracy or the addressComponents array. Also in ver 3, the annotation titles don’t set correctly because ‘address’ is null (in addition to the ones missing that we expect).
Victor
August 17th, 2010 at 10:04 pm
Think I figured out the issue. Line 220 of BSGoogleV3KmlParser.m should be changed from:
else if ([elementName isEqualToString:@"formattedAddress"]) {
to:
else if ([elementName isEqualToString:@"formatted_address"]) {
then I think the annotation title will set correctly.
Davide Benini
August 20th, 2010 at 9:55 am
I second Victor’s suggestion. I am using your wonderful classes in a project of mine, and also had noticed the address was missing from the BSKMLResult; by applying Victor’s patch everything is fixed.
At any rate, thanks a ton for this classes Björn. If you ever come to Verona, Italy, there’s a pint for you here.
Jorge
August 26th, 2010 at 12:04 am
the example is great, thanks a lot for sharing.
all i wonder: is it allowed to use the google api for a iphone-app?
if one wants to retrieve the locations, but not immediately show it on a google map – maybe at some point later.
and what if apple switches the maps to some other provider one time?
i’m not sure how to understand the google terms, when it comes down to a (commercial) iphone-app…
Björn Sållarp
August 26th, 2010 at 8:14 am
@Jorge, I am unable to correctly answer the legal questions, but my assumption is that it’s probably not ok to use their API in a commercial app for free. There must be a reason other than laziness that it’s not included in MAPKit.
If you do use it however it won’t matter if Apple replace the maps. Google return standard wgs84 coordinates that apply to any map.
Ariel Krieger
September 3rd, 2010 at 4:40 pm
Hey Bjorn,
First let me just say WOW. That is some great code you put up here…thanks!
I was wondering though, can your code be modified in some way to calculate distance between two searched locations? Perhaps using Google’s Directions API?
I thought maybe using your observeValueForKeyPath function which spits out a coordinate.latitude value for each selected annotation…
Thanks again
Iulian
September 13th, 2010 at 1:01 pm
You should change your v3 parser to get formatted_address not formatedAddress … it doesn’t work otherwise
Raj
September 23rd, 2010 at 11:15 pm
Maybe I’m missing something but Google geocoding API’s terms of service doesn’t allow for caching of the results. In fact, it’s says that query results have to be directly mapped and not stored. So how did you get around this? (I haven’t had a chance to look at the code yet in detail.)
Björn Sållarp
September 23rd, 2010 at 11:37 pm
@Raj
I “got around it” by mapping them directly. Be aware that this is just code to use Googles’ service, it doesn’t automatically give you the rights to use their services any way you like. If you’re concerned about breaking terms etc you should really read them and perhaps contact google and ask to use it.
Aaron McDonald
September 26th, 2010 at 2:05 pm
When I call forwardGeocoder’s findLocation location method within a for loop, the results are very erratic. If I have 3 different addresses to loop through, typically it will place 3 pushpins on one address instead of on each of the addresses.
Has anyone gotten this to work consistently within a loop?
Stan
September 27th, 2010 at 10:21 am
Hi,
I’m using your library.. It runs very well when I use the simulator, but when I make some tests with my device, I often have this error G_GEO_TOO_MANY_QUERIES
I don’t know how to manage this error and why it happens?
Do I need an google API key ?
Stan
Björn Sållarp
September 27th, 2010 at 10:06 pm
@Stan. Are you using V2 or V3? There are limits on how many lookups you can make per day and there also seem to be a limit to how many requests you can make in a row. If you google the error you find a lot of information however, many seem to be guessing around what limits there are and how they are triggered.
Stan
September 27th, 2010 at 11:57 pm
Björn,
I m using the V3 (hope you are talking about this variable “int version = 3;”
The error code doesn’t always appear and if I perform the same lookup, with the same address, a couple seconds after, I get the correct map….
I’m also confused about the google API key.. I used to use it for a webapplication and to get a key I had to send my website url.. but how does it work with Iphone device ?
Stan
Anook
October 1st, 2010 at 10:14 am
Good job, thanks!
Ryan
October 7th, 2010 at 11:37 pm
I found that I needed to check to see if particular result types were returned so I updated the BSKmlResult class to include an NSString *type, you’ll notice in the returned xml from google there is also a ‘type’ associated with the result. In the BSGoogleV3Parser.h I added a BOOL inAddressComponent that I initialized in BSGoogleV3Parser.m to NO. In the ‘didStartElement’ I update the BOOL to YES after the element name is address_component and update the BOOL to NO in the ‘didEndElement’ for address_component.
This way in the:
else if ([elementName isEqualToString:@"type"]){
if(inAddressComponent){
[typesArray addObject:elementValue]
} else {
currentResult.type = elementValue;
}
}
Hope this helps someone else!!
Christoph
November 6th, 2010 at 1:33 pm
Really great tutorial and really helpful Blog !!!!
klemens
November 12th, 2010 at 6:38 pm
great lib!
thx for your effort, and many thanks for publishing it.
can’t find a BSForwardGeocoder code repository. why not pushing it to github, google code or something like that?
this way other people (like me) could contribute patches.
regards,
klemens
Björn Sållarp
November 17th, 2010 at 12:01 pm
klemens,
I’ve put this code on Google Code now, here’s the url: https://code.google.com/p/bsforwardgeocoder/
Dima Gutzeit
November 18th, 2010 at 9:10 pm
Excuse me for a noob question but
what are the advantages of using use this API over direct usage and not MapKit directly ?
Björn Sållarp
November 18th, 2010 at 9:21 pm
@Dima,
MapKit include reverse geocoding, not forward geocoding.
nick velloff
December 2nd, 2010 at 5:46 am
Thanks so much for this example. This worked perfectly.
I removed all of the v2 bits and the key declaration and release.
There is a warning pertaining to the v3 parser:
…/bsforwardgeocoder-read-only/Classes/BSGoogleV3KmlParser.m:35: warning: class ‘BSGoogleV3KmlParser’ does not implement the ‘NSXMLParserDelegate’ protocol
I’m sure I can just implement the protocol but I wanted you to be aware of this.
Thanks for sharing this!
Faire de la géolocalisation | iOsDev
December 4th, 2010 at 11:15 am
[...] Heureusement, il y a plusieurs solutions en termes de service sur internet. Notamment le service de forward geocoding de Google. Il y a même quelques classes bien pratique pour trouver la localisation d’une adresse grâce à Björn Sållarp. [...]
Robert
January 18th, 2011 at 12:28 pm
Thanks very much Björn. Your code works nicely and has been of great use.
randomdude
January 23rd, 2011 at 1:29 pm
thanks, this saves a lot of time.
I just would like to point out a small mistake in your parser code:
one of the elements in a kml result is “formatted_address” and not “formattedAddress”. Your parser is looking for “formattedAddress” which is never there, so kmlresult.address ==nil…
other than that, seems fine. Thanks again.
Min
January 28th, 2011 at 7:58 pm
Björn, how come BSAddressComponent.h and .m have a different copyright? Specifically, copyright to Apple Inc.
Thanks
Björn Sållarp
January 28th, 2011 at 8:23 pm
@Min,
That header-comment is the XCode default, I forgot to copy/paste my usual no-copyright header. I will update the disclaimer when I get back from my vacation. Sorry about that!
Min Xu
January 29th, 2011 at 12:24 pm
Thanks for the update. Appreciate it very much. What with the Android/Java header copyright line issues, I figure I better pay attentions there too
mike
March 2nd, 2011 at 2:29 am
i’ve tried to put this into my project and it always crashes when releasing the pool in startGeocoding().
i cant seem to figure it out – has anyone else had this problem.
i’m using SDK 4.1
i’ve tried making my app do nothing but run this code, but to no avail.
i’m tempted just to write my own from scratch.
mike
March 6th, 2011 at 12:22 am
is there a reason the searchbar delegate points to the mapView?
Is this necessary? My searchbar is not in the same view as the UIMapView. I have the searchbar delegate in my own viewcontroller, then I’m calling the map setRegion function from there.
could this be causing my app to crash?
mike
March 6th, 2011 at 1:32 am
well got it working.
i copied the code from the sample application.
the code from googlecode worked but only using 3.2 SDK. using the 4.1 SDK it crashed.
Björn Sållarp
March 6th, 2011 at 3:40 pm
@mike,
If you look at the code the delegate for the searchBar is set in Forward_GeocodingViewController which overrides IB. The delegate in IB should be removed to avoid confusion.
I just ran the sample with the latest iOS (4.3) and it still works. The problem you’re having in 4.1 is probably your problem, not the code.
natwar
April 6th, 2011 at 1:30 pm
how to use it for multiple requests
Paul
July 12th, 2011 at 2:10 pm
Thank you for your Tutorial. When I use your Code in an iPhone-Project one error appears. “Could not found iPhone OS”
Could please upload a working iPhone Version?
Thank you very much.
Paul
naresh
July 17th, 2011 at 5:51 pm
hi Björn Sållarp thank u so much at first for providing this useful code for us. k i use this in iphone application development and its working fine over the simulator but when i installed it over the device it does not returning any values only empty array it showing (actually i use this inorder to get the locations into the array with out mapview). and my ipod version is 4.0 that is the reason i am thing ok any way can u provide version 4.0 forward geocoder api as well as the required code . thank u in advance . if i will receive the required data from ur end it will be so useful pls try to provide the information to me as early as possible.
Andrew
July 18th, 2011 at 6:16 am
this is a newb question (because i’m a newb), but what is the easiest way to implement your project into mine? there’s so many components/folders i’m not sure what’s the best way to do it @_@
naresh
July 18th, 2011 at 6:21 am
hi bjornsallarp thank u for publishing this useful code to the public. i am also one of the person who got support from your code but here i am facing one problem that is actually i want to use this for i iphone without mapview means i would like to get the locations informations that’s all . its working over the simulator but not working over the device my device version is ios 4.0. so can u provide any solution for my problem….
Björn Sållarp
July 19th, 2011 at 11:44 am
@Paul, the code does work. Did you move the code into your own project or did you launch my sample? My sample is an iPad project and you are likely to have to change the base sdk to run it. Good luck!
Björn Sållarp
July 19th, 2011 at 12:06 pm
@Andrew,
Are you using git? If so, look into adding it as a submodule. If not.
Make a folder (a real one) in your project, ie BSForwardGeocoder, and put the files in the classes folder into it. Then add the files to your Xcode project either as a folder reference or create a group and just add the files. I prefer folder reference as it automatically includes all files in the folder.
Best solution is to use git and add my code as a submodule. That way you can easily pull updates to my code into your project.
Andrew
July 23rd, 2011 at 9:21 pm
i’m not sure how to do a submodule, but i followed your second option and it works great! thanks for the awesome geocoder
Navnath
September 7th, 2011 at 8:26 am
thank you so much Björn Sållarp for the code. I am using this in my project
BP
September 8th, 2011 at 9:27 pm
Thanks Björn for the code, it works great.
Sime the man
October 20th, 2011 at 12:43 am
Brilliant! Works like a dream. Thank, Björn.
AS
October 26th, 2011 at 3:43 pm
Works perfectly, thanks Björn.
AS
November 10th, 2011 at 12:05 am
Björn, how would you create a Google Maps Query to search for POI’s? I can’t seem to figure it out. Any help would be greatly appreciated. Thanks,
AS
DB
November 22nd, 2011 at 6:33 am
Have you tried searching for “New Zealand”? The map crashes when trying to set the region. I believe the reason is because the longitudeDelta is -327.216797. Will look into it.
Greg
December 9th, 2011 at 2:18 am
Hi Bjorn – is it possible to extend this to a “select location” type UI approach for the user where they can either (a) type in a name, and/or (b) move the placemarker? For example they may type in a place and get close, then on the map adjust the location by moving the marker, and then getting back a final lat,long. So a I guess (a) is this possible, and (b) roughly what would be involved at a high level…?
Tom
December 9th, 2011 at 3:47 am
Björn,
i’ve tried to do this thing with JSON and got stuck with problem with JSON which unsolved. thanks for this class very much!!!!