Reputation: 627
This is a problem that I fought with for a bit and couldn't find a good answer out there. I did eventually solve it in R, but thought I would post it anyway in case others need it. If anyone has a more elegant solution, I would love to see it. This is a bit of a brute force effort.
I have a series of paired XY (Cartesian) coordinates. I can easily get the angles between them using a simple atan() command. However, I want the angles in compass (polar? cardinal?) directions (where north=0°, east=90°, and so forth). Here's a minimal example to make the data and the Cartesian angles, and I've posted my brute force conversion to compass angle below. The degree conversion (from radians) uses deg() from the 'circular' package.
require(circular)
test <- data.frame(x=c(0,1,1,1,0,-1,-1,-1),y=c(1,1,0,-1,-1,-1,0,1))
test$angle <- deg(atan(test$y/test$x))
test
...produces
x y angle
1 0 1 90
2 1 1 45
3 1 0 0
4 1 -1 -45
5 0 -1 -90
6 -1 -1 45
7 -1 0 0
8 -1 1 -45
Note that the angles into the lower-left and upper-left quadrants are the same as that into the lower- and upper-right quadrants, losing the directionality of the vectors.
Upvotes: 3
Views: 6971
Reputation: 193
recently I bumped into the same problem
the trick here is the use of:
this is my reproducible example
x <- c(1, 1, 0, -1, -1, -1, 0, 1)
y <- c(0, 1, 1, 1, 0, -1, -1, -1)
mydf<- data.frame(x,y)
mydf$radians<-with(mydf, atan2(y,x))
# define a function for calculation of degrees from radians
rad2deg <- function(rad) {(rad * 180) / (pi)}
mydf$deg_cartesian<-rad2deg(mydf$radians)
# here calculating the modulus by 450 mod degrees
# 450 = 360+90. i.e. shifting by 90 degrees
mydf$deg_compass<-with(mydf,(450-deg_cartesian)%%360)
mydf
posted as a follow-up and for future memory (especially mine!)
Upvotes: 1
Reputation: 117
Its all linear. There are two difficulties: the direction of increasing angle is CCW in a Cartesian system, and CW in a Navigation system, and 0° Cartesian is 90° on a chart. We can fix the first problem by changing the sign: N <= -C.
Now 0 maps to 0. We want 0 to map to 90, so just add 90: N <= -C + 90.
But, whoops, that gives us some angles outside the usual range 0-360. But, angles repeat mod 360, so we can fix that easily: N <= -c + 90 modulo 360.
Modulo operators disagree as to how to deal with negative numbers, so to be safe, add 360 first to eliminate the problem: N <= (-C + 90 + 360) modulo 360.
In Excel, MOD(450 - C, 360) or in javascript, ((450 - C)) % 360. I'm afraid I don't know R. [But in Fortran IV, it would be FN = mod(450.0 - C, 360.0)]
Upvotes: 3
Reputation: 132706
ang <- function(x,y) {
z <- x + 1i * y
res <- 90 - Arg(z) / pi * 180
res %% 360
}
ang(test$x, test$y)
#[1] 0 45 90 135 180 225 270 315
Upvotes: 3
Reputation: 627
First, shifting the angles to measure of the y-axis (or North=0) can be done easily by altering the angle line of the code by subtracting 90 degrees (thanks JK):
test$angle <- 90-deg(atan(test$y/test$x))
However, the conversion of the left-bound vectors needs to be maintained, so an adjustment for these angles based on the signs of the X and Y values was my solution:
# Make new column for the polar/compass angles
test$polar <- test$angle
# Then make the necessary adjustments
# Adjustment for quadrant C (bottom left, 180 to 270°)
test[sign(test$x)==-1 & sign(test$y)==-1,"polar"] <- ((1-(test[sign(test$x)==-1 & sign(test$y)==-1,"angle"]/90))*90)+180
# Adjustment for quadrant D (top left, 270 to 360°)
test[sign(test$x)==-1 & sign(test$y)>=0, "polar"] <- abs(test[sign(test$x)==-1 & sign(test$y)>=0,"angle"])+180
...producing:
x y angle polar
1 0 1 0 0
2 1 1 45 45
3 1 0 90 90
4 1 -1 135 135
5 0 -1 180 180
6 -1 -1 45 225
7 -1 0 90 270
8 -1 1 135 315
Again, I'm posting this as a working solution, but more elegant or easier suggestions would be appreciated too!
Upvotes: 1