Angle difference between two bearings
Finding the angle between two bearings is often confusing.[1]
- Task
We need to find the angle which is the result of the subtraction b2-b1, where b1 and b2 are both bearings. Input bearings are expressed by numbers in the range -180 to +180 degrees. The result must also be expressed in the range +180 to -180 degrees. Compute the angle for the following pairs:
- 20 degrees (b1) and 45 degrees (b2)
- -45 and 45
- -85 and 90
- -95 and 90
- -45 and 125
- -45 and 145
- 29.4803 and -88.6381
- -78.3251 and -159.036
Optional extra: allow the input bearings to be any (finite) value. Test cases:
- -70099.74233810938 and 29840.67437876723
- -165313.6666297357 and 33693.9894517456
- 1174.8380510598456 and -154146.66490124757
- 60175.77306795546 and 42213.07192354373
C++
<lang cpp>#include <cmath>
- include <iostream>
using namespace std;
double getDifference(double b1, double b2) { double r = b2-b1; int a = ((int)r) / 360.0; r -= a * 360.0; if (r < -180.0) r += 360.0; if (r >= 180.0) r -= 360.0; return r; }
int main() { cout << "Input in -180 to +180 range" << endl; cout << getDifference(20.0, 45.0) << endl; cout << getDifference(-45.0, 45.0) << endl; cout << getDifference(-85.0, 90.0) << endl; cout << getDifference(-95.0, 90.0) << endl; cout << getDifference(-45.0, 125.0) << endl; cout << getDifference(-45.0, 145.0) << endl; cout << getDifference(-45.0, 125.0) << endl; cout << getDifference(-45.0, 145.0) << endl; cout << getDifference(29.4803, -88.6381) << endl; cout << getDifference(-78.3251, -159.036) << endl;
cout << "Input in wider range" << endl; cout << getDifference(-70099.74233810938, 29840.67437876723) << endl; cout << getDifference(-165313.6666297357, 33693.9894517456) << endl; cout << getDifference(1174.8380510598456, -154146.66490124757) << endl; cout << getDifference(60175.77306795546, 42213.07192354373) << endl;
return 0; }</lang>
- Output:
Input in -180 to +180 range 25 90 175 -175 170 -170 170 -170 -118.118 -80.7109 Input in wider range -139.583 -72.3439 -161.503 37.2989
Haskell
<lang Haskell>import Text.Printf
type Radians = Float
type Degrees = Float
angleBetweenDegrees :: Degrees -> Degrees -> Degrees angleBetweenDegrees a b = degrees $ relBearing (radians a) (radians b)
relBearing :: Radians -> Radians -> Radians relBearing a b -- sign * dot-product
= sign * acos ((ax * bx) + (ay * by)) where (ax, ay) = (sin a, cos a) (bx, by) = (sin b, cos b) sign -- cross-product > 0 ? = if ((ay * bx) - (by * ax)) > 0 then 1 else (-1)
degrees :: Radians -> Degrees degrees = (/ pi) . (180 *)
radians :: Degrees -> Radians radians = (/ 180) . (pi *)
main :: IO () main =
mapM_ putStrLn $ displayRow <$> [ (20.0, 45.0) , (-45.0, 45.0) , (-85.0, 90.0) , (-95.0, 90.0) , (-45.0, 125.0) , (-45.0, 145.0) ] where displayRow (x, y) = printf "%6.2f° + %6.2f° -> %7.2f°" x y $ angleBetweenDegrees x y</lang>
- Output:
20.00° + 45.00° -> 25.00° -45.00° + 45.00° -> 90.00° -85.00° + 90.00° -> 175.00° -95.00° + 90.00° -> -175.00° -45.00° + 125.00° -> 170.00° -45.00° + 145.00° -> -170.00°
J
<lang j>relativeBearing=: -&360^:(180&<)@(360 | 720 + -~)/</lang> <lang j>tests=: _99&".;._2 noun define 20 45 -45 45 -85 90 -95 90 -45 125 -45 145 29.4803 -88.6381 -78.3251 -159.036 -70099.74233810938 29840.67437876723 -165313.6666297357 33693.9894517456 1174.8380510598456 -154146.66490124757 60175.77306795546 42213.07192354373 )
tests ,. relativeBearing"1 tests 20 45 25 _45 45 90 _85 90 175 _95 90 _175 _45 125 170 _45 145 _170 29.4803 _88.6381 _118.118
_78.3251 _159.036 _80.7109 _70099.7 29840.7 _139.583
_165314 33694 _72.3439 1174.84 _154147 _161.503 60175.8 42213.1 37.2989</lang>
JavaScript
ES5
This approach should be reliable but it is also very inefficient.
<lang javascript>function relativeBearing(b1Rad, b2Rad) { b1y = Math.cos(b1Rad); b1x = Math.sin(b1Rad); b2y = Math.cos(b2Rad); b2x = Math.sin(b2Rad); crossp = b1y * b2x - b2y * b1x; dotp = b1x * b2x + b1y * b2y; if(crossp > 0.) return Math.acos(dotp); return -Math.acos(dotp); }
function test() { var deg2rad = 3.14159265/180.0; var rad2deg = 180.0/3.14159265; return "Input in -180 to +180 range\n" +relativeBearing(20.0*deg2rad, 45.0*deg2rad)*rad2deg+"\n" +relativeBearing(-45.0*deg2rad, 45.0*deg2rad)*rad2deg+"\n" +relativeBearing(-85.0*deg2rad, 90.0*deg2rad)*rad2deg+"\n" +relativeBearing(-95.0*deg2rad, 90.0*deg2rad)*rad2deg+"\n" +relativeBearing(-45.0*deg2rad, 125.0*deg2rad)*rad2deg+"\n" +relativeBearing(-45.0*deg2rad, 145.0*deg2rad)*rad2deg+"\n"
+relativeBearing(29.4803*deg2rad, -88.6381*deg2rad)*rad2deg+"\n" +relativeBearing(-78.3251*deg2rad, -159.036*deg2rad)*rad2deg+"\n"
+ "Input in wider range\n" +relativeBearing(-70099.74233810938*deg2rad, 29840.67437876723*deg2rad)*rad2deg+"\n" +relativeBearing(-165313.6666297357*deg2rad, 33693.9894517456*deg2rad)*rad2deg+"\n" +relativeBearing(1174.8380510598456*deg2rad, -154146.66490124757*deg2rad)*rad2deg+"\n" +relativeBearing(60175.77306795546*deg2rad, 42213.07192354373*deg2rad)*rad2deg+"\n";
}</lang>
- Output:
Input in -180 to +180 range 25.000000000000004 90 174.99999999999997 -175.00000041135993 170.00000000000003 -170.00000041135996 -118.1184 -80.71089999999998 Input in wider range -139.5833974814558 -72.34414600076728 -161.50277501127033 37.2988761562732
ES6
<lang JavaScript>(() => {
const [Pi, sin, cos, acos] = ['PI', 'sin', 'cos', 'acos'] .map(k => Math[k]), degRad = x => Pi * x / 180.0, radDeg = x => 180.0 * x / Pi;
// relBearing :: Radians -> Radians -> Radians const relBearing = (ar, br) => { const [ax, ay] = [sin(ar), cos(ar)], [bx, by] = [sin(br), cos(br)],
// Cross-product > 0 ? sign = ((ay * bx) - (by * ax)) > 0 ? +1 : -1;
// Sign * dot-product return sign * acos((ax * bx) + (ay * by)); }
// TEST
// justifyRight :: Int -> Char -> Text -> Text const justifyRight = (n, cFiller, strText) => n > strText.length ? ( (cFiller.repeat(n) + strText) .slice(-n) ) : strText;
// showMap :: Degrees -> Degrees -> String const showMap = (da, db) => justifyRight(6, ' ', `${da}° +`) + justifyRight(11, ' ',` ${db}° -> `) + justifyRight(7, ' ', `${(radDeg(relBearing(degRad(da), degRad(db)))) .toPrecision(4)}°`);
return [ [20, 45], [-45, 45], [-85, 90], [-95, 90], [-45, 125], [-45, 145] ].map(xy => showMap(...xy)) .join('\n');
})();</lang>
- Output:
20° + 45° -> 25.00° -45° + 45° -> 90.00° -85° + 90° -> 175.0° -95° + 90° -> -175.0° -45° + 125° -> 170.0° -45° + 145° -> -170.0°
Perl 6
<lang perl6>sub infix:<∠> (Rat $b1, Rat $b2) {
(my $b = ($b2 - $b1 + 720) % 360) > 180 ?? $b - 360 !! $b;
}
- TESTING
for 20.0, 45.0,
-45.0, 45.0, -85.0, 90.0, -95.0, 90.0, -45.0, 125.0, -45.0, 145.0, 29.4803, -88.6381, -78.3251, -159.036, -70099.74233810938, 29840.67437876723, -165313.6666297357, 33693.9894517456, 1174.8380510598456, -154146.66490124757, 60175.77306795546, 42213.07192354373
-> $b1, $b2 { say "$b1 ∠ $b2 = ", $b1 ∠ $b2 }</lang>
- Output:
20 ∠ 45 = 25 -45 ∠ 45 = 90 -85 ∠ 90 = 175 -95 ∠ 90 = -175 -45 ∠ 125 = 170 -45 ∠ 145 = -170 29.4803 ∠ -88.6381 = -118.1184 -78.3251 ∠ -159.036 = -80.7109 -70099.74233810938 ∠ 29840.67437876723 = -139.58328312339 -165313.6666297357 ∠ 33693.9894517456 = -72.3439185187 1174.8380510598456 ∠ -154146.66490124757 = -161.5029523074156 60175.77306795546 ∠ 42213.07192354373 = 37.29885558827
Python
<lang python>from __future__ import print_function
def getDifference(b1, b2): r = (b2 - b1) % 360.0 # Python modulus has same sign as divisor, which is positive here, # so no need to consider negative case if r >= 180.0: r -= 360.0 return r
if __name__ == "__main__": print ("Input in -180 to +180 range") print (getDifference(20.0, 45.0)) print (getDifference(-45.0, 45.0)) print (getDifference(-85.0, 90.0)) print (getDifference(-95.0, 90.0)) print (getDifference(-45.0, 125.0)) print (getDifference(-45.0, 145.0)) print (getDifference(-45.0, 125.0)) print (getDifference(-45.0, 145.0)) print (getDifference(29.4803, -88.6381)) print (getDifference(-78.3251, -159.036))
print ("Input in wider range") print (getDifference(-70099.74233810938, 29840.67437876723)) print (getDifference(-165313.6666297357, 33693.9894517456)) print (getDifference(1174.8380510598456, -154146.66490124757)) print (getDifference(60175.77306795546, 42213.07192354373))</lang>
- Output:
Input in -180 to +180 range 25.0 90.0 175.0 -175.0 170.0 -170.0 170.0 -170.0 -118.11840000000001 -80.71089999999998 Input in wider range -139.58328312338563 -72.34391851868713 -161.50295230740448 37.29885558826936
Racket
see my comments in discussion regards bearing-heading or vice versa
<lang racket>#lang racket (define (% a b) (- a (* b (truncate (/ a b)))))
(define (bearing- bearing heading)
(- (% (+ (% (- bearing heading) 360) 540) 360) 180))
(module+ main
(bearing- 20 45) (bearing- -45 45) (bearing- -85 90) (bearing- -95 90) (bearing- -45 125) (bearing- -45 145) (bearing- 29.4803 -88.6381) (bearing- -78.3251 -159.036)
(bearing- -70099.74233810938 29840.67437876723) (bearing- -165313.6666297357 33693.9894517456) (bearing- 1174.8380510598456 -154146.66490124757) (bearing- 60175.77306795546 42213.07192354373))
(module+ test
(require rackunit)
(check-equal? (% 7.5 10) 7.5) (check-equal? (% 17.5 10) 7.5) (check-equal? (% -7.5 10) -7.5) (check-equal? (% -17.5 10) -7.5))</lang>
- Output:
-25 -90 -175 175 -170 170 118.11839999999995 80.71090000000004 139.58328312338563 72.34391851868713 161.50295230740448 -37.29885558826936
REXX
A little extra coding was added for a better visual presentation; the angles were centered, the answers were aligned. <lang rexx>/*REXX pgm calculates the difference between 2 angles (degrees), normalizes the result. */ numeric digits 25 /*use enough decimal diigits for angles*/ call show 20, 45 /*display the angular difference (deg).*/ call show -45, 45 /* " " " " " */ call show -85, 90 /* " " " " " */ call show -95, 90 /* " " " " " */ call show -45, 125 /* " " " " " */ call show 45, 145 /* " " " " " */ call show 29.4803, -88.6361 /* " " " " " */ call show -78.3251, -159.036 /* " " " " " */ call show -70099.74233810938, 29840.67437876723 /* " " " " " */ call show -165313.6666297357, 33693.9894517456 /* " " " " " */ call show 1174.8380510598456,-154146.66490124757 /* " " " " " */ call show 60175.773067955546, 42213.07192354373 /* " " " " " */ exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ show: procedure; parse arg a,b; d=digits(); $='º' /*obtain the 2 angles (are in degrees)*/
x=format( ( ( ((b-a) // 360) + 540) // 360) - 180, 4, d-4) /*compute and format. */ if pos(., x)\==0 then x=strip( strip(x, 'T', 0), "T", .) /*strip trailing chaff.*/ say center(a || $, d) '─' center(b || $, d) "───►" x || $ return /* [↑] display the angular difference*/</lang>
output
20º ─ 45º ───► 25º -45º ─ 45º ───► 90º -85º ─ 90º ───► 175º -95º ─ 90º ───► -175º -45º ─ 125º ───► 170º 45º ─ 145º ───► 100º 29.4803º ─ -88.6361º ───► -118.1164º -78.3251º ─ -159.036º ───► -80.7109º -70099.74233810938º ─ 29840.67437876723º ───► -139.58328312339º -165313.6666297357º ─ 33693.9894517456º ───► -72.3439185187º 1174.8380510598456º ─ -154146.66490124757º ───► -161.5029523074156º 60175.773067955546º ─ 42213.07192354373º ───► 37.298855588184º
zkl
<lang zkl>fcn bearingAngleDiff(b1,b2){ // -->Float, b1,b2 can be int or float
( (b:=(0.0 + b2 - b1 + 720)%360) > 180 ) and b - 360 or b;
}</lang> <lang zkl>T( 20,45, -45,45, -85,90, -95,90, -45,125, -45,145 ) .pump(Console.println,Void.Read,
fcn(b1,b2){ "%.1f\UB0; + %.1f\UB0; = %.1f\UB0;" .fmt(b1,b2,bearingAngleDiff(b1,b2)) });</lang>
- Output:
20.0° + 45.0° = 25.0° -45.0° + 45.0° = 90.0° -85.0° + 90.0° = 175.0° -95.0° + 90.0° = -175.0° -45.0° + 125.0° = 170.0° -45.0° + 145.0° = -170.0°