Reputation: 2329
I am working with a cartesian system with all positive values. I need to calculate the planimetric deflection between two points (x,y,z). I am mainly interested in returning a 0-360 value clockwise relative to a "north" reference vector, though I have written the function (I think) so I can assign the reference vector to any direction. The following code seems to return what I want, but the if statement seems like a bit of a hack. Is there a cleaner way to handle it purely within the calculations?
I tried arctan2 with similar results.
import numpy as np
# define 'origin' point where coords are X,Y,Z
p1 = [10000,10000,0]
# create function to compute angle
def ang(p1,p2):
"""
p1 is [x,y,z] 'from'
p2 is [x,y,z] 'to'
"""
# set reference point relative to p1
p0 = [p1[0],p1[1]+1000,0] # 1000 unit offset is arbitrary
# assign X,Y points to variables as arrays
a = np.array(p0[0:2])
b = np.array(p1[0:2]) # the origin
c = np.array(p2[0:2])
# create vectors
ba: ndarray = a - b # subtract the origin
bc: ndarray = c - b # subtract the origin
# calculate angle
cosine_angle = np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc))
angle = np.arccos(cosine_angle)
# adjust for hemisphere
if bc[0] < 0:
return(360 - np.degrees(angle))
else:
return(np.degrees(angle))
test_lst = [[10000, 20000,0],[20000, 20000,0],[20000,10000,0],[20000,0,0],[10000,0,0],[0,0,0],[0,10000,0],[0,20000,0]]
for i in test_lst:
a = ang(p1,i)
print(a)
with the if statement (this is what I want):
expected | returned
0.0 | 0.0
45.0 | 45.0
90.0 | 90.0
135.0 | 135.0
180.0 | 180.0
225.0 | 225.0
270.0 | 270.0
315.0 | 315.0
without the if statement:
expected | returned
0.0 | 0.0
45.0 | 45.0
90.0 | 90.0
135.0 | 135.0
180.0 | 180.0
225.0 | 135.0
270.0 | 90.0
315.0 | 45.0
Upvotes: 0
Views: 1769
Reputation: 932
This is not about Python, but about how the math works. The arccos
(also arcsin
, etc.) functions return a non-unique value, since the [-1, 1]
range of possible sine/cosine values maps to an interval which is 180 degrees wide. The arctan2
, which returns the angle in the range of -180 to 180 degrees, was introduced (into C, as atan2
) to fight the very same issue. Fortunately, it's an easy way to convert [-180, 180]
to [0, 360]
in Python:
angle = np.arctan2(...)
return np.degrees(angle) % 360.0 # the modulo operator does this job well
Upvotes: 1