Parsing NSArray for CLLocation Information - ios

My iOS app uses the information provided by Apple, here, to get various location information about my user. cllocationmanager provides a method for getting location information about the user every-time it changes based on certain parameters:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
This method is absolutely amazing, and returns all of the data I need, however it returns it as a single value in an NSArray:
<+37.33069626,-122.02967106> +/- 10.00m (speed 3.62 mps / course 82.41) # 2/3/13, 4:42:58 PM Eastern Standard Time
This has all the information I need (by the way, what does the course variable stand for), but I can't figure out how to get it into a more usable form. By usable form, I mean simply the coordinates, time, speed, etc. all in separate NSString / NSDictionary / NSArray values. How can I split the single NSArray value (shown above) into different parts?
I've tried splitting the single NSArray value here using nspredicate, but I could only get the NSPredicate to return a boolean value.
I've tried splitting it with the componentsJoinedByString method, however that can only split the array value with one string and there's no single string that every value can be consistently split by.
Any suggestions on how I can split up the pieces of data given to my iOS app through CLLocationManager? Are there methods or functions which could retrieve / split the same data in a more usable form.
Edit
It seems there was a misunderstanding both on my part and others. I had thought that the CLLocation object was an NSArray object with one value. However, as I learned in the comments (thank you #holex) it is not.

According to this part of the doc, the array contains CLLocations objects. So, just retrieve and process them.
for (CLLocation *location in locations) {
NSLog(#"Latitude: %f, Longitude: %f", location.coordinate.latitude, location.coordinate.longitude);
}

Related

Regarding iOS CLLocation API CLLocationCoordinate2DIsValid, how does OS treat GPS valid or not?

Regarding iOS CLLocation API CLLocationCoordinate2DIsValid, how does OS treat CLLocationCoordinate2D (lat and lon) valid or not? As you know lat and lon in CLLocationCoordinate2D are all double data type, double means more precious, such as 3.24579999999, it will always be treated as invalid by CLLocationCoordinate2DIsValid. This kind of problem will always occur in server protocol definition, which needs to change from NSString to double value。Anyone who experienced the same issue please discuss together and thanks in advance.
BOOL CLLocationCoordinate2DIsValid(CLLocationCoordinate2D coord)
You are intermixing 2 things:
CLLocationCoordinate2DIsValid describes whether the values (lat/lon) are in valid range.
The LocationManager will mark an invalid location in the field horizontalAccuracyof CLLLocation, by a negative value. This CLLocation is delivered e.g in locationManager:didUpdateLocations. Such an invalid marked coordinate means there was not a sufficient (GPS) position (navigation solution). Although the lat/lon values of that invalid CLLocation could still could be in valid range (see CLLocationCoordinate2DIsValid)
Further to topic 2, iOS does not state when it marks a location as invalid, but most probably it will directly use the fix-valid flag of the GPS Protocoll. (chip to iOS intern)

How to check if a user is at a specified latitude/longitude in iOS?

I'm wondering if there is a way to check if a user is actually at a specific location or not (or maybe within 50-100 feet of it). I have a CLLocationCoordinate2D defined and I know I can check my mapView.userLocation.coordinate.latitude and mapView.userLocation.coordinate.longitude, but I'm not sure what the best way to check this would be.
Basically you just have to compare the lat lon and calculate the distance. Luckily, iOS has a built-in method for that inside the CLLocation class.
Here is a simple way to do it.
CLLocation* location = [[CLLocation alloc] initWithLatitude:marker.coordinate.latitude longitude:marker.coordinate.longitude];
CLLocationDistance distance = [location distanceFromLocation:userLocation];
the returned value is in meters which you can then easily convert to feet or any other unit of measurement that you are interested in.
Here is the documentation for reference

Altitude Disappears from MKPlacemark

So I have some code that gets the user's location from the phone as a CLLocation, then I do a reverse geocode on it. The problem is that the resulting MKPlacemark has 0 for altitude, despite the fact that the CLLocation had a value in the altitude field.
It makes sense that if I just ask for the address of some coordinates, I don't necessarily get altitude (as that would require topographic logic). Most of the questions on here suggest calling out to a topo service.
I am wondering why the reverse geocoder would not just preserve the altitude, and also asking people what their preferred solution has been to this problem. It's not like it's hard to figure out: I can pass the altitude in separately and then just jam it into my ultimate object (my own address class), but that's ugly.
This is indeed the state of these classes at this time. Probably a bug report with Apple is in order.

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.

Order of CLLocation objects in -distanceFromLocation:

This is my first time posting a question here, but I have found a lot of help from other people's questions.
My question refers to the -distanceFromLocation: function in CLLocation. I had assumed that finding the distance between point A and point B would be the same as that between point B and point A, but I have found that it is not. I have tested this in a very simple case:
CLLocation *test1 = [[CLLocation alloc] initWithLatitude:37.529530 longitude:-122.259232];
CLLocation *test2 = [[CLLocation alloc] initWithLatitude:47.900002 longitude:-103.495102];
NSLog(#"%f vs %f",[test2 distanceFromLocation:test1],[test1 distanceFromLocation:test2]);
[test1 release];
[test2 release];
The output to this was 1907269.942754 vs 1908105.959114, a difference of almost 900 meters.
Though 900 meters may be a small percentage, I am trying to determine if the user is closer to something than an annotation and 900 meters can change that.
My question is, which one of these values, if any, is correct? Also if which location I test against is important, do I need to always test against the same location (e.g. user to destination and annotation to destination rather than destination to user and annotation to destination)?
Sorry that the sample is a bit out of context, as my project is rather large and this illustrates my problem. Any help would be appreciated as this has caused me quite a bit of confusion and frustration.
The error you're observing is 1 part in 2000. Presumably the algorithm used in this method is optimized for speed, so sorting a list of locations on distance is fast, rather than accurate to the millimeter.
If you need accurate results, don't use these methods but find another way. If you need reproducible results, order the arguments in a defined way, e.g. always start with the lower latitude.

Resources