Does stopMonitoringForRegion stops only the duplicated region or also the original region as well? - ios

I am working on a beacon monitoring project and currently I am investigating the stopMonitoringForRegion method. What I am doing is, duplicate the original region and start a new monitoring for it. After 30 - 60 seconds, I stop the copied region:
[self.locationManager stopMonitoringForRegion:self.duplicatedRegion];
So the original and the duplicated regions minor and major ids are same. When I stop monitoring the duplicated region am I also stoping the original region? Because the minor and major ids are same. The only difference in between the original and duplicated region is the identifiers. The original one has a Vendor GUID whereas the duplicated has "testRegion" string.
self.duplicatedRegion = [[CLBeaconRegion alloc] initWithProximityUUID:self.originalRegion.proximityUUID
major:[self.originalRegion.major intValue]
minor:[self.originalRegion.minor intValue]
identifier:#"testRegion"];
self.duplicatedRegion.notifyEntryStateOnDisplay = NO;
self.duplicatedRegion.notifyOnEntry = YES;
self.duplicatedRegion.notifyOnExit = YES;
[self.locationManager startMonitoringForRegion:self.duplicatedRegion];
I am asking this because when I stop monitoring the duplicated region, the app doesn't receive any enter and exit region events anymore. In my code I am checking whether the original region is being monitored and yes it is on the self.locationManager monitoredRegions array. Also I never stop the original region. I am just start and stoping the duplicated one.

The critical point is to use a different string for the identifier field:
self.duplicatedRegion = [[CLBeaconRegion alloc] initWithProximityUUID:self.originalRegion.proximityUUID
major :[self.originalRegion.major intValue]
minor :[self.originalRegion.minor intValue]
identifier :#"testRegion"];
If originalRegion has an identifier of testRegion and the duplicated one has that same value, it will cause the symptoms you describe. This is because CoreLocation uses the identifier field to signal that the Region objects are the same, allowing you to replace one object with another.
To have both regions active at the same time, simply supply a different identifier value.

Related

Setting the major and minor value of iBeacon

I want to set the major and minor value correctly as I enter in a region.For example,I have different queues for different kinds of customer. Like Premium and silver categories.
If a customer goes in the lane of Premium, He should broadcast his major value as the value of the lane he is standing i.e. Premium and the number as minor.
How to set these values correctly according to the current position of the customer?
Premium = 1;
Silver = 2;
self.myBeaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid major:premium minor:2 identifier:#"com.example.beacon"];
The major and minor values are set on the beacon - these are the values that are received by your app to indicate the region where the device is located.
So, in your case you would have to have two beacons, one configured the with 'premium' value and one with the 'silver' values but with the same UUID. In your app you can define the region just by UUID and examine the major/minor in didEnterRegion To determine which specific beacon was detected.
However, Unless the queues are a fair distance apart you may have difficulty locating the user accurately

CLLocationManager giving wrong location updates

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;

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