132

atan2(y, x) has that discontinuity at 180° where it switches to -180°..0° going clockwise.

How do I map the range of values to 0°..360°?

here is my code:

CGSize deltaPoint = CGSizeMake(endPoint.x - startPoint.x, endPoint.y - startPoint.y);
float swipeBearing = atan2f(deltaPoint.height, deltaPoint.width);

I'm calculating the direction of a swiping touch event given the startPoint and endPoint, both XY point structs. The code is for the iPhone but any language that supports atan2f() will do.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
willc2
  • 38,991
  • 25
  • 88
  • 99
  • Note: The posted update method will not return zero degrees, but values from just above 0 to 360.0. – chux - Reinstate Monica Sep 27 '13 at 02:37
  • 2
    > **[How to Get angle from 2 positions][1]** [1]: http://stackoverflow.com/questions/9457988/bearing-from-one-coordinate-to-another/29471137#29471137 – MAnoj Sarnaik Apr 08 '15 at 08:45
  • This function works great, however the angle of "bearingDegrees" calculation is flipped. for example, 45 degrees would typically by the 1st quadrant, however this in the 4th quadrant. 135 degrees would typically be in the 2nd quadrant but this function returns it to be in the 3rd quadrant. i can simply take the function return value x and negate that from 360 to get the correct angle value however I'm curious to know why this is happening in the first place? – goelv May 18 '15 at 10:16

16 Answers16

130

Solution using Modulo

A simple solution that catches all cases.

degrees = (degrees + 360) % 360;  // +360 for implementations where mod returns negative numbers

Explanation

Positive: 1 to 180

If you mod any positive number between 1 and 180 by 360, you will get the exact same number you put in. Mod here just ensures these positive numbers are returned as the same value.

Negative: -180 to -1

Using mod here will return values in the range of 180 and 359 degrees.

Special cases: 0 and 360

Using mod means that 0 is returned, making this a safe 0-359 degrees solution.

Liam George Betsworth
  • 18,373
  • 5
  • 39
  • 42
75
(x > 0 ? x : (2*PI + x)) * 360 / (2*PI)
BartoszKP
  • 34,786
  • 15
  • 102
  • 130
erikkallen
  • 33,800
  • 13
  • 85
  • 120
  • 8
    Probably also want x >= 0 for the x = 0 case. – bpw1621 Nov 19 '11 at 16:14
  • 15
    For those not comfortable with this notation, and without the conversion to degrees built in: if(x>0) {radians = x;} else {radians = 2*PI + x;} so we are just adding 2PI to the result if it is less than 0. – David Doria Sep 25 '12 at 19:05
  • 3
    Or `(x >= 0 ? x : (2*PI + x)) * 180/PI` as in `(x < 0 ? 2*PI + x : x) * 180/PI` – user3342816 Nov 09 '14 at 09:06
52

Add 360° if the answer from atan2 is less than 0°.

Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
dave4420
  • 46,404
  • 6
  • 118
  • 152
44

Or if you don't like branching, negate the two parameters and add 180° to the answer.

(Adding 180° to the return value puts it nicely in the 0-360 range, but flips the angle. Negating both input parameters flips it back.)

Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
aib
  • 45,516
  • 10
  • 73
  • 79
  • 2
    I'd rather modify my code to use denormalized angles (<0, >=360) but there always seems to be someone aiming for that fake "optimized" feel; that's why I wanted to add this. (Or was it because this was the quicker way around some temporary debug code I used? hmm) – aib Apr 22 '12 at 00:26
  • 2
    Definitely not straightforward to grok, as I can concur after 2+ years. So: Adding 180° to the return value puts it nicely in the 0-360 range, but flips the angle. Negating both input parameters flips it back. – aib Nov 19 '14 at 10:57
  • This can have some issues when $x = 0$ and $y > 0$ iirc – Trinidad Feb 10 '15 at 11:44
24

@erikkallen is close but not quite right.

theta_rad = atan2(y,x);
theta_deg = (theta_rad/M_PI*180) + (theta_rad > 0 ? 0 : 360);

This should work in C++: (depending on how fmod is implemented, it may be faster or slower than the conditional expression)

theta_deg = fmod(atan2(y,x)/M_PI*180,360);

Alternatively you could do this:

theta_deg = atan2(-y,-x)/M_PI*180 + 180;

since (x,y) and (-x,-y) differ in angles by 180 degrees.

Jason S
  • 184,598
  • 164
  • 608
  • 970
14

I have 2 solutions that seem to work for all combinations of positive and negative x and y.

1) Abuse atan2()

According to the docs atan2 takes parameters y and x in that order. However if you reverse them you can do the following:

double radians = std::atan2(x, y);
double degrees = radians * 180 / M_PI;
if (radians < 0)
{
    degrees += 360; 
}

2) Use atan2() correctly and convert afterwards

double degrees = std::atan2(y, x) * 180 / M_PI;
if (degrees > 90)
{
    degrees = 450 - degrees;
}
else
{
    degrees = 90 - degrees;
}
Fibbs
  • 1,350
  • 1
  • 13
  • 23
10

@Jason S: your "fmod" variant will not work on a standards-compliant implementation. The C standard is explicit and clear (7.12.10.1, "the fmod functions"):

if y is nonzero, the result has the same sign as x

thus,

fmod(atan2(y,x)/M_PI*180,360)

is actually just a verbose rewriting of:

atan2(y,x)/M_PI*180

Your third suggestion, however, is spot on.

Stephen Canon
  • 103,815
  • 19
  • 183
  • 269
10

Here's some javascript. Just input x and y values.

