CLLocationManager giving wrong location updates - ios

I am working on a tracking app, which needs to display the tracking path on map. When I start updating location and does not move the device after the installation, the locationManagar reports wrong location updates,
I am drawing a route based on location points reported,
Also when user is driving in a vehicle at high speed on straight road, it shows glitches like this as well,
How can I ignore these erroneous location updates.
You can download the app here and test yourself

Have a look at Horizontal Accuracy check this to make sure you have an accurate location. For example below 10meters or below zero:
// test that the horizontal accuracy does not indicate an invalid measurement
if (lastLocation.horizontalAccuracy < 0)
return;
And make sure you have a new location because it will return the last one default:
// test the age of the location measurement to determine if the measurement is cached
// in most cases you will not want to rely on cached measurements
NSTimeInterval locationAge = -[lastLocation.timestamp timeIntervalSinceNow];
if (locationAge > 5.0)
return;
Also make sure you set the right desiredAccuracy on the CLLocationManager. For example:
_manager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;

Related

iOS: Tracking User Movement 24/7

I need to track when the user is traveling. For this I setup CLLocationManager like this:
self.locationManager.requestAlwaysAuthorization()
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
self.locationManager.distanceFilter = 200
self.locationManager.pausesLocationUpdatesAutomatically = false
self.locationManager.allowsBackgroundLocationUpdates = true
self.locationManager.activityType = .other
So my app gets the location even if it is not running, saves the location in the DB and gets to background again. Later when the user is starting the app the travel is getting extracted from the new locations in the db.
This is working more or less with an energy consumption of ~15%. The position is determined with the help of cell towers and not by gps.
I realized that on some days the tracking is not working for some regions (train tracks). Do you have an idea why this happens? (Only iOS10 devices).
Do you see improvements to reach my goal? Is there anything else that can be used to determine a travel? (A travel is when you commute with more than 15 km/h and more than 15 min).
Thanks
There can be multiple activity type which can help you locate tracks, boats etc. Following is the apple doc on activity type:
public enum CLActivityType : Int {
case other
case automotiveNavigation // for automotive navigation
case fitness // includes any pedestrian activities
case otherNavigation // for other navigation cases (excluding pedestrian navigation), e.g. navigation for boats, trains, or planes
}
You can use self.locationManager.activityType = .otherNavigation
With iOS 12 you can also monitor position using the new activity type:
"Use this activity type if the expected user activity is well above
ground level."
CLActivityType.airborne
As Apple suggest in the reference: https://developer.apple.com/documentation/corelocation/clactivitytype/airborne

iOS location of PHAsset: accuracy is always 0

I'm trying to extract location information from the user's Photo library using PhotoKit. Trouble is, all CLLocation objects have a horizontalAccuracy of 0...
Wether it is PHAssetCollection.approximateLocation.horizontalAccuracy or PHAsset.location.horizontalAccuracy doesn't make a difference.
This information is important in my use case, and I know for a fact that in certain circumstances accuracy can be absolutely dreadful( error radius of more than 500 metres ).
Any insights appreciated!

CoreLocation find distance travelled and use as variable iOS

