Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

(iOS) Multiple notifications via locationManager:didExitRegion: when exiting a region

I'm working on a location-based app that makes use of the CLLocationManager region monitoring.

I'm using a single CLLocationManager and single delegate (which are set up in the main app delegate at startup), and I'm noticing that I often get a burst of multiple calls to my delegate (on locationManager:didExitRegion:) when exiting a monitored region -- usually two calls, but sometimes more. Has anyone else experienced this, or have any ideas what can be going wrong?

I'm instantiating the CLLocationManager as follows, in a class that is instantiated in the app delegate:

    _locationManager = [[CLLocationManager alloc] init];
    _locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    _locationManager.delegate = self;

I'm setting up region monitoring like this:

    // The region instance has a radius of 300 meters
    [_locationManager startMonitoringForRegion:region desiredAccuracy:1000];

As I understand from the documentation, providing the desired accuracy of 1000 means that locationManager:didExitRegion: should only be called once we're 1000 meters outside of the region.

On additional point -- as far as I've seen, I only get a burst multiple notifications if I'm in the car (and therefore travelling quite quickly). It doesn't seem to happen if I'm on a bike or on foot. Any pointers as to what I'm doing wrong (or if this is an issue that others have already encountered) are appreciated.

like image 522
Gabriel Reid Avatar asked Feb 16 '12 06:02

Gabriel Reid


2 Answers

tl;dr: It's really quite simple - you're getting as much information as Apple can give you about the fact that you're crossing cell tower boundaries - meaning, not really good data at all.

Now the real story:

Generally, there are only a couple of ways that CoreLocation can determine one's position:

  • By GPS. This is highly accurate (tens of meters), but takes lots of power to use.
  • By WiFi. This is generally accurate, though wifi base stations can and do change, which makes it fuzzy. This method cross-references the wifi stations in the area vs some known accurate locations - so it knows when it sees "FooWifiStation" that it's associated with a particular area, measured by precise instruments, or perhaps even other phones with GPS turned on (which is controversial, we may never know if Apple uses this method)
  • By cell tower locations. These don't move, so it knows you're within a big fuzzy dot of coverage when you're associated with a tower. This is the least accurate, but least power-consuming, because your phone is already doing the work to stay in contact.

You can even see this if you go into the maps application "cold": you start out immediately with a big fuzzy blue dot, (at least 1km where I am), then it shrinks as it gets a wifi location fix, then it more or less disappears when GPS gets its fix. It's doing [cell tower] => [wifi] => [gps] accuracies in real time.

From my experience, the "significant location change" clause means "We'll let you know whenever you move in a big way, as long as we don't have to do any more work or expend any more power to get you that data. This means de facto that the very best you can rely on is using transitions between cell towers. Apple deliberately left this vague, because if another app uses something that has better resolution - say, you open Maps.app, and it gets a GPS fix - it is possible that you will suddenly get a great fix on location, but you can't depend on that always being the case. You've asked for a "weak reference" to location.

Now think about what happens when you are wandering about in your car: your cell phone has to manage that transition. It gets handed off, talks to multiple towers at once, that sort of thing, to manage a seamless transition which must be viable while you are having a conversation. Just that is a pretty neat feat for any little box to do. Necessarily, this will cause you to see a bit of bounce in location updates, because, to the phone, you're vibrating between cell towers at this time as it negotiates the transition.

So what the radius really means is that you're interested in data of roughly that accuracy - but the API will not guarantee that you get updates within that radius. You can think of it as Apple saying, "We're going to bucket your accuracy into one of 3 groups - but we didn't give you an enumeration, because we want to reserve the right to develop better methods of fixing your location without you having to change your code". Of course, in reality, any real app will change if they change their method of getting location, because they are annoyingly vague about this location stuff.

In other words, you will get a location fix at the cell tower with some totally made up guess as to how good that accuracy is; when you move to the next tower, you will instantly jump to its location, with a similarly fuzzy fix - does that make sense? CoreLocation is telling you you're at the cell tower with accuracy of however far the cell tower's signal reaches; as you move to another tower, you will probably get the bounciness of the handoff.

So when it comes to it, to really do a good job, you have to assume that the data is one of "great", "ok", or "bad" and use other methods - like Kalmann filters - if you really need to have a better guess at where the user is. As a zeroth-order approximation, I would just debounce the callbacks based on the time of the update, which is given, and assume that the user isn't really leaping kilometers back and forth within a few seconds, but rather, travelling in the direction of the first new update.

like image 143
Matt Avatar answered Sep 18 '22 22:09

Matt


I think you'd be better off, using a desiredAccuracy that is smaller than the radius of 300m, i.e. kCLLocationAccuracyHundredMeters. Have you tried that? There is no documentation about the underlying logic, but I'd assume, that desiredAccuracy can be regarded as the minimium distance you'd have to travel that the movement counts as a "border crossing".
Region monitoring is based on "significant location events", not GPS – otherwise the battery wouldn't last half a day.

If you use such a high desiredAccuracy, the system might get more than one significant location event (these seem to be generated about every 500m – also depending how many wifi networks you have in the area. In that case, the system might compare the current location resulting from the significant change with the distance to all sides of your region. If you're just outside 1000m from opposite side of your region, it might notify again and only stop notifying once your outside the 1000m from each side of your region.

The reason for the accuracy parameter is rather to avoid border crossings, if you are too close to the border, so you're not seeing inside-outside-inside-outside etc while traveling just outside the border...

In my apps, I tried many many different combinations of radius and accuracy and settled on 500m/100m and 100m/10m – those work very well in my real work within-city szenario.

(see also the excellent article series http://longweekendmobile.com/2010/07/22/iphone-background-gps-accurate-to-500-meters-not-enough-for-foot-traffic/)

like image 21
k1th Avatar answered Sep 18 '22 22:09

k1th