2010-10-13 14 views
22

Estoy tratando de escribir una categoría para CLLocation para devolver el rumbo a otro CLLocation.CL Categoría de ubicación para calcular el rumbo con la función Haversine

Creo que estoy haciendo algo mal con la fórmula (el cálculo no es mi punto fuerte). El cojinete devuelto siempre está apagado.

He estado buscando en esta pregunta y trató de aplicar los cambios que fueron aceptadas como una respuesta correcta y la página web que hace referencia:

Calculating bearing between two CLLocationCoordinate2Ds

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

Gracias por cualquier punteros. Intenté incorporar los comentarios de esa otra pregunta y todavía no estoy obteniendo nada.

Gracias

Aquí está mi categoría -

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

¿Por qué estás convirtiendo los valores de latitud y lon de grados a radianes? ¿La función Haversine requiere esa conversión? –

+0

Para responder mi propia pregunta, sí. La función Haversine requiere esa conversión como se muestra aquí: http://www.movable-type.co.uk/scripts/latlong.html –

Respuesta

25

Su código me parece bien. No hay nada malo con el cálculo. No se especifica lo lejos que sus resultados son, pero se puede tratar de ajustar su radianes/grados convertidores a esto:

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

Si está recibiendo cojinetes negativos, anadir 2*M_PI al resultado final en radiansBearing (o 360 si lo haces después de convertir a grados). atan2 devuelve el resultado en el rango -M_PI a M_PI (-180 a 180 grados), por lo que es posible que desee para convertirlo en compás de cojinetes, usando algo como el siguiente código

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

¡Muchas gracias! Debería haber dado algunos resultados esperados y reales, pero de todos modos detectó el problema. Simplemente no manejaba los grados negativos y la conversión a grados de brújula. Buen punto que especifica los 180 como flotadores también. Todo funciona perfectamente ahora. – Nick

4

Aquí está otra implementación

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

No entiendo estos algoritmos cómo se puede aplicar pecado a una coordenada en grados en lugar de en radianes. –

+0

http://williams.best.vwh.net/avform.htm#Crs es la forma en que se hace en la aviación, creo. – Jeef

+0

Mi problema es que cualquier algoritmo que elijo da un resultado diferente. En la parte inferior, un Swift porting de la categoría antes escrita. –

5

Esta es una de portabilidad en Swift de la Categoría al principio:

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

utilizo la ley de los cosenos en Swift. Funciona más rápido que Haversine y su resultado es extremadamente similar. Variación de 1 metro en distancias enormes.

¿Por qué utilizo la ley de los cosenos: (porque no hay ninguna función sqrt)

  • correr rápido
  • suficiente a menos que haga un poco de astronomía
  • Perfecto para una tarea en segundo plano
precisa

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

Este es otro CLLocalización extensión puede b e utiliza en Swift 3 y 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

Trabajo Swift 3 y 4

probado tantas versiones y éste, finalmente, da valores correctos!

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

    } 


} 

Uso:

let degrees = location1.bearingDegreesTo(location: location2) 
Cuestiones relacionadas