Difference (angle) between two bearings - r

Using geosphere::bearing I can calculate the bearing of two lines, but is it possible to calculate the angle between the two bearings ?
Of course you can try and subtract or sum up the bearings but in specific cases where one is negative and the other is positive this doesn't work.
For example if the ber1 = - 175 and ber2 = 175 the angle between should be 10.
Any suggestions ?

I am not sure of a ready-made package but in case you are interested in a solution then you can try
angle_diff <- function(theta1, theta2){
theta <- abs(theta1 - theta2) %% 360
return(ifelse(theta > 180, 360 - theta, theta))
}
which gives the angle between your example bearings -175 & 175 as
angle_diff(-175, 175)
#[1] 10

Related

r - find the side of turning to achieve smallest angle between two bearings

This question is building on Difference (angle) between two bearings.
Basically, if we can find the difference between two bearings, can we find the side on which the object needs to turn to achieve the smallest amount of rotation.
Using the solution/function in R, from the previous question:
angle_diff <- function(ber1, ber2){
theta <- abs(ber1 - ber2) %% 360
return(ifelse(theta > 180, 360 - theta, theta))
}
To show what is needed here are 2 examples:
First ex.: if we have ber1 = - 175 and ber2 = 175, in order for the object to go from bearing -175 to bearing 175 it needs to turn counter clockwise for 10 degrees.
Second ex.: if we have ber1 = - 10 and ber2 = 50, in order for the object to go from bearing -10 to bearing 50 it needs to turn clockwise for 60 degrees.
Finding the amount of degrees to make the shortest term is answered in the question mentioned above, but is it possible to find whether the turn needs to be made clockwise or counter clockwise ?
Perhaps something like this?
bearing_diff <- function(b1, b2) {
angle <- b2 - b1
clockwise <- angle %% 360
counter_clockwise <- 360 - clockwise
if(abs(clockwise) < abs(counter_clockwise)) {
paste(abs(clockwise), "degrees clockwise")
} else {
paste(abs(counter_clockwise), "degrees counter-clockwise")
}
}
bearing_diff(175, -175)
#> [1] "10 degrees clockwise"
bearing_diff(0, 180)
#> [1] "180 degrees counter-clockwise"
bearing_diff(0, 185)
#> [1] "175 degrees counter-clockwise"
Created on 2020-08-15 by the reprex package (v0.3.0)

In geosphere package in R, why aren't bearings in 0-360 degrees?

When I calculated bearings between points using the bearing function in the geosphere package, the resulting bearings spanned -180 - 180 degrees. However, based on the geosphere package documentation, I expected the bearings to span 0-360 degrees. Here's a quote from the documentation:
Directions are expressed in degrees (North = 0 and 360, East = 90, Sout = 180, and West = 270 degrees).
What am I missing?
Here's a small example:
# set up
library(geosphere)
library(ggplot2)
# create data frame of long/lat
long <- c(-55.25, -55.25, -55.25, -55, -55, -55, -54.75, -54.75, -54.75)
lat <- c(-13.5, -13.25, -13, -13.5, -13.25, -13, -13.5, -13.25, -13)
id <- c("a", "b", "c", "d", "e", "f", "g", "h", "i")
pts <- data.frame(id=id, long=long, lat=lat)
# plot
ggplot(pts, aes(x=long, y=lat, colour=id)) +
geom_point()
# calculate bearings from point e to all other points
pts <- pts[,c(2:3)]
b <- bearing(pts[5,], pts)
# I expected this:
# b[1] = 225
# b[2] = 270
# b[3] = 315
# but instead, found this:
b[1]
b[2]
b[3]
Dependent on the navigational task, terms like 'angle', 'direction', 'heading', and/or 'bearing' can mean different things.
The documentation of the bearing() function speaks about
the initial bearing (direction; azimuth) to go from point p1 to point
p2.
Thus, the directions that the function geosphere::bearing() deliver are azimuths (angles). Obviously, these are expressed in degrees -180 ... 180. That is the relative direction you would turn when facing North. This is important as it assumes you are looking to the North and thus your destination (or point p2) could be reached with an initial left/right turn, i.e. negative azimuths reflect a left turn, positive azimuths showing your destination on your right-hand side.
When you speak about a standardised initial course direction (North := 000 or 360 degrees), your reference is not which way you look, but which course direction you follow.
For example, ships or aircraft fly a specific course while they have to correct for wind offsets. I do not go into more details here on the difference between course and heading (the direction the nose of the ship or aircraft points).
However, in order to determine the course, a left turn (negative azimuth) needs to be subtracted from the 'North direction 360', while positive azimuths are added to the (North direction interpreted as 0).
To fix this mathematically, you can go the long angle-math way (c.f. this atan-based solution), or apply a more pragmatic approach:
We can force a positive sign by adding a "full circle" (360 degrees) and then check how far we have moved beyond the North-mark (360) by using the modulo operator.
E.g. 90 + 360 = 450 with 450 %modulo-360% = 1 * 360 + 90 = 90. For negative azimuths, this yields -90 + 360 = 270 or expressed as modulo of 360: 270 = 0 * 360 + 270.
The modulo is always the rest after the plus. Please note that you could also add 2 or more full circles. This will not impact the rest.
To do this in R simply use the modulo operator, i.e. %%, to determine the rest beyond 360:
course <- (b + 360) %% 360 # add full circle, i.e. +360, and determine modulo for 360
pts$BEARING <- b
pts$COURSE <- course
pts

Working with degrees of a circle in R

