CLLocationManager sometimes provides wrong coordinates on iOS 13 - cllocationmanager

Sometimes location manager gives GPS-point, which is located about hundreds meters, or several kilometers away from real device location. After it location manager continues provide us with wrong coordinates, which slowly became closer to real device location.
Such points have quite good accuracy about ~30 - ~65 meters, and some of them have speed > 0 m/s.
real path was:
Authorization status is "Always allow"
location manager is adjustment:
CLLocationManager *manager = [CLLocationManager new];
manager.allowsBackgroundLocationUpdates = YES;
manager.pausesLocationUpdatesAutomatically = NO;
manager.showsBackgroundLocationIndicator = YES;
manager.distanceFilter = 50; // meters
manager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
manager.delegate = self;
This problem is actual only for iOS 13. I made about 50 experiments, registering movement trajectories of ~5 km - ~150 km length. Same code works perfectly with iOS 12.

Related

Swift iOS 13.3 bad GPS when speed is nearly 0

I have an app that tracks the journeys of the user.
I noticed that in background, from iOS 13.3, the system slows down the sending of locations when the speed slows down. In case of high speed, the location are sent every second. Is this possible? Could it be a bug? This is my location manager:
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.activityType = .other
locationManager.allowsBackgroundLocationUpdates = true

iOS locationManager - car gauge delay

I am trying to make an app thats shows your speed (velocity) on a gauge (like in a car).
the problem I have is that CLlocationManager delegate method didUpdateLocations gives me a read of the location and the speed only once a second and so if I accelerate the car too fast i get delay with the needle position on the gauge.
for example: I get the speed 13 m/s and after one second i get 26 m/s and so the needle in the UI goes to 13 m/s and waits there for a second and only then goes to 26 m/s. so i get a non continuous movement of needle in the UI.
from reading other question i understood that i can't control the sample rate that I get from CLlocation and that i can't tell him to give me a read every 0.25 sec (with different values).
1. is is true?
2. how can I solve it so it will look good to the user?
//in viewdidload:
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
self.locationManager.distanceFilter = kCLDistanceFilterNone
if versionNum == 8 {
self.locationManager.requestAlwaysAuthorization()
}
self.locationManager.startUpdatingLocation()
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
println(locations)
var loc = locations.last as! CLLocation
var speed = loc.speed
var time = loc.timestamp
var hour = getCurrentHour(time)
println("hour inside manager: \(hour)")
if hour == 18 && self.isItDay {
self.currentTimeHour = hour
self.setDayBool()
self.setRightImg()
}
else if hour == 6 && !self.isItDay {
self.currentTimeHour = hour
self.setDayBool()
self.setRightImg()
}
if self.unit == "MPH" {
speed = speed * 3.280839895013123
}
if speed >= 0 {
self.speedLabel.text = "\(Int(speed))"
self.animateNeedle(CGFloat(speed))
}
println("speed: \(speed)")
}
func animateNeedle(speed : CGFloat) {
var rotation : CGFloat = 0
if self.unit == "KMH" {
rotation = (speed * 4) - 5
}
else if self.unit == "MPH" {
rotation = speed + ((speed / 3) - 5)
}
if let trans = self.needleImg.layer.presentationLayer() {
self.currentValue = self.needleImg.layer.presentationLayer().valueForKeyPath("transform.rotation") as! Float
}
println(currentValue)
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fillMode = kCAFillModeBoth
rotateAnimation.removedOnCompletion = false
rotateAnimation.fromValue = currentValue
rotateAnimation.toValue = self.degToRad(rotation)
rotateAnimation.duration = 0.3
self.needleImg.layer.addAnimation(rotateAnimation, forKey: "transform.rotation")
}
As Lefteris says, you should set your distance filter to 0. You should also select kCLLocationAccuracyBest as the desiredAccuracy property of your location manager.
iOS GPS is rather crude in any case, so I doubt if either of those things will help a whole lot.
I would suggest animating your speedometer needle from it's old position to the new. A duration of .2 - .3 seconds should be enough to create the impression of movement without taking so long that it interferes with keeping the needle position current.
You can use UIView animation methods like animateWithDuration:animations, or Core Animation. Core Animation is a lot harder to use but offers more options.
You need to set the distance filter that notify changes:
/* Notify changes when device has moved x meters.
* Default value is kCLDistanceFilterNone: all movements are reported.
*/
self.locationManager.distanceFilter = 10.0f;
From Apple Documentation:
For more option look at this full example:
self.locationManager = [CLLocationManager new];
self.locationManager.purpose = #"Tracking your movements on the map.";
self.locationManager.delegate = self;
/* Pinpoint our location with the following accuracy:
*
* kCLLocationAccuracyBestForNavigation highest + sensor data
* kCLLocationAccuracyBest highest
* kCLLocationAccuracyNearestTenMeters 10 meters
* kCLLocationAccuracyHundredMeters 100 meters
* kCLLocationAccuracyKilometer 1000 meters
* kCLLocationAccuracyThreeKilometers 3000 meters
*/
self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
/* Notify changes when device has moved x meters.
* Default value is kCLDistanceFilterNone: all movements are reported.
*/
self.locationManager.distanceFilter = 10.0f;
/* Notify heading changes when heading is > 5.
* Default value is kCLHeadingFilterNone: all movements are reported.
*/
self.locationManager.headingFilter = 5;
// update location
if ([CLLocationManager locationServicesEnabled]){
[self.locationManager startUpdatingLocation];
}
You need to set the distance filter to 0:
self.locationManager.distanceFilter = kCLDistanceFilterNone;
Also as Massimo mentioned above, you should also change the desired accuracy:
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;