var angle = (Math.atan2(x,y) * (180/Math.PI) + 360) % 360;
7

This is what I normally do:

float rads = atan2(y, x);
if (y < 0) rads = M_PI*2.f + rads;
float degrees = rads*180.f/M_PI;
andrés
  • 412
  • 6
  • 14
4

An alternative solution is to use the mod () function defined as:

function mod(a, b) {return a - Math.floor (a / b) * b;}

Then, with the following function, the angle between ini(x,y) and end(x,y) points is obtained. The angle is expressed in degrees normalized to [0, 360] deg. and North referencing 360 deg.

    function angleInDegrees(ini, end) {
        var radian = Math.atan2((end.y - ini.y), (end.x - ini.x));//radian [-PI,PI]
        return mod(radian * 180 / Math.PI + 90, 360);
    }
3
angle = Math.atan2(x,y)*180/Math.PI;

I have made a Formula for orienting angle into 0 to 360

angle + Math.ceil( -angle / 360 ) * 360;
Saad Ahmed
  • 1,077
  • 9
  • 9
2
double degree = fmodf((atan2(x, y) * (180.0 / M_PI)) + 360, 360);

This will return degree from 0°-360° counter-clockwise, 0° is at 3 o'clock.

Pang
  • 9,564
  • 146
  • 81
  • 122
Finallz
  • 71
  • 4
2

A formula to have the range of values from 0 to 360 degrees.

f(x,y)=180-90*(1+sign(x))* (1-sign(y^2))-45*(2+sign(x))*sign(y)

     -(180/pi())*sign(x*y)*atan((abs(x)-abs(y))/(abs(x)+abs(y)))
1

The R packages geosphere will calculate bearingRhumb, which is a constant bearing line given an origin point and easting/northing. The easting and northing must be in a matrix or vector. The origin point for a wind rose is 0,0. The following code seems to readily resolve the issue:

windE<-wind$uasE
windN<-wind$vasN
wind_matrix<-cbind(windE, windN)
wind$wind_dir<-bearingRhumb(c(0,0), wind_matrix)
wind$wind_dir<-round(wind$wind_dir, 0)
Ariane
  • 11
  • 1
1
theta_rad = Math.Atan2(y,x);
if(theta_rad < 0)
  theta_rad = theta_rad + 2 * Math.PI;    //if neg., add 2 PI to it
theta_deg = (theta_rad/M_PI*180) ;        //convert from radian to degree

//or
theta_rad = Math.Atan2(y,x);
theta_rad = (theta_rad < 0) ? theta_rad + 2 * Math.PI : theta_rad;
theta_deg = (theta_rad/M_PI*180) ;

-1 deg becomes (-1 + 360) = 359 deg
-179 deg becomes (-179 + 360) = 181 deg

PhilC
  • 11
  • 1
1

For your application I suspect you don't need exact degrees and would prefer a more approximate compass angle, eg 1 of 16 directions? If so then this code avoids atan issues and indeed avoids floating point altogether. It was written for a video game so uses 8 bit and 16 bit integers:

/*

                                           349.75d         11.25d, tan=0.2034523
                                              \             /
                                               \   Sector  /      
                                                \    0    /  22.5d tan = ?2 - 1
                                             15      |      1   33.75
                                                     |         /   45d, tan = 1
                                        14           |            2 _56.25
                                                     |             /  67.5d, tan = 1 + ?2
                                     13              |               3
                                                     |                __ 78.75
                                                     |                
                                    12---------------+----------------4 90d tan = infty
                                                     |                __ 101.25
                                                     |                
                                     11              |               5
                                                     |               
                                        10           |            6
                                                     |          
                                             9       |      7
                                                     8



*/

// use signs to map sectors:
static const int8_t map[4][5] = {  /* +n means n >= 0, -n means n < 0 */
  /* 0: +x +y */ {0, 1, 2, 3, 4},
  /* 1: +x -y */ {8, 7, 6, 5, 4},
  /* 2: -x +y */ {0, 15, 14, 13, 12},
  /* 3: -x -y */ {8, 9, 10, 11, 12}
};

int8_t sector(int8_t x, int8_t y) { // x,y signed in range -128:127, result 0:15 from north, clockwise.
  int16_t tangent; // 16 bits
  int8_t quadrant = 0;
  if (x > 0) x = -x; else quadrant |= 2; // make both negative avoids issue with negating -128 
  if (y > 0) y = -y; else quadrant |= 1;
  if (y != 0) {
    // The primary cost of this algorithm is five 16-bit multiplies.
    tangent = (int16_t)x*32;   // worst case y = 1, tangent = 255*32 so fits in 2 bytes.
    /*
       determine base sector using abs(x)/abs(y).
       in segment:
           0 if         0 <= x/y < tan 11.25   -- centered around 0     N
           1 if tan 11.25 <= x/y < tan 33.75   --                 22.5  NxNE
           2 if tan 33.75 <= x/y < tan 56.25   --                 45    NE
           3 if tan 56.25 <= x/y < tan 78.75   --                 67.5  ExNE
           4 if tan 78.75 <= x/y < tan 90      --                 90    E
    */
    if (tangent > y*6  ) return map[quadrant][0]; // tan(11.25)*32
    if (tangent > y*21 ) return map[quadrant][1]; // tan(33.75)*32
    if (tangent > y*47 ) return map[quadrant][2]; // tan(56.25)*32
    if (tangent > y*160) return map[quadrant][3]; // tan(78.75)*32
    // last case is the potentially infinite tan(90) but we don't need to check that limit.
  }
  return map[quadrant][4];
}
Graham Toal
  • 324
  • 1
  • 7