Reputation: 2282
I have my location (coordinate) and my heading (compass) and am trying to calculate the heading towards a given other coordinate, so that I can show an arrow on screen pointing towards that location. What I'm building is basically a glorified compass that doesn't point north, but towards a specific coordinate, so it will need to take into account the bearing towards that coordinate and the user's compass heading.
I was expecting CoreLocation to contain the necessary building blocks for this, but aside from getting the distance between two coordinates, there doesn't appear to be anything there.
Unfortunately my tries of manually calculating this haven't been very fruitful and it's hard to guess where I'm going wrong. I've since tried adapting what I found here, and I get the same results for the sample data there, but it goes totally awry as soon as I use my own coordinates.
extension Double {
var radians: Double {
self * .pi / 180
}
}
let A = CLLocationCoordinate2D(latitude: 51.0295437, longitude: 13.7277793)
let B = CLLocationCoordinate2D(latitude: 51.026819, longitude: 13.726348)
let ΔL = abs(A.longitude) - abs(B.longitude)
let X = cos(B.latitude.radians) * sin(ΔL.radians)
let Y = cos(A.latitude.radians) * sin(B.latitude.radians) - sin(A.latitude.radians) * cos(B.latitude.radians) * cos(ΔL.radians)
let bearing = atan2(X, Y)
let heading = bearing - userHeading
Once I incorporate the device's heading into this (the substraction at the end) it appears as if my values are somehow flipped along the W-E-axis (pointing left instead of right and the other way around), but the N-S-axis appears to be correct. The calculation of the bearing must be off somehow. I really don't have much knowledge regarding geocoordinate systems.
Upvotes: 0
Views: 1798
Reputation: 536037
Let's try it this way. First I need to establish some conversions:
extension Double {
var toRadians : Double {
var m = Measurement(value: self, unit: UnitAngle.degrees)
m.convert(to: .radians)
return m.value
}
var toDegrees : Double {
var m = Measurement(value: self, unit: UnitAngle.radians)
m.convert(to: .degrees)
return m.value
}
}
Now I'll make a formula; a
is where I am, b
is the distant point whose bearing I want:
let a = // some CLLocationCoordinate2D
let b = // some CLLocationCoordinate2D
let deltaL = b.longitude.toRadians - a.longitude.toRadians
let thetaB = b.latitude.toRadians
let thetaA = a.latitude.toRadians
let x = cos(thetaB) * sin(deltaL)
let y = cos(thetaA) * sin(thetaB) - sin(thetaA) * cos(thetaB) * cos(deltaL)
let bearing = atan2(x,y)
let bearingInDegrees = bearing.toDegrees
print(bearingInDegrees) // sanity check
Now I'll test some actual bearings. I'll give a
and b
followed by what was printed (bearingInDegrees
). I'll start with the page we got the formula from:
let a = CLLocationCoordinate2D(latitude: 39.099912, longitude: -94.581213)
let b = CLLocationCoordinate2D(latitude: 38.627089, longitude: -90.200203)
// 96.51262423499941, yep, that's the answer given on example page
Okay, that worked. Now I'll try myself and some points in various directions from me:
let a = CLLocationCoordinate2D(latitude: 34.439931, longitude: -119.263984)
let b = CLLocationCoordinate2D(latitude: 34.489290, longitude: -119.221670)
// 35 degrees, about right
let a = CLLocationCoordinate2D(latitude: 34.439931, longitude: -119.263984)
let b = CLLocationCoordinate2D(latitude: 34.484479, longitude: -119.308273)
// -40 degrees, about right
let a = CLLocationCoordinate2D(latitude: 34.439931, longitude: -119.263984)
let b = CLLocationCoordinate2D(latitude: 34.376978, longitude: -119.329645)
// -139, about right
Okay, let's try it in some other location, where longitude values are positive:
let a = CLLocationCoordinate2D(latitude: 52.518044, longitude: 13.374602) // Berlin
let b = CLLocationCoordinate2D(latitude: 53.444748, longitude: 14.534668) // Szczecin
// 36, looks good to me
Fine! Now let's fill in the last piece of the puzzle, namely the bearing given the way I am actually facing. I think in degrees, so let's keep using degrees. I'll take the last result as an example. I'm in Berlin but now I'm facing east. Call the top of my device zero. What angle should the needle point, in order to point to Szczecin?
let myHeading = 90.0 // (I'm facing east)
let bearingFromMe = bearingInDegrees - myHeading
print(bearingFromMe) // -53, sounds good
-53
means forward and to my left. Sure enough, I'm facing east, so that's the direction we want to go.
Upvotes: 8