iOS: get Accurate Reverse Geocoding using CLLocationManager? - iphone

Background: I want to allow users to get their location within seconds when they want it. However, the location is mostly inaccurate. I want to ask the user to if they would like to try again for a more accurate address.
Question: How to code such that it guarantees that the next guess, is always better than the last guess.
What I have tried:
CLLocationManager *locationManager;
- (CLLocationManager *)locationManager {
if (locationManager != nil) {
return locationManager;
}
locationManager = [[CLLocationManager alloc] init];
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[locationManager setDelegate:self];
return locationManager;
}
-(void)myLocationClickedWithCount: (int) count{
[[self locationManager] startUpdatingLocation];
CLLocation *location = [locationManager location];
CLGeocoder *locationGeocoded = [[CLGeocoder alloc] init];
[locationGeocoded reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
if (!placemark){
if (count > 0){
//placemark not available, try again after 1 second
sleep(1);
[self myLocationClickedWithCount:count-1];
}
else{
//Unable to get location after 3 tries
UIAlertView *locationNotFoundAlert = [[UIAlertView alloc]initWithTitle:#"Unable to Locate" message:#"Would you like to try again?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
locationNotFoundAlert.tag = locationNotFoundAlertTag;
[locationNotFoundAlert show];
}
}else{
// got location but not accurate.
if (location.horizontalAccuracy > accuracyRadius) {
UIAlertView *locationInaccurateAlert = [[UIAlertView alloc]initWithTitle:#"Inaccurate Location" message:[NSString stringWithFormat:#"Your GPS shows the radius of %.2f meters. Would you like to try again?",location.horizontalAccuracy] delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
locationInaccurateAlert.tag = locationInaccurateAlertTag;
[locationInaccurateAlert show];
}else {
address.text = placemark;
}
}
}];
}
Update
When the user asks again, it can take a little longer to guess. I just want to know how I should code it. IE: Should I call [[self locationManager] startUpdatingLocation]; again? or will that reset my location so far. Hence, how can i improve my guess the second time based on the current information GPS has (the longer GPS is on, the better accuracy right?).

there is nothing much you can do regarding accuracy via code... apart from
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
which you have already done...
As far as I have seen if you are on 3G or EDGE than your accuracy increases considerably (accuracy level of 5-10m can be achieved).
you can refer this article by Apple which talks how the iphone GPS accuracy works...
hoping this helps..

Related

Posting current location to rails from iPhone

I am building a rails-backed iphone app that uses AFNetworking to create posts at specific locations. So the post model has lat/lng parameters that should be filled in with the client's current location.
At this point, the posts can be made, but the lat/lng comes up as null.
In my (save:) method I pass a conditional to see if a location was found- this is what is failing i.e. "No Location" is logged.
- (void)save:(id)sender {
[self getLocation];
NSArray *locations;
CLLocation *location = [locations objectAtIndex:0];
Post *post = [[Post alloc] init];
post.content = self.contentTextView.text;
post.photoData = UIImagePNGRepresentation(self.imageView.image);
[self.view endEditing:YES];
ProgressView *progressView = [ProgressView presentInWindow:self.view.window];
if (location) {
[post savePostAtLocation:location withBlock:^(CGFloat progress) {
[progressView setProgress:progress];
} completion:^(BOOL success, NSError *error) {
[progressView dismiss];
if (success) {
[self.navigationController popViewControllerAnimated:YES];
} else {
NSLog(#"ERROR: %#", error);
}
}];
} else {
NSLog(#"No Location");
}
}
I have also attempted to implement a locationManager like so
-(void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
[self getLocation];
}
-(CLLocation *) getLocation{
CLLocationManager * locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
self.locationManager.distanceFilter = 80.0f;
[locationManager startUpdatingLocation];
CLLocation * location = [locationManager location];
return location;
}
I think ideally I would implement the savePostAtlocation in the CLLocationManagerDelegate where I could pass in the locations array like this:
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations objectAtIndex:0 /* firstObject */];
if (location) {
[Post createPostAtLocation:location...
But I want to have the post created onSave so I am trying to identify the location but running into some problems..
How do I properly get the current location and pass it into the dictionary?
Any advice on this would be greatly appreciated. Thanks!
Looking at your code, I think you have a slight misunderstanding about how CLLocationManager is designed to work. It looks like you are trying to call [self getLocation] from inside locationManager:didUpdateLocations. This is not correct. Try something like this, inside your save method that is called when you press your button (I would remove the code that's currently in there while testing):
CLLocationManager * locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
self.locationManager.distanceFilter = 80.0f;
[locationManager startUpdatingLocation];
Then it will start generating location data. When that happens, the phone will automatically call locationManager:didUpdateLocations very rapidly. Then, in locationManager:didUpdateLocations you could use:
CLLocation * location = [manager location];
NSLog(#"%#", location);
To see your location data in the console.
What I have written here should get the phone generating location data for you. What you say about createPostAtLocation: in locationManager:didUpdateLocations is probably the correct way to go. When you get the location data, call [manager stopUpdatingLocation] to make the phone stop, then post the location data you got back to your server.

Calculating exact distance in iPhone

I am trying to calculate distance from start using Core Location framework, but when i put the Application on an iPhone device, the data is not correct. Distance keeps on fluctuating and showing random data. Kindly help me out. Also, Altitude is showing zero.
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
//Altitude
if(startingPoint==nil)
self.startingPoint=newLocation;
NSString *currentAltitude = [[NSString alloc] initWithFormat:#"%g",
newLocation.altitude];
heightMesurement.text=currentAltitude;
[currentAltitude release];
//Distance
if(startingPoint==nil)
self.startingPoint=newLocation;
//if(newLocation.horizontalAccuracy <= 100.0f) {
// [locationManager stopUpdatingLocation];
//}
//test start.......................................................
//startlocation
NSString *sp = [[NSString alloc]
initWithFormat:#"%f",startingPoint];
NSLog(#"\nStarting point=%#",startingPoint);
ssp.text=sp;
//endlocation
NSString *ep = [[NSString alloc]
initWithFormat:#"%f",newLocation];
NSLog(#"\nStarting point=%#",newLocation);
eep.text=ep;
//test end............................................................
CLLocationDistance mydistance=[newLocation distanceFromLocation:startingPoint];
NSString *tripString = [[NSString alloc]
initWithFormat:#"%f",mydistance];
distLabel.text=tripString;
[tripString release];
//test........................
[sp release];
[ep release];
}//Location Manager ends..
//Time interval of 3 sec....
-(void)locationUpdate:(NSTimer*)timer{
if(timer != nil) [timer invalidate];
[self.locationManager startUpdatingLocation];
}
As to why your altitude may be zero, please see this answer to a similar question.
This may just be a problem with your NSLog statements, but both the starting and ending points are printed out with NSLog statements that say
NSLog(#"\nStarting point=%#",
The way you seem to have scheduled a 3-second timer is not really the way iOS wants you to use CLLocationManager. The preferred way is to tell CLLocationManager what your location criterion are, and then just start it updating. You don't actually need to keep telling it to start updating every 3 seconds. You can just do it once, and then if you ever decide you don't need any more updates, then call
[locationManager stopUpdatingLocation];
If the OS has no new location information, it probably doesn't make sense to keep asking. It'll tell you when it has new location information, via locationManager:didUpdateToLocation:fromLocation. So, I would recommend starting the process more like this:
CLLocationManager* locationManager = [[CLLocationManager alloc] init];
if ([locationManager locationServicesEnabled]) {
Reachability* netStatus = [Reachability sharedReachability];
if (([netStatus internetConnectionStatus] != NotReachable) || ([netStatus localWiFiConnectionStatus] != NotReachable)) {
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
locationManager.distanceFilter = 100.0; // 100 m, or whatever you want
[locationManager startUpdatingLocation];
} else {
// TODO: error handling
}
}
self.locMgr = locationManager;
[locationManager release];
The location manager will often deliver you multiple results, with increasing accuracy as it hones in on your location. If you're continually restarting it, I'm wondering if that's causing it problems.

Can anyone help me out with using CLLocationManager?

I have two questions. (First) I'm trying to make it where whenever the iPhone moves in location at all, an alert box pops up and shows the longitude where they were, and the latitude where they are now. This is for testing purposes for part of a bigger project I'm working on. Problem is, no alert box is popping up at all. I thought that when the location manager gets a new location, it fires up the delegate which should show the location in an alert box, however nothing is happening at all.
Here is how I'm setting up the Location Manager:
- (void)viewDidLoad
{
locationManager =[[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
[locationManager startUpdatingLocation];
}
Here is the delegate:
-(void) locationmanager: (CLLocationManager *) manager
didUpdateToLocation: (CLLocation *) newLocation
fromLocation: (CLLocation *) oldLocation
{
float oldlat;
float oldlng;
float lat;
float lng;
NSDate *oldtime;
NSDate *newtime;
lat = newLocation.coordinate.latitude;
lng = newLocation.coordinate.longitude;
newtime = newLocation.timestamp;
oldlat = oldLocation.coordinate.latitude;
oldlng = oldLocation.coordinate.longitude;
oldtime = oldLocation.timestamp;
NSNumber *oldlong = [NSNumber numberWithFloat:oldlng];
NSNumber *newlat = [NSNumber numberWithFloat:lat];
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:oldlong
message:newlat
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Submit",nil];
[alert show];
(Secondly) once I'm satisfied that I'm able to track the new and old latlng, does anybody know the best way to store the data? I have researched sqlite, core data, and just using arrays, but i'm still having doubts as to whats the best way to store the lat, lng, timestamp and username that will be generated probably a couple hundred times a day for around 200 users, and then send it to a server.
I know this was a long question, but any insight at all would be greatly appreciated!
Ok. I am really astonished why your application did not crash at this line:
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:oldlong
message:newlat
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Submit",nil];
If try to understand UIAlertView parameters properly you will see initWithTitle and message parameters are of type NSString and what you are passing is NSNumber so take two
NSString for lattitude and longitude and pass them as a parameter like this.
NSString *long1=[[NSString alloc]initWithFormat:#"%f",oldlong];
NSString *lat1=[[NSString alloc]initWithFormat:#"%f",newlat];
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:long1
message:lat1
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Submit",nil];
But showing alert in didUpdateToLocation won't be a good idea as it will constantly gets called so you will have alert pop up each second locking up your interaction. Though this code worked for me. Have a look at screenshot.

Stuck on CLLocation

This is more of a theoretical question than code:
What i did till now :
My app is heavily dependent upon location tracking.
I posted a question about it around 5 hours back without a suitable answer and it is now pretty old to put a new question there .i will closing it after i get an answer for this question.
What i want to do is to have a button that user presses.it makes location manager to start updating locations and then for every 5 feet moved . i want to make iPhone play random sounds.
i implemented correct code given below and now i tested on my device moving from one place to here in my house. through trial and errors i got to know that first few locations are iPhone trying to get an accurate results and are generally to be avoided so i made it a condition to process location which come after more than 2 seconds to previous.
Now my Real ques :
I have my device on wifi.(don't have gps and can't have) so after few initial useless location updates . my alerts stop showing.. i was like : why isn't working . i set it to accuracy best. did filtering to 1.5 meters .. i was moving like 15 feets in the house and no update ?..now i think it is because it is taking locations through wifi and hence it is founding no new locations even if i move 40 feeds around that wifi.? am i correct?
If that is true .. can anyone tell will this work correctly in gps .. for U.S. users..where they have lots of towers.. so that my app will correctly update that new location is at least 5 meters away from its previous location..
Sorry for the long question.. There are too many doubts for me..
-(id) init
{
if(self = [super init])
{
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
// locationManager.distanceFilter = 1.5;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
return self;
}
return nil;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"New Lcoation"
message:nil
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
self.currentLocation = newLocation;
BOOL newLocationIsValid = [self isValidLocation:newLocation withOldLocation:oldLocation];
if(newLocationIsValid && oldLocation)
{
int distance = [newLocation distanceFromLocation:oldLocation];
if(distance >2 && distance <10)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Distance"
message:[NSString stringWithFormat:#"%i meters",distance]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
[fileURl release];
}
}
}
-(BOOL)isValidLocation:(CLLocation*)newLocation withOldLocation:(CLLocation*)oldLocation
{
if(newLocation.horizontalAccuracy < 0 )
{
return NO;
}
NSTimeInterval secondsSinceLastPoint = [newLocation.timestamp timeIntervalSinceDate:oldLocation.timestamp];
if(secondsSinceLastPoint < 0)
{
return NO;
}
if(secondsSinceLastPoint < 2)
{
int distance = [newLocation distanceFromLocation:oldLocation];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Distance"
message:[NSString stringWithFormat:#"%i meters",distance]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
return NO;
}
return YES;
}
yes, doesn't work because you use wifi for locating. The Location-Service just know where your wifi is, not your position relative to your wifi (that would be nice).
so for your service you must use gps. and even then, the accuracy is not garanteed.
see http://www.kowoma.de/en/gps/accuracy.htm
When you are not using the real GPS signal the location is retrieved via triangulation, which is in no way near accuracy you want. Even if there are many WiFi or cell towers the accuracy is still a couple of meter, more like 100 then 10.
The accuracy you want can only be achieved with GPS, which is some cases will only have an accuracy of 100 meters or so. It all depends on the GPS signal quality.

iphone gps giving incorrect location

I want to show user's current location with iphone gps feature but problem is it is giving incorrect location .
When i drop pin on map then it drops pin at exact position but when i try to see the location in text then it gives inaccurate location with inaccuracy of 500 to 800 meters approx.I have used reverse geocoding and google api but all giving same location. PLease tell me why this happens and how can i show the exact location of user ?
My code is:
cllocationmanager *locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
locationManager.desiredAccuracy = kCLLocationAccuracyBest; // 100 m
[locationManager startUpdatingLocation];
}
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
CLLocationCoordinate2D here = newLocation.coordinate;
NSLog(#"%f %f ", here.latitude, here.longitude);
MKReverseGeocoder *geocoder = [[MKReverseGeocoder alloc] initWithCoordinate:here];
[geocoder setDelegate:self];
[geocoder start];
}
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error{
}
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark
{
NSLog(#"The geocoder has returned: %#", [placemark addressDictionary]);
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error" message:[NSString stringWithFormat:#"%#",[placemark addressDictionary]] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
[alertView release];
}
Quick guess: did you check the CLLocation's horizontal accuracy? Quite often the first response I get is very inaccurate, and then subsequent calls get better.
Edit: #pankaj First of all, can you confirm that it is an accuracy issue. If it is the problem that I'm suggesting, then the CLLocation horizontalAccuracy will be large. If not the case then you can ignore my answer and these comments. However, if horizontalAccuracy is a large error then you will have to wait for a better lock. There are two ways to do this:
Wait for a short period of time (a second or two) and see if you get a better lock.
Start requesting location much earlier on, e.g. when the app launches, or when the UIViewController that requires location starts.