I am making an alarm that shuts off only after the phone has been moved a certain distance(to force the user out of bed). I need to be able to find the distance travelled after the alarm has gone off, then take this distance and use it in a method to shut the alarm sound off if the minimum distance has been travelled. Any ideas?
I'm using the following to update the location. Any thoughts on how to incorporate this into disabling the alarm?
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
if(!newLocation) {
NSLog(#"No movement");
}
if ((oldLocation.coordinate.latitude != newLocation.coordinate.latitude) &&
(oldLocation.coordinate.longitude != newLocation.coordinate.longitude))
{
CLLocation *loc1 = [[CLLocation alloc] initWithLatitude:oldLocation.coordinate.latitude longitude:oldLocation.coordinate.longitude];
CLLocation *loc2 = [[CLLocation alloc] initWithLatitude:newLocation.coordinate.latitude longitude:newLocation.coordinate.longitude];
CLLocationDistance distance = [loc2 distanceFromLocation:loc1];
if (distance>=4) {
NSLog(#"Very good!");
}
}
NSLog(#"Location:%#", newLocation);
}
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"Could not find location: %#", error);
}
A couple of thoughts:
You're not considering horizontalAccuracy. First if this is negative for either coordinate, then you just can't do any comparison. And even if they're both non-negative, you probably want to back out the horizontal accuracies if you really want to determine if the user moved a certain distances.
Look at not only the latitude and longitude, but the horizontalAccuracy, too.
You're not considering that when you turn on location services, there are frequently a whole series of coordinates that come in, and the coordinates may bounce around as the horizontal accuracy gets better and better. You want to make sure you don't consider this slow triangulation as the movement necessary to qualify as having gotten out of bed.
Make sure you do your experimentation on a real device, in real world scenarios. Using the simulator is definitely not a good test. Nor is using a device plugged into your computer (because you you want to see your GPS in real world scenario, where someone has moved around before going to bed).
I'd suggest starting with a simple app that logs location events in a database (or other format) and perhaps show the log in a tableview, so you don't always have to go back to your computer to review the results. Fire up that app, walk around, and then look at the location events you get. That will help you get your arms around the patterns you see as people use their actual devices.
Note that if this app is destined for the app store, you'll want to engage lots of different people in different environments and different devices and different scenarios (poor GPS locations, with wifi, no wifi, etc.). Even when you do your own real-world GPS experience with a physical device, you must appreciate that everyone else's may experience different GPS results.
I must confess to some reservations whether the iPhone GPS is accurate enough for this sort of app as a generalized solution, and you'll probably want to warn the user to make sure to plug in their iPhone before they go to sleep so the GPS doesn't drain the battery. You might also want to play tricks like turning on location services after the user has demonstrated that they're up and about. You might also not even want to turn on location services until a few minutes before the alarm, thereby assuring that the GPS is as accurate as it can be when the alarm goes off, but not draining their battery as they sleep if they don't happen to have it plugged in and charging.
Update:
Probably easier than calculating distances yourself, use the location services distance filter (i.e. only generate events when the location changes by x meters). For example:
- (void)startLocationManager
{
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = 5.0; // detect when I move 5 meters
self.locationManager.delegate = self;
[self.locationManager startUpdatingLocation];
}
When I do that, standing completely still, as soon as I turn on location services, I get a whole bunch of events before I even start moving. You can see the GPS warming up, and getting more and more accurate:
latitude longitude horizontalAccuracy
---------------- ------------------ ------------------
39.9482837548282 (82.4963494095571) 65
39.9482817585030 (82.4964543834170) 10
39.9482392622539 (82.4964914314290) 5
39.9481753502422 (82.4964918505242) 5
39.9481269028419 (82.4964797805836) 5
I wait for that to quite down (it took 15-20 seconds in my case), and then I started walking in a straight line, looking at the distance from the last location I got above.
latitude longitude horizontalAccuracy distance
---------------- ------------------ ------------------ --------
39.9481722908476 (82.4964962091138) 5 5.3
39.9482206544289 (82.4965029146363) 5 10.4
39.9482627315828 (82.4965282279839) 5 15.4
39.9483157471204 (82.4965248752227) 5 21.2
I must confess that while I didn't measure it, these distances didn't feel quite right, but possibly within the 5 meter tolerance I set up with my distanceFilter.
Anyway, as all of this evidences, the right process is probably going to be
turn on location services, setting the distanceFilter appropriate for your app;
wait for the location to settle down;
make sure the horizontalAccuracy is even plausible for this process to work at all; and
wait for the new location notification based upon the GPS determining that the distanceFilter has been exceeded.
This never will be perfect (e.g. I had to walk a good 6-10 meters before my "5 meter" distanceFilter kicked in, probably a combination of the GPS lagging a few seconds and the horizontalAccuracy), but it might be "good enough for government work."
(By the way, I've changed my coordinates in the above log, so don't look for me in this Ohio cornfield, but it gives you and idea of the sort of pattern you may see.)

Tracking multiple (20+) locations with iOS geofencing