SKMap not showing because of SKCoordinateRegion

I have a strange issue that started happening recently, and i cannot figure out why.
When I initialise my map with this piece of code, everything works perfectly:
_mapView = [[SKMapView alloc] initWithFrame:CGRectMake( 0.0f, 0.0f, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) )];
[self.view insertSubview:_mapView atIndex:0];
_mapView.settings.rotationEnabled = NO;
_mapView.settings.displayMode = SKMapDisplayMode2D;
[SKRoutingService sharedInstance].mapView = _mapView;
But, when I want to zoom in to a specific region on the map, things go funky and the screen is just blue.
Code:
[self.locationManager startUpdatingLocation];
_lattitude = self.locationManager.location.coordinate.latitude;
_longitude = self.locationManager.location.coordinate.longitude;
SKCoordinateRegion region;
region.center = CLLocationCoordinate2DMake(_latitude, _longitude);
region.zoomLevel = 14;
_mapView.visibleRegion = region;
[self.locationManager stopUpdatingLocation];
Screenshot:
Now, I'd like to mention that the blue screen only appears on the first launch. If i close the app and start it again, the map will be displayed properly (Both in Emulator and on the real device).
I tracked which piece of code is causing this, and it's this one:
_mapView.visibleRegion = region;
How can I fix this?
Probably the location manager has not yet received its position and gives you 0,0 which is somewhere in front of Africas coast. Ocean = blue.
You will need to setup a delegate for the location manager and wait for a positive fix on your position and then display the region in your map. In your code above you immediately request the location after starting the location manager.

Giving the different distance between two points using wifi and 3G

