Search code examples
rangle

Relative angle between point and vector


I need to calculate the angle between an array of points and an array of vectors (adjusting for the direction of the vectors). I'm using the angle2 function (based on atan2) from this to calculate the angle, however I seem to be doing something wrong.

The data are 2 continuous tracks. One track comes with a course value, the other one is simply coordinates. So one track (Easting2, Northing2) is an array of vectors, the other (Easting, Northing) - of just points. I figured I had to make the points into vectors relative to the other, vector-based, track, then use the atan2 approach. Here's my thought process:

  1. Use track2 (Easting2, Northing2) to adjust the position of track1 (Easting, Northing; i.e., the points of track 2 become the origin for track1)
  2. Calculate a set of coordinates if track2 took a step of length 1 at the same direction as the given track2 course (this is the next.easting and next.northing)
  3. Calculate the angle between the re-positioned track1 and the imaginary point of track2.

However, in the example here, I would have expected to get an angle of ~30 degrees for the first row and ~50 degrees for the second row. What am I doing wrong??

toy example:

library(dplyr)
df <- structure(list(Easting = c(519984.801109577, 520254.016648783
), Northing = c(8015778.00697284, 8017130.41190275), Easting2 = c(520033.416692364, 
518599.418722116), Northing2 = c(8029164.59837475, 8023952.71894817
), Course = c(195.6, 196)), .Names = c("Easting", "Northing", 
"Easting2", "Northing2", "Course"), row.names = c(NA, -2L), class = c("tbl_df", 
"tbl", "data.frame"))


# function from the linked post, to calculate angles between vectors
angle2 <- function(M,N){
atan2(N[2],N[1]) - atan2(M[2],M[1]) 
        }

df %>%
mutate(Easting.adj = Easting - Easting2,
    Northing.adj = Northing - Northing2,
    Course.rad = Course * pi / 180,
    Next.Easting2 = cos(Course.rad),
    Next.Northing2 = sin(Course.rad)) %>%
rowwise() %>%
mutate(Angle = angle2(c(Next.Easting2, Next.Northing2), c(Easting.adj, Northing.adj)),
    # convert back to degrees
    Angle = Angle * 180 / pi)

Here's a Google Earth image if the two lines. enter image description here


Solution

  • The image is not totally accurate for the supplied numbers, since Easting.adj is negative for row 1 and positive for row 2. The problem is caused because Mathematical angles are defined counter-clockwise from the x-direction, while your nautical course seems to be defined clockwise from the north-direction. One should use 90 - Course:

    df %>%
      mutate(Easting.adj = Easting - Easting2,
             Northing.adj = Northing - Northing2,
             Course.rad = (90 - Course) * pi / 180,
             Next.Easting2 = cos(Course.rad),
             Next.Northing2 = sin(Course.rad)) %>%
      rowwise() %>%
      mutate(Angle = angle2(c(Next.Easting2, Next.Northing2), c(Easting.adj, Northing.adj)),
             # convert back to degrees
             Angle = Angle * 180 / pi)
    

    The above code generates 15.4 degree and 29.6 degree as your angles. Alternatively, you can stay with angles:

    df %>%
      mutate(Easting.adj = Easting - Easting2,
             Northing.adj = Northing - Northing2,
             math_course = 90 - Course) %>%
      rowwise() %>%
      mutate(direction = atan2(Northing.adj, Easting.adj) * 180 / pi,
             angle = direction - math_course)