Calculate Bends in a Line

3910
2
Jump to solution
03-21-2016 12:44 AM
KrishnaRao3
New Contributor II

Is there a way to create a simple python script to calculate bends in a line!

0 Kudos
1 Solution

Accepted Solutions
DanPatterson_Retired
MVP Emeritus

simple no... you can decompose you lines to sequential points and then determine the cosine similarity of the progression between the points themselves or calculate sequential cosines for 3 point moving sequences.  The former can be done with scipy.spatial.distance cosine method (which can be implemented in pure numpy or python).  The latter (3 point sequence cosines) can also be done in numpy and python and is very easy to do using the following.

You can use a searchcursor if needed to get the points into an array or list format, or simply work with numpy directly.

Obviously, a straight line for 3 sequential points will form a 180 degree line if the middle point is used as the reference so you are calculating the angle between the vectors a-b b-c with respect to the middle point b

def _vector_check(u, dtype=None):
    """after _validate_vector from scipy.spatial.distance
    : Requires: list/array arrays need to be 1D
    : Returns:  proper 1D vector
    : Notes Ensure values such as u=1 and u=[1] still return 1-D arrays.
    """
    u = np.asarray(u, dtype=np.float64, order='c').squeeze() 
    u = np.atleast_1d(u)
    if u.ndim > 1:
        raise ValueError("Input vector should be 1-D.")
    return u

def cos_3pnt(a,b,c):
    """cosine between 3 points
    : Requires  3 points, the middle one is assumed to form the start of the vectors
    : Output    the angle between bac
    : Notes    This form facilitates determining angles as one progresses along a line
    """
    a, b, c = [_vector_check(i) for i in [a,b,c] ]
    ba = a - b
    bc = c - b
    cos_a = np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc))
    angle = np.arccos(cos_a)
    if np.allclose(angle,np.pi):
        return np.pi, np.degrees(np.pi)
    if np.allclose(angle,0.0):
        return 0.0, 0.0
    return angle, np.degrees(angle)

here is an example... I used a list comprehension (LC) to assemble and perform the calculation, which you might find easier than doing a full numpy vectorized approach.  This function and the LC can be used if you are doing a searchcursor as well.  Do notice the first example is simply a straight line... the second is for the same line with one coordinate altered

>>> d
array([[  1.,   1.],
       [  2.,   2.],
       [  3.,   3.],
       [  4.,   4.],
       [  5.,   5.],
       [  6.,   6.],
       [  7.,   7.],
       [  8.,   8.],
       [  9.,   9.],
       [ 10.,  10.]])
>>> ang = [cos_3pnt(d[i-1],d[i], d[i+1])[1] for i in range(1, len(d)-1)]
>>> ang
[180.0, 180.0, 180.0, 180.0, 180.0, 180.0, 180.0, 180.0]
>>> d[5]
array([ 6.,  6.])
>>> d[5,1]
6.0
>>> d[5,1]=5.5
>>> d
array([[  1. ,   1. ],
       [  2. ,   2. ],
       [  3. ,   3. ],
       [  4. ,   4. ],
       [  5. ,   5. ],
       [  6. ,   5.5],
       [  7. ,   7. ],
       [  8. ,   8. ],
       [  9. ,   9. ],
       [ 10. ,  10. ]])
>>> ang = [cos_3pnt(d[i-1],d[i], d[i+1])[1] for i in range(1, len(d)-1)]
>>> ang
[180.0, 180.0, 180.0, 161.565051177078, 150.2551187030578, 168.69006752597974, 180.0, 180.0]

View solution in original post

2 Replies
DanPatterson_Retired
MVP Emeritus

simple no... you can decompose you lines to sequential points and then determine the cosine similarity of the progression between the points themselves or calculate sequential cosines for 3 point moving sequences.  The former can be done with scipy.spatial.distance cosine method (which can be implemented in pure numpy or python).  The latter (3 point sequence cosines) can also be done in numpy and python and is very easy to do using the following.

You can use a searchcursor if needed to get the points into an array or list format, or simply work with numpy directly.

Obviously, a straight line for 3 sequential points will form a 180 degree line if the middle point is used as the reference so you are calculating the angle between the vectors a-b b-c with respect to the middle point b

def _vector_check(u, dtype=None):
    """after _validate_vector from scipy.spatial.distance
    : Requires: list/array arrays need to be 1D
    : Returns:  proper 1D vector
    : Notes Ensure values such as u=1 and u=[1] still return 1-D arrays.
    """
    u = np.asarray(u, dtype=np.float64, order='c').squeeze() 
    u = np.atleast_1d(u)
    if u.ndim > 1:
        raise ValueError("Input vector should be 1-D.")
    return u

def cos_3pnt(a,b,c):
    """cosine between 3 points
    : Requires  3 points, the middle one is assumed to form the start of the vectors
    : Output    the angle between bac
    : Notes    This form facilitates determining angles as one progresses along a line
    """
    a, b, c = [_vector_check(i) for i in [a,b,c] ]
    ba = a - b
    bc = c - b
    cos_a = np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc))
    angle = np.arccos(cos_a)
    if np.allclose(angle,np.pi):
        return np.pi, np.degrees(np.pi)
    if np.allclose(angle,0.0):
        return 0.0, 0.0
    return angle, np.degrees(angle)

here is an example... I used a list comprehension (LC) to assemble and perform the calculation, which you might find easier than doing a full numpy vectorized approach.  This function and the LC can be used if you are doing a searchcursor as well.  Do notice the first example is simply a straight line... the second is for the same line with one coordinate altered

>>> d
array([[  1.,   1.],
       [  2.,   2.],
       [  3.,   3.],
       [  4.,   4.],
       [  5.,   5.],
       [  6.,   6.],
       [  7.,   7.],
       [  8.,   8.],
       [  9.,   9.],
       [ 10.,  10.]])
>>> ang = [cos_3pnt(d[i-1],d[i], d[i+1])[1] for i in range(1, len(d)-1)]
>>> ang
[180.0, 180.0, 180.0, 180.0, 180.0, 180.0, 180.0, 180.0]
>>> d[5]
array([ 6.,  6.])
>>> d[5,1]
6.0
>>> d[5,1]=5.5
>>> d
array([[  1. ,   1. ],
       [  2. ,   2. ],
       [  3. ,   3. ],
       [  4. ,   4. ],
       [  5. ,   5. ],
       [  6. ,   5.5],
       [  7. ,   7. ],
       [  8. ,   8. ],
       [  9. ,   9. ],
       [ 10. ,  10. ]])
>>> ang = [cos_3pnt(d[i-1],d[i], d[i+1])[1] for i in range(1, len(d)-1)]
>>> ang
[180.0, 180.0, 180.0, 161.565051177078, 150.2551187030578, 168.69006752597974, 180.0, 180.0]
WesMiller
Regular Contributor III

You may also be interested in these two threads

How to automatically calculate the angle between two intersecting polylines

Calculating angles using ArcGIS Desktop 10.2

Edit: This has come up a few times recently.