8

I've been looking for a solution to convert cartesian coordinates (lat, long) that I have to polar coordinates in order to facilitate a simulation that I want to run, but I haven't found any questions or answers here for doing this in R. There are a number of options, including the built in function cart2pol in Matlab, but all of my data are in R and I'd like to continue getting comfortable working in this framework.

Question:

I have lat/long coordinates from tagging data, and I want to convert these to polar coordinates (meaning jump size and angle: http://en.wikipedia.org/wiki/Polar_coordinate_system) so that I can then shuffle or bootstrap them (haven't decided which) about 1,000 times, and calculate the straight-line distance of each simulated track from the starting point. I have a true track, and I'm interested in determining if this animal is exhibiting site affinity by simulating 1,000 random tracks with the same jump sizes and turning angles, but in completely different orders and combinations. So I need 1,000 straight-line distances from the origin to create a distribution of distances and then compare this to my true data set's straight-line distance.

I'm comfortable doing the bootstrapping, but I'm stuck at the very first step, which is converting my cartesian lat/long coordinates to polar coordinates (jump size and turning angle). I know there are built in functions to do this in other programs such as Matlab, but I can't find any way to do it in R. I could do it manually by hand in a for-loop, but if there's a package out there or any easier way to do it, I'd much prefer that.

Ideally I'd like to convert the data to polar coordinates, run the simulation, and then for each random track output an end point as cartesian coordinates, lat/long, so I can then calculate the straight-line distance traveled.

I didn't post any sample data, as it would just be a two-column data frame of lat and long coordinates.

Thanks for any help you can provide! If there's an easy explanation somewhere on this site or others that I missed, please point me in that direction! I couldn't find anything.

Cheers

stewart6
  • 259
  • 2
  • 4
  • 15
  • you mean Matlab's `cart2pol` function? – Nishanth May 03 '13 at 03:53
  • The `circular` package may be of interest to you: http://cran.r-project.org/web/packages/circular/circular.pdf – Ricardo Saporta May 03 '13 at 04:09
  • Yes, cart2pol. I'll take a look at the circular package, thanks! – stewart6 May 03 '13 at 04:24
  • 1
    Why is the answer not just: `r= sqrt(x^2+y^2)`? (And if you don't know the inverse transformation, just look at Wikipedia or any coordinate geometry text?) – IRTFM May 03 '13 at 05:43
  • @42 In real world axis are tilted and the origin is not 0,0 – skan Feb 05 '16 at 12:56
  • Then `r= sqrt( (x-x0)^2+(y-y0)^2)` ... the result should be invariant w.r.t. rotation. But if the question is really about the "over-the-Earth's-surface" distance, then it will vary greatly depending on latitude, because lat/long are not generally "Cartesian". If this was data all gathered in a small range of latitudes, then you could apply a simple cosine correction factor to all the longitude values before doing the calculation. – IRTFM Feb 05 '16 at 18:22

7 Answers7

7

For x-y coordinates that are in the same units (e.g. meters rather than degrees of latitude and degrees of longitude), you can use this function to get a data.frame of jump sizes and turning angles (in degrees).

getSteps <- function(x,y) {
    d <- diff(complex(real = x, imaginary = y))
    data.frame(size = Mod(d), 
               angle = c(NA, diff(Arg(d)) %% (2*pi)) * 360/(2*pi))
}

## Try it out   
set.seed(1)
x <- rnorm(10)
y <- rnorm(10)
getSteps(x, y)
#        size     angle
# 1 1.3838360        NA
# 2 1.4356900 278.93771
# 3 2.9066189 101.98625
# 4 3.5714584 144.00231
# 5 1.6404354 114.73369
# 6 1.3082132 135.76778
# 7 0.9922699  74.09479
# 8 0.2036045 141.67541
# 9 0.9100189 337.43632

## A plot helps check that this works
plot(x, y, type = "n", asp = 1)
text(x, y, labels = 1:10)

enter image description here

Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
  • Hi Josh—this worked great for converting my lat/long points to step sizes and angles. Lat and long coordinates have about the same distance between degrees at the equator, so they're essentially in the same units. How would you convert back to cartesian coordinates from a data frame of sizes and turning angles (i.e. after I randomize them?) – stewart6 May 03 '13 at 15:51
  • @stewart6 -- To convert back, convert your angles from degrees back to radians (by **dividing** by `360/(2*pi)`, and then use `sin()` and `cos()` to move from polar coordinates to x-y coordinates. I'll leave that as a good exercise for you ;) Also, unless you really are working near the equator, do be careful not to assume that degrees of lat/long are equivalent. Up where I live, around latitude 45, degrees of longitude are only about 70% the length of degrees of latitude... – Josh O'Brien May 03 '13 at 16:05
  • Hi Josh—I worked on implementing your solution this morning, and managed to get reasonable looking outputs, but I believe that for some reason the data that it's spitting out is incorrect... I've posted in more detail below. I'd appreciate any suggestions! – stewart6 May 03 '13 at 18:59
3

You can do a transformation bewteen cartesian and polar this way:

polar2cart <- function(r, theta) {
  data.frame(x = r * cos(theta), y = r * sin(theta))
}

cart2polar <- function(x, y) {
  data.frame(r = sqrt(x^2 + y^2), theta = atan2(y, x))
}
Eyayaw
  • 1,033
  • 5
  • 10
2

Since it is fairly straight forward, you can write your own function. Matlab-like cart2pol function in R:

cart2pol <- function(x, y)
{
  r <- sqrt(x^2 + y^2)
  t <- atan(y/x)

  c(r,t)
}
Nishanth
  • 6,932
  • 5
  • 26
  • 38
1

I used Josh O'Brien's code and got what appear to be reasonable jumps and angles—they match up pretty well to eyeballing the rough distance and heading between points. I then used a formula from his suggestions to create a function to turn the polar coordinates back to cartesian coordinates, and a for loop to apply the function to the data frame of all of the polar coordinates. The loops appear to work, and the outputs are in the correct units, but I don't believe the values that it's outputting are corresponding to my data. So either I did a miscalculation with my formula, or there's something else going on. More details below:

Here's the head of my lat long data:

> head(Tag1SSM[,3:4])
       lon       lat
1 130.7940 -2.647957
2 130.7873 -2.602994
3 130.7697 -2.565903
4 130.7579 -2.520757
5 130.6911 -2.704841
6 130.7301 -2.752182

When I plot the full dataset just as values, I get this plot: Lat-Long Plot

which looks exactly the same as if I were to plot this using any spatial or mapping package in R.

I then used Josh's function to convert my data to polar coordinates:

x<-Tag1SSM$lon
y<-Tag1SSM$lat

getSteps <- function(x,y) {
  d <- diff(complex(real = x, imaginary = y))
  data.frame(size = Mod(d), 
             angle = c(NA, diff(Arg(d)) %% (2*pi)) * 360/(2*pi))
}

which produced the following polar coordinates appropriately:

> polcoords<-getSteps(x,y)
> head(polcoords)
        size     angle
1 0.04545627        NA
2 0.04103718  16.88852
3 0.04667590 349.38153
4 0.19581350 145.35439
5 0.06130271  59.37629
6 0.01619242  31.86359

Again, these look right to me, and correspond well to the actual angles and relative distances between points. So far so good.

Now I want to convert these back to cartesian coordinates and calculate a euclidian distance from the origin. These don't have to be in true lat/long, as I'm just comparing them amongst themselves. So I'm happy for the origin to be set as (0,0) and for distances to be calculated in reference x,y values instead of kilometers or something like that.

So, I used this function with Josh's help and a bit of web searching:

polar2cart<-function(x,y,size,angle){

  #convert degrees to radians (dividing by 360/2*pi, or multiplying by pi/180)
  angle=angle*pi/180
  if(is.na(x)) {x=0} #this is for the purpose of the for loop below
  if(is.na(y)) {y=0}
  newx<-x+size*sin(angle)  ##X #this is how you convert back to cartesian coordinates
  newy<-y+size*cos(angle)  ##Y
  return(c("x"=newx,"y"=newy)) #output the new x and y coordinates
}

And then plugged it into this for loop:

u<-polcoords$size
v<-polcoords$angle
n<-162 #I want 162 new coordinates, starting from 0
N<-cbind(rep(NA,163),rep(NA,163)) #need to make 163 rows, though, for i+1 command below— first row will be NA
for(i in 1:n){
  jump<-polar2cart(N[i,1],N[i,2],u[i+1],v[i+1]) #use polar2cart function above, jump from previous coordinate in N vector 
  N[i+1,1]<-jump[1] #N[1,] will be NA's which sets the starting point to 0,0—new coords are then calculated from each previous N entry
  N[i+1,2]<-jump[2]
  Dist<-sqrt((N[163,1]^2)+(N[163,2]^2)) 
}

And then I can take a look at N, with my new coordinates based on those jumps:

> N
               [,1]        [,2]
  [1,]           NA          NA
  [2,]  0.011921732  0.03926732
  [3,]  0.003320851  0.08514394
  [4,]  0.114640605 -0.07594871
  [5,]  0.167393509 -0.04472125
  [6,]  0.175941466 -0.03096891

This is where the problem is... the x,y coordinates from N get progressively larger—there's a bit of variation in there, but if you scroll down the list, y goes from 0.39 to 11.133, with very few backward steps to lower values. This isn't what my lat/long data do, and if I calculated the cart->pol and pol->cart properly, these new values from N should match my lat/long data, just in a different coordinate system. This is what the N values look like plotted:

back-converted cartesian coordinates

Not the same at all... The last point in N is the farthest point from the origin, while in my lat/long data, the last point is actually quite close to the first point, and definitely not the farthest point away. I think the issue must be in my conversion from polar coordinates back to cartesian coordinates, but I'm not sure how to fix it...

Any help in solving this would be much appreciated!

Cheers

stewart6
  • 259
  • 2
  • 4
  • 15
  • Good work. Sorry, don't have much time, but here's what first pops out at me. I would expect to see something like `v <- cumsum(polcoords$angle)` in place of `v <- polcoords$angle` (after doing `v[is.na(v)] <- 0` to get rid of the NAs). The bearing of each jump is, after all, an accumulation of all the previous turn angles, and I don't *think* your code yet reflects that. Won't be able to look at this more for a while, so I hope I just *happen* to have stumbled on the (only) error. – Josh O'Brien May 03 '13 at 19:59
  • One more idea -- while you're constructing this code, you might want to play around with a very simple set of jumps. I'd probably construct and use a data.frame consisting of 4 right angle jumps and a couple of straight jumps. That will make it easier to see just where any error in our code lies. – Josh O'Brien May 03 '13 at 20:17
  • Hi Josh—thanks for the suggestions. I'll see what I can come up with and post a solution or further issues here. Your help is very much appreciated! – stewart6 May 05 '13 at 01:18
1

I think this code I wrote converts to polar coordinates:

# example data
x<-runif(30)
y<-runif(30)
# center example around 0
x<-x-mean(x)
y<-y-mean(y)

# function to convert to polar coordinates
topolar<-function(x,y){

    # calculate angles 
    alphas<-atan(y/x)

    # correct angles per quadrant
    quad2<-which(x<0&y>0)
    quad3<-which(x<0&y<0)
    quad4<-which(x>0&y<0)
    alphas[quad2]<-alphas[quad2]+pi
    alphas[quad3]<-alphas[quad3]+pi
    alphas[quad4]<-alphas[quad4]+2*pi

    # calculate distances to 0,0
    r<-sqrt(x^2+y^2)

    # create output
    polar<-data.frame(alphas=alphas,r=r)

}

# call function
polar_out<-topolar(x,y)
# get out angles
the_angles<-polar_out$alphas
Jintram
  • 31
  • 2
0

Another option only in degree

pol2car = function(angle, dist){
    co = dist*sin(angle)
    ca = dist*cos(angle)
    return(list(x=ca, y=co))
}
pol2car(angle = 45, dist = sqrt(2))
Laurel
  • 5,965
  • 14
  • 31
  • 57
Edwin Torres
  • 117
  • 2
  • 6
0

cart2sph {pracma} Transforms between cartesian, spherical, polar, and cylindrical coordinate systems in two and three dimensions.

LBV
  • 11
  • 3