Wednesday, May 16, 2007

Calculate turning angles and step lengths from location data

anglefun <- function(xx,yy,bearing=TRUE,as.deg=FALSE){
  ## calculates the compass bearing of the line between two points
## xx and yy are the differences in x and y coordinates between two points
## Options:
## bearing = FALSE returns +/- pi instead of 0:2*pi
## as.deg = TRUE returns degrees instead of radians
c = 1
if (as.deg){
c = 180/pi
}

b<-sign(xx)
b[b==0]<-1 #corrects for the fact that sign(0) == 0
tempangle = b*(yy<0)*pi+atan(xx/yy)
if(bearing){
#return a compass bearing 0 to 2pi
#if bearing==FALSE then a heading (+/- pi) is returned
tempangle[tempangle<0]<-tempangle[tempangle<0]+2*pi
}
return(tempangle*c)
}

bearing.ta <- function(loc1,loc2,loc3,as.deg=FALSE){
## calculates the bearing and length of the two lines
## formed by three points
## the turning angle from the first bearing to the
## second bearing is also calculated
## locations are assumed to be in (X,Y) format.
## Options:
## as.deg = TRUE returns degrees instead of radians
if (length(loc1) != 2 | length(loc2) != 2 | length(loc3) !=2){
print("Locations must consist of either three vectors, length == 2,
or three two-column dataframes"
)
return(NaN)
}
c = 1
if (as.deg){
c = 180/pi
}

locdiff1<-loc2-loc1
locdiff2<-loc3-loc2
bearing1<-anglefun(locdiff1[1],locdiff1[2],bearing=F)
bearing2<-anglefun(locdiff2[1],locdiff2[2],bearing=F)

if(is.data.frame(locdiff1)){
dist1<-sqrt(rowSums(locdiff1^2))
dist2<-sqrt(rowSums(locdiff2^2))
}else{
dist1<-sqrt(sum(locdiff1^2))
dist2<-sqrt(sum(locdiff2^2))
}

ta=(bearing2-bearing1)

ta[ta < -pi] = ta[ta < -pi] + 2*pi
ta[ta > pi] = ta[ta > pi] - 2*pi
return(list(bearing1=unlist(bearing1*c),bearing2=unlist(bearing2*c),
ta=unlist(ta*c),dist1=unlist(dist1),dist2=unlist(dist2)))
}

8 comments:

Mollie said...

I used a version of your code to do some simulations. I used
x2=x1+dist*sin(angle)
y2=y1+dist*cos(angle)

Then a more mathy friend pointed out that cos is more typically associated with x and sin with y. So I changed the code to be
anglefun=function(dx,dy)
{
b=sign(dy)
b[b==0]=1
temp=b*(dx<0)*pi+atan(dy/dx)
temp
}
and used
x2=x1+dist*cos(angle)
y2=y1+dist*sin(angle)

Thanks for getting me headed in the right direction!

Forester said...

It all depends on what you are referencing as North. Mathy folks like to reference their angles to the x-axis; however, I like to use the y-axis as a reference because I think of my animal movements in terms of UTM coordinates, with x and y corresponding to Easting and Northing, respectively. A five unit movement due North (bearing = 0 degrees), would mean dx=0, dy=5.

5*cos(0)= 5
5*sin(0)= 0

So the method you have suggested is not wrong (from a math perspective is is actually the best way to go), but you need to keep in mind that 0 deg bearings in your simulation correspond to 90 degrees (East) on a real map. In practice this only really matters when you are trying to include directional biases that are related to compass bearings.

Gabriel said...

I can't understand the R code, but am interested in the formula you use for calculating the differences in the angle of two successive steps. Could you please post or send via email, in mathematical rather than R terms?
Thanks.

Tigga said...

Just a note to say thank you. I'm working on animal movement data and you saved me a lot of time as, otherwise, I would have had to implement these myself today.

Ben said...
This comment has been removed by the author.
Ben said...

Hey, I'm currently working on animal movement data. I have calculated the compass heading from North, and now trying to calculate the turn angle without using coordinates. I was wondering what formula you would recommend as my data set consists of over 3000 rows and simply cant do this by hand.

Thank you for your time,

Nn said...

Hi!
Thanks for the code. It is great. I was wondering how I should cite it.
Thanks!

Sachin said...

Simple and extremely useful script! Thanks for putting this up.