MKMapView's user location is wrong on startup or resume
Asked Answered
T

7

9

When I start my application fresh, or resume after a long time, MKMapView's notion of the userLocation is wrong and shows me in the middle of the sea.

I am using the following code:

self.mapView.centerCoordinate = self.mapView.userLocation.location.coordinate;
[mapView setCenterCoordinate:self.mapView.userLocation.location.coordinate zoomLevel:ZOOM_LEVEL animated:YES];

Happens after a lengthy resume of the app or brand new start....

Thigpen answered 9/2, 2011 at 6:35 Comment(2)
Hey, did you ever find a solution to this problem? Was the location in lat:0.0 and long:0.0?Psittacosis
I've hit this same issue as well, but there's little information online on how to resolve it. I'm curious if there's a cleaner way other than using the last known location.Carbrey
C
21

That's the expected behavior : the user location isn't always tracked by the iPhone using GPS (it would consume to much battery). So as soon as the map is displayed, the MKMapView instance shows the last 'best' user position it knows and then, improves the accuracy by activating the tracking (this is a seamless process, you don't have to care about it) .

You can monitor when the MKMapView updates the user location on the map by implementing the MKMapViewDelegate protocol. Just implement :

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation      {
    CLLocationAccuracy accuracy = userLocation.location.horizontalAccuracy;
    if (accuracy ......) {
    } 
}

(More info from the apple documentation here )

The code above in my example checks the accuracy of the position currently being displayed by the mapView and reacts accordingly.

[EDIT]
If showing the user location in the middle of the sea at first really bother you, you can you hide the user location until you get a location that is accurate/fresh enough.
To do so, set the showsUserLocation property of the MKMapView to NO at first until you get an accurate enough location (thanks to the previous delegate callback) and then set it to YES.
By doing you, you will avoid displaying a location that is not accurate or too old to be diplayed (there is a timestamp property in the CLLocation to check wether it's an old location or not)

N.B:You don't have to create a CLLocationManager instance on your side, the MKMapView creates one internally and publish locations it receives via this delegate selector.

Confession answered 3/11, 2011 at 8:10 Comment(1)
+1 you're the first person to address the viewing in the middle of the sea part of this question.Carbrey
D
3

When using CLLocationManager directly, you normally get a cached location in the first callback. It's normally a good location, although old. After that you quickly get additional callbacks giving better locations using wifi, cell tower (if available). If you have asked for < 1000m accuracy you will (after more seconds) get GPS triangulation.

None of those should be inaccurate enough to be in the middle of the ocean. I suspect that the this line of code:

self.mapView.centerCoordinate = self.mapView.userLocation.location.coordinate;

is accessing the coordinate while userLocation or location is nil. If userLocation or location is nil, this will return 0 coordinates. The location of lat=0, lon=0 is in the Atlantic Ocean, off the coast of Africa. You could add a check of location to make sure it is not nil before getting the coordinate from it, ie:

if (self.mapView.userLocation.location) {
    self.mapView.centerCoordinate = self.mapView.userLocation.location.coordinate;
    [mapView setCenterCoordinate:self.mapView.userLocation.location.coordinate zoomLevel:ZOOM_LEVEL animated:YES];           
}

You will also want to wait for callbacks to the MKMapViewDelegate mapView:didUpdateUserLocation: to know when there is a valid location available. Your implementation of didUpdateUserLocation: should discard any location that has a horizontalAccuracy < 0 which indicates an invalid location.

-(void)mapView:(MKMapView*)mapView didUpdateUserLocation:(MKUserLocation*)userLocation
{
    if (userLocation.location.horizontalAccuracy > 0) {
        [mapView setCenterCoordinate:self.mapView.userLocation.location.coordinate zoomLevel:ZOOM_LEVEL animated:YES];           
    }
}
Dogeared answered 7/11, 2011 at 19:32 Comment(1)
Fixed the Middle Sea Issue in maps, Thanks!Fistula
S
1

This is unfortunately a function of the GPS chip. It's not always on, so the data will usually be wrong for the first couple of moments. Your best bet would probably be to store the last position recorded by the app in NSUserDefaults, then wait for the precision to be where you want it to be before switching to live data, or else hide the MkMapView until the precision is where you want it to be, then display it at that point (you could show a loading screen in the interim)

Sheetfed answered 9/2, 2011 at 6:52 Comment(2)
How do I make sure the precision is correct? I can stall untill it is correct, but how do I verify that?Thigpen
call - (void)startUpdatingLocation on your CLLocationManager, then override locationManager:didUpdateToLocation:fromLocation: in your delegate. If you store the last known position, you can determine when the user is a reasonable distance away from their last position and display the mapSheetfed
D
1

Just as Jeremy Massel wrote you have to sort out the first bad positions on app start / continue. Found a great blog post a couple of months ago:

http://troybrant.net/blog/2010/02/detecting-bad-corelocation-data/

Decay answered 2/11, 2011 at 16:11 Comment(0)
C
1

What I do here is-

  1. start location updates
  2. on location update, check the age of location, if it's too old, I wait
  3. on receiving new update I update it on the map

If you need very accurate location, then put a check on accuracy as well and a timeout beyond which you can't tolerate waiting and use the less accurate fix.

Claiborn answered 4/11, 2011 at 18:45 Comment(1)
add checking for lat and lon to be 0, and this answer is how i deal with the problemActinopod
H
0

How far off is it from your actual location? When an application just starts or resumes from a long period of time the user location is general always wrong to begin with. As the application runs it will get more and more accurate. There are methods for getting the accuracy of the user location in CLLocationManager, see what kind of values you get there.

Homothallic answered 9/2, 2011 at 6:52 Comment(1)
Should I wait in the load up of the application then? Like a sleep?Thigpen
M
0

In my apps I used these methods:

First - it's good to have a default position (for example - application is about a city - then zoom in to show whole city) (on first - first app opening, when no data downloaded)

Second - if user has already used map - can store his coordinates, and show those (+ span(zoom) level) in case there are no other data available.

Third - in case of data:

1.) If database has already some coordinates - on first map opening (when application has been closed) - zoom out/in to show all pins on map.

2.) When still in map and just arrived updates (new locations or changed something), there are two possibilities :

2.1.) If user has not zoomed in to any location - map zooms in/out to show all locations.

2.2.) if user has zoomed in - locations are updated, but map doesnt zoom out

Ofcourse - then there are also some filter buttons (for example : search, user location, and some more - which filters locations, and zooms out/in to show filtered results)

Magnanimous answered 2/11, 2011 at 18:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.