I am working on a spatial analysis problem and part of this workflow is to calculate the angle between connected line segments.

Each line segment is composed of only two points, and each point has a pair of XY coordinates (Cartesian). Here is the image from GeoGebra. **I am always interested in getting a positive angle in 0 to 180 range**. However, I get all kind of angles depending on the order of vertices in input line segments.

The input data I work with is provided as tuples of coordinates. Depending on the vertex creation order, the last/end point for each line segment can be different. Here are some of the cases in Python code. The order of line segments in which I get them is random, but in a tuple of tuples, the first element is the start point and the second one is the end point. `DE`

line segment, for instance, would have `((1,1.5),(2,2))`

and the `(1,1.5)`

is the start point because it has the first position in the tuple of coordinates.

I however need to make sure I will get the same angle between `DE,DF`

and `ED,DF`

and so forth.

```
vertexType = "same start point; order 1"
#X, Y X Y coords
lineA = ((1,1.5),(2,2)) #DE
lineB = ((1,1.5),(2.5,0.5)) #DF
calcAngle(lineA, lineB,vertexType)
#flip lines order
vertexType = "same start point; order 2"
lineB = ((1,1.5),(2,2)) #DE
lineA = ((1,1.5),(2.5,0.5)) #DF
calcAngle(lineA, lineB,vertexType)
vertexType = "same end point; order 1"
lineA = ((2,2),(1,1.5)) #ED
lineB = ((2.5,0.5),(1,1.5)) #FE
calcAngle(lineA, lineB,vertexType)
#flip lines order
vertexType = "same end point; order 2"
lineB = ((2,2),(1,1.5)) #ED
lineA = ((2.5,0.5),(1,1.5)) #FE
calcAngle(lineA, lineB,vertexType)
vertexType = "one line after another - down; order 1"
lineA = ((2,2),(1,1.5)) #ED
lineB = ((1,1.5),(2.5,0.5)) #DF
calcAngle(lineA, lineB,vertexType)
#flip lines order
vertexType = "one line after another - down; order 2"
lineB = ((2,2),(1,1.5)) #ED
lineA = ((1,1.5),(2.5,0.5)) #DF
calcAngle(lineA, lineB,vertexType)
vertexType = "one line after another - up; line order 1"
lineA = ((1,1.5),(2,2)) #DE
lineB = ((2.5,0.5),(1,1.5)) #FD
calcAngle(lineA, lineB,vertexType)
#flip lines order
vertexType = "one line after another - up; line order 2"
lineB = ((1,1.5),(2,2)) #DE
lineA = ((2.5,0.5),(1,1.5)) #FD
calcAngle(lineA, lineB,vertexType)
```

I have written a tiny function that takes combinations of the lines as args and calculates the angle between them. I am using the `math.atan2`

which seemed to be best suited for this.

```
def calcAngle(lineA,lineB,vertexType):
line1Y1 = lineA[0][1]
line1X1 = lineA[0][0]
line1Y2 = lineA[1][1]
line1X2 = lineA[1][0]
line2Y1 = lineB[0][1]
line2X1 = lineB[0][0]
line2Y2 = lineB[1][1]
line2X2 = lineB[1][0]
#calculate angle between pairs of lines
angle1 = math.atan2(line1Y1-line1Y2,line1X1-line1X2)
angle2 = math.atan2(line2Y1-line2Y2,line2X1-line2X2)
angleDegrees = (angle1-angle2) * 360 / (2*math.pi)
print angleDegrees, vertexType
```

The output I get is:

```
> -299.744881297 same start point; order 1
> 299.744881297 same start point; order 2
> 60.2551187031 same end point; order 1
> -60.2551187031 same end point; order 2
> -119.744881297 one line after another - down; order 1
> 119.744881297 one line after another - down; order 2
> -119.744881297 one line after another - up; line order 1
> 119.744881297 one line after another - up; line order 2
```

As you can see, I am getting different values depending on the order of vertices in a line segment and line segments order. I have tried to post-process the angles by finding out what kind of relation the source line had and flipping the lines, editing the angle etc. I’ve ended with a dozen of such cases and at some point they start to overlap and I cannot any longer find out whether -119.744 should become 60.255 (acute angle) or be left as 119.744 (obtuse angle) etc.

**Is there any discrete way to process the output angle values I receive from math.atan2 to get only a positive value in 0 to 180 range?**

**If not, what kind of other approach should I take?**

Best answer

The easiest and most logically way to get at this problem is using the dot-product.

Try this code (I’ve commented practically everything):

```
import math
def dot(vA, vB):
return vA[0]*vB[0]+vA[1]*vB[1]
def ang(lineA, lineB):
# Get nicer vector form
vA = [(lineA[0][0]-lineA[1][0]), (lineA[0][1]-lineA[1][1])]
vB = [(lineB[0][0]-lineB[1][0]), (lineB[0][1]-lineB[1][1])]
# Get dot prod
dot_prod = dot(vA, vB)
# Get magnitudes
magA = dot(vA, vA)**0.5
magB = dot(vB, vB)**0.5
# Get cosine value
cos_ = dot_prod/magA/magB
# Get angle in radians and then convert to degrees
angle = math.acos(dot_prod/magB/magA)
# Basically doing angle <- angle mod 360
ang_deg = math.degrees(angle)%360
if ang_deg-180>=0:
# As in if statement
return 360 - ang_deg
else:
return ang_deg
```

Now try your variations of lineA and lineB and all should give the same answer.