Calculating the sine, cosine and angle between 3 points - python

I've written a function to calculate the cosine, sine and degrees of the angle between three points of which I have the x and y coordinates - point 1 (x1, y1), point 2 (x2, y2) and point 3 (x3, y3). I've written the function and been trying to test it out, but I'm not completely confident in how accurate it is. Does anyone know if I've made a mistake in my calculations?
def path_angle_degree(x1, y1, x2, y2, x3, y3):
u = (x2 - x1, y2 - y1)
v = (x3 - x2, y3 - y2)
norm_u = math.sqrt(u[0] * u[0] + u[1] * u[1])
norm_v = math.sqrt(v[0] * v[0] + v[1] * v[1])
# this conditional is to check there has been movement between the points
if norm_u < 0.001 or norm_v < 0.001:
return (None, None, None)
prod_n = norm_u * norm_v
dot_uv = u[0] * v[0] + u[1] * v[1]
cos_uv = dot_uv / prod_n
# fixes floating point rounding
if cos_uv > 1.0 or cos_uv < -1.0:
cos_uv = round(cos_uv)
radians = math.acos(cos_uv)
sin_uv = math.sin(radians)
degree = math.degrees(radians)
return (cos_uv, sin_uv, degree)
An example of this function being called on a straight path would be:
print(path_angle_degree(6,6,7,6,8,6))
Thanks so much!

In addition to being expensive, sin_uv is unstable and doesn't cope with numerous edge cases. Use the cross product instead (you only need the z-component).
Also, you'll find it simpler and cheaper to normalize u and v before computing the products.
Once you have cos_uv and sin_uv, use atan2 to get the angle.

You might want to look at the arctan2() function.

Related

Mathematical calculation in python abs function

I am trying to create the equation in python.
Sorry in advance if this has already been asked! If so, I couldn't find it, so please share the post!
I run into the problem that I don't know how to code the part in the red square (see equation ).
As I understand it the "|u1|" stands for the absolute value of u1. However, if I code it like the equation is written i.e. abs(u1)abs(u2) I get a syntax error (which I kind of expected).
My problem is the line of code:
angle = np.arccos((Mu1*Mu2)/(abs(Mu1)abs(Mu2)))
My complete code is:
import numpy as np
from math import sqrt
#first direction vector
#punt 1, PQ
# [x,y]
P = (1,1)
Q = (5,3)
#punt 2, RS
R = (2,3)
S = (4,1)
#direction vector = arctan(yq-yp/xq-xp)
#create function to calc direction vector of line
def dirvec(coord1, coord2):
#pull coordinates into x and y variables
x1 , y1 = coord1[0], coord1[1]
x2 , y2 = coord2[0], coord2[1]
#calc vector see article
v = np.arctan((y2-y1)/(x2-x1))
#outputs in radians, not degrees
v = np.degrees(v)
return v
print(dirvec(P,Q))
print(dirvec(R,S))
Mu1 = dirvec(P,Q)
Mu2 = dirvec(R,S)
angle = np.arccos((Mu1*Mu2)/(abs(Mu1)abs(Mu2)))
print(angle)
Thins I tried:
multiply the two abs, but then I'll get the same number (pi) every time:
np.arccos((Mu1*Mu2)/(abs(Mu1)*abs(Mu2)))
+ and - but I cannot imagine these are correct:
np.arccos((Mu1Mu2)/(abs(Mu1)+abs(Mu2))) np.arccos((Mu1Mu2)/(abs(Mu1)-abs(Mu2)))
In the formula, the numerator is the dot product of two vectors, and the denominator is the product of the norms of the two vectors.
Here is a simple way to write your formula:
import math
def dot_product(u, v):
(x1, y1) = u
(x2, y2) = v
return x1 * x2 + y1 * y2
def norm(u):
(x, y) = u
return math.sqrt(x * x + y * y)
def get_angle(u, v):
return math.acos( dot_product(u,v) / (norm(u) * norm(v)) )
def make_vector(p, q):
(x1, y1) = p
(x2, y2) = q
return (x2 - x1, y2 - y1)
#first direction vector
#punt 1, PQ
# [x,y]
P = (1,1)
Q = (5,3)
#punt 2, RS
R = (2,3)
S = (4,1)
angle = get_angle(make_vector(p,q), make_vector(r,s))
print(angle)
From what I see, the result of your code would always be pi or 0. It will be pi if one of the mu1 or mu2 is negative and when both are negative or positive it will be zero.
If I remember vectors properly :
Given two vectors P and Q, with say P = (x, y) and Q = (a, b)
Then abs(P) = sqrt(x^2 + y^2) and P. Q = xa+yb. So that cos# = P. Q/(abs(P) *abs(Q)). If am not clear you can give an example of what you intend to do
Okay so apparently I made a mistake in my interpretation.
I want to thank everyone for your solutions!
After some puzzling it appears that:
import math
import numpy as np
#punt 1, PQ
# [x,y]
P = (1,1)
Q = (5,3)
x1 = P[0]
x2 = Q[0]
y1 = P[1]
y2 = Q[1]
#punt 2, RS
R = (0,2)
S = (4,1)
x3 = R[0]
x4 = S[0]
y3 = R[1]
y4 = S[1]
angle = np.arccos(((x2 - x1) * (x4 - x3) + (y2 - y1) * (y4 - y3)) / (math.sqrt((x2 - x1)**2 + (y2 - y1)**2) * math.sqrt((x4 - x3)**2 + (y4 - y3)**2)))
print(angle)
Is the correct way to calculate the angle between two vectors.
This is obviously not pretty code, but it is the essence of how it works!
Again I want to thank you all for you reaction and solutions!

