Objective C / iPhone comparing 2 CLLocations /GPS coordinates - iphone

have an app that finds your GPS location successfully, but I need to be able to compare that GPS with a list of GPS locations, if both are the same , then you get a bonus.
I thought I had it working, but it seems not.
I have 'newLocation' as the location where you are, I think the problem is that I need to be able to seperate the long and lat data of newLocation.
So far ive tried this:
NSString *latitudeVar = [[NSString alloc] initWithFormat:#"%g°", newLocation.coordinate.latitude];
NSString *longitudeVar = [[NSString alloc] initWithFormat:#"%g°", newLocation.coordinate.longitude];
An example of the list of GPS locations:
location:(CLLocation*)newLocation;
CLLocationCoordinate2D bonusOne;
bonusOne.latitude = 37.331689;
bonusOne.longitude = -122.030731;
and then
if (latitudeVar == bonusOne.latitude && longitudeVar == bonusOne.longitude) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"infinite loop firday" message:#"infloop" delegate:nil cancelButtonTitle:#"Stinky" otherButtonTitles:nil ];
[alert show];
[alert release];
}
this comes up with an error 'invalid operands to binary == have strut NSstring and CLlocationDegrees'
Any thoughts?

Generally you should be careful with comparing floating point numbers directly. Because of the way they are defined, the internal value may not be exactly as you initialize them meaning that they will very seldom be identical. Instead you should check to see if the difference between them are below a certain threshold, for instance
if(fabs(latitude1 - latitude2) <= 0.000001)
...
Another option could be to check how far the person is from the desired location by calculating the distance. This could also take into account the fact that the coordinate from the GPS is not exactly correct, but might differ up to like 10 meters even under good conditions:
CLLocation *loc1 = [[CLLocation alloc] initWithLatitude:lat1 longitude:lon1];
double distance = [loc1 getDistanceFrom:position2];
if(distance <= 10)
...
Claus

Why are you not comparing bonusOne.latitude and newLocation.coordinate.latitude directly? You are conevrting a floating point number to a string and then comparing it to a floating point number which is why you are getting that error.
Also, given that the gps unit tends to jump around a bit, you probably want to either
a: measure the distance between bonusOne and newLocation.coordinate (using the pythagorean theorem for the hypotenuse of triangles, no reason to us something more accurate than that when on this small a scale. if you're feeling picky, use the map kit distance measuring function) and specify its less than a certain amount.
b: round the latitude and longitude off to a certain number of digits so that being, within, say 100 feet would work.
Either of those will work better for you than relying on the two floats to be equal, which is both generally problematic in software and specifically an issue when the device you're measuring is has a high noise level.

Related

Finding closest match of Latitude & Longitude in a list of Lat/Longs

I'm creating an iPhone app with Weather lookup for particular locations and I have the following problem that i'm not sure the best way to tackle.
I have the latitude and longitude of a location and want to find the closest lat/long match from a list of 5000+ locations
The 5000+ locations come from a JSON feed from Met Office Datapoint API and are in the form of a NSArray of NSDictionaries, the NSDictionary includes id, lat, long and name.
I want to match my location to the nearest location from the list from the Met Office and grab the id key value.
Many Thanks in advance
I'm assuming you're using CLLocation objects in this...
- (CLLocation*)closestLocationToLocation:(CLLocation*)currLocation
{
CLLocationDistance minDistance;
CLLocation *closestLocation = nil;
for (CLLocation *location in arrayOfLocations) {
CLLocationDistance distance = [location distanceFromLocation:currLocation];
if (distance <= minDistance
|| closestLocation == nil) {
minDistance = distance;
closestLocation = location;
}
}
//closestLocation is now the location from your array which is closest to the current location or nil if there are no locations in your array.
return closestLocation;
}
There may be a quicker way of doing this but this will get it done.
EDITED to use CLLocation functions
I did a similar thing once (finding all lat/lon Objects surrounding a point with a max. radius) and used the formula given here:
http://www.movable-type.co.uk/scripts/latlong.html
However, that was quite time consuming. So I sort of "boxed" the objects fist. Based on the calculation above (reverted of course) I calculated the latitude and longitude of those coordinates north, west, south and east that had exactly the maximum distance. With hose max and min values (for lat and lon) I queried all objects in question. And only for those I calculated the exact distance and included them in the list of results or excluded them.
However, so far that does not exactly match your problem. But I tried to fasten the calculations even further. For that I said to myself, that I do not need the exact distance from the searched object to mine but it is enogh to know wether it is closer than one of the boxes coordinates. And that part is the one that corresponds well to your question:
Your case could be much easier. Assuming that the locations in question (the shortest once) are close to the one location which you try to assign, all this complex math may not play a role. You do not need the exact distance. What you need is the cosest one. For that I would assume that the earth is flat and that the distances between longitudes (or latitude) are linear. That is not true of course but should be good enough to figure out, which of those is the closest.
Going from there you could use pythagoras.
Distance = sqrt(sqr(difference-in-lat) + sqr(difference-in-lon));
For the mere purpose of comparing the distances and finding the shortest, you could even replace the time consuming square route with a much faster sqare operation.
Square-Of-Distance = sqr(difference-in-lat) + sqr(difference-in-lon).
And then compare the various Square-Of-Distance rather than the Distance. The result will be the same but much faster.
BTW, that was a PHP projects. That's why I cannot provide sample code but just explain the algorihm.
I'd suggest something like this:
NSMutableArray *tempArray = [NSMutableArray new];
for (NSMutableDictionary *location in yourArrayOfLocations){
CLLocation coord;
coord.latitude = [location objectForKey:#"latitude"];
coord.longitude = [location objectForKey:#"longitude"];
[location setValue:[usersLocation distanceFromLocation:coord] forKey:#"distance"];
[tempArray addObject:location];
}
// Now sort the array
NSArray *sortedArray = [tempArray sortedArrayUsingComparator:^(id o1, id o2) {
NSDictionary *location1 = (NSDictionary *)o1;
NSDictionary *location2 = (NSDictionary *)o2;
return [[location1 objectForKey:#"distance"] compare:[location2 objectForKey:#"distance"]];
}];
[tempArray release];
Now you have an array ordered by distance. You can use the object at index 0, as it is the closest to the user's position.
Good Luck!

Optimization for MKAnnotations and Core Data

I have some locations ( in this case >3000 ) stored with Core Data. Once I open the map, I fetch the locations and store them in an array. Each time the mapview region is changed I call a function which will calculate which annotations are visible in the current visibleMaprect and filter them by pixel-distance. ( I know there would be more complex optimizations, like quadtrees, but I would not really implement it right now, if it's not extremely necessary ).
This is my code :
//locations is an array of NSManagedObjects
for (int i =0 ; i < [locations count]; i++)
{
// managed object class for faster access, valueforkey takes ages ...
LocationEntity * thisLocation = [locations objectAtIndex:i];
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake( [thisLocation.latitude doubleValue], [thisLocation.longitude doubleValue]) ;
// mapRect is mapView.visibleMapRect
BOOL isOnScreen = MKMapRectContainsPoint(mapRect, MKMapPointForCoordinate(coord));
if (isOnScreen)
{
CGPoint cgp = [mapView convertCoordinate:coord toPointToView:mapView];
// compare the distance to already existing annotations
for (int idx = 0; idx < [annotations count] && hasEnoughDistance; idx++)
{
CGPoint cgp_prev = [mapView convertCoordinate:[[annotations objectAtIndex:idx] coordinate] toPointToView:mapView];
if ( getDist(cgp, cgp_prev) < dist ) hasEnoughDistance = FALSE;
}
}
if (hasEnoughDistance)
// if it's ok, create the annotation, add to an array and after the for add all to the map
}
The map is freezing for a few seconds after each zoom/movement.
I checked with time profiler and the simple obtainment of coordinates is taking sometimes 1 whole second, sometimes just 0.1, even though the coordinates are indexed attributes in my model... Also these type of lines seem to take ages :
CGPoint cgp = [mapView convertCoordinate:coord toPointToView:mapView];
Any suggestions how could I calculate the pixel/point distance between two annotations/coordinates without going through this function ? Or any optimization suggestions for Core Data?
Thanks :)
Ok, I sort of missed the not having them too close bit from your explanation. The conversion between the coordinates is very slow. The way you can alleviate it is to precompute the coordinates into map points with MKMapPointForCoordinate and store them persistently - they only depend on the coordinates. Then you can quickly calculate the distance between the map points of two annotations, scale it depending on your current zoom level of the map and this will quite closely relate to the actual distance on the screen. It should be accurate enough and will be much faster.
I would recommend calculating the squared distance and comparing it to squared dist. You would be saving a lot on the sqrt().
If you still get boggled down on getDist() (or getSqDist()) you could either go for a kd tree or use the Accelerate Framework to do the calculations. I've done the latter when I needed to calculate distances between many points and the speedup was very good. But the details of this is an another cup of tea. Let me know if you need any help with that.
The fact that your coordinates are indexed would only help if you actually searched for annotations by the coordinates, so it won't help if you just look through all of them.
A way of dealing with long loading times from CoreData would be to try making your annotations as lightweight as possible, so only storing the coordinates and map points. Then you could have a way of getting the rest of the annotation data as needed. This could be done with the proxy pattern.
One more thing. Fast enumeration might be faster and is better practice as well, so
for(LocationEntity* thisLocation in locations)
instead of
for (int i =0 ; i < [locations count]; i++)

GPS coordinates in degrees to calculate distances

On the iPhone, I get the user's location in decimal degrees, for example: latitude 39.470920 and longitude = -0.373192; That's point A.
I need to create a line with another GPS coordinate, also in decimal degrees, point B. Then, calculate the distance (perpendicular) between the line from A to B and another point C.
The problem is I get confused with the values in degrees. I would like to have the result in meters. What's the conversion needed? How will the final formula to compute this look like?
Why don't you use CLLocations distanceFromLocation: method? It will tell you the precise distance between the receiver and another CLLocation.
CLLocation *locationA = [[CLLocation alloc] initWithLatitude:12.123456 longitude:12.123456];
CLLocation *locationB = [[CLLocation alloc] initWithLatitude:21.654321 longitude:21.654321];
CLLocationDistance distanceInMeters = [locationA distanceFromLocation:locationB];
// CLLocation is aka double
[locationA release];
[locationB release];
It's as easy as that.
(CLLocationDistance)distanceFromLocation:(const CLLocation *)location is the method to get the distance from on CLLocation to another.
Your problem is also of finding the shortest line between a line (A,B) and point C.
I guess if your 3 CLLocations are near ( less than a few kilometers apart), you can do the math "as if" the coordinates are points on a single plane, and use this in C++, or this or this and just use the CLLocations "as if" they were x and y coordinates on a plane.
If your coordinates are far away, or exact accuracy is important then the spherical shape of the earth matters, and you need to do things using great circle distance and other geometry on the face of a sphere.
Swift 3.0+
Only calculate distance between two coordinates:
let distance = source.distance(from: destination)
When you have array of locations:
To get distance from array of points use below reduce method.
Here locations is array of type CLLocation.
let calculatedDistance = locations.reduce((0, locations[0])) { ($0.0 + $0.1.distance(from: $1), $1)}.0
Here you will get distance in meters.

Objective C - iPhone comparing 2 CLLocations /GPS coordinates

Ok , so thanks to Claus Broch I made some progress with comparing two GPS locations. I need to be able to say "IF currentlocation IS EQUAL TO (any GPS position from a list ) THEN do something
My code at the moment is :
CLLocationCoordinate2D bonusOne;
bonusOne.latitude = 37.331689;
bonusOne.longitude = -122.030731;
Which is the simulators GPS location at Infinite Loop
CLLocation *loc1 = [[CLLocation alloc] initWithLatitude:bonusOne.latitude longitude:bonusOne.longitude];
double distance = [loc1 getDistanceFrom:newLocation];
if(distance <= 10000000) {
Then do something
}
Any number under 10000000 and it assumes that there is no match.
Yes, you can use getDistanceFrom to get the distance between two points. The distance is in meters. You can use that comparison, compared with the current horizontal accuracy from the location manager, to determine if you are roughly at the "bonus one" position.
CLLocation *bonusLocation = [[CLLocation alloc] initWithLatitude:bonusOne.latitude longitude:bonusOne.longitude];
float distance = [bonusLocation getDistanceFrom:newLocation];
float threshold = 2 * [newLocation horizontalAccuracy]; // Or whatever you like
if(distance <= threshold) {
// You are at the bonus location.
}

how to calculate two coordinates distance in objective c?

as title how to? i have tried the code from google earth, but seem like the result is different with the google map calculation result. below provided the code i did
-(double)GetDistance:(double)lat1 long1:(double)lng1 la2:(double)lat2 long2:(double)lng2 {
//NSLog(#"latitude 1:%.7f,longitude1:%.7f,latitude2:%.7f,longtitude2:%.7f",lat1,lng1,lat2,lng2);
double radLat1 = [self rad:lat1];
double radLat2 = [self rad:lat2];
double a = radLat1 - radLat2;
double b = [self rad:lng1] -[self rad:lng2];
double s = 2 * asin(sqrt(pow(sin(a/2),2) + cos(radLat1)*cos(radLat2)*pow(sin(b/2),2)));
s = s * EARTH_RADIUS;
s = round(s * 10000) / 10000;
return s;
}
-(double)rad:(double)d
{
return d *3.14159265 / 180.0;
}
the EARTH_RADIUS value is 6378.138
by using this function by provided two coordinates the result come out is 4.5kM
but when i use google map get direction between two same coordinates, it show me the distance is about 8km
can anyone help to point out the problem of my code?
Since this is tagged iPhone, why not use the built-in distance function rather than rolling your own? location1 and location2 are CLLocation objects.
CLLocationDistance distance = [location1 getDistanceFrom:location2];
Here is a simple code (supposing you just have latitude and longitude of the two points)
CLLocation *startLocation = [[CLLocation alloc] initWithLatitude:startLatitude longitude:startLongitude];
CLLocation *endLocation = [[CLLocation alloc] initWithLatitude:endLatitude longitude:endLongitude];
CLLocationDistance distance = [startLocation distanceFromLocation:endLocation]; // aka double
Don't forget to add MapKit Framework to your project, and import MapKit in your file :
#import <MapKit/MapKit.h>
Google Maps is likely to be giving you the driving distance, whereas the great circle equation you have listed is going to be the straight line surface distance. If there was a straight line surface road directly from point A to point B, Google Maps would likely give you the same distance as the equation you have there.
Since
getDistanceFrom:
isDeprecated
Try use the
[newLocation distanceFromLocation:oldLocation
You should be able to use the google API directly to calculate either great circle distance or driving distance depending on your application needs.
See GLatLong::distanceFrom and GDirections::getDistance.