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

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.
}

Related

Find Minimum/Maximum latitude and Longitude

My Question is how can i find minimum and maximum latitude and longitude of specific area (500 meter) from current location.
In my case, Such like i need to get X and Y CLLocation (latitude and longitude) from 500meter of area
See my image (sorry for this may be bad drawing )
I also have to tried to googling and i got link such like
How can i get minimum and maximum latitude and longitude using current location and radius?
But i don't know how it implement in my case.
Pleas help me in this issue.
NOTE : I do not want to use CLLocationDistance distance = [currentLocation distanceFromLocation:newLocation]; because it is not helpful in my case so..
If you don't need a really precise value, then use the approximation that 1 degree is 111 km. Based on this, you need to add and remove 0.0025 degrees to the current coordinates to get corners of the area you are looking for.
rectanglesidelengthmeters = 500
degreedeltalat = 0.001 * (rectanglesidelengthmeters / 2.0) * cos(current.lon)
degreedeltalon = 0.001 * (rectanglesidelengthmeters / 2.0) * cos(current.lat)
minlat = current.lat - degreedeltalat
maxlat = current.lat + degreedeltalat
minlon = current.lon - degreedeltalon
maxlon = current.lon + degreedeltalon
You may need to correct the result a little for staying in the -90 .. 90 range for latitude and -180 .. 180 range for longitude values but I think CLClocation will handle that for you too.
You have to do some radius calculation from current location in km.
double kilometers = 0.5;
double curve = ABS( (cos(2 * M_PI * location.coordinate.latitude / 360.0) ));
MKCoordinateSpan span;
span.latitudeDelta = kilometers/111; //like allprog said.
span.longitudeDelta = kilometers/(curve * 111);
MKCoordinateRegion region;
region.span = span;
region.center = location.coordinate;
[self.mapView setRegion:region animated:YES];
This way i set mapView to show distance region to 0.5 km.
EDIT:
Whoa, i digging in my old 'liked' question to show you some original answer, but found a better one below accepted one:
how to make mapview zoom to 5 mile radius of current location
Look at #Anurag answer
To get precise value you should try with
minLattitude = currentLattitude - (RadiusInKm/111.12);
maxLattitude = currentLattitude + (RadiusInKm/111.12);
Thus in your case RadiusInKm = 0.5
For finding in & max longitude data you need to follow the same thing but but you have to multiply the result with cosine function of latitude
I would do this way.
double accuracy = 0.1;//How accurate do you want. Smaller value, slower perform
double distance = 500;//Distance you want
Create infinite loop.
In the loop check whether distance is bigger than 500. If yes, break. If not, add 0.1 value to latitude or longitude.
Do above way to get Max longitude, max latitude, min longitude and min latitude.
Compare your DB, if CLLocation is inside of the value, then return.
I cannot say this is the best way to solve your problem. Because we are guessing value...If you know how to convert CLLocation from given distance, that is better!
This should be correct (in php)
https://www.movable-type.co.uk/scripts/latlong-db.html
$R = 6371; // earth's mean radius, km
$rad = 0.5
// first-cut bounding box (in degrees)
$maxLat = $lat + rad2deg($rad/$R);
$minLat = $lat - rad2deg($rad/$R);
$maxLon = $lon + rad2deg(asin($rad/$R) / cos(deg2rad($lat)));
$minLon = $lon - rad2deg(asin($rad/$R) / cos(deg2rad($lat)));

Check user is following the route or not (iphone)

