CLLocation Category for Calculating Bearing w/ Hav

2019-01-05 01:27发布

I'm trying to write a category for CLLocation to return the bearing to another CLLocation.

I believe I'm doing something wrong with the formula (calculous is not my strong suit). The returned bearing is always off.

I've been looking at this question and tried applying the changes that were accepted as a correct answer and the webpage it references:

Calculating bearing between two CLLocationCoordinate2Ds

http://www.movable-type.co.uk/scripts/latlong.html

Thanks for any pointers. I've tried incorporating the feedback from that other question and I'm still just not getting something.

Thanks

Here's my category -

----- CLLocation+Bearing.h

#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>


@interface CLLocation (Bearing)

-(double) bearingToLocation:(CLLocation *) destinationLocation;
-(NSString *) compassOrdinalToLocation:(CLLocation *) nwEndPoint;

@end

---------CLLocation+Bearing.m

#import "CLLocation+Bearing.h"

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


@implementation CLLocation (Bearing)

-(double) bearingToLocation:(CLLocation *) destinationLocation {

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

 double lat2 = DegreesToRadians(destinationLocation.coordinate.latitude);
 double lon2 = DegreesToRadians(destinationLocation.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);

 return RadiansToDegrees(radiansBearing);
}

6条回答
Rolldiameter
2楼-- · 2019-01-05 01:56

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)
查看更多
SAY GOODBYE
3楼-- · 2019-01-05 02:02

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))
    }
}
查看更多
手持菜刀,她持情操
4楼-- · 2019-01-05 02:10

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                                                                     
查看更多
乱世女痞
5楼-- · 2019-01-05 02:13

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;
查看更多
可以哭但决不认输i
6楼-- · 2019-01-05 02:17

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

}
查看更多
来,给爷笑一个
7楼-- · 2019-01-05 02:18

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;

}
查看更多
登录 后发表回答