This question already exists:
I want to build something like that if a user enters in a room he recieve a notification in swift ios is it possible?
Closed 3 months ago.
I want to calculate distance between two coordinates within 5 meters or even within one meters is it possible
I have tried haversine formula but not getting the desired result
func calculateDistanceWithHaversin(crrLat: Double, crrLong: Double, desLat: Double = 23.1780068, desLong: Double = 75.7865060, radius: Double = 6367444.7) -> Double {
print("CrrLat \(crrLat) = CrrLong = \(crrLong)")
let haversin = { (angle: Double) -> Double in
return (1 - cos(angle))/2
}
let ahaversin = { (angle: Double) -> Double in
return 2 * asin(sqrt(angle))
}
// degree to radian
let dToR = { (angle: Double) -> Double in
return (angle / 360) * 2 * .pi
}
let lat1 = dToR(crrLat)
let lon1 = dToR(crrLong)
let lat2 = dToR(desLat)
let lon2 = dToR(desLong)
return radius * ahaversin(haversin(lat2 - lat1) + cos(lat1) * cos(lat2) * haversin(lon2 - lon1))
}
i have tried this also
func calculateDistance(crrLat: Double, crrLong: Double) {
let destinationLocation = CLLocation(latitude: 23.1780068, longitude: 75.7865060)
let currentLocation = CLLocation(latitude: crrLat, longitude: crrLong)
distance = currentLocation.distance(from: destinationLocation)
print(String(format: "The distance to my buddy is %.02f m", distance))
}
You can calculate distance with this Builtin Function provided by CoreLocation . The provided distance will be in meters
import CoreLocation
let locationOne = CLLocation(latitude: 37.899, longitude: 74.8989)
let locationTwo = CLLocation(latitude: 38.0900, longitude: 78.98898)
let distance = locationOne.distance(from: locationTwo)
In Swift, suppose I have an SCNVector3 variable called p and I want to be able to set p by the following code:
p = [a, b, c]
no matter [a, b, c] is either a [Float] or a [CGFloat].
How can I do that?
You can't make it generic, because you do need Float values to initialize SCNVector3, but you can unite a number of types with a protocol.
Here I made SCNVector3 ExpressibleByArrayLiteral with array elements that conform to protocol ConvertibleToFloat. That protocol handles the ability to covert the value to Float. I've implemented it for Float, CGFloat, Double, and Int and it can be extended to other types as needed:
import UIKit
import SceneKit
public protocol ConvertibleToFloat {
var floatValue: Float { get }
}
extension Float: ConvertibleToFloat {
public var floatValue: Float { return self }
}
extension CGFloat: ConvertibleToFloat {
public var floatValue: Float { return Float(self) }
}
extension Double: ConvertibleToFloat {
public var floatValue: Float { return Float(self) }
}
extension Int: ConvertibleToFloat {
public var floatValue: Float { return Float(self) }
}
extension SCNVector3: ExpressibleByArrayLiteral {
public typealias ArrayLiteralElement = ConvertibleToFloat
public init(arrayLiteral: ConvertibleToFloat...) {
let values = arrayLiteral.map { $0.floatValue } + [0, 0, 0]
self.init(x: values[0], y: values[1], z: values[2])
}
}
Testing it out:
var sv = SCNVector3(0, 0, 0)
// Initialize with array literal of CGFloat
sv = [CGFloat(1.1), CGFloat(2.2), CGFloat(3.3)]
print(sv) // SCNVector3(x: 1.1, y: 2.2, z: 3.3)
// Initialize with array literal of Float
sv = [Float(1.2), Float(2.3), Float(3.4)]
print(sv) // SCNVector3(x: 1.2, y: 2.3, z: 3.4)
// Not enough values, rest default to 0
sv = [CGFloat(3)]
print(sv) // SCNVector3(x: 3.0, y: 0.0, z: 0.0)
// Initialize with empty array
sv = []
print(sv) // SCNVector3(x: 0.0, y: 0.0, z: 0.0)
// Initilize with [Double]
sv = [1.4, 2.6, 3.7]
print(sv) // SCNVector3(x: 1.4, y: 2.6, z: 3.7)
// Initialize with [Int]
sv = [8, 9, 10]
print(sv) // SCNVector3(x: 8.0, y: 9.0, z: 10.0)
// As in the question
let a: CGFloat = 3.1
let b: CGFloat = 4.2
let c: CGFloat = 5.3
sv = [a, b, c]
print(sv) // SCNVector3(x: 3.1, y: 4.2, z: 5.3)
My little extension from #vacawama ‘s code:
import UIKit
import SceneKit
// MARK: - RealNumber (protocol)
public protocol RealNumber {
var realValue: Float { get }
}
// MARK: - RealNumber (operations)
// -a (additive inverse)
public prefix func - (a:RealNumber) -> Float {
return -a.realValue
}
// a + b (addition)
public func + <U:RealNumber, V:RealNumber>(a:U, b:V) -> Float {
return a.realValue + b.realValue
}
// a - b (substraction)
public func - <U:RealNumber, V:RealNumber>(a:U, b:V) -> Float {
return a.realValue - b.realValue
}
// a * b (multiplication)
public func * <U:RealNumber, V:RealNumber>(a:U, b:V) -> Float {
return a.realValue * b.realValue
}
// a / b (division)
public func / <U:RealNumber, V:RealNumber>(a:U, b:V) -> Float {
return a.realValue / b.realValue
}
// MARK: - RealNumber (conforming types)
extension Float: RealNumber { public var realValue: Float { return self } }
extension CGFloat: RealNumber { public var realValue: Float { return Float(self) } }
extension Double: RealNumber { public var realValue: Float { return Float(self) } }
extension Int: RealNumber { public var realValue: Float { return Float(self) } }
// MARK: - test RealNumber
let c = CGFloat(3) // CGFloat
let f = Float(2) // Float
let d = 1.5 // Double
let i = 4 // Int
// test operations
i + d // 5.5
c - f // 1.0
i * d // 6.0
i / d // 2.667
// MARK: - RealVector (protocol)
public protocol RealVector: ExpressibleByArrayLiteral {
// must have dimension (of the vector space)
static var dimension: Int { get }
// can get/set coords of the vector
var coordinates: [Float] { get set }
// must have default init
init()
// can init with [RealNumber]
init(arrayLiteral: RealNumber...)
// must have vector addition, additive inverse, and scalar multiplication
static func + (u:Self, v:Self) -> Self // vector addition
static prefix func - (u:Self) -> Self // additive inverse
static func * (a:RealNumber, v:Self) -> Self // scalar multiplication
// can get/set coordinate by subscript index
subscript(i:Int) -> Float { get set }
}
// MARK: - RealVector (default behaviors)
extension RealVector {
// no default get/set coordinates
// you have to implement them yourself ‼️
// default subscript implementation
public subscript(i:Int) -> Float {
get { return coordinates[i] }
set {
var coords = self.coordinates
coords[i] = newValue
self.coordinates = coords
}
}
// default u + v (vector addition)
public static func + (u:Self, v:Self) -> Self {
var coords = (0...(Self.dimension - 1)).map { u[$0] + v[$0] }
var vec = Self.init()
vec.coordinates = coords
return vec
}
// default u + a (convenient vector addition)
public static func + (u:Self, a:RealNumber) -> Self {
var coords = (0...(Self.dimension - 1)).map { u[$0] + a.realValue }
var vec = Self.init()
vec.coordinates = coords
return vec
}
// default -v (additive inverse)
static public prefix func - (u:Self) -> Self {
var coords = (0...(Self.dimension - 1)).map { -u[$0] }
var vec = Self.init()
vec.coordinates = coords
return vec
}
// default * (scalar multiplication)
public static func * (a:RealNumber, v:Self) -> Self {
var coords = (0...(Self.dimension - 1)).map { a.realValue * v[$0] }
var vec = Self.init()
vec.coordinates = coords
return vec
}
// default / (scalar division)
public static func / (v:Self, a:RealNumber) -> Self {
return (1/a.realValue) * v
}
// other default operations ( u-v, v*a, u+=v, u-=v, u*=a, ... )
public static func - (u:Self, v:Self) -> Self { return u + (-v) }
public static func - (u:Self, a:RealNumber) -> Self { return u + (-a) }
public static func * (v:Self, a:RealNumber) -> Self { return a * v }
public static func += (u:inout Self, v:Self) { u = u + v }
public static func -= (u:inout Self, v:Self) { u = u - v }
public static func += (u:inout Self, a:RealNumber) { u = u + a }
public static func -= (u:inout Self, a:RealNumber) { u = u - a }
public static func *= (u:inout Self, a:RealNumber) { u = u * a }
public static func /= (u:inout Self, a:RealNumber) { u = u / a }
// default init with [RealNumber]
public init(arrayLiteral elements: RealNumber...) {
var coords = elements.map { $0.realValue } + [Float](repeating: 0, count: Self.dimension)
self.init()
self.coordinates = coords
}
// default init with another RealVector
public init<V:RealVector>(_ v:V) {
self.init()
self.coordinates = v.coordinates
}
}
// vector operations of two (different types of) RealVector
// U + V -> U
public func + <U:RealVector, V:RealVector>(u:U, v:V) -> U {
return u + U.init(v)
}
// U - V -> U
public func - <U:RealVector, V:RealVector>(u:U, v:V) -> U {
return u - U.init(v)
}
// MARK: - RealVector (conforming types)
// SCNVector3
extension SCNVector3: RealVector {
// 3-dimensional
public static var dimension: Int { return 3 }
// custom get/set coordinates
public var coordinates: [Float] {
get { return [x, y, z] }
set {
var coords = newValue + [0,0,0]
x = coords[0]
y = coords[1]
z = coords[2]
}
}
}// end: SCNVector3: RealVector
// CGPoint
extension CGPoint: RealVector {
// 2-dimensional
public static var dimension: Int { return 2 }
// custom get/set coordinates
public var coordinates: [Float] {
get { return [x, y].map { $0.realValue } }
set {
var coords = newValue + [0,0]
x = CGFloat(coords[0])
y = CGFloat(coords[1])
}
}
}// end: CGPoint
// CGVector
extension CGVector: RealVector {
// 2-dimensional
public static var dimension: Int { return 2 }
// custom get/set coordinates
public var coordinates: [Float] {
get { return [dx, dy].map { $0.realValue } }
set {
var coords = newValue + [0,0]
dx = CGFloat(coords[0])
dy = CGFloat(coords[1])
}
}
}// end: CGVector
// CGSize
extension CGSize: RealVector {
// 2-dimensional
public static var dimension: Int { return 2 }
// custiom get/set coordinates
public var coordinates: [Float] {
get { return [width, height].map { $0.realValue } }
set {
var coords = newValue + [0,0]
width = CGFloat(coords[0])
height = CGFloat(coords[1])
}
}
}
// SCNVector4
extension SCNVector4: RealVector {
// 4-dimensional
public static var dimension: Int { return 4 }
// custom get/set coordinates
public var coordinates: [Float] {
get { return [x, y, z, w] }
set {
var coords = newValue + [0,0,0,0]
x = coords[0]
y = coords[1]
z = coords[2]
w = coords[3]
}
}
}// end: SCNVector4
// CGRect
extension CGRect: RealVector {
// 4-dimensional
public static var dimension: Int { return 4}
// custom get/set coordinates
public var coordinates: [Float] {
get {
return [origin.x, origin.y, width, height].map { $0.realValue }
}
set {
var v = newValue + [0,0,0,0]
origin = [v[0], v[1]]
size = [v[2], v[3]]
}
}
}// end: CGRect
// MARK: - test RealVector
var p: CGPoint = [5, 6]
p.coordinates
CGPoint.dimension
// test init
p = CGPoint(SCNVector3(1,2,3)) // init with SCNVector3
p = [CGFloat(1.1), CGFloat(2.2), CGFloat(3.3)] // init with [CGFloat]
p = [Float(1.2), Float(2.3), Float(3.4)] // init with [Float]
p = [1.2, 3.4, 5.6] // init with [Double]
p = [1, 2, 3] // init with [Int]
p = [3] // Not enough values (rest default to 0)
p = [] // init with empty array
// test vector subscript
p[1] // get: 0.0
p[1] = 3 // set: 3.0
p // (0.0, 3.0)
// test vector operations
let u: SCNVector3 = [1,2,3]
let v: SCNVector3 = [4,5,6]
var w = SCNVector3(p) // init with CGPoint
w = -u
w = u + [1,2,3]
w = u + v
w = u - v
w = 2 * u
w = u * 2
w = u / 2
w += 0.5 // (1.0, 1.5, 2.0)
w -= 2 // (-1.0, -0.5, 0)
w *= 4 // (-4, -2, 0)
w /= 2 // (-2, -1, 0)
w += [7,8,9]// (5, 7, 9)
// vector linear combinations
w = 4*u - 3*v // (-8, -7, -6)
// vector operations of two different conforming types
w + p // (-8, -4, -6)
w - p // (-8, -10, -6)
I want to calculate bearing between two location in Swift. I tried some code but it do not work. I searched about this problem but I didn't find any result about this.
func calculat(userlocation:CLLocation){
let latuserlocation: () = DegreesToRadians(userlocation.coordinate.latitude)
let lonuserlocatioc: () = DegreesToRadians(userlocation.coordinate.longitude)
latitude = NSString (string: places[activePlace]["lat"]!).doubleValue
longitude = NSString (string: places[activePlace]["lon"]!).doubleValue
let targetedPointLatitude: () = DegreesToRadians(latitude)
let targetedPointlongitude: () = DegreesToRadians(longitude)
let dlon = lonuserlocatioc - targetedPointlongitude
let y = sin(dLon) * cos(targetedPointLatitude);
let x = cos(latuserlocation) * sin(targetedPointLatitude) - sin(latuserlocation) * cos(targetedPointLatitude) * cos(dLon);
let radiansBearing = atan2(y, x);
return RadiansToDegrees(radiansBearing)
The error on let dlon = lonuserlocatioc - targetedPointlongitude is:
(cannot invoke '-' with an argument list of type '((), ())')
Compared to CLLocation Category for Calculating Bearing w/ Haversine function, your dlon is different. That answer has
let dlon = targetedPointlongitude - lonuserlocatioc
I don't know if that's your problem but it looks odd.
Swift function like this;
func radians(n: Double) -> Double{
return n * (M_PI / 180);
}
func degrees(n: Double) -> Double{
return n * (180 / M_PI);
}
func logC(val:Double,forBase base:Double) -> Double {
return log(val)/log(base);
}
func getBearing(startLat: Double,startLon:Double,endLat: Double,endLon: Double) -> Double{
var s_LAT: Double , s_LON: Double, e_LAT: Double, e_LON: Double, d_LONG: Double, d_PHI: Double;
s_LAT = startLat;
s_LON = startLon;
e_LAT = endLat;
e_LON = endLon;
d_LONG = e_LON - s_LON;
d_PHI = logC(tan(e_LAT/2.0+M_PI/4.0)/tan(s_LAT/2.0+M_PI/4.0),forBase :M_E);
if (abs(d_LONG)>M_PI){
if(d_LONG>0.0){
d_LONG = -(2.0 * M_PI - d_LONG);
}else{
d_LONG = (2.0 * M_PI - d_LONG);
}
}
return degrees(atan2(d_LONG, d_PHI)+360.0)%360.0;
}
I'm trying to implement a verlet rope in swift. I'm running into the problem where when trying to fix the position of the point masses from constraints, they very rapidly grow apart and then the coordinates become NaNs. Any idea what could be wrong in my code?
import Foundation
private let DEF_POINT_COUNT = 10
private let CONST_ITERATIONS = 5
#objc class PointMass: DebugPrintable {
var point: NSPoint
private var oldPoint: NSPoint
var x: Double {
get { return Double(point.x) }
}
var y: Double {
get { return Double(point.y) }
}
var debugDescription: String {
get { return "\(point)" }
}
init(_ point: NSPoint) {
self.point = point
self.oldPoint = point
}
func updatePosition() {
let dx = point.x - oldPoint.x
let dy = (point.y - oldPoint.y)
oldPoint = point
point = NSPoint(x: point.x + dx, y: point.y + dy)
}
func updatePosition(point: NSPoint) {
let dx = point.x - self.point.x
let dy = point.y - self.point.y
self.oldPoint = NSPoint(x: oldPoint.x + dx, y: oldPoint.y + dy)
self.point = point
}
}
struct Constraint {
var p1: PointMass
var p2: PointMass
var len: Double
func fixPoints() {
let dx = p2.x - p1.x
let dy = p2.y - p1.y
let dist = sqrt(dx*dx + dy*dy)
let diff = (dist - len)/len
p2.updatePosition(NSPoint(x: p2.x - diff*dx*0.5, y: p2.y - diff*dy*0.5))
p1.updatePosition(NSPoint(x: p1.x + diff*dx*0.5, y: p1.y + diff*dy*0.5))
}
}
#objc class Rope: NSObject {
let points: [PointMass]
let constraints: [Constraint]
init(anchor: NSPoint, end: NSPoint, length: Double, count: Int = DEF_POINT_COUNT) {
let anchorPoint = PointMass(anchor)
let endPoint = PointMass(end)
let dx = (anchorPoint.x - endPoint.x)/Double(count)
let dy = (anchorPoint.y - endPoint.y)/Double(count)
let constraintLength = length/Double(count)
var points = [endPoint]
var constraints: [Constraint] = []
for i in 1...count {
let prevPoint = points[i-1]
let newPoint = PointMass(NSPoint(x: prevPoint.x + dx, y: prevPoint.y + dy))
let constraint = Constraint(p1: prevPoint, p2: newPoint, len: constraintLength)
points.append(newPoint)
constraints.append(constraint)
}
self.points = points
self.constraints = constraints
}
func update(anchor: NSPoint, endPoint: NSPoint) {
points.first?.updatePosition(endPoint)
points.last?.updatePosition(anchor)
for point in points {
point.updatePosition()
}
for i in 0...CONST_ITERATIONS {
for constraint in constraints {
constraint.fixPoints()
}
}
}
}
Found it. The problem was in the fixPoints() method, as I suspected.
The line
let diff = (dist - len)/len
should be instead
let diff = (len - dist)/dist
I'm trying to write a category for CLLocation to return the bearing to another CLLocation.
I believe I'm doing something wrong with the formula (calculous is not my strong suit). The returned bearing is always off.
I've been looking at this question and tried applying the changes that were accepted as a correct answer and the webpage it references:
Calculating bearing between two CLLocationCoordinate2Ds
http://www.movable-type.co.uk/scripts/latlong.html
Thanks for any pointers. I've tried incorporating the feedback from that other question and I'm still just not getting something.
Thanks
Here's my category -
----- 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);
}
Your code seems fine to me. Nothing wrong with the calculous. You don't specify how far off your results are, but you might try tweaking your radian/degrees converters to this:
double DegreesToRadians(double degrees) {return degrees * M_PI / 180.0;};
double RadiansToDegrees(double radians) {return radians * 180.0/M_PI;};
If you are getting negative bearings, add 2*M_PI to the final result in radiansBearing (or 360 if you do it after converting to degrees). atan2 returns the result in the range -M_PI to M_PI (-180 to 180 degrees), so you might want to convert it to compass bearings, using something like the following code
if(radiansBearing < 0.0)
radiansBearing += 2*M_PI;
This is a porting in Swift of the Category at the beginning:
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))
}
}
Here is another implementation
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;
}
Working Swift 3 and 4
Tried so many versions and this one finally gives correct values!
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))
}
}
Usage:
let degrees = location1.bearingDegreesTo(location: location2)
This is an another CLLocation extension can be used in Swift 3 and 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)
}
}
I use the Law of Cosines in Swift. It runs faster than Haversine and its result is extremely similar. Variation of 1 metre on huge distances.
Why do I use the Law of Cosines:
Run fast (because there is no sqrt functions)
Precise enough unless you do some astronomy
Perfect for a background task
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
}
Worth mentioning that if you are using Google map GMSMapView, there's an out-of-the-box solution using the GMSGeometryHeading method:
GMSGeometryHeading(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D)
Returns the initial heading (degrees clockwise of North) at from of
the shortest path to to.
Implemented this in Swift 5. Focus is on accuracy, not speed, but it runs in real time np.
let earthRadius: Double = 6372456.7
let degToRad: Double = .pi / 180.0
let radToDeg: Double = 180.0 / .pi
func calcOffset(_ coord0: CLLocationCoordinate2D,
_ coord1: CLLocationCoordinate2D) -> (Double, Double) {
let lat0: Double = coord0.latitude * degToRad
let lat1: Double = coord1.latitude * degToRad
let lon0: Double = coord0.longitude * degToRad
let lon1: Double = coord1.longitude * degToRad
let dLat: Double = lat1 - lat0
let dLon: Double = lon1 - lon0
let y: Double = cos(lat1) * sin(dLon)
let x: Double = cos(lat0) * sin(lat1) - sin(lat0) * cos(lat1) * cos(dLon)
let t: Double = atan2(y, x)
let bearing: Double = t * radToDeg
let a: Double = pow(sin(dLat * 0.5), 2.0) + cos(lat0) * cos(lat1) * pow(sin(dLon * 0.5), 2.0)
let c: Double = 2.0 * atan2(sqrt(a), sqrt(1.0 - a));
let distance: Double = c * earthRadius
return (distance, bearing)
}
func translateCoord(_ coord: CLLocationCoordinate2D,
_ distance: Double,
_ bearing: Double) -> CLLocationCoordinate2D {
let d: Double = distance / earthRadius
let t: Double = bearing * degToRad
let lat0: Double = coord.latitude * degToRad
let lon0: Double = coord.longitude * degToRad
let lat1: Double = asin(sin(lat0) * cos(d) + cos(lat0) * sin(d) * cos(t))
let lon1: Double = lon0 + atan2(sin(t) * sin(d) * cos(lat0), cos(d) - sin(lat0) * sin(lat1))
let lat: Double = lat1 * radToDeg
let lon: Double = lon1 * radToDeg
let c: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: lat,
longitude: lon)
return c
}
I found that Haversine nailed the distance versus CLLocation's distance method, but didn't provide a bearing ready-to-use with CL. So I'm not using it for the bearing. This gives the most accurate measurement I've encountered from all the math I've tried. The translateCoord method will also precisely plot a new point given an origin, distance in meters, and a bearing in degrees.