Категория CLLocation для расчета подшипника с функцией Haversine - PullRequest
26 голосов
/ 13 октября 2010

Я пытаюсь написать категорию для CLLocation, чтобы вернуть отношение к другому CLLocation.

Я полагаю, что я делаю что-то не так с формулой (калькулезный не моя сильная сторона). Возвращенный подшипник всегда выключен.

Я смотрел на этот вопрос и пытался применить изменения, которые были приняты как правильный ответ, и веб-страницу, на которую он ссылается:

Расчет направления между двумя CLLocationCoordinate2Ds

http://www.movable -type.co.uk / скрипты / latlong.html

Спасибо за любые указатели. Я попытался включить обратную связь от этого другого вопроса, и я все еще просто не получаю что-то.

Спасибо

Вот моя категория -

----- 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 ]

25 голосов
/ 13 октября 2010

Твой код мне подходит.Ничего плохого в этом нет.Вы не указываете, насколько далеки ваши результаты, но вы можете попробовать настроить преобразователи в радианы / градусы так:

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

Если вы получаете отрицательные ориентиры, добавьте 2*M_PI к окончательному результатуradiansBearing (или 360, если вы делаете это после преобразования в градусы).atan2 возвращает результат в диапазоне от -M_PI до M_PI (от -180 до 180 градусов), поэтому вы можете преобразовать его в подшипники компаса, используя что-то вроде следующего кода

if(radiansBearing < 0.0)
    radiansBearing += 2*M_PI;
6 голосов
/ 04 августа 2015

Это портирование в Swift категории в начале:

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 голосов
/ 01 мая 2015

Вот еще одна реализация

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;

}
2 голосов
/ 30 июня 2017

Working Swift 3 и 4

Перепробовал так много версий, и эта наконец дает правильные значения!

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

    }


}

Использование:

let degrees = location1.bearingDegreesTo(location: location2)
2 голосов
/ 11 мая 2017

Это еще одно расширение CLLocation , которое можно использовать в Swift 3 и 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)
    }

}
1 голос
/ 20 февраля 2016

Я использую закон косинусов в Свифте. Он работает быстрее, чем Haversine, и его результат очень похож. Разброс 1 метра на огромных расстояниях.

Почему я использую закон косинусов:

  • Беги быстро (потому что нет функций sqrt)
  • Достаточно точно, если вы не занимаетесь астрономией
  • Идеально подходит для фоновой задачи

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

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...