I am calculating the distance between two locations like below
CLLocation *locA = [[CLLocation alloc] initWithLatitude:lat longitude:lon];
CLLocationCoordinate2D coordinate = [self getLocation];
CLLocation *locB = [[CLLocation alloc] initWithLatitude:coordinate.latitude longitude:coordinate.longitude];
CLLocationDistance distance = [locA distanceFromLocation:locB];
CLLocationDistance kilometers = distance / 1000.0;
But it giving the different result when i connect to wifi and 3G network.how to get the same result for both.
You invariably will not get the same location for both. In fact, if you come back to the exact same location tomorrow and turn on location services, you might not get the same latitude and longitude even if you stayed with the same technology.
The latitude and longitude you receive from a CLLocationManager is not an exact science. In fact, if you look at the original CLLocation objects you received, you'll notice that there is a horizontalAccuracy property that reflects how accurate/inaccurate the coordinate could be. When you first start location services, you'll even see multiple locations come in, generally with increasing accuracy.
But it will never be perfect and you shouldn't expect it to be. This is one of the reasons why it's important to test location-aware code on a real device, because the simulator will lull one into a false sense of absolute accuracy which doesn't exist in the real world. You should write code that anticipates this. And, if nothing else, you should start considering horizontalAccuracy of the objects you receive from the location manager.
there's nothing wrong in the code lines you posted,
the only line that could give you differences in the 2 cases (wifi and 3g) is your method [self getLocation]
where i guess you get the device location.
In this case your device may use both gps and wifi access to calculate its location, so little differences may occurs (you may have noticed that some map applications advice you to turn your wifi on to get more accuracy)
Anyway,
in your getLocation method try to add a more precise accuracy when you use
CLLocationManager
i tried this:
- (CLLocationCoordinate2D )getLocation {
self.locationManager = [[CLLocationManager alloc] init];
locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
[locationManager startUpdatingLocation];
CLLocationCoordinate2D startCoord = CLLocationCoordinate2DMake(locationManager.location.coordinate.latitude, locationManager.location.coordinate.longitude);
return startCoord;
}
and the accuracy is enough to get no differences between 3G/wifi
but you could set it to
kCLLocationAccuracyBestForNavigation;
kCLLocationAccuracyBest;
kCLLocationAccuracyNearestTenMeters;
ps
i hope you are using a device with GPS, of course...

CLLocation elevation accuracy

I have a location service app, in which the user's elevation is provided. I have found, though, that the elevation accuracy provided by the CLLocationManager is extremely inaccurate. When I test the same two spots twice, sometimes the elevation difference is forty feet and sometimes it is a hundred. Is there any way that I can improve the vertical accuracy of the CLLocation, or is there a different way of acquiring the user's elevation (third-party libraries)? I have already tried checking the verticalAccuracy property of the newLocation provided by the CLLocationManager and throwing it out if it isn't sufficient, but even when it is 'accurate' enough, it still often isn't actually accurate. Thanks for your time!
Right now I'm using:
if([newLocation verticalAccuracy] < 30) {
if(isCustomary)
altitudeLabel.text = [NSString stringWithFormat:#"%.2f feet", ([newLocation altitude] * 3.2808399)];
else
altitudeLabel.text = [NSString stringWithFormat:#"%.2f meters", [newLocation altitude]];
}
Problems with that code: often the verticalAccuracy never gets below 30, and when it does, that is still not even close to accurate enough; I have tested the code walking around my house (one story), and it says the elevation changes up to nineteen feet, which I'm sure isn't true.
The elevation derived from GPS satellites is inherently less accurate than the horizontal solution from GPS due to the geometry of the satellites. You should expect the vertical accuracy to be usually about 1.5 to 3 times worse than the horizontal accuracy. That's just a limitation of GPS and why aviation requires WAAS correction before you can use GPS for instrument approaches.
When the vertical accuracy is 20 or 30 meters you should expect the altitude to be off up to 100 feet. Expecting it to not vary by 19 feet as you walk around the house in unrealistic.
What you could do is keep a rolling, weighted average (or a Kalman filter) of the recent "good" altitude readings to help filter out the measurement errors.
What code have you tried?
This is the best code for elevation so I don't think there is anything beyond this:
-(void)awakeFromNib {
locmanager = [[CLLocationManager alloc] init];
[locmanager setDelegate:self];
[locmanager setDesiredAccuracy:kCLLocationAccuracyBest];
[locmanager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
heightMesurement.text = [NSString stringWithFormat: #"%.2f m", newLocation.altitude];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
heightMesurement.text = #"0.00 m";
}

Resources