valid way to calculate angle between 2 CLLocations?
Asked Answered
R

4

4

Is this a valid way to calculate an angle (in radians) from one CLLocation to another?

-(float)angleFromLocation:(CLLocationCoordinate2D)start toLocation:(CLLocationCoordinate2D)end {
float deltaX = start.latitude - end.latitude;
float deltaY = start.longitude - end.longitude;
float ang = atan2(deltaY, deltaX);

return ang;}

Please advise!

Any help will be much appreciated.

Riana answered 3/8, 2011 at 10:3 Comment(1)
You're getting close. Check out the solutions to this answer - #6140545Kelter
T
5

Swift 5 version:

extension FloatingPoint {

    var degreesToRadians: Self { return self * .pi / 180 }
    var radiansToDegrees: Self { return self * 180 / .pi }
}

extension CLLocationCoordinate2D {
       
    func heading(to: CLLocationCoordinate2D) -> Double {
        let lat1 = self.latitude.degreesToRadians
        let lon1 = self.longitude.degreesToRadians

        let lat2 = to.latitude.degreesToRadians
        let lon2 = to.longitude.degreesToRadians

        let dLon = lon2 - lon1
        let y = sin(dLon) * cos(lat2)
        let x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon)

        let headingDegrees = atan2(y, x).radiansToDegrees
        if headingDegrees >= 0 {
            return headingDegrees
        } else {
            return headingDegrees + 360
        }
    }
}
Turino answered 3/11, 2017 at 9:42 Comment(3)
Worked Perfectly as I used extension function directly in class because In extension I'm getting this error: Implementation of 'Equatable' cannot be automatically synthesized in an extension in a different file to the typeSwanskin
Your code work perfectly. Please add some explanation to your code that help in understanding. Thank youMillard
This worked great, Equatable is not needed though, and throws an error in later versions of Swift. I tried editing the answer but there are currently too many pending edits.Streamlet
S
3

I used a variant of this question and answer and it works well:

double DegreesToRadians(double degrees) {return degrees * M_PI / 180.0;};
double RadiansToDegrees(double radians) {return radians * 180.0/M_PI;};

- (double)bearingFromLocation:(CLLocation *)fromLocation toLocation:(CLLocation *)toLocation
{

    double lat1 = DegreesToRadians(fromLocation.coordinate.latitude);
    double lon1 = DegreesToRadians(fromLocation.coordinate.longitude);

    double lat2 = DegreesToRadians(toLocation.coordinate.latitude);
    double lon2 = DegreesToRadians(toLocation.coordinate.longitude);

    double dLon = lon2 - lon1;

    double y = sin(dLon) * cos(lat2);
    double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon);
    double radiansBearing = atan2(y, x);

    double degreesBearing = RadiansToDegrees(radiansBearing);

    if (degreesBearing >= 0) {
        return degreesBearing;
    } else {
        return degreesBearing + 360.0;
    }
}
Sudhir answered 17/10, 2015 at 0:27 Comment(1)
I have a similar problem. I am tracking the user's location based on GPS readings. The GPS reading gives me a "course" value, which determines the direction due north in degrees. When I calculate the difference of angle between two coordinates, things get ugly if the user moves from 0 to 359 degree. In real time, it could have been a 1 degree change, but my code is giving 359 degree change. Any ideas?Upanishad
R
0

The best method I found for this computation was to use the Spherical Law of Cosines. There is a C function to do this available here on github called headingInDegrees. It takes two lat/long pairs and returns heading:

/*------------------------------------------------------------------------- 
* Given two lat/lon points on earth, calculates the heading 
* from lat1/lon1 to lat2/lon2.   
*  
* lat/lon params in degrees 
* result in degrees 
*-------------------------------------------------------------------------*/
double headingInDegrees(double lat1, double lon1, double lat2, double lon2);

Since a CLLocationCoordinate2d contains latitude and longitude, it is easy to pass those two fields to this function and get the heading back.

Rookie answered 8/9, 2011 at 17:37 Comment(0)
H
0

I had some problems using the given answer where there were certain locations which would give the wrong result. My solution is based off https://www.sunearthtools.com/tools/distance.php#txtDist_3

extension CLLocationCoordinate2D {


func getRadiansFrom(degrees: Double ) -> Double {

    return degrees * .pi / 180

}

func getDegreesFrom(radians: Double) -> Double {

    return radians * 180 / .pi

}

public func bearing(location: CLLocationCoordinate2D) -> Double {

    let lat1 = self.getRadiansFrom(degrees:self.latitude)
    let long1 = self.getRadiansFrom(degrees:self.longitude)
    let lat2 = self.getRadiansFrom(degrees:location.latitude)
    let long2 = self.getRadiansFrom(degrees:location.longitude)
    

    let deltaLat = log(tan(lat2/2 + .pi/4)/tan(lat1/2 + .pi/4))
    var deltaLong = (long2-long1)
    
    if(deltaLong > .pi){
        deltaLong = (long1-long2)
    }
    
    return self.getDegreesFrom(radians: atan2(deltaLong, deltaLat))
}

}

Hereunto answered 12/5, 2022 at 19:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.