2

With help I've written code to determine the angle between two line geometries. However, the code assumes each line extends into infinity and doesn't account for the shared point. Thus an angle of 150 can be returned as 30, or 20 can be returned as 160. I can't come up with a way of refining my code to return the proper angle.

Here's the code:

def AngleFromLines (lines):
    #lines is a python list of line geometries that share a vertex
    for line1 in lines:
        for line2 in lines:
            if line1 == line2:
                continue
            line1StPnt, line1EndPnt = LineToXYs (line1) #get start and end xys for first line
            line2StPnt, line2EndPnt  = LineToXYs (line2)#get start and end xys for second line
            angle1 = GetAngle (line1StPnt, line1EndPnt) #calculate declination
            angle2 = GetAngle (line2StPnt, line2EndPnt) #calculate declination
            angle = abs (angle1 - angle2) #angle between points - doesn't take into account shared point
            #further code not shown....

def LineToXYs (line): #return first and last coordinates
    firstX, firstY = (line.firstPoint.X, line.firstPoint.Y)
    lastX, lastY = (line.lastPoint.X, line.lastPoint.Y)
    return [(firstX, firstY), (lastX, lastY)]

def GetAngle (p1, p2):
    x1, y1 = p1
    x2, y2 = p2
    dX = x2 - x1
    dY = y2 - y1
    rads = math.atan2 (-dY, dX)
    return math.degrees (rads)

AngleFromLines ([lineGeom1, lineGeom2]) #lineGeom* = arcpy polyline geometry object

Example, where the angle is calculated as 39 instead of the desired result of 141.

enter image description here

1
  • What is your reference system? clockwise or anticlockwise? I wonder why you use -dY in math.atan2 (-dY, dX). A picture might help. My guess is that you should use a MOD operator instead of a ABS function in line 11, but only guessing
    – Marco
    Nov 2, 2017 at 19:21

2 Answers 2

1

It turns out that the order of points matter. You must always subtract the X from the shared point from the point that is not shared. Here's the code:

def GetAngle (p1, p2):
    x1, y1 = p1
    x2, y2 = p2
    dX = x1 - x2
    dY = y1 - y2
    rads = math.atan2 (dY, dX)
    return math.degrees (rads)

def LineToXYs (line): #return first and last coordinates
    firstX, firstY = (line.firstPoint.X, line.firstPoint.Y)
    lastX, lastY = (line.lastPoint.X, line.lastPoint.Y)
    return [(firstX, firstY), (lastX, lastY)]

def AngleFromLines (lines):
    #lines is a python list of line geometries that share a vertex
    for line1 in lines:
        for line2 in lines:
            if line1 == line2:
                continue
            line1Pnts = LineToXYs (line1) 
            line2Pnts  = LineToXYs (line2)
            firstPnt = [p for p in line1Pnts if not p in line2Pnts] [0]
            midPnts = [p for p in line1Pnts if p in line2Pnts]
            if not midPnts:
                continue
            midPnt = midPnts [0]
            lastPnt = [p for p in line2Pnts if not p in line1Pnts] [0]
            angle1 = GetAngle (firstPnt, midPnt) #order of points important
            angle2 = GetAngle (lastPnt, midPnt) #order of points important
            angle = abs (angle1 - angle2)
1
  • You saved my life! I've been trying to get this working for days. I didn't realise that the order matters.
    – ketar
    Jun 16, 2022 at 21:19
0

Review the use of angle = abs (angle1 - angle2) in line 11. Negative angles have a meaning, you d not want to make 31 when is -31. I thought about trying the function math.fmod(x, y) for this operation, it seems return the sing of x. Then, what you want to do seems to me:

angle = (angle1 - angle2) % 360

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.