An iOS application uses the geofencing for notifying the user about predefined nearby locations. The application is allowed to miss some location (the user is not getting a notification about a nearby location), but it is desirable to keep the missing rate low.
One way to implement this would be to start monitoring for significant change locations with startMonitoringSignificantLocationChanges and each time the "location change" event is fired, look for locations within, let say, 500m radius of the reported location.
What worries me is the requirement to perform the query for the nearby regions each time the significant location change occurs and it impact on the battery.
The other way to do it would be to register the locations with startMonitoringForRegion but Apple has put a (reasonable) limitation on the number of simultaneously tracked regions which is 20 and we have significantly more than 20 locations. So some sort of dynamic updating of the tracked regions is required but I am still unsure what is the best way to do it.
Any ideas on how can it be done so that it keeps the battery consumption low but also has the low missing rate for locations?
Since there was not much activity on the question I will describe how we are currently solving this problem.
We tied the reloading of the new regions to significant location change (SLC) events. When an SLC takes place, we check for 20 neighbouring regions that should be "geofenced". To find the 20 closest regions we are simply approximating 1'' of the latitude and longitude according to the following formulae:
Latitude: 1 deg = 110.54 km
Longitude: 1 deg = 111.320 * cos(latitude) km
and just check the bounding square of the current position of the device for the centers of the monitored regions (see: Simple calculations for working with lat/lon + km distance?)
So, for example, if (10N,10E) is the current location of the device we start with the bounding square with vertices at (10-1',10-1'), (X-10',10+1'), (10+1',10+1'), (10+1',10-1') (at latitude (10N,10E) one latitude/longitude minute approximates 1,85 km).
If there are 20 (or almost 20) - we register them for the geofencing and wait for the next SCL. If less/more, just increase/decrease the size of the bounding rectangle and repeat the search.
You can tweak this search algorithm for a better performance, but the one described here will already do the job.
You could reserve a location for a "meta-geofence" encompassing all the currently monitored locations. When the user leaves this geofence, the app will be notified. Then the app can update itself and stop tracking the farthest areas and start tracking new areas in the vicinity.
I thought I would add another option for using more than 20 Geofences in your app. This way has been working well in our app for a long time now and uses CLLocation methods that are built-in.
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
if (locations.count > 0) {
CLLocation *location = locations[0];
NSMutableArray *sortedFences = [[NSMutableArray alloc] init];
// add distance to each fence to be sorted
for (GeofenceObject *geofence in enabledFences) {
// create a CLLocation object from my custom object
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(geofence.latitude, geofence.longitude);
CLLocation *fenceLocation = [[CLLocation alloc] initWithLatitude:coordinate.latitude longitude:coordinate.longitude];
// calculate distance from current location
CLLocationDistance distance = [location distanceFromLocation:fenceLocation];
// save distance so we can filter array later
geofence.distance = distance;
[sortedFences addObject:geofence];
}
// sort our array of geofences by distance and add we can add the first 20
NSSortDescriptor *sortByName = [NSSortDescriptor sortDescriptorWithKey:#"distance" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortByName];
NSArray *sortedArray = [sortedFences sortedArrayUsingDescriptors:sortDescriptors];
// should only use array of 20, but I was using hardcoded count to exit
for (GeofenceObject *geofence in sortedArray) {
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(geofence.latitude, geofence.longitude);
CLLocationDistance radius = geofence.radius;
NSString *ident = geofence.geofenceId;
CLCircularRegion *fenceRegion = [[CLCircularRegion alloc] initWithCenter:coordinate radius:radius identifier:ident];
fenceRegion.notifyOnEntry = geofence.entry;
fenceRegion.notifyOnExit = geofence.exit;
[locationController.locationManager startMonitoringForRegion:fenceRegion];
}
}
}
Hopefully this will help someone or steer them on the right path.
If you are concerned about performing the proximity check on each significant location change, you could use a spatial indexing/search method like R-trees or R*-tree to reduce the number of comparisons needed for each location change, as those search algorithms will filter out (possibly large) spatially irrelevant regions. That should reduce the time/battery power needed to perform the proximity checks.
I know this post is old, but for those looking to do something similar, Skyhook offers the ability to geofence an infinite number of venues.
From their marketing:
Skyhook’s Context Accelerator enables app developers and advertisers to instantly deploy Infinite Geofences to any brand chain (such as CVS) or venue category (such as convenience stores) through a simple web interface. Using the same patented technology from Skyhook’s first-party location network, the Context Accelerator SDK manages those active geofences on-device, regardless of OS limitations allowing for infinite geofencing.

distanceFromLocation returns huge value without moving the tiniest bit

I'm trying to call a function in case the distance traveled since last checked is > 30 m. Based on the Class description of CLLocationDistance the value returned is in m.
I use this code:
[locationManager startUpdatingLocation];
if (!startingLocation)
startingLocation = locationManager.location;
// NSLog(#"Latitude:%f Longitude:%f", startingLocation.coordinate.latitude, startingLocation.coordinate.longitude);
updatedLocation = locationManager.location;
CLLocationDistance distance = [updatedLocation distanceFromLocation:startingLocation];
if (distance > 30) {
NSLog(#"%f",distance);
NSLog(#"Latitude:%f Longitude:%f", updatedLocation.coordinate.latitude, updatedLocation.coordinate.longitude);
[self stop];
The console output with NSLog returns 7946754.993111, and I did not even touch the phone.
Any suggestions are appreciated!
8000 km between two points? I'll bet the first point is 0, 0 and you're about 8000 km from there, right?
In any case be sure to only use valid locations by checking to make sure the horizontalAccuracy is >=0 before using the location coordinates. Invalid locations have a negative horizontalAccuracy.
Also, you should probably discard any cached locations by checking the age of the location data and don't use it if it is more than a few seconds old.
See the sample code here that does these checks.
Another thing to watch out for is that you can get locations that show false motion due to changes in accuracy. See this answer for details.
Is it possible that because you are calling startUpdatingLocation then getting that location then immediately asking for an update that the location is still trying to figure out exactly where it is, hence the big movement.
Try calling startUpdatingLocation from another method then implement the delegate method from CLLocationManager locationManager:didUpdateToLocation:fromLocation to get the movement.

Resources