I have a function that utilizes LocationManager for iOS development. The function fulfills the very common purpose of pulling latitude and longitude from the LocationManager object and populating some method variables to build a set of directions.
Currently the method works fine on the iOS simulator when there are some specifically placed breakpoints. I will step into each step until I see that the lat and long have been populated and then continue program execution. The view is correctly altered to bring up a set of directions.
If the breakpoints are removed however, the variables are never populated and the view defaults to 0.000 for lat and longitudes.
I also see an error in one of the threads (I'm completely unfamiliar with thread based programming. This is not my app I am debugging): Thread 1: EXC_BAD_ACCESS (CODE =1, address= {some address}
Below is the function code (asdf is just the instance name for LocationManager):
(void)webViewDidFinishLoad:(UIWebView *)webView
{
asdf = [[CLLocationManager alloc] init];
asdf.delegate = self;
asdf.distanceFilter = kCLDistanceFilterNone;
asdf.desiredAccuracy = kCLLocationAccuracyHundredMeters;
[asdf startUpdatingLocation];
CLLocation *location = [asdf location];
float longitude=location.coordinate.longitude;
float latitude=location.coordinate.latitude;
NSLog(#"dLongitude : %f", longitude);
NSLog(#"dLatitude : %f", latitude);
double userLatitude = latitude;
double userLongitude = longitude;
[locationManager stopUpdatingLocation];
NSString *userLat = [NSString stringWithFormat:#"%f", userLatitude];
NSString *userLng = [NSString stringWithFormat:#"%f", userLongitude];
NSString *destinationLatitude = site.siteGPSN;
NSString *destinationLongitude = site.siteGPSW;
NSString *javascriptString = [NSString stringWithFormat:#"buildMap('%#','%#','%#','%#')",destinationLatitude, destinationLongitude, userLat, userLng];
[webView stringByEvaluatingJavaScriptFromString:javascriptString];
}
You're not using CLLocationManager correctly. You need to implement the CLLocationManagerDelegate. Otherwise there is no guarantee there will be a location object cached by the manager when you try to use it.
The implementation would look something like this:
- (void) locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
// handle error
}
- (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations lastObject];
// use location here
}
- (void) locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
if (status == kCLAuthorizationStatusAuthorized)
{
[manager startUpdatingLocation];
}
}
Remember to call stopUpdatingLocation at the appropriate time as well (whenever you are done).
I would recommend looking at Apple's "LocateMe" sample project which provides some great starting code on how to use this protocol correctly.
https://developer.apple.com/library/ios/samplecode/LocateMe/Listings/Classes_GetLocationViewController_m.html#//apple_ref/doc/uid/DTS40007801-Classes_GetLocationViewController_m-DontLinkElementID_8
Location manager never gets an update as soon as you start it using startUpdatingLocation this is when the delegate methods come into action. Delegate methods are invoked only when a location update is available or the process failed for some reason.
I found that you have set:
asdf.delegate = self;
That means you are registering the current class as a handler for location updates. So you should declare the CLLocationManagerDelegate protocol in the header (.h file) of your class, like following:
#interface <# Your Class Name #> : NSObject<CLLocationManagerDelegate> // your class might have inherited from anything other than NSObject
////......
#end
Now in your implementation file (.m) implement the following methods:
-(void)locationManager:(CLLocationManager*)manager didUpdateLocations:(NSArray*)locations {
if (!locations || [locations count] == 0) {
return;
}
CLLocation* location = locations[[locations count] - 1];
if (location.horizontalAccuracy < 0) {
return;
}
float longitude=location.coordinate.longitude;
float latitude=location.coordinate.latitude;
NSLog(#"dLongitude : %f", longitude);
NSLog(#"dLatitude : %f", latitude);
double userLatitude = latitude;
double userLongitude = longitude;
//[locationManager stopUpdatingLocation]; -- dont stop it here
NSString *userLat = [NSString stringWithFormat:#"%f", userLatitude];
NSString *userLng = [NSString stringWithFormat:#"%f", userLongitude];
NSString *destinationLatitude = site.siteGPSN;
NSString *destinationLongitude = site.siteGPSW;
NSString *javascriptString = [NSString stringWithFormat:#"buildMap('%#','%#','%#','%#')",destinationLatitude, destinationLongitude, userLat, userLng];
[webView stringByEvaluatingJavaScriptFromString:javascriptString];
}
-(void)locationManager:(CLLocationManager*)manager didUpdateToLocation:(CLLocation*)newLocation fromLocation:(CLLocation*)oldLocation { //for backward compatibility
if (newLocation.horizontalAccuracy < 0) {
return;
}
float longitude=newLocation.coordinate.longitude;
float latitude=newLocation.coordinate.latitude;
NSLog(#"dLongitude : %f", longitude);
NSLog(#"dLatitude : %f", latitude);
double userLatitude = latitude;
double userLongitude = longitude;
//[locationManager stopUpdatingLocation]; -- dont stop it here
NSString *userLat = [NSString stringWithFormat:#"%f", userLatitude];
NSString *userLng = [NSString stringWithFormat:#"%f", userLongitude];
NSString *destinationLatitude = site.siteGPSN;
NSString *destinationLongitude = site.siteGPSW;
NSString *javascriptString = [NSString stringWithFormat:#"buildMap('%#','%#','%#','%#')",destinationLatitude, destinationLongitude, userLat, userLng];
[webView stringByEvaluatingJavaScriptFromString:javascriptString];
}
-(void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)error {
NSLog(#"core location error: %#",[error localizedDescription]);
if ([error code] != kCLErrorLocationUnknown) { //when location update is not available for that time don't continue receiving updates
NSLog(#"location service will terminate now");
[manager stopUpdatingLocation];
}
}
After all these set up, your location manager would invoke the appropriate delegate method when an update is available (or failed) and you can stop your location service when you don't actually need it anymore, may be in your viewWillDisappear or dealloc methods.
Related
I have been working to implement route trace map for walking,biking and driving.
However, as you see in the following screenshot, my coordinate jumps all of a sudden from time to time even though I did not walk/bike or drive that location. The circle has been drawn on the image to point out the problem. My question is why all of a sudden coordinates jumps ?
Here is my implementation snapshot:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
CoordinateModel *coord = [[CoordinateModel alloc] init];
coord.latitude = newLocation.coordinate.latitude;
coord.longitude = newLocation.coordinate.longitude;
ActivityType currentActivityType = [DataManager sharedInstance].activityType;
if (currentActivityType == 0) {
// walking
[appDelegate.walkingCoordinates addObject:coord];
}
else if(currentActivityType == 1) {
[appDelegate.bikingCoordinates addObject:coord];
}
else if(currentActivityType == 2) {
// driving
[appDelegate.drivingCoordinates addObject:coord];
}
self.coordinate = newLocation.coordinate;
}
I suggest you not to use the delegate method locationManager:didUpdateToLocation:fromLocation: anymore and it has been deprecated.
You should use locationManager:didUpdateLocations instead.
About your question, the location "jumping" like you mention is due to the GPS that is unable to determine the accuracy of your location during a certain time. If you record down the coordinate and also the accuracy for all the time including when you are indoor, you will realize that the accuracy when you are staying indoor is not good, you might see the accuracy 1414 when you are connected to Wifi. GPS does not work well when you are indoor. So, your code has to be smart enough to only draw a path or send the coordinate to the server when only the coordinate is good enough.
The below code are some of the criteria that I use to filter out the bad coordinates.
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
for(int i=0;i<locations.count;i++){
CLLocation * newLocation = [locations objectAtIndex:i];
CLLocationCoordinate2D theLocation = newLocation.coordinate;
CLLocationAccuracy theAccuracy = newLocation.horizontalAccuracy;
NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
if (locationAge > 30.0)
continue;
//Select only valid location and also location with good accuracy
if(newLocation!=nil&&theAccuracy>0
&&theAccuracy<2000
&&(!(theLocation.latitude==0.0&&theLocation.longitude==0.0))){
self.myLastLocation = theLocation;
self.myLastLocationAccuracy= theAccuracy;
NSMutableDictionary * dict = [[NSMutableDictionary alloc]init];
[dict setObject:[NSNumber numberWithFloat:theLocation.latitude] forKey:#"latitude"];
[dict setObject:[NSNumber numberWithFloat:theLocation.longitude] forKey:#"longitude"];
[dict setObject:[NSNumber numberWithFloat:theAccuracy] forKey:#"theAccuracy"];
//Add the valid location with good accuracy into an array
//Every 1 minute, I will select the best location based on accuracy and send to server
[self.shareModel.myLocationArray addObject:dict];
}
}
}
After a certain period (eg: 3 minutes), I will again choose the best coordinate from self.shareModel.myLocationArray before drawing the coordinate on map and send the coordinate to the server.
You may see the full solution and sample project from here: Background Location Services not working in iOS 7
Don't forget to upvote if my answer is good enough. ;)
Same Problem has been still in the code.
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
iNEAT_o_GamesAppDelegate *appDelegate = (iNEAT_o_GamesAppDelegate *)[[UIApplication sharedApplication] delegate];
CoordinateModel *coord = [[CoordinateModel alloc] init];
ActivityType currentActivityType = [DataManager sharedInstance].activityType;
for(int i=0;i<locations.count;i++){
CLLocation * newLocation = [locations objectAtIndex:i];
CLLocationCoordinate2D theLocation = newLocation.coordinate;
CLLocationAccuracy theAccuracy = newLocation.horizontalAccuracy;
NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
if (locationAge > 30.0)
continue;
//Select only valid location and also location with good accuracy
if(newLocation!=nil&&theAccuracy>0
&&theAccuracy<2000
&&(!(theLocation.latitude==0.0&&theLocation.longitude==0.0))){
coord.latitude = theLocation.latitude;
coord.longitude = theLocation.longitude;
if (currentActivityType == 0) {
// walking
[appDelegate.walkingCoordinates addObject:coord];
}
else if(currentActivityType == 1) {
[appDelegate.bikingCoordinates addObject:coord];
}
else if(currentActivityType == 2) {
// driving
[appDelegate.drivingCoordinates addObject:coord];
}
}
}
}
What is the propery way to obtain CLLocationCoordinate2D?
In .h:
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
In .m
//Called when the location can be obtained
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
//Get the latitude and longitude location
CLLocation *lastLocation = [locations lastObject];
_latitude = [[NSString alloc] initWithFormat:#"%f", lastLocation.coordinate.latitude];
_longitude = [[NSString alloc] initWithFormat:#"%f", lastLocation.coordinate.longitude];
}
Is it this:
_coordinate = lastLocation.coordinate;
or this:
_coordinate = manager.location.coordinate;
First read the documentation as already stated in the comments, because this is a must know for location programming.
Second pay attention that is not the first data you get from delegate, because it could be a cached data (you need to check the timestamp) or an invalid data (with horizontal accuracy less that zero).
You could use that code:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
CLLocation * newLocation = [locations lastObject];
if (newLocation.horizontalAccuracy < 0) {
return;
}
NSTimeInterval interval = [newLocation.timestamp timeIntervalSinceNow];
if (abs(interval)>5) {
return;
}
//YOUR CODE HERE
}
Hope this helps, Andrea
i try to catch the longitude and latitude values and to display them in the console (NSLog) for the moment, i have puted the correspondant code in the viewWillAppear method, any way, it's not a big deal, my problem is when i show the view the first time, my console show me that:
latitude: 0.000000
longitude:0.000000
and the second time it shows me that :
latitude : 134217731.444448
longitude : 0.700000
the third time :
latitude : 134217731.444448
longitude : 0.700000
the n time :
latitude : 134217731.444448
longitude : 0.700000
this my code in the viewWillAppear (i'm sure it's logic is pretty right and i'm waiting for your clarification):
-(void)viewWillAppear:(BOOL)animated
{
// locationManager update as location
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
[locationManager startUpdatingLocation];
CLLocation *location = [locationManager location];
// Configure the new event with information from the location
CLLocationCoordinate2D coordinate = [location coordinate];
NSString *latitude = [NSString stringWithFormat:#"%f", coordinate.latitude];
NSString *longitude = [NSString stringWithFormat:#"%f", coordinate.longitude];
NSLog(#"Latitude : %#", latitude);
NSLog(#"Longitude : %#",longitude);
}
note that i work on the iPhone simulator, thx for help :)))
You shouldn't check coordinate here.
Implement locationManager:didUpdateToLocation:fromLocation: method in the same class and check the location inside this method:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
CLLocationCoordinate2D coordinate = [newLocation coordinate];
NSString *latitude = [NSString stringWithFormat:#"%f", coordinate.latitude];
NSString *longitude = [NSString stringWithFormat:#"%f", coordinate.longitude];
NSLog(#"Latitude : %#", latitude);
NSLog(#"Longitude : %#",longitude);
}
Your current class should implement CLLocationManagerDelegate protocol, i.e. it should be a delegate of location manager.
i use core location to get the longitude and latitude value of my current position and i have displayed it into a label, so for my case it's always 0.000000 for both longitude and latitude, is it because i work on the simulator ??
//longitude and latitude
// locationManager update as location
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
[locationManager startUpdatingLocation];
CLLocation *location = [locationManager location];
// Configure the new event with information from the location
CLLocationCoordinate2D coordinate = [location coordinate];
NSString *latitude = [NSString stringWithFormat:#"%f", coordinate.latitude];
NSString *longitude = [NSString stringWithFormat:#"%f", coordinate.longitude];
Just initialization the location manager doesn't fetch you values.
you have to write this
[self.locationManager startUpdatingLocation];
then you have to override a callback method
- (void) locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *) newLocation
fromLocation: (CLLocation *) oldLocation {
[self.locationManager stopUpdatingLocation];
NSString *latitude = [NSString stringWithFormat:#"%f", newLocation.latitude];
NSString *longitude = [NSString stringWithFormat:#"%f", newLocation.longitude];
[self.locationManager release];
}
Hope that helps!
Shuanak
No. CoreLocation on the iPhone simulator is fixed at 37.3317 North, 122.0307 West (Apple HQ).
Try moving your locationManager code away from your code with the label. Put the location manager code in the viewDidLoad or something and then put this code
CLLocation *location = [locationManager location];
// Configure the new event with information from the location
CLLocationCoordinate2D coordinate = [location coordinate];
NSString *latitude = [NSString stringWithFormat:#"%f", coordinate.latitude];
NSString *longitude = [NSString stringWithFormat:#"%f", coordinate.longitude];
in a function that you call from a button. CoreLocation needs time to get itself going. This is true on the device for sure, but perhaps it is also true on the simulator.
I follow a tutorial from http://www.vellios.com/2010/08/16/core-location-gps-tutorial/
to make an app to show user's current location.
It has a method called locationUpdate
- (void)locationUpdate:(CLLocation *)location {
//locLabel.text = [location description];
speedLabel.text = [NSString stringWithFormat:#"SPEED: %f", [location speed]];
latitudeLabel.text = [NSString stringWithFormat:#"LATITUDE: %f", location.coordinate.latitude];
longitudeLabel.text = [NSString stringWithFormat:#"LONGITUDE: %f", location.coordinate.longitude];
altitudeLabel.text = [NSString stringWithFormat:#"ALTITUDE: %f", [location altitude]];
}
How do i call location.coordinate.latitude outside this method?
Make a variable of CLLocation in your .h File, and then set that variable when the location updates (in the below method).
- (void)locationUpdate:(CLLocation *)location