11

Given two bearing, how do I find the smallest angle between them?

So for example if 1 heading is 340 degrees and the second is 10 degrees the smallest angle will be 30 degrees.

I've attached a picture to show what I mean. I've tried subtracting one from the other but that didn't work because of the wrap around effect of a circle. I've also tried using negative degrees (180 - 359 being -180 to 0) but that got messed up when trying to calculate the angle between positive and negative number.

I'm sure there must be an easier way that having lots of if statements.

Thank for your help. Adam

BTW. This is a navigation question so the radius of the circle is unknown.

Finding the angle between two headings

Adam Davies
  • 2,742
  • 4
  • 33
  • 52

6 Answers6

19

I ended up using the following formula found on this message board because I needed the result to be signed based on the direction (clockwise or counterclockwise). It has a good explanation of exactly what's going on.

((((bearing - heading) % 360) + 540) % 360) - 180
Damian Dixon
  • 889
  • 8
  • 21
Luke
  • 334
  • 4
  • 11
  • The initial `% 360` is unnecessary if |bearing - heading| <= 360 =: Bound. Can also add a multiple of 360 to 540 if Bound is larger. – user66081 Nov 13 '18 at 09:53
12
float getDifference(float a1, float a2) {
    return Math.min((a1-a2)<0?a1-a2+360:a1-a2, (a2-a1)<0?a2-a1+360:a2-a1)
}
Rob Watts
  • 6,866
  • 3
  • 39
  • 58
7

What about:

angle = Math.abs(a1-a2);
if (angle > 180)
    angle = 360 - angle;

You mention an issue regarding positive and negative numbers, so perhaps there is something I'm not considering here...

femtoRgon
  • 32,893
  • 7
  • 60
  • 87
  • I believe that your answer is equivalent to mine, though perhaps a bit easier to understand. Another option that looks like a cross between the two we've already got is `Math.min(Math.abs(a1-a2), 360 - Math.abs(a1-a2));` – Rob Watts Apr 24 '13 at 15:24
  • They are equivalent in the sense that they are both correct and valid. To my mind, your implementation treats the issue as a problem of correctness of the OP's algorithm, while I thought of it as a problem of formatting an already correct value. Both are valid ways to look at the problem, and I think there is enough difference in approach that both are of value. – femtoRgon Apr 24 '13 at 15:43
2

For navigation, you might want to know if b1 is left or right of b2, so here it is in a nice function. (Assumes exactly 0 is not a use case)

function isBearing1LeftOrRightOfBearing2 (b1, b2) {
  if (Math.sign(((b1 - b2 + 540) % 360) - 180) > 0) {
    return 'left'
  } else {
    return 'right'
  }
}
Simon Hutchison
  • 2,949
  • 1
  • 33
  • 32
1

You need to consider the difference in both directions.

public static double bearingDiff(double a, double b) {
    double maxBearing = Math.max(a, b);
    double minBearing = Math.min(a, b);
    double antiClockwiseDiff = maxBearing - minBearing;
    double clockwiseDiff = minBearing + 360 - maxBearing;
    return Math.min(antiClockwiseDiff, clockwiseDiff);
}
Michael Fry
  • 1,090
  • 9
  • 12
1

If angle direction is needed, then this will work:

    int maxBearing = Math.max(bearing0, bearing1);
    int minBearing = Math.min(bearing0, bearing1);
    int firstDir = maxBearing - minBearing;  
    int secondDir = minBearing + 360 - maxBearing;  
    int diff = Math.min(firstDir, secondDir);

    boolean anticlock_dir = false;

    int anticlock = bearing1 + diff;
    if (anticlock >= 360)
        anticlock = anticlock - 360;

    if (anticlock ==  bearing0)
        anticlock_dir = true;
Martin Koubek
  • 433
  • 4
  • 8