Help in segmenting a polyline layer into equally length segments

2205
15
03-18-2021 02:35 PM
RoderickPerendy
New Contributor III

Hello, I have a script that my intentions are for it to segment polylines into equal intervals with the last polyline as a remainder. Initially, I attempted to conduct this through GeneratePointsAlongLines at equal distance and then SplitLinesAtPoint functions but noticed that this produced segmented lines that had lengths that were inconsistent with the required length. Furthermore, I'm the only person at my company that has an Advanced Pro license and I would like to make this tool work for others.

My script is able to successfully iterate through the source polylines layer and add information to an output layer using cursors and segmentAlongLine method. Though the attributes table has the correct number of rows, the feature layer still only shows the original polyline when selected. I believe what is happening is that the segmented polyline doesn't actually exist. Any ideas on what I'm doing wrong? Thank You!
Below is my script:

RoderickPerendy_0-1616103090255.pngRoderickPerendy_1-1616103164728.png

import arcpy
arcpy.env.overwriteOutput = True
arcpy.env.workspace = r"C:\Users\roder\OneDrive\Documents\ArcGIS\Projects\PythonScripting\PythonScripting.gdb" # Change to getParameter
polylines = "CranePath_OnRoad"#Change to getParameter
spatial_ref = arcpy.Describe(polylines).spatialReference
output = "CranePath_OnRoad_Segmented" #Change to getParameter
interval_dist = 20 # feet, Change to get Parameter
#distance_interval_unit = getParameter
arcpy.AddField_management(polylines,"Line_Length","DOUBLE")
arcpy.management.CalculateGeometryAttributes(polylines, "Line_Length LENGTH_GEODESIC", "FEET_US")
mem_polyline = arcpy.CreateFeatureclass_management("in_memory", "mem_polyline", "POLYLINE", polylines, "DISABLED", "DISABLED", spatial_ref)
arcpy.AddField_management(mem_polyline,"LineID","LONG")
in_flds = ["OID@", "SHAPE@", "Line_Length"]
out_flds = ["SHAPE@", "Line_Length", "LineID"]
cnt_in = 0
cnt_out = 0
with arcpy.da.InsertCursor(mem_polyline,out_flds) as curs_out:
with arcpy.da.SearchCursor(polylines,in_flds) as curs_in:
for row_in in curs_in:
cnt_in += 1
polyline_in = row_in[1]
from_dist = 0
lineID = row_in[0]
max_dist = float(row_in[2])
if cnt_in % 100 == 0:
print("Processing line: {}, generated {} segments".format(cnt_in, cnt_out))

while from_dist <= max_dist:
cnt_out += 1
to_dist = from_dist + interval_dist
remainder_dist = max_dist - from_dist
segment = polyline_in.segmentAlongLine(from_dist,to_dist,False)
lst_out = list((segment,interval_dist,lineID))
curs_out.insertRow(lst_out)
from_dist += interval_dist
else:
cnt_out += 1
segment = polyline_in.segmentAlongLine(from_dist-interval_dist,max_dist,False)
lst_out = list((segment,remainder_dist,lineID))
curs_out.insertRow(lst_out)

mem_polyline_fl = arcpy.MakeFeatureLayer_management(mem_polyline, "Polylines_memory")
arcpy.CopyFeatures_management(mem_polyline_fl, output)
arcpy.Delete_management(mem_polyline)
arcpy.Delete_management(mem_polyline_fl)

0 Kudos
15 Replies
by Anonymous User
Not applicable

Taking another look at the code,  I am curious about the two and have always wanted to ask why ESRI puts arcpy.management.CalculateGeometryAttributes in the documents above the parameter table but always uses the style we know arcpy.CalculateGeometryAttributes_management in the examples.  I think it is confusing to people. I have copied that line above the parameter table a few times when they came out with it thinking that is the syntax but the scripts threw not found exceptions.

 

arcpy.management.CalculateGeometryAttributes()

 

The example shows:

 

arcpy.CalculateGeometryAttributes_management()

 

I fixed that in my post above- and I do wonder how it ran on yours without throwing an error.

0 Kudos
by Anonymous User
Not applicable

I took your code and did some testing and I got the Attribute 'isLicensed' error when running it in 3.6.  I changed to 2.7 and got it to run and produce segments.  One issue was it needed to create the field 'LENGTH_GEODESIC' but that is too long so it was truncating the name to 'LENGTH_GEO'.  Using 'LENGTH_GEO' instead of Line_Length worked.

 

Edited:  I made some changes and it worked in 3.6 without the attribute error, and just using AddGeometryAttributes_management.  Updated the code below-

 

 

# ------------------------------------------------------------------------------
# setup input parameters and variables
arcpy.env.workspace = arcpy.GetParameter(0)
if arcpy.env.workspace == '#' or not arcpy.env.workspace:
    arcpy.env.workspace = r"C:\Users\...\PythonTests\PythonTests.gdb"

polylines = arcpy.GetParameter(1)
if polylines == '#' or not polylines:
    polylines = "driveway"