I have a bearing of X degrees and I wish to calculate other bearings either side of X. We all know a circle has 360 degrees but if I'm given a bearing of 0 degrees and I wish to calculate the bearings + and - 30 degrees of 0 how can I program this such that I am looking for 0 - 30 = 330 degs and 0 + 30 = 30 degrees?
The modulus operator (%%) can help you with that
x <- 0
(x+30) %% 360
# [1] 30
(x-30) %% 360
# [1] 330
In this case it will keep all values in the range [0, 360). See the ?"%%" help page for more information or even the wikipedia page for these types of operations

Calculating if an angle is between two angles

So I am making a little game where I am checking if a character can "see" another where character A can see character B if A is within a certain distance of B, and the direction in degrees of A is +/- 45 degrees of the angle B is facing.
Currently, I do a little calculation where I'm checking if
(facingAngle - 45) =< angleOfTarget =< (facingAngle + 45)
This works fine except for when we cross the 360 degree line.
Let's say facingAngle = 359, angleOfTarget = 5. In this situation, the target is only 6 degrees off center, so I want my function to return true. Unfortunately, 5 is not between 314 and 404.
Just try
anglediff = (facingAngle - angleOfTarget + 180 + 360) % 360 - 180
if (anglediff <= 45 && anglediff>=-45) ....
The reason is that the difference in angles is facingAngle - angleOfTarget although due to wrapping effects, might be off by 360 degrees.
The add 180+360 then modulo 360 then subtract 180, effectively just converts everything to the range -180 to 180 degrees (by adding or subtracting 360 degrees).
Then you can check the angle difference easily, whether it is within -45 to 45 degrees.
Here is a simple function I found online, and modified. It works correctly for any angles (can be outside of 0-360). (This function is made to work in c, works in Xcode.)
Remember, it checks COUNTER-CLOCKWISE from angle A to angle B. It returns YES (true) if the angle is between :)
First, a simple conversion function to make all angles 1-360
//function to convert angle to 1-360 degrees
static inline double angle_1to360(double angle){
angle=((int)angle % 360) + (angle-trunc(angle)); //converts angle to range -360 + 360
if(angle>0.0)
return angle;
else
return angle + 360.0;
}
Check if angle is between :)
//check if angle is between angles
static inline BOOL angle_is_between_angles(float N,float a,float b) {
N = angle_1to360(N); //normalize angles to be 1-360 degrees
a = angle_1to360(a);
b = angle_1to360(b);
if (a < b)
return a <= N && N <= b;
return a <= N || N <= b;
}
Eg. To check if the angle 300 is between 180 and 10 degrees:
BOOL isBetween=angle_is_between_angles( 300, 180,10);
//RETURNS YES
There is a trigonometric solution that avoids the wrapping problem.
I'm assuming that you have (x, y) coordinates for both characters P1 and P2. You've already specified that you know the distance between the two which you presumably calculated using Pythagoras' theorem.
You can use the dot product of two vectors to calculate the angle between them:
A . B = |A| . |B| . cos(theta).
If you take A as the facingAngle vector it will be [cos(fA), sin(fA)], and will have magnitude |A| of 1.
If you take B as the vector between the two characters, and your distance above you get:
cos(theta) = (cos(fA) * (P2x - P1x) + sin(fA) * (P2y - P1y)) / |B|
where |B| is the distance you've already calculated.
You don't need to actually take the inverse cosine to find theta, since for range of -45 to +45 you just need to check for cos(theta) >= 0.70710678 (i.e. 1 / sqrt(2)).
This might seem slightly complicated, but the chances are that you've already got all of the required variables hanging around in your program anyway.
A simple solution to handle wrapping at the low end (into negative values), is just to add 360 to all your values:
(facingAngle + 315) =< (angleOfTarget + 360) =< (facingAngle + 405)
That way, the subtraction of 45 can never go negative, because it no longer happens.
To handle wrapping at the top end, you need to check again, adding another 360 to the angleOfTarget value:
canSee = (facingAngle + 315 <= angleOfTarget + 360) &&
(angleOfTarget + 360 <= facingAngle + 405);
canSee |= (facingAngle + 315 <= angleOfTarget + 720) &&
(angleOfTarget + 720 <= facingAngle + 405);
Another way using always minimum positive difference and comparing with threshold:
anglediff = Math.min(Math.abs(facingAngle - angleOfTarget), 360 - Math.abs(angleOfTarget - allowDirection));
if (anglediff <= 45)
Restating Alnitak's answer in a different way, a solution that avoids the angle wrap at 360 degrees is to restate the problem in a different coordinate system where the angles are always small. Here is the code:
def inside_angle(facing, target):
dot = cos(facing)*cos(target) + sin(facing)*sin(target)
angle = acos(dot)
return angle <= pi/4
This is done using vector projection. Assuming the vectors |facing> = [cos(facing) sin(facing)] and |target> = [cos(target) sin(target)], when projecting the target into the facing vector, the angle will range from zero, when the target is exactly at the facing vector or will increase to either side. This way we can just compare it to pi/4 (45 degrees). The formula for the angle is the following:
cos(angle) = <facing|target> / <target|target> <facing|facing>
That is, the cosine of the angle is the dot product between the vectors |facing> and |target> divided their modules, which is 1 in this case, which becomes:
angle = acos(<facing|target>)
Reference:
https://en.wikipedia.org/wiki/Vector_projection

Calculating reflex and obtuse angles

Given two lines of common origin point, whose rotational angles I have in degrees, what's the best way in Lua to calculate the reflex and obtuse angles involved?
Assuming that you want the reflex angle that complements the obtuse angle, and don't worry about getting values of 90 or 180 degrees for the obtuse value:
function obtuse_reflex(a, b)
local diff = (a - b) % 180
if diff < 90 then diff = 180 - diff end
return diff, 360 - diff
end

Resources