2

I need to find the 'inner' angle in degrees between two segments (lines), the less than 180 degrees that is. Any fast way to do it in python2.7? (Shapely doesnt seem to have a function for this)

segment1 is x1,y1,x2,y2

segment2 is x3,y3,x4,y4

enter image description here

OHTO
  • 313
  • 3
  • 15
  • if you are able to calculate the angle between 3 points, then if > 180: innerAngle = 360.0 - angle; Your accepted solution is not recomended (ill conditioned acos() ), better search further. – AlexWien Oct 10 '13 at 19:01
  • 1
    A general way to calculated the angle between two line segments [is shown in this answer](http://stackoverflow.com/a/3366569/327026). – Mike T Oct 17 '13 at 20:52

1 Answers1

5

I originally suggested using the law of cosines in vector form: if your two line segments are given by the vectors b and c, and the angle between them is θ, then

b · c = |b| |c| cos θ

and so

θ = cos−1((b · c) / |b| |c|)

But as Alex Wien points out in comments, this gives poor results when θ is close to zero:

>>> theta = 1e-6
>>> a = Vector(1, 0)
>>> b = Vector(cos(theta), sin(theta))
>>> acos(a.dot(b) / (a.length * b.length))
9.999334257726859e-07
>>> abs(theta - _) / theta
6.657422731408927e-05

which is a relative error of getting on for one part in ten thousand. For very small angles you can get 100% relative error:

>>> theta = 1e-9
>>> a = Vector(1, 0)
>>> b = Vector(cos(theta), sin(theta))
>>> acos(a.dot(b) / (a.length * b.length))
0.0

An alternative formula uses the arc tangent instead of the arc cosine:

θ = tan−1(|a × b| / (a · b))

and this gives a more accurate result for small angles:

>>> atan2(abs(a.cross(b)), a.dot(b))
1e-09
>>> theta == _
True

(The alternative formula follows from the property of the cross product that |a × b| = |a| |b| sin θ. Divide this by the law of cosines to get the result used here.)

Gareth Rees
  • 64,967
  • 9
  • 133
  • 163
  • How do I write this cos^-1( etc...) in python? – OHTO Oct 10 '13 at 14:07
  • Well, you read the manual! Mathematical functions like arc cosine are in the [`math`](http://docs.python.org/3/library/math.html) module. – Gareth Rees Oct 10 '13 at 14:11
  • This is unusable for an computer program. This is far away from working code. – AlexWien Oct 10 '13 at 18:47
  • 3
    The inverse cos is ill conditioned for computer programms, the right solution uses atan2() for this task, and handles special cases like division by 0 – AlexWien Oct 10 '13 at 18:59
  • I thik acos is enough for this case ... he needs just angles between vectors < 180deg , but if he needed 4 quadrants direct angle then your right and atan2 is the only choice – Spektre Oct 28 '13 at 10:05
  • @AlexWien: I've revised the answer; how do you like it now? – Gareth Rees Oct 29 '13 at 14:20
  • What module are we using here? The question mentions Shapely but I can't find a `Vector` class in Shapely. Or am I missing something? – Bill Oct 12 '16 at 17:52
  • @Bill: Any vector class with cross and dot products will do, so use whichever is your favourite. – Gareth Rees Oct 12 '16 at 18:12