I am trying to use reverse geocode but the function is not executing and not really sure why..
Here I attached my code. Thank you for your help
//-- reverse geocode, converting from point to address
- (BNRMapPoint *)geocodeLocation:(CLLocation *)location title:(NSString *)title
{
if(!geocoder)
{
geocoder = [[CLGeocoder alloc] init];
}
__block BNRMapPoint * annotation;
[self.geocoder reverseGeocodeLocation:location completionHandler:
^(NSArray * placeMarks, NSError * errors)
{
NSLog(#"in block code");
if([placeMarks count] > 0)
{
NSLog(#"%#", [placeMarks objectAtIndex:0]);
annotation = [[BNRMapPoint alloc] initWithCoordinate:location.coordinate title:title
placeMark:[placeMarks objectAtIndex:0]];
NSLog(#"%#", annotation.placeMark.name);
}
else
{
NSLog(#"failed!!");
}
}];
return annotation;
}
it is not printing anything at console.. so I figured it's not entering the block code.
But I am really confused why it's not entering. Thank you again for your help
I seem to remember having a problem with NSLog in a block passed to reverseGeocodeLocation. The Geocoder executes on a separate thread, as will the block, I expect. (I'm sure someone will correct me if I'm wrong about that). In my case, I think `UIAlertView worked even in the block code, so you might try that ...
I reviewed your code against an app I have that's working with reverseGeocodeLocation and I don't see a problem with your code. The only difference is that I do a check for if (![geocoder isGeocoding])... before I do the reverseGeocode. That's just to keep geoCoder calls from stacking up, which may not be possible in your app depending on how your code gets called.
Possibly self.geocoder = nil?
replace
if(!geocoder)
{
geocoder = [[CLGeocoder alloc] init];
}
with
if(!self.geocoder)
{
self.geocoder = [[CLGeocoder alloc] init];
}
Related
Hai I am writing the code inside the block to get the placemarks of the vehicle. My problem is I stored the placemarks inside the NSMutablearray but i can't access the array outside the block. kindly advice me to overcome this problem. Thanks in advance. My code is below...
CLLocation *someLocation=[[CLLocation alloc]initWithLatitude:latitude longitude:longitude];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:someLocation completionHandler:^(NSArray *placemarks, NSError *error) {
if ([placemarks count] > 0) {
NSDictionary *dictionary = [[placemarks objectAtIndex:0] addressDictionary];
addressOutlet=[dictionary valueForKey:#"Street"];
City=[dictionary valueForKey:#"City"];
State=[dictionary valueForKey:#"State"];
if (addressOutlet!=NULL&&City!=NULL)
{
SubTitle=[NSString stringWithFormat:#"%#,%#,%#",addressOutlet,City,State];
}
else if (addressOutlet==NULL&&City!=NULL)
{
SubTitle=[NSString stringWithFormat:#"%#,%#",City,State];
}
else if (addressOutlet!=NULL&&City==NULL)
{
SubTitle=[NSString stringWithFormat:#"%#,%#",addressOutlet,State];
}
else if(addressOutlet==NULL&&City==NULL&&State!=NULL)
{
SubTitle=[NSString stringWithFormat:#"%#",State];
}
else if (addressOutlet==NULL&&City==NULL&&State==NULL)
{
SubTitle=[NSString stringWithFormat:#"%#",#""];
}
[storeArray addObject:SubTitle];
}
NSLog(#"%#",storeArray);// I can access here
}];
NSLog(#"%#",storeArray);// Here it shows empty array
}
What you should know is the execution-flow.
In ancient programs, the code execution flown always from top to bottom. Anyway, this premise broken a long time ago. Modern programs are built with several components, such as function, class, and blocks(closure), and the program does not always flow top to bottom. (well, though it mostly does)
A block(closure) is one of this non-sequential program. You save a code block into a variable to execute it later. The point is, the code block is not being executed when it is being defined.
What you did here is:
Make an empty array.
Create and pass a code block which fills the array into a method.
Print the array using NSLog.
At the point of #2, the code block is not executed immediately, but will be executed when the operation will be completed. Then, the array is not yet filled when you printed it, then it prints just an empty array.
You should treat the code block defined at #2 will be executed at unknown future point of time, and you really can't control the timing. This is a bad point of asynchronous programming, and something you need to be familiar with.
This is due to block.
The Array you collected is on asynch executed some time after the process happens.
But the outer log gets executed before the completion and collection of data and hence it returns no value and doesn't print anything
[geocoder reverseGeocodeLocation:someLocation completionHandler:^(NSArray *placemarks, NSError *error) {
//Some process collecting values in array
[storeArray addObject:SubTitle];
}
NSLog(#"%#",storeArray);// I can access here
//Do everything here after data gets populated
[tableview reloadData];
}];
//Here no values exist since execuion of this happens before the inner block execution
}
That's because reverseGeocodeLocation:completionHandler method is executed asynchronously, and in your case the second NSLog get called earlier then the first one which is in completion block.
Check this: Obj-C blocks
---------EDIT-----------
You must continue your logic in completion block (i.e. when you call NSLog at first time).
-(void)downloadLocations:(void(^)(void))callback
{
storeArray = [[NSMutableArray alloc] init];
//...
for(int f=0;f<Vehicle_No.count;f++){
//...
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:someLocation completionHandler:^(NSArray *placemarks, NSError *error) {
//...
NSLog(#"%#",storeArray);// I can access here
if (callback) {
callback();
}
}];
}
}
- (void)foo
{
//...
[self downloadLocations:^{
//do something here with storeArray
}];
}
I have an array of coordinates that I step through with a for loop. I would like to place annotations on a map for each location and have the subtitle for the callout be the address of the coordinate, found by using reverseGeocodeLocation In the for loop, I call the reverseGeocodeLocation method, and inside the completion block I create the annotation and display it on the map. However, when I run the app, only one annotation shows up. I went in the debugger, and the completion block is only getting called once (for two calls to the reverseGeocodeLocation method). Any suggestions to fix this?
My for loop:
for(int i = 0; i < [locations count]; i++)
{
CLLocation *location = [locations objectAtIndex:i];
__block NSString *info;
NSLog(#"Resolving the Address");
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error)
{
NSLog(#"Found placemarks: %#, error: %#", placemarks, error);
if (error == nil && [placemarks count] > 0)
{
placemark = [placemarks lastObject];
info = [NSString stringWithFormat:#"%# %# %# %#, %#",
placemark.subThoroughfare, placemark.thoroughfare,
placemark.postalCode, placemark.locality,
placemark.administrativeArea];
[self remainderOfMethod:location withAddress:info atIndex:i];
}
else
{
NSLog(#"%#", error.debugDescription);
}
} ];
}
And the method called at the completion block:
- (void) remainderOfMethod: (CLLocation *)location withAddress:(NSString *)info atIndex: (int)i
{
MKPointAnnotation* annotation = [[MKPointAnnotation alloc] init];
if (location != nil)
{
[annotation setSubtitle:[NSString stringWithFormat:#"%#", info]];
annotation.coordinate = location.coordinate;
[self.mapView addAnnotation:annotation];
}
}
Thanks!
From the official Apple documentation:
After initiating a reverse-geocoding request, do not attempt to
initiate another reverse- or forward-geocoding request
You can find the docs here: https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLGeocoder_class/Reference/Reference.html#//apple_ref/occ/instm/CLGeocoder/reverseGeocodeLocation:completionHandler:
One way to solve it is to do only one request at a time in a recursive method that pops a location from a stack (or array) on each iteration.
Even in that case, consider what Apple has to say about it:
Geocoding requests are rate-limited for each app, so making too many
requests in a short period of time may cause some of the requests to
fail
So you may want to request geocoding on demand, for example when a user taps on an annotation.
I am trying to find a user's location from a coordinate to save into my database.
To find the location name I am using reverseGeocode. However since it is a block method my self.locationName will return (and save as nil) into the database. So I have tried to find a solution to the problem, and tried to put together the following solution using semaphores to try and block until I get a locationName I can save, but the app just hangs when the save button is pressed. Should I even be going about this problem in this way or is there a better way?
dispatch_semaphore_t semaphore;
- (void)reverseGeocode:(CLLocation *)location {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"Finding address");
if (error) {
NSLog(#"Error %#", error.description);
} else {
CLPlacemark *placemark = [placemarks lastObject];
self.locationName = [NSString stringWithFormat:#"%#", ABCreateStringWithAddressDictionary(placemark.addressDictionary, NO)];
dispatch_semaphore_signal(semaphore);
}
}];
}
-(NSString *)findLocation:(CLLocation *)startingLocation
{
semaphore = dispatch_semaphore_create(0);
[self reverseGeocode:startingLocation];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); //should maybe timeout
return self.locationName;
}
You are thinking about this all wrong. That is not how asynchronous code works. Do NOT block until the code returns. Just launch the code to start the reverse geocode, and finish up. Now when the reverse geocode finishes, it calls you back and you can do whatever you want with the returned info. That is the whole point of the completion handler: it doesn't run until the reverse geocode has completed.
Just get rid of the semaphores and let things happen asynchronously. Here is a complete example without the secondary method:
CLLocation* loc = userLocation.location;
[geo reverseGeocodeLocation:loc
completionHandler:^(NSArray *placemarks, NSError *error)
{
if (placemarks) {
CLPlacemark* p = [placemarks objectAtIndex:0];
NSLog(#"%#", p.addressDictionary); // do something with address
}
}];
As you've already been told, if you really want to call this from another method and then do something further, then pass a block to this method and call the block inside the completion handler. That means the block you passed is code that will run when the geocoding has completed, which is exactly what you want - without semaphores and without freezing the app.
Freezing the app is bad form and the WatchDog will kill your app dead if you do it for too long. Just don't do it.
I am fetching GPS information of all my images and they are stored in a Dictionary. I would pass on the lat & long values from this dictionary to the reverseGeocodeLocation function call & store the results in my database.
The problem here is that this function is an asynchronous call & I need to synchronize the whole process for my record to get inserted into the table.
Eg: My array read following coordinates: 32.77003,96.87532. It now calls the reverseGeocodeLocation function, passing on these coordinates as CLLocation object. Now before this async function returns me back the geo-coded location name, next request with new set of coordinates is sent to reverseGeocodeLocation function. This causes inconsistency to insert the record into database.
Is there any way to have this whole task turn synchronous?
i.e Make my for-loop wait until reverseGeocodeLocation returns a value and then move on to next record to be geo-coded?
A bit of my code is here:
for (int imgIdx=0; imgIdx<[imageMetaMutArray count]; imgIdx++)
{
NSDictionary *localGpsDict = [[NSDictionary alloc]initWithDictionary: [imageMetaMutArray objectAtIndex:imgIdx]];
imgLatLoc=[localGpsDict valueForKey:#"Latitude"];
imgLongLoc=[localGpsDict valueForKey:#"Longitude"];
dateStamp=[localGpsDict valueForKey:#"DateStamp"];
timeStamp=[localGpsDict valueForKey:#"TimeStamp"];
if(imgLatLoc && imgLongLoc && dateStamp && timeStamp)
{
CLGeocoder *geoCoder=[[CLGeocoder alloc]init];
CLLocation *currentLocation=[[CLLocation alloc]initWithLatitude:[imgLatLoc doubleValue] longitude:[imgLongLoc doubleValue]];
[geoCoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placeMarks, NSError *err){
if(err)
{
NSLog(#"Reverse geo-coding failed because: %#",err);
return;
}
if(placeMarks && placeMarks.count>0)
{
CLPlacemark *placeMarkers=placeMarks[0];
NSDictionary *locationDictionary=placeMarkers.addressDictionary;
NSString *country=[locationDictionary objectForKey:(NSString *)kABPersonAddressCountryKey];
NSString *city=[locationDictionary objectForKey:(NSString *)kABPersonAddressCityKey];
NSString *state=[locationDictionary objectForKey:(NSString *)kABPersonAddressStateKey];
NSLog(#"logged in from:");
if(city)
{
NSLog(#"city: %#",city);
locName = [[NSString alloc]initWithString:city];
if(state)
{
NSLog(#"state: %#",state);
locName=[locName stringByAppendingString:#","];
locName=[locName stringByAppendingString:state];
}
if(country)
{
NSLog(#"country: %#",country);
locName=[locName stringByAppendingString:#","];
locName=[locName stringByAppendingString:country];
}
}
else
{
if(state)
{
NSLog(#"state: %#",state);
locName = [[NSString alloc]initWithString:state];
if(country)
{
NSLog(#"country: %#",country);
locName=[locName stringByAppendingString:#","];
locName=[locName stringByAppendingString:country];
}
}
else
{
NSLog(#"country: %#",country);
locName = [[NSString alloc]initWithString:country];
}
}
}
else
{
NSLog(#"Placemark Error code:: %lu\n%#",(unsigned long)placeMarks.count,placeMarks);
}
[locName retain];
NSLog(#"location decoded is: %#",locName);
/*Call for Insert into Images table*/
[self insertDataImgTbl:locName];
});
}
}
}
The short answer is that you can't make it synchronous.
What you want to do is move the code that goes on to the next object into the completion block of the reverseGeocodeLocation because that is the soonest that you can submit another reverseGeocodeLocation request. Let me see if I can make some pseudocode here... (that is, I haven't compiled this so it might not be exactly right...)
// in place of the original for loop:
[self reverseGeocodeForIndex:0];
// Doing the reverse geocode is in a method so you can easily call it from within the completion block.
// Maybe your parameter is not the imgIdx value but is instead some object -- I'm just hacking your posted code
// The point is that your completion block has to be able to tell when
// it is done and how to go on to the next object when it is not done.
(void) reverseGeocodeForIndex:(int) imgIdx {
NSDictionary *localGpsDict = [[NSDictionary alloc]initWithDictionary: [imageMetaMutArray objectAtIndex:imgIdx]];
imgLatLoc=[localGpsDict valueForKey:#"Latitude"];
imgLongLoc=[localGpsDict valueForKey:#"Longitude"];
dateStamp=[localGpsDict valueForKey:#"DateStamp"];
timeStamp=[localGpsDict valueForKey:#"TimeStamp"];
if(imgLatLoc && imgLongLoc && dateStamp && timeStamp)
{
CLGeocoder *geoCoder=[[CLGeocoder alloc]init];
CLLocation *currentLocation=[[CLLocation alloc]initWithLatitude:[imgLatLoc doubleValue] longitude:[imgLongLoc doubleValue]];
[geoCoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placeMarks, NSError *err){
// completion block
if(err)
{
// error stuff
}
if(placeMarks && placeMarks.count>0)
{
// what happens when you get some data
}
// now see if we are done and if not do the next object
if (imgIdx<[imageMetaMutArray count])
{
[self reverseGeocodeForIndex:imgIdx+1];
} else {
// Everything is done maybe you want to start something else
}
}];
} else {
// Since you might not reverseGeocode an object you need an else case
// here to move on to the next object.
// Maybe you could be more clever and not duplicate this code.
if (imgIdx<[imageMetaMutArray count])
{
[self reverseGeocodeForIndex:imgIdx+1];
} else {
// Everything is done maybe you want to start something else
}
}
}
And, of course, you can't depend on this being done to do anything else except that you might kick something off when you have reverseGeocoded the last object.
This asynchronous programming can drive you nuts.
An alternative approach could be to place a synchronous request to the following URL, which returns reverse geo-coded results in XML format. You can later parse it, convert to JSON or whatever. The best part: 1) You're force synchronizing the whole process of reverse-geo coding
2) There's no restriction in terms of max requests you can make(I think its 50/min in case of calls to reverseGeocodeLocation handler). If exceeded, you get kCLErrorDomain code 2 error. So we avoid all that by the following approach below. Some sample code that works for me:
-(NSString *)giveMeLocName: (double)gpsLat :(double)gpsLong
{
NSString *finalLocation=[[NSString alloc]init];
//Below is URL we need to send request
NSString *reverseGeoCodeLoc = [NSString
stringWithFormat:#"http://nominatim.openstreetmap.org/reverse?format=xml&zoom=18&addressdetails=1&accept-language=en&lat=%lg&lon=%lg",gpsLat,gpsLong];
NSURL *myLocUrl = [NSURL URLWithString:reverseGeoCodeLoc];
ASIHTTPRequest *myLocRequest = [ASIHTTPRequest requestWithURL:myLocUrl];
[myLocRequest setDidFinishSelector:#selector(reverseGeoCodeImg:)];
[myLocRequest setDelegate:self];
[myLocRequest startSynchronous];
NSLog(#"waiting for location info..");
//Do stuff after receiving results here
}
//This block receives HTTP response as XML(containing reverse geo-coded info. I parse this to JSON using XMLReader class(downloadable)
-(void)reverseGeoCodeImg:(ASIHTTPRequest *)request
{
/*Allocations*/
locDict=[[NSDictionary alloc]init];
revGeoCodePart=[[NSDictionary alloc]init];
addressParts=[[NSDictionary alloc]init];
cityName=[[NSString alloc]init];
stateName=[[NSString alloc]init];
countryName=[[NSString alloc]init];
NSLog(#"starting reverse geo-code!!");
NSString *responseString = [request responseString];
NSError *parseError = nil;
locDict=[XMLReader dictionaryForXMLString:responseString error:&parseError];
[locDict retain];
revGeoCodePart=[locDict objectForKey:#"reversegeocode"];
[revGeoCodePart retain];
addressParts=[revGeoCodePart objectForKey:#"addressparts"];
[addressParts retain];
cityName=[[addressParts objectForKey:#"city"] objectForKey:#"text"];
[cityName retain];
stateName=[[addressParts objectForKey:#"state"]objectForKey:#"text"];
[stateName retain];
countryName=[[addressParts objectForKey:#"country"]objectForKey:#"text"];
[countryName retain];
NSLog(#"city: %#\nstate: %#\ncountry: %#",cityName,stateName,countryName);
}
In the following block of code, the first method calls the second method, which should return the results of a geocoding process:
- (void)foo {
CLPlacemark *currentMark = [self reverseGeocodeLocation:locationManager.location];
}
- (CLPlacemark *)reverseGeocodeLocation:(CLLocation *)location {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
__block CLPlacemark *placeMark;
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
if (!error) {
if ([placemarks firstObject])
placeMark = [placemarks firstObject];
}
}];
return placeMark;
}
However, since the program's execution, doesn't wait for the geocoding to complete before continuing (hence the completion block), there is always a danger that the placeMark variable will be returned uninstantiated before the geocoding process finishes and the completion block is called. I've faced the same predicament when making HTTP requests to web services whose results will not return for an indeterminate amount of time.
The only solution I've seen thus far is to nest the all the code from foo in the geocoder's completion block, which quickly becomes very ugly and difficult to maintain.
What is the best way for the currentMark variable in foo to be set to the result of the second method's completion block without nesting it in the block?
Instead of having the function return a value, just add a callback block to return the value.
Try this:
- (void)reverseGeocodeLocation:(CLLocation *)location withCallback:(void(^)(CLPlacemark *resultPlacemark, NSError *error))_block {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
_block([placemark objectAtIndex:0], error);
}];
}
and then foo would be
- (void)foo {
__block CLPlacemark *currentMark;
[self reverseGeocodeLocation:(CLLocation *)location withCallback:(CLPlacemark *mark, NSError *error) {
currentMark = mark;
}];
}
It seems that the best way to handle this issue overall is to use delegates. That way, the flow of the program isn't obstructed by waiting an indeterminate amount of time for the completion block to return.
Here's a short example of what I've decided on:
- (void)reverseGeocodeLocation:(CLLocation *)location {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
if (!error) {
if ([placemarks firstObject])
[delegate performSelectorOnMainThread:#selector(done:) withObject:[placemarks firstObject];
}
}];
return placeMark;
}
In hindsight, this actually seems rather elegant. The control flow of the main thread (i.e. UI presentation), isn't hindered in any way, and the view controller querying data can essentially be "notified" that data has loaded, rather than directly demanding a returned value.