How to map atan2() to degrees 0-360
MathQuartz 2dAtan2Math Problem Overview
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.
Math Solutions
Solution 1 - Math
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.
Solution 2 - Math
(x > 0 ? x : (2*PI + x)) * 360 / (2*PI)
Solution 3 - Math
Just add 360° if the answer from atan2 is less than 0°.
Solution 4 - Math
Or if you don't like branching, just 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.)
Solution 5 - Math
@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.
Solution 6 - Math
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;
}
Solution 7 - Math
@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.
Solution 8 - Math
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;
Solution 9 - Math
Here's some javascript. Just input x and y values.
var angle = (Math.atan2(x,y) * (180/Math.PI) + 360) % 360;
Solution 10 - Math
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);
}
Solution 11 - Math
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;
Solution 12 - Math
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.
Solution 13 - Math
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)))
Solution 14 - Math
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)
Solution 15 - Math
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
Solution 16 - Math
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];
}