Assuming your points are ordered:
- Run Points To Line
- Copy/Paste the script below into your Python Window (without lines 79 and after) and call divide_lines_into_straight_lines
- alternatively, turn the script into a script tool and run that (also enclosed at end of this post)
import arcpy
import math
from pathlib import Path
# function to divide a list of vertices
def divide_path_into_straight_paths(path, angle_tolerance):
""" Divides an arcpy line path into straight parts.
path: [arcpy.Point], the vertices of the path
angle_tolerance: float, difference of consecutive angles (in degrees)
between vertices smaller than this value are considered to be straight
Returns the split paths as [[arcpy.Point]]
"""
segments = [
[path[i-1], path[i]]
for i in range(1, len(path))
]
angles = [
math.degrees(math.atan2(s[0].Y - s[1].Y, s[0].X - s[1].X)) % 180 # % 180 to get the same angle regardless of vertex order
for s in segments
]
split_paths = []
split_path_angles = []
for i, (segment, angle) in enumerate(zip(segments, angles)):
if i == 0:
split_paths.append(segment)
split_path_angles.append([angle])
continue
angle_difference = abs(angle - angles[i-1])
if angle_difference > 90:
angle_difference = abs(angle_difference - 180) # to make 179° comparable to 1°
if angle_difference <= angle_tolerance:
split_paths[-1].append(segment[-1])
split_path_angles[-1].append(angle)
else:
split_paths.append(segment)
split_path_angles.append([angle])
return split_paths, split_path_angles
# function to divide a line geometry
def divide_line_into_straight_lines(line, angle_tolerance):
""" Divides an arcpy line geometry into straight parts.
line: arcpy.Polyline, the line geometry
angle_tolerance: float, difference of consecutive angles (in degrees)
between vertices smaller than this value are considered to be straight
Returns the split lines as [arcpy.Polyline]
"""
split_lines = []
for part in line:
split_paths, split_path_angles = divide_path_into_straight_paths(part, angle_tolerance)
for path in split_paths:
split_lines.append(arcpy.Polyline(arcpy.Array([path]), spatial_reference=line.spatialReference))
return split_lines, split_path_angles
# function to divide all lines in a fc and write the output into a new fc
def divide_lines_into_straight_lines(input_lines, output_lines, angle_tolerance):
""" Divides all polylines in a featureclass or layer (honors selection)
into straight parts.
input_lines: str, path to the input featureclass / name of the layer
output_lines: str, path of the output featureclass
angle_tolerance: float, difference of consecutive angles (in degrees)
between vertices smaller than this value are considered to be straight
"""
lines = [row for row in arcpy.da.SearchCursor(input_lines, ["OID@", "SHAPE@"])]
out_path = Path(output_lines)
out_fc = arcpy.management.CreateFeatureclass(str(out_path.parent), str(out_path.stem), "POLYLINE", spatial_reference=lines[0][1].spatialReference)
arcpy.management.AddField(out_fc, "FID", "LONG")
arcpy.management.AddField(out_fc, "LENGTH", "DOUBLE")
arcpy.management.AddField(out_fc, "ANGLE_START", "DOUBLE")
arcpy.management.AddField(out_fc, "ANGLE_MIN", "DOUBLE")
arcpy.management.AddField(out_fc, "ANGLE_MAX", "DOUBLE")
arcpy.management.AddField(out_fc, "ANGLE_RANGE", "DOUBLE")
arcpy.management.AddField(out_fc, "VERTEX_COUNT", "LONG")
with arcpy.da.InsertCursor(out_fc, ["FID", "SHAPE@", "LENGTH", "ANGLE_START", "ANGLE_MIN", "ANGLE_MAX", "ANGLE_RANGE", "VERTEX_COUNT"]) as cursor:
for oid, shp in lines:
split_shapes, split_angles = divide_line_into_straight_lines(shp, angle_tolerance)
for shape, angles in zip(split_shapes, split_angles):
a_start = angles[0]
a_min = min(angles)
a_max = max(angles)
a_range = a_max - a_min
if a_range > angle_tolerance:
a_range = abs(a_range - 180)
v_count = len(angles) + 1
cursor.insertRow([oid, shape, shape.length, a_start, a_min, a_max, a_range, v_count])
# you can use the script as tool, parameters:
# Feature Layer, Required, Input, Filter for Polylines
# Feature Layer, Required, Output, Filter for Polylines
# Double, Required, Input
if __name__ == "__main__":
input_lines = arcpy.GetParameterAsText(0)
output_lines = arcpy.GetParameterAsText(1)
angle_tolerance = arcpy.GetParameter(2)
divide_lines_into_straight_lines(input_lines, output_lines, angle_tolerance)
Points:
Points To Line:
Straight Lines:
Have a great day!
Johannes