Correct way to calculate triangle area from 3 vertices

Is this the correct way to calculate the area of a triangle given the 3 triangle points/vertices? The vertices will never be negative values.
def triangle_area(tri):
x1, y1, x2, y2, x3, y3 = tri[0][0], tri[0][1], tri[1][0], tri[1][1], tri[2][0], tri[2][1]
return 0.5 * (((x2-x1)*(y3-y1))-((x3-x1)*(y2-y1)))
It is necessary to add abs to this formula to avoid negative area value (sign depends on orientation, not on positive/negative coordinates)
Yes, this formula is correct and it implements the best approach if you have vertices coordinates. It is based on cross product properties.
def triangle_area(tri):
x1, y1, x2, y2, x3, y3 = tri[0][0], tri[0][1], tri[1][0], tri[1][1], tri[2][0], tri[2][1]
return abs(0.5 * (((x2-x1)*(y3-y1))-((x3-x1)*(y2-y1))))
Almost, but you need to take the absolute value at the end.
You're formula can be derived from the Shoelace formula which applies to any simple (no crossing edges, no holes) polygon.
If you want to calculate a triangles area, you can calculate the surrounding rectangles area and substract the 3 triangles which have 90° angles around it:
def triangle_area(tri):
x_min = min([point[0] for point in tri])
x_max = max([point[0] for point in tri])
y_min = min([point[1] for point in tri])
y_max = max([point[1] for point in tri])
area_rectangle = (y_max - y_min) * (x_max - x_min)
t1 = 0.5 * abs((tri[0][0] - tri[1][0]) * (tri[0][1] - tri[1][1]))
t2 = 0.5 * abs((tri[0][0] - tri[2][0]) * (tri[0][1] - tri[2][1]))
t3 = 0.5 * abs((tri[1][0] - tri[2][0]) * (tri[1][1] - tri[2][1]))
return area_rectangle - t1 - t2 - t3
Alternatively, you can use Herons Formula

Calculate the length of polyline from csv

I'm still new to python. I need helps to calculate the length of polyline with simple distance calculation:
distance = sqrt( (x1 - x2)**2 + (y1 - y2)**2 )
For instance my input csv looks like this.
id x y sequence
1 1.5 2.5 0
1 3.2 4.9 1
1 3.6 6.6 2
1 4.4 5.0 3
2 2.0 4.5 0
2 3.5 6.0 1
I have 'id' and 'sequence' (sequence number of the line vertices). How read the csv file? if current 'id' has the same value as previous row 'id', then perform the distance calculation : sqrt( (x[i] - x[i-1])**2 + (y[i] - y[i-1])**2 ). After that, group by 'id' and sum their 'distance' value.
The output csv would look like this:
id distance
1 ?
2 ?
Thanks in advance.
Polyline:
Distance between two points and the distance between a point and polyline:
def lineMagnitude (x1, y1, x2, y2):
lineMagnitude = math.sqrt(math.pow((x2 - x1), 2)+ math.pow((y2 - y1), 2))
return lineMagnitude
#Calc minimum distance from a point and a line segment (i.e. consecutive vertices in a polyline).
def DistancePointLine (px, py, x1, y1, x2, y2):
#http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/source.vba
LineMag = lineMagnitude(x1, y1, x2, y2)
if LineMag < 0.00000001:
DistancePointLine = 9999
return DistancePointLine
u1 = (((px - x1) * (x2 - x1)) + ((py - y1) * (y2 - y1)))
u = u1 / (LineMag * LineMag)
if (u < 0.00001) or (u > 1):
#// closest point does not fall within the line segment, take the shorter distance
#// to an endpoint
ix = lineMagnitude(px, py, x1, y1)
iy = lineMagnitude(px, py, x2, y2)
if ix > iy:
DistancePointLine = iy
else:
DistancePointLine = ix
else:
# Intersecting point is on the line, use the formula
ix = x1 + u * (x2 - x1)
iy = y1 + u * (y2 - y1)
DistancePointLine = lineMagnitude(px, py, ix, iy)
return DistancePointLine
For more information visit: http://www.maprantala.com/2010/05/16/measuring-distance-from-a-point-to-a-line-segment/

Finding out if two points are on the same side

