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
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
REXX
<lang rexx>/*REXX pgm calculates the difference between 2 angles (degrees), normalizes the result. */ 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 /* " " " " " */ exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ show: procedure; parse arg a,b; $='º' /*obtain the 2 angles (are in degrees).*/
say right(a,5)$ ' -'right(b,5)$ " ───► "right(((((b-a)//360)+540)//360)-180, 5)$ return d /* [↑] display the angular difference.*/</lang>
output
20º - 45º ───► 25º -45º - 45º ───► 90º -85º - 90º ───► 175º -95º - 90º ───► -175º -45º - 125º ───► 170º -45º - 145º ───► -170º
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°