2010-10-13 12 views
22

Próbuję napisać kategorię dla CLLocation, aby przywrócić łożysko do innej CLLocation.Kategoria CLLocation do obliczania łożyska z funkcją Haversine

Wierzę, że robię coś nie tak z formułą (nie jest to mój silny kombinezon). Zwrócone łożysko jest zawsze wyłączone.

Szukałem na to pytanie i starał zastosowaniu zmian, które zostały przyjęte jako poprawną odpowiedź, a strona odwołuje:

Calculating bearing between two CLLocationCoordinate2Ds

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

Dzięki za wszelkie wskazówki. Próbowałem wykorzystywać opinie z tego drugiego pytania i nadal po prostu nie dostaję czegoś.

Dzięki

Oto moja kategoria -

----- 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); 
} 
+0

Czemu konwersji LAT i LON wartości ze stopni na radiany? Czy funkcja Haversine wymaga tej konwersji? –

+0

Aby odpowiedzieć na moje własne pytanie, tak. Funkcja Haversine wymaga tej konwersji, jak pokazano tutaj: http://www.movable-type.co.uk/scripts/latlong.html –

Odpowiedz

25

Twój kod wydaje się być dla mnie w porządku. Nic złego w obliczeniach. Nie określić jak daleko są wyniki, ale można spróbować szczypanie radian/stopnie konwertery do tego:

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

Jeśli otrzymujesz łożyska negatywne, dodać 2*M_PI do wyniku końcowego w radiansBearing (lub 360 jeśli zrobisz to po konwersji na stopnie). atan2 zwraca wynik w przedziale -M_PI do M_PI (-180 do 180 stopni), więc może chcesz przekonwertować go na kompas łożysk, używając coś jak następującego kodu

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

Dziękuję bardzo! Powinienem był dać oczekiwane i rzeczywiste wyniki, ale i tak zauważyłeś problem. Po prostu nie radziłem sobie z ujemnymi stopniami i konwersowałem na stopnie kompasu. Dobry punkt określający również 180-te jako pływaki. Wszystko działa teraz idealnie. – Nick

4

Tutaj jest inną implementacją

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; 

} 
+0

Nie rozumiem tego, jak te algorytmy mogą być zastosowane do współrzędnych w stopniach, a nie w radianach. –

+0

http://williams.best.vwh.net/avform.htm#Crs jego sposób w lotnictwie wierzę. – Jeef

+0

Mój problem polega na tym, że niezależnie od wybranego przeze mnie algorytmu uzyskano inny wynik. U dołu portowanie Swifa wcześniejszej kategorii. –

5

to Porting w Swift w kategorii na początku:

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

używam twierdzenie cosinusów w Swift. Działa szybciej niż Haversine, a jego wynik jest bardzo podobny. Odmiana 1 metra na dużych odległościach.

Dlaczego używam twierdzenie cosinusów:

  • szybko biegać (no bo nie ma funkcji sqrt)
  • wystarczająco chyba zrobić kilka astronomię
  • idealny na zadania w tle
Precyzyjne

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 

} 
0

jest to kolejna CLLocation przedłużenie może b e stosowane w Swift 3 i 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) 
    } 

} 
0

roboczej Swift 3 i 4

Próbowałem tak wiele wersji i ten w końcu daje poprawne wartości!

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

    } 


} 

Zastosowanie:

let degrees = location1.bearingDegreesTo(location: location2)