If I have a definite line segment and then am given two random different points, how would i be able to determine if they are on the same side of the line segment?
I am wanting to use a function def same_side (line, p1, p2). I know based on geometry that cross products can be used but am unsure how to do so with python.
If you can get the equation of the line in slope-intercept form, you should be able to set up an inequality.
Say we have points (x1, y1), (x2, y2), and the line y = mx+b
You should be able to plug in the x and y values for both points, and if both make the equation y < mx + b or both make it y > mx + b, they are on the same side.
If either point satisfies the equation (y = mx + b), that point is on the line.
if (y1 > m * x1 + b and y2 > m * x2 + b) or (y1 < m * x1 + b and y2 < m *x2 + b):
return True #both on same side
Based on the tutorial here:
def same_side (line, p1, p2):
return ((line['y1']−line['y2'])*(p1['x']−line['x1'])+(line['x2']−line['x1'])*(p1['y']−line['y1']))*((line['y1']−line['y2'])*(p2['x']−line['x1'])+(line['x2']−line['x1'])*(p2['y']−line['y1']))>0
Example:
# Same side
line = {'x1':0,'y1':0,'x2':1,'y2':1}
p1 = {'x':0,'y':1}
print same_side (line, p1, p1)
# Same side
line = {'x1':0,'y1':0,'x2':1,'y2':1}
p1 = {'x':0,'y':1}
p2 = {'x':0,'y':2}
print same_side (line, p1, p2)
# Different side
line = {'x1':0,'y1':0,'x2':1,'y2':1}
p1 = {'x':0,'y':1}
p2 = {'x':0,'y':-2}
print same_side (line, p1, p2)
Assuming you have the line segment's endpoints (x1, y1) and (x2, y2), the equation for the line passing through them is y = y1 + (x - x1)/(x2 - x1) * (y2 - y1). Points above the line will have larger y values and points below will have smaller y values. So you simply have to plug each query point's x value into this formula, which gives you the y-coordinate of the line at that x, and compare it to the query point's x. If both points are on the same side, their x's will both be larger or both be smaller.
The one exception to this is when the line is perfectly vertical (x1 == x2). Here the formula breaks down, so you'll need a special case to check for this. In this case, compare the x-coordinates of the query points to the x-coordinate of the line (x1 or x2, doesn't matter which).
If you have numpy available, this is as simple as:
import numpy as np
def same_side(line, point1, point2):
v1 = np.array([line.start.x - line.end.x, line.start.y - line.end.y])
v2 = np.array([line.start.x - point1.x, line.start.y - point1.y])
v3 = np.array([line.start.x - point2.x, line.start.y - point1.y])
return (np.dot(np.cross(v1, v2), np.cross(v1, v3)) >= 0)

Speeding up summation for loop in python

I have the following bottleneck and am wondering if anyone can suggest ways to speed it up.
I have three lists x,y,z of length N. and I apply the following summation.
def abs_val_diff(x1, x2, x3, y1, y2, y3):
""" Find the absolute value of the difference between x and y """
return py.sqrt((x1 - y1) ** 2.0 + (x2 - y2) ** 2.0 + (x3 - y3) ** 2.0)
R = 0.1
sumV = 0.0
for i in xrange(N):
for j in xrange(i + 1, N):
if R > abs_val_diff(x[i], y[i], z[i],
x[j], y[j], z[j]):
sumV += 1.0
I have tried using numpy arrays, but either I am doing something wrong or there is a reduction in speed of about a factor of 2.
Any ideas would be highly appreciated.
I believe you can utilize numpy a little more efficiently by doing something like the following. Make a small modification to your function to use the numpy.sqrt:
import numpy as np
def abs_val_diff(x1, x2, x3, y1, y2, y3):
""" Find the absolute value of the difference between x and y """
return np.sqrt((x1 - y1) ** 2.0 + (x2 - y2) ** 2.0 + (x3 - y3) ** 2.0)
Then call with the full arrays:
res = abs_val_diff(x[:-1],y[:-1],z[:-1],x[1:],y[1:],z[1:])
Then, because you're adding 1 for each match, you can simply take the length of the array resulting from a query against the result:
sumV = len(res[R>res])
This lets numpy handle the iteration. Hopefully that works for you
Is there any reason you actually need to take the square root in your function? If all you do with the result is to compare it against a limit why not just square both sides of the comparison?
def abs_val_diff_squared(x1, x2, x3, y1, y2, y3):
""" Find the square of the absolute value of the difference between x and y """
return (x1 - y1) ** 2.0 + (x2 - y2) ** 2.0 + (x3 - y3) ** 2.0
R = 0.1
R_squared = R * R
sumV = 0.0
for i in xrange(N):
for j in xrange(i + 1, N):
if R_squared > abs_val_diff_squared(x[i], y[i], z[i],
x[j], y[j], z[j]):
sumV += 1.0
I also feel there ought to be much bigger savings gained from sorting the data into something like an octtree so you only have to look at nearby points rather than comparing everything against everything, but that's outside my knowledge.
It turns out long, ugly, list comprehensions are generally faster than explicit loops in python because they can be compiled to more efficient bytecode. I'm not sure if it'll help for you, but try something like this:
sumV = sum((1.0 for j in xrange(1+1, N) for i in xrange(N) if R > abs_val_diff(x[i], y[i], z[i], x[j], y[j], z[j])))
Yes, it looks absolutely atrocious, but there you go. More info can be found here and here.

Categories