Calculating bearing between two CLLocation points in Swift
Solution 1
Here is an Objective-C solution
which can easily be translated to Swift:
func degreesToRadians(degrees: Double) -> Double { return degrees * .pi / 180.0 }
func radiansToDegrees(radians: Double) -> Double { return radians * 180.0 / .pi }
func getBearingBetweenTwoPoints1(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)
}
The result type is Double
because that is how all location coordinates are
stored (CLLocationDegrees
is a type alias for Double
).
Solution 2
This isn't exactly accurate, but you're probably looking for something along the lines of:
func XXRadiansToDegrees(radians: Double) -> Double {
return radians * 180.0 / M_PI
}
func getBearingBetweenTwoPoints(point1 : CLLocation, point2 : CLLocation) -> Double {
// Returns a float with the angle between the two points
let x = point1.coordinate.longitude - point2.coordinate.longitude
let y = point1.coordinate.latitude - point2.coordinate.latitude
return fmod(XXRadiansToDegrees(atan2(y, x)), 360.0) + 90.0
}
I appropriated the code from this NSHipster article that goes into more detail about what's wrong with it. The basic issue is that it's using the coordinates as though the world is flat (which it isn't, right?). Mattt's article can show you how to get the real directions using MKMapPoint
s instead of CLLocation
s.
Comments
-
Jeef almost 4 years
I'm trying to calculate a bearing between two CLLocation points in swift-only code. I've run into some difficulty and was assuming this is a pretty simple function. Stack overflow didn't seem to have anything listed.
func d2r(degrees : Double) -> Double { return degrees * M_PI / 180.0 } func RadiansToDegrees(radians : Double) -> Double { return radians * 180.0 / M_PI } func getBearing(fromLoc : CLLocation, toLoc : CLLocation) { let fLat = d2r(fromLoc.coordinate.latitude) let fLng = d2r(fromLoc.coordinate.longitude) let tLat = d2r(toLoc.coordinate.latitude) let tLng = d2r(toLoc.coordinate.longitude) var a = CGFloat(sin(fLng-tLng)*cos(tLat)); var b = CGFloat(cos(fLat)*sin(tLat)-sin(fLat)*cos(tLat)*cos(fLng-tLng)) return atan2(a,b) }
I'm getting an error with my atan2 call about lvalue cgfloat or something...
-
nr5 over 7 yearsWhy can't we calculate this by subtracting the course value on CLLocation object? But even if I try this, just imagine a case where user goes from 0 to 359 degrees, in that case, what if the actual angle of change was 1 degree not 359 degrees? How can I eliminate this??
-
nr5 over 7 yearsWhy can't we calculate this by subtracting the course value on CLLocation object? But even if I try this, just imagine a case where user goes from 0 to 359 degrees, in that case, what if the actual angle of change was 1 degree not 359 degrees? How can I eliminate this??
-
Martin R over 7 years@Nil: The course value is the direction in which the device is currently moving. This code is about the direction from one given point to another point and unrelated to the devices location or moving. (So the parameters could better be CLLocationCoordinate2D instead of CLLocation. I'll update that later.)
-
nr5 over 7 yearsok. But can you help with my other concern, i.e the 0-359 degreee case.
-
Martin R about 7 years@Nil: What exactly is the problem? The above code should return a value between -180 and +180 degrees.
-
nr5 about 7 yearsLets say the user is driving on a 4 lane road. In 1st lane he is travelling in the direction of 0 degree towards north. Now the road gets a small curve towards left and user steers the vehicle so that he is now travelling to 345 degree towards north. Now i need the angle of that curve which should be 15 degrees and not 345 degrees. Simple subtraction will not help me here...
-
Martin R about 7 years@Nil: That is quite unrelated to this Q&A. But you can always normalize the difference by adding or subtracting 360 to be in the range -180...+180.
-
nr5 about 7 yearsI agree. But consider these two data points. In this case the bearing is just around 1 degrees which is correctly found by your logic. But consider a round about where you take a 3th exit. So you completed an angle of 270 degrees. But your answer will say 90 degrees. Am I correct?
-
Uma Madhavi about 7 years@Guy Kogus. I am working marker position on map but its not working . Can u guide me how to achieve this.
-
Guy Kogus about 7 yearsSorry Uma, I've never actually had to use it myself so not sure I can be much help.
-
Abin Baby about 6 years@Nil Did you find a solution for the issue which you have mentioned in your comment.
-
Abin Baby about 6 years@MartinR Any suggestions on this issue. I tried the above mentioned code to calculate direction.
-
nr5 about 6 years@AbinBaby In my case I was calculating the angle of difference between the consecutive location coordinates. So I calculated bothways and used the smaller one assuming in just 2 corrdinates, user can't change 359 degress
-
Borys T over 4 yearsIs there a reason why this test will fail? let location1 = CLLocation(latitude: 50, longitude: -120.1) let location2 = CLLocation(latitude: 50, longitude: -120) let expectedDirection: CLLocationDirection = 90 let direction = LocationService.getDirectionFromTwoPoints(point1: location1, point2: location2) XCTAssertEqual(expectedDirection, direction)
-
William J Bagshaw almost 4 yearsScale of long and lat are very different. Say, for example, at the north pole.
-
Desmond Hume almost 4 years
cos(lat2)
is called twice, could be extracted into a precomputed variable. -
Dave Hubbard over 2 yearsYou probably want to use the 'haversine' algorithm. which accounts for the earth being a sphere. You get two bearings, the initial one, and the final one (yes it changes). For short distance the initial bearing works fine. See movable-type.co.uk/scripts/latlong.html for an excellent discussion. I wrote it in Java, and now will be converting to Swift.