spatial_ref = arcpy.Describe(polylines).spatialReference

output = arcpy.GetParameter(2)
if output == '#' or not output:
    output = "driveway_Segmented"

interval_dist = arcpy.GetParameter(3)
if interval_dist == '#' or not interval_dist:
    interval_dist = 20 #distance_interval_unit = getParameter
# ------------------------------------------------------------------------------

def insertSegment(insertList):
    with arcpy.da.InsertCursor(outFC, ["SHAPE@", "Line_Length", "LineID"]) as icur:
        icur.insertRow(insertList)

def createOutputFeatureClass():
    spatial_ref = arcpy.Describe(polylines).spatialReference
    mem_polyline = arcpy.CreateFeatureclass_management("in_memory", "mem_polyline", "POLYLINE", polylines, "DISABLED", "DISABLED", spatial_ref)
    arcpy.AddField_management(mem_polyline, "LineID", "LONG")
    return mem_polyline

def createTempPolyLines():
    polylineTemp = arcpy.MakeFeatureLayer_management(polylines, 'tempLyr')
    arcpy.AddField_management(polylineTemp, "Line_Length", "DOUBLE")

    arcpy.AddGeometryAttributes_management(polylineTemp, "LENGTH_GEODESIC", 'FEET_US', '', spatial_ref)

    return polylineTemp

def createSegments(outFC, polylineTemp):
    with arcpy.da.SearchCursor(polylineTemp, ["OBJECTID", "SHAPE@", "LENGTH_GEO"]) as scur:
        for srow in scur:
            cnt_out = 0
            polyline_in = srow[1] #shape@
            from_dist = 0
            lineID = srow[0] #OID
            max_dist = float(srow[2]) #Line_Length
            remainder_dist = ''

            while from_dist <= max_dist:
                cnt_out += 1
                to_dist = from_dist + interval_dist
                remainder_dist = max_dist - from_dist
                segment = polyline_in.segmentAlongLine(float(from_dist), float(to_dist), False)
                insertSegment([segment, interval_dist, lineID])
                from_dist += interval_dist
            else:
                cnt_out += 1
                segment = polyline_in.segmentAlongLine(float(from_dist-interval_dist), float(max_dist), False)
                insertSegment([segment, remainder_dist, lineID])

            print("Processing line OID: {}, generated {} segments".format(srow[0], cnt_out))

    arcpy.CopyFeatures_management(outFC, output)


if __name__ == "__main__":
    outFC = createOutputFeatureClass()
    polylineTemp = createTempPolyLines()
    createSegments(outFC, polylineTemp)

 

  segment.png

0 Kudos
RoderickPerendy
New Contributor III

Jeff, 

Thank your for cleaning up my code. I had to make a few changes, naming changing "OBJECTID" to "OID@" in 'scur' and add arcpy.AddField_management(mem_polyline, "Line_Length", "DOUBLE") to createOutputFeatureClass() in order to get it to run but I'm still getting the same issues so I'm bewildered how it was able to work for you. 

RoderickPerendy_0-1616188724961.pngRoderickPerendy_1-1616188756184.png

import arcpy
arcpy.env.overwriteOutput = True


# USER INPUT FIELDS 
arcpy.env.workspace = r"C:\Users\roder\OneDrive\Documents\ArcGIS\Projects\PythonScripting\PythonScripting.gdb" # Change to getParameter
polylines = "CranePath_OnRoad"#Change to getParameter
spatial_ref = arcpy.Describe(polylines).spatialReference
output = "CranePath_OnRoad_Segmented" #Change to getParameter
interval_dist = 20 # feet, Change to get Parameter
#distance_interval_unit = getParameter

def insertSegment(insertList):
    with arcpy.da.InsertCursor(outFC, ["SHAPE@", "Line_Length", "LineID"]) as icur:
        icur.insertRow(insertList)

def createOutputFeatureClass():
    spatial_ref = arcpy.Describe(polylines).spatialReference
    mem_polyline = arcpy.CreateFeatureclass_management("in_memory", "mem_polyline", "POLYLINE", polylines, "DISABLED", "DISABLED", spatial_ref)
    arcpy.AddField_management(mem_polyline, "LineID", "LONG")
    arcpy.AddField_management(mem_polyline, "Line_Length", "DOUBLE")
    return mem_polyline

def createTempPolyLines():
    polylineTemp = arcpy.MakeFeatureLayer_management(polylines, 'tempLyr')
    arcpy.AddField_management(polylineTemp, "Line_Length", "DOUBLE")

    arcpy.AddGeometryAttributes_management(polylineTemp, "LENGTH_GEODESIC", 'FEET_US', '', spatial_ref)

    return polylineTemp

