Категория CLLocation для расчета подшипника с функцией Haversine
Я пытаюсь написать категорию для CLLocation, чтобы вернуть опору в другое место CLL.
Я считаю, что я делаю что-то неправильно с формулой (расчетливый не мой сильный костюм). Возвращаемый подшипник всегда выключен.
Я рассматривал этот вопрос и пытался применить изменения, которые были приняты как правильный ответ и веб-страницу, на которую он ссылается:
Расчет подшипника между двумя CLLocationCoordinate2Ds
http://www.movable-type.co.uk/scripts/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);
}
Ответы
Ответ 1
Ваш код кажется мне хорошим. Ничего плохого в калькулированном. Вы не указываете, насколько далеко от ваших результатов, но вы можете попробовать настроить ваши преобразователи радиан/градус на это:
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;
Ответ 2
Это перенос в 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))
}
}
Ответ 3
Вот еще одна реализация
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;
}
Ответ 4
Это другое расширение 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)
}
}
Ответ 5
Я использую Закон Косинеса в Свифт. Он работает быстрее, чем 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
}
Ответ 6
Рабочая скорость 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)