iphone mapkit core-location cllocation haversine

iphone - CL Categoría de ubicación para calcular la función de rodamientos w/Haversine



mapkit core-location (6)

Estoy intentando escribir una categoría para CLLocation para devolver el rumbo a otra CLLocation.

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

He estado viendo esta pregunta y he intentado aplicar los cambios que se aceptaron como una respuesta correcta y la página web a la que hace referencia:

Cálculo del rumbo entre dos CLLocationCoordinate2Ds

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

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

Gracias

Esta es 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); }


Aquí hay 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; }


Esta es otra extensión de CLLocation que se puede usar 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) } }


Esta es una migración 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)) } }


Tu código me parece bien. No hay nada malo con el cálculo. No especifica qué tan lejos están sus resultados, pero puede intentar ajustar sus convertidores de radianes / grados a esto:

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

Si está obteniendo rodamientos negativos, agregue 2*M_PI al resultado final en radianes Teniendo (o 360 si lo hace 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 convertirlo en rodamientos de brújula, utilizando algo como el siguiente código

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


Uso la Ley de Coseno 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é uso la Ley de Coseno?

  • Corre rápido (porque no hay funciones sqrt)
  • Lo suficientemente preciso a menos que hagas algo de astronomía
  • Perfecto para una tarea de fondo

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 }


Trabajando Swift 3 y 4

¡Intentó tantas versiones y esta 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)