def createSegments(outFC, polylineTemp):
    with arcpy.da.SearchCursor(polylineTemp, ["OID@", "SHAPE@", "LENGTH_GEO"]) as scur:
        for srow in scur:
            cnt_out = 0
            polyline_in = srow[1] #shape@
            from_dist = 0
            lineID = srow[0] #OID
            max_dist = float(srow[2]) #Line_Length
            remainder_dist = ''

            while from_dist <= max_dist:
                cnt_out += 1
                to_dist = from_dist + interval_dist
                remainder_dist = max_dist - from_dist
                segment = polyline_in.segmentAlongLine(float(from_dist), float(to_dist), False)
                insertSegment([segment, interval_dist, lineID])
                from_dist += interval_dist
            else:
                cnt_out += 1
                segment = polyline_in.segmentAlongLine(float(from_dist-interval_dist), float(max_dist), False)
                insertSegment([segment, remainder_dist, lineID])

            print("Processing line OID: {}, generated {} segments".format(srow[0], cnt_out))

    arcpy.CopyFeatures_management(outFC, output)

outFC = createOutputFeatureClass()
polylineTemp = createTempPolyLines()
createSegments(outFC, polylineTemp)
0 Kudos
by Anonymous User
Not applicable

That is very odd.  I just ran your code and it gave me the desired output like before.  ... any updates waiting to be installed?

segment2.png

 

0 Kudos
RoderickPerendy
New Contributor III

It looks as though I'm up to date:

RoderickPerendy_0-1616191262487.png

Guess I'll have to reach out to Esri and report on a bug.

0 Kudos
by Anonymous User
Not applicable

Here is the script that I put together to work with the data.  I think the key is projecting it from WGS84 to stateplane, and deleting the full line that is getting inserted somewhere by selecting by attributes and toasting anything over 21.

# ------------------------------------------------------------------------------
# setup input parameters and variables
arcpy.env.workspace = arcpy.GetParameter(0)
if arcpy.env.workspace == '#' or not arcpy.env.workspace:
    arcpy.env.workspace = r"C:\Users\klingj\Documents\ArcGIS\Cranewalk - On Road.gdb"

polylines = arcpy.GetParameter(1)
if polylines == '#' or not polylines:
    polylines = r"Polylines_Project"

# for the stateplane projection .
spatialref = arcpy.GetParameter(1)
if spatialref == '#' or not spatialref:
    spatialref = r"Polylines_Project"
spatial_ref = arcpy.Describe(spatialref).spatialReference

outFC = arcpy.GetParameter(2)
if outFC == '#' or not outFC:
    outFC = "cranePath_Seg"

interval_dist = arcpy.GetParameter(3)
if interval_dist == '#' or not interval_dist:
    interval_dist = 20 #distance_interval_unit = getParameter
# ------------------------------------------------------------------------------

def insertSegment(insertList, output):
    with arcpy.da.InsertCursor(output, ["SHAPE@", "Line_Length", "LineID"]) as icur:
        icur.insertRow(insertList)

def createOutputFeatureClass():
    mem_polyline = arcpy.CreateFeatureclass_management('memory', "cranePath_Seg", "POLYLINE", polylines, "DISABLED", "DISABLED", spatial_ref)
    arcpy.AddField_management(mem_polyline, "LineID", "LONG")
    return mem_polyline

def createTempPolyLines():
    # project data here to stateplane
    polylineTemp = arcpy.MakeFeatureLayer_management(polylines, 'tempLyr')
    arcpy.AddField_management(polylineTemp, "Line_Length", "DOUBLE")
    arcpy.AddGeometryAttributes_management(polylineTemp, "LENGTH_GEODESIC", 'FEET_US', '', spatial_ref)
    return polylineTemp

def createSegments(FCGeom, output):
    outFC = os.path.join(arcpy.env.workspace, 'cranePath_Seg')
    with arcpy.da.SearchCursor(FCGeom, ["OID@", "SHAPE@", "LENGTH_GEO"]) as scur:
        for srow in scur:
            cnt_out = 0
            polyline_in = srow[1] #shape@
            from_dist = 0
            lineID = srow[0] #OID
            max_dist = float(srow[2]) #LENGTH_GEO
            remainder_dist = ''

            while from_dist <= max_dist:
                cnt_out += 1
                to_dist = from_dist + interval_dist
                remainder_dist = max_dist - from_dist
                segment = polyline_in.segmentAlongLine(from_dist, to_dist, False)
                insertSegment([segment, interval_dist, lineID], output)
                from_dist += interval_dist
            else:
                cnt_out += 1
                # print("Processing line OID: {},  segment from: {} to remainder: {}".format(srow[0], from_dist-interval_dist, remainder_dist))
                segment = polyline_in.segmentAlongLine(from_dist-interval_dist, remainder_dist, False)
                insertSegment([segment, remainder_dist, lineID], output)

            print("Processing line OID: {}, generated {} segments".format(srow[0], cnt_out))

        selctFeatures = arcpy.SelectLayerByAttribute_management(output, 'New_Selection', '''SHAPE_LENGTH > 21''')
        arcpy.DeleteFeatures_management(selctFeatures)
        arcpy.CopyFeatures_management(output, outFC)
        # project to WGS84

if __name__ == "__main__":
    output = createOutputFeatureClass()
    FCGeom = createTempPolyLines()
    createSegments(FCGeom, output)

 

0 Kudos