CLLocation Category for Calculating Bearing w/ Haversine function

∥☆過路亽.° 提交于 2019-11-26 18:46:06

Your code seems fine to me. Nothing wrong with the calculous. You don't specify how far off your results are, but you might try tweaking your radian/degrees converters to this:

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

If you are getting negative bearings, add 2*M_PI to the final result in radiansBearing (or 360 if you do it after converting to degrees). atan2 returns the result in the range -M_PI to M_PI (-180 to 180 degrees), so you might want to convert it to compass bearings, using something like the following code

if(radiansBearing < 0.0)
    radiansBearing += 2*M_PI;
Fabrizio Bartolomucci

This is a porting in Swift of the Category at the beginning:

import Foundation
import CoreLocation
public extension CLLocation{

    func DegreesToRadians(_ degrees: Double ) -> Double {
        return degrees * M_PI / 180
    }

    func RadiansToDegrees(_ radians: Double) -> Double {
        return radians * 180 / M_PI
    }


    func bearingToLocationRadian(_ destinationLocation:CLLocation) -> Double {

        let lat1 = DegreesToRadians(self.coordinate.latitude)
        let lon1 = DegreesToRadians(self.coordinate.longitude)

        let lat2 = DegreesToRadians(destinationLocation.coordinate.latitude);
        let lon2 = DegreesToRadians(destinationLocation.coordinate.longitude);

        let dLon = lon2 - lon1

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

        return radiansBearing
    }

    func bearingToLocationDegrees(destinationLocation:CLLocation) -> Double{
        return   RadiansToDegrees(bearingToLocationRadian(destinationLocation))
    }
}

Here is another implementation

public func bearingBetweenTwoPoints(#lat1 : Double, #lon1 : Double, #lat2 : Double, #lon2: Double) -> Double {

func DegreesToRadians (value:Double) -> Double {
    return value * M_PI / 180.0
}

func RadiansToDegrees (value:Double) -> Double {
    return value * 180.0 / M_PI
}

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

let degrees = RadiansToDegrees(atan2(y,x))

let ret = (degrees + 360) % 360

return ret;

}

Working Swift 3 and 4

Tried so many versions and this one finally gives correct values!

extension CLLocation {


    func getRadiansFrom(degrees: Double ) -> Double {

        return degrees * .pi / 180

    }

    func getDegreesFrom(radians: Double) -> Double {

        return radians * 180 / .pi

    }


    func bearingRadianTo(location: CLLocation) -> Double {

        let lat1 = self.getRadiansFrom(degrees: self.coordinate.latitude)
        let lon1 = self.getRadiansFrom(degrees: self.coordinate.longitude)

        let lat2 = self.getRadiansFrom(degrees: location.coordinate.latitude)
        let lon2 = self.getRadiansFrom(degrees: location.coordinate.longitude)

        let dLon = lon2 - lon1

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

        var radiansBearing = atan2(y, x)

        if radiansBearing < 0.0 {

            radiansBearing += 2 * .pi

        }


        return radiansBearing
    }

    func bearingDegreesTo(location: CLLocation) -> Double {

        return self.getDegreesFrom(radians: self.bearingRadianTo(location: location))

    }


}

Usage:

let degrees = location1.bearingDegreesTo(location: location2)

This is an another CLLocation extension can be used in Swift 3 and Swift 4

public extension CLLocation {

    func degreesToRadians(degrees: Double) -> Double {
        return degrees * .pi / 180.0
    }

    func radiansToDegrees(radians: Double) -> Double {
        return radians * 180.0 / .pi
    }

    func getBearingBetweenTwoPoints(point1: CLLocation, point2: CLLocation) -> Double {
        let lat1 = degreesToRadians(degrees: point1.coordinate.latitude)
        let lon1 = degreesToRadians(degrees: point1.coordinate.longitude)

        let lat2 = degreesToRadians(degrees: point2.coordinate.latitude)
        let lon2 = degreesToRadians(degrees: point2.coordinate.longitude)

        let dLon = lon2 - lon1

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

        return radiansToDegrees(radians: radiansBearing)
    }

}

I use the Law of Cosines in Swift. It runs faster than Haversine and its result is extremely similar. Variation of 1 metre on huge distances.

Why do I use the Law of Cosines:

  • Run fast (because there is no sqrt functions)
  • Precise enough unless you do some astronomy
  • Perfect for a background task

func calculateDistance(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D) -> Double {

    let π = M_PI
    let degToRad: Double = π/180
    let earthRadius: Double = 6372797.560856

    // Law of Cosines formula
    // d = r . arc cos (sin 𝜑A sin 𝜑B + cos 𝜑A cos 𝜑B cos(𝜆B - 𝜆A) )

    let 𝜑A = from.latitude * degToRad
    let 𝜑B = to.latitude * degToRad
    let 𝜆A = from.longitude * degToRad
    let 𝜆B = to.longitude * degToRad

    let angularDistance = acos(sin(𝜑A) * sin(𝜑B) + cos(𝜑A) * cos(𝜑B) * cos(𝜆B - 𝜆A) )
    let distance = earthRadius * angularDistance

    return distance

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!