i am making an navigation based application. In this application i am drawing a route from points selected by the user. I have requirement of recalculating route if user is not following the route.
for Calculating the route i have used Google direction API. and for drawing the route i have used this code
- (void) drawRoute:(NSArray *) path
{
NSInteger numberOfSteps = path.count;
[self.objMapView removeOverlays: self.objMapView.overlays];
CLLocationCoordinate2D coordinates[numberOfSteps];
for (NSInteger index = 0; index < numberOfSteps; index++)
{
CLLocation *location = [path objectAtIndex:index];
CLLocationCoordinate2D coordinate = location.coordinate;
coordinates[index] = coordinate;
}
for( id <MKOverlay> ovr in [self.objMapView overlays])
{
MKPolylineView *polylineView = [[MKPolylineView alloc] initWithPolyline:ovr];
if (polylineView.tag == 22)
{
[self.objMapView removeOverlay:ovr];
}
[polylineView release];
}
MKPolyline *polyLine = [MKPolyline polylineWithCoordinates:coordinates count:numberOfSteps];
[self.objMapView addOverlay:polyLine];
}
Till now every thing is okey.
Now, i want a notification if user is out of route (more than 100 meters).and i can get the notification also
PROBLEM:~ if road is straight (more than 100mt) then i cant get points on the road. To explain the problem i have attached the image...
In this image suppose black line is my path (polyline) and red circles are the points i got form google apis. but in the straight path shown as blue circle i cant get points to compare and in this path recalculation function is called.
Can any one tell me the solution from which i can get all points of route even if it is a straight road.
I know this is an old thread, but recently ran into the same problem and found an OK solution.
The concept is that you don't calculate the distance to EACH line segment but only to the TWO segments connected to the closest point.
calculate the distance of your current location to all the points in the
MKPolyline and take the minimum from that. (There's probably some nice way to optimize this. Like not iterating through all the points on every location update, but don't have time to dig in to that now).
You now know the distance to the closest polyline-point. However that point might still be far away while the polyline itself (connecting this point and the previous or the next point) might be closer. So, calculate the distance between your current location and these two line-segments and you have the closest distance.
Now, this is not waterproof. While it minimizes the nr of api calls, on some occasions (If you have crazy bends and curves in the MKPolyline) it might call the api while not needed, but hey, then the same line will be drawn again, no damage done. In my tests it worked fine and you can also adjust the accuracy. I've set it to 200m (0.2km) in the code below.
//Get Coordinates of points in MKPolyline
NSUInteger pointCount = routeLineGuidanceTurn.pointCount;
CLLocationCoordinate2D *routeCoordinates = malloc(pointCount * sizeof(CLLocationCoordinate2D));
[routeLineGuidanceTurn getCoordinates:routeCoordinates
range:NSMakeRange(0, pointCount)];
NSLog(#"route pointCount = %d", pointCount);
//Determine Minimum Distance and GuidancePoints from
double MinDistanceFromGuidanceInKM = 1000;
CLLocationCoordinate2D prevPoint;
CLLocationCoordinate2D pointWithMinDistance;
CLLocationCoordinate2D nextPoint;
for (int c=0; c < pointCount; c++)
{
double newDistanceInKM = [self distanceBetweentwoPoints:Currentcordinate.latitude longitude:Currentcordinate.longitude Old:routeCoordinates[c].latitude longitude:routeCoordinates[c].longitude];
if (newDistanceInKM < MinDistanceFromGuidanceInKM) {
MinDistanceFromGuidanceInKM = newDistanceInKM;
prevPoint = routeCoordinates[MAX(c-1,0)];
pointWithMinDistance = routeCoordinates[c];
nextPoint = routeCoordinates[MIN(c+1,pointCount-1)];
}
}
free(routeCoordinates);
NSLog(#"MinDistanceBefore: %f",MinDistanceFromGuidanceInKM);
//If minimum distance > 200m we might have to recalc GuidanceLine.
//To be sure we take the two linesegments connected to the point with the shortest distance and calculate the distance from our current position to that linedistance.
if (MinDistanceFromGuidanceInKM > 0.2) {
MinDistanceFromGuidanceInKM = MIN(MIN([self lineSegmentDistanceFromOrigin:Currentcordinate onLineSegmentPointA:prevPoint pointB:pointWithMinDistance], [self lineSegmentDistanceFromOrigin:Currentcordinate onLineSegmentPointA:pointWithMinDistance pointB:nextPoint]),MinDistanceFromGuidanceInKM);
if (MinDistanceFromGuidanceInKM > 0.2) {
// Call the API and redraw the polyline.
}
}
Here's the fun that calculate sthe distance between two points. I know there is a built in function for it, but had it in my code already.
-(double)distanceBetweentwoPoints:(double)Nlat longitude:(double)Nlon Old:(double)Olat longitude:(double)Olon {
//NSLog(#"distanceBetweentwoPoints");
double Math=3.14159265;
double radlat1 = Math* Nlat/180;
double radlat2 = Math * Olat/180;
double theta = Nlon-Olon;
double radtheta = Math * theta/180;
double dist = sin(radlat1) * sin(radlat2) + cos(radlat1) * cos(radlat2) * cos(radtheta);
if (dist>1) {dist=1;} else if (dist<-1) {dist=-1;}
dist = acos(dist);
dist = dist * 180/Math;
dist = dist * 60 * 1.1515;
return dist * 1.609344;
}
And here's the bit that calculates the distance between a point and a line segment between two other points. I got this from here: https://stackoverflow.com/a/28028023/3139134 Modified it a bit to work with CLLocationCoordinate2D and return the distance.
- (CGFloat)lineSegmentDistanceFromOrigin:(CLLocationCoordinate2D)origin onLineSegmentPointA:(CLLocationCoordinate2D)pointA pointB:(CLLocationCoordinate2D)pointB {
CGPoint dAP = CGPointMake(origin.longitude - pointA.longitude, origin.latitude - pointA.latitude);
CGPoint dAB = CGPointMake(pointB.longitude - pointA.longitude, pointB.latitude - pointA.latitude);
CGFloat dot = dAP.x * dAB.x + dAP.y * dAB.y;
CGFloat squareLength = dAB.x * dAB.x + dAB.y * dAB.y;
CGFloat param = dot / squareLength;
CGPoint nearestPoint;
if (param < 0 || (pointA.longitude == pointB.longitude && pointA.latitude == pointB.latitude)) {
nearestPoint.x = pointA.longitude;
nearestPoint.y = pointA.latitude;
} else if (param > 1) {
nearestPoint.x = pointB.longitude;
nearestPoint.y = pointB.latitude;
} else {
nearestPoint.x = pointA.longitude + param * dAB.x;
nearestPoint.y = pointA.latitude + param * dAB.y;
}
CGFloat dx = origin.longitude - nearestPoint.x;
CGFloat dy = origin.latitude - nearestPoint.y;
return sqrtf(dx * dx + dy * dy) * 100;
}
For each pair of points in each step, you can calculate the distance between them using the Pythagorean Theorem:
distance = sqrt( pow((point1.x - point2.x), 2) + pow((point1.y - point2.y), 2) )
Then, if the distance is greater than 100m, add intermediary points along the line segment.

How to get the nearest area from a list of locations?

I have a API which returns a list of different areas within a city with the weather at that area. I want to get the nearest area based on my current location.
API returns
Area
Latitude
Longitude
Weather
How to find the nearest area based on this data?
You would have to create CLLocation objects for all the areas, plus one for the current location of the user. Then use a loop similar to the one below to get the closest location:
NSArray *allLocations; // this array contains all CLLocation objects for the locations from the API you use
CLLocation *currentUserLocation;
CLLocation *closestLocation;
CLLocationDistance closestLocationDistance = -1;
for (CLLocation *location in allLocations) {
if (!closestLocation) {
closestLocation = location;
closestLocationDistance = [currentUserLocation distanceFromLocation:location];
continue;
}
CLLocationDistance currentDistance = [currentUserLocation distanceFromLocation:location];
if (currentDistance < closestLocationDistance) {
closestLocation = location;
closestLocationDistance = currentDistance;
}
}
One thing to note is that this method of calculating a distance uses a direct line between point A and point B. No roads or other geographical objects are taken into account.

Calculate new coordinate x meters and y degree away from one coordinate

I must be missing somthing out in the docs, I thought this should be easy...
If I have one coordinate and want to get a new coordinate x meters away, in some direction. How do I do this?
I am looking for something like
-(CLLocationCoordinate2D) translateCoordinate:(CLLocationCoordinate2D)coordinate
translateMeters:(int)meters
translateDegrees:(double)degrees;
Thanks!
Unfortunately, there's no such function provided in the API, so you'll have to write your own.
This site gives several calculations involving latitude/longitude and sample JavaScript code. Specifically, the section titled "Destination point given distance and bearing from start point" shows how to calculate what you're asking.
The JavaScript code is at the bottom of that page and here's one possible way to convert it to Objective-C:
- (double)radiansFromDegrees:(double)degrees
{
return degrees * (M_PI/180.0);
}
- (double)degreesFromRadians:(double)radians
{
return radians * (180.0/M_PI);
}
- (CLLocationCoordinate2D)coordinateFromCoord:
(CLLocationCoordinate2D)fromCoord
atDistanceKm:(double)distanceKm
atBearingDegrees:(double)bearingDegrees
{
double distanceRadians = distanceKm / 6371.0;
//6,371 = Earth's radius in km
double bearingRadians = [self radiansFromDegrees:bearingDegrees];
double fromLatRadians = [self radiansFromDegrees:fromCoord.latitude];
double fromLonRadians = [self radiansFromDegrees:fromCoord.longitude];
double toLatRadians = asin( sin(fromLatRadians) * cos(distanceRadians)
+ cos(fromLatRadians) * sin(distanceRadians) * cos(bearingRadians) );
double toLonRadians = fromLonRadians + atan2(sin(bearingRadians)
* sin(distanceRadians) * cos(fromLatRadians), cos(distanceRadians)
- sin(fromLatRadians) * sin(toLatRadians));
// adjust toLonRadians to be in the range -180 to +180...
toLonRadians = fmod((toLonRadians + 3*M_PI), (2*M_PI)) - M_PI;
CLLocationCoordinate2D result;
result.latitude = [self degreesFromRadians:toLatRadians];
result.longitude = [self degreesFromRadians:toLonRadians];
return result;
}
In the JS code, it contains this link which shows a more accurate calculation for distances greater than 1/4 of the Earth's circumference.
Also note the above code accepts distance in km so be sure to divide meters by 1000.0 before passing.
I found one way of doing it, had to dig to find the correct structs and functions. I ended up not using degrees but meters for lat and long instead.
Here's how I did it:
-(CLLocationCoordinate2D)translateCoord:(CLLocationCoordinate2D)coord MetersLat:(double)metersLat MetersLong:(double)metersLong{
CLLocationCoordinate2D tempCoord;
MKCoordinateRegion tempRegion = MKCoordinateRegionMakeWithDistance(coord, metersLat, metersLong);
MKCoordinateSpan tempSpan = tempRegion.span;
tempCoord.latitude = coord.latitude + tempSpan.latitudeDelta;
tempCoord.longitude = coord.longitude + tempSpan.longitudeDelta;
return tempCoord;
}
And of course, if I really need to use degrees in the future, it's pretty easy (I think...) to do some changes to above to get it to work like I actually asked for.
Using an MKCoordinateRegion has some issues—the returned region can be adjusted to fit since the two deltas may not exactly map to the projection at that latitude, if you want zero delta for one of the axes you are out of luck, etc.
This function uses MKMapPoint to perform coordinate translations which allows you to move points around in the map projection's coordinate space and then extract a coordinate from that.
CLLocationCoordinate2D MKCoordinateOffsetFromCoordinate(CLLocationCoordinate2D coordinate, CLLocationDistance offsetLatMeters, CLLocationDistance offsetLongMeters) {
MKMapPoint offsetPoint = MKMapPointForCoordinate(coordinate);
CLLocationDistance metersPerPoint = MKMetersPerMapPointAtLatitude(coordinate.latitude);
double latPoints = offsetLatMeters / metersPerPoint;
offsetPoint.y += latPoints;
double longPoints = offsetLongMeters / metersPerPoint;
offsetPoint.x += longPoints;
CLLocationCoordinate2D offsetCoordinate = MKCoordinateForMapPoint(offsetPoint);
return offsetCoordinate;
}
Nicsoft's answer is fantastic and exactly what I needed. I've created a Swift 3-y version which is a little more concise and can be called directly on a CLLocationCoordinate2D instance:
public extension CLLocationCoordinate2D {
public func transform(using latitudinalMeters: CLLocationDistance, longitudinalMeters: CLLocationDistance) -> CLLocationCoordinate2D {
let region = MKCoordinateRegionMakeWithDistance(self, latitudinalMeters, longitudinalMeters)
return CLLocationCoordinate2D(latitude: latitude + region.span.latitudeDelta, longitude: longitude + region.span.longitudeDelta)
}
}

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.