Table of Contents
The Video
Introduction
As of writing this blog post there is not neat ArcPy function to segment a polyline into equal line segments. You could use the Generate Points Along Lines tool followed by the Split Line at Point tool, but alas, the latter is only available on an Advanced license. You could of course use our Split Line at Point tool using ArcPy on a Basic license to overcome this. But let’s take a look how to create the workflow as a standalone tool in its own right!
Leverage ArcPy for geospatial data management workflows within ArcGIS Pro. Learn the fundamentals of utilising ArcGIS Pro geoprocessing tools with ArcPy for data management, conversions, and analysis. This course is packed with amazing content that will help you discover the power of automating tasks within the ArcGIS Pro environment. Take your ArcPy skills from beginner to snake charmer. A little code goes a long way, a little ArcPy provides you with an in-demand skill. Sign up now for our highly rated course.
Import the ArcPy module
ArcPy is a Python site package that enables automation, analysis, and management of geographic data within the ArcGIS software environment. Import math from the standard Python library to help with some calculations.
import arcpy
Define the parameters
We need to define three user input parameters; the in_features represents the filepath to the linear feature class, the out_feature_class represents the filepath for the output segmented feature class, and the num_features is the amount of equal length segments you want in the output.
## input linear feature class
in_features = "INPUT_FC_PATH"
## output feature class containg the ticks
out_feature_class = "OUTPUT_FC_PATH"
## how many line segments to chop the line up into
num_features = 8 # number of segments to split each line into
Required Python objects for the tool
We require a small amount of Python objects to help us complete our task. We want a list of the input field names that will be copied over from the in_features, and we need a copy of this list to aid with creating the output schema and populating with data. Our output will have the same spatial reference as the input.
## srs id of the in_features so we can assign the output the same
srs_id = arcpy.Describe(in_features).spatialReference.factoryCode
## we need the OID field for the ORIG_ID output_field
oid_fld = [fld.name for fld in arcpy.ListFields(in_features) if fld.type=="OID"][0]
## this list will hold the names of the input fields in order
in_fld_names = [
fld.name for fld in arcpy.ListFields(in_features)
if fld.type not in ("Blob","Geometry","GlobalID","Guid","OID","Raster")
]
## remove Shape_Length
if "Shape_Length" in in_fld_names:
in_fld_names.remove("Shape_Length")
## the field names for the output feature class
## this will be used in an InsertCursor as we create our output lines
out_fld_names = list(in_fld_names) + ["SHAPE@"]
## this will be used in a SearchCursor as we iterate over the
## original input lines
in_fld_names = [oid_fld] + in_fld_names + ["SHAPE@"]
Create a temporary feature class and define schema
We create a linear feature class in the memory workspace and define the schema to suit our output feature class. We want to add the ORIG_FID and the ORIG_SEQ fields to our output and populate later in our workflow.
## in memory feature class to hold the split lines
temp_fc = arcpy.arcpy.management.CreateFeatureclass(
out_path = "memory",
out_name = "temp_fc",
geometry_type = "POLYLINE",
template = in_features,
has_m = "SAME_AS_TEMPLATE",
has_z = "SAME_AS_TEMPLATE",
spatial_reference = srs_id
)
## add the ORIG_FID field
arcpy.management.AddField(
in_table = temp_fc,
field_name = "ORIG_FID",
field_type="LONG",
field_is_nullable="NULLABLE"
)
## add the ORIG_SEQ field
arcpy.management.AddField(
in_table = temp_fc,
field_name = "ORIG_SEQ",
field_type="LONG",
field_is_nullable="NULLABLE"
)
## adjust our in_fld_names list to account for the added fields.
out_fld_names = ["ORIG_FID"] + out_fld_names + ["ORIG_SEQ"]
The Main Event: Segment each line and add to the temporary feature class
We use an InsertCursor for our temporary feature class, and use a SearchCursor to iterate over each polyline from the input feature class. For each line we get our segments.
We then iterate over each segment and add as record to our temporary feature class populating with the correct attributes and the the geometry for the segment. The ORIG_FID and ORIG_SEQ are handled here.
## insert records with an insert cursor
with arcpy.da.InsertCursor(
in_table = temp_fc,
field_names = out_fld_names
) as u_cursor:
## iterate over every line in the feature class
with arcpy.da.SearchCursor(
in_table = in_features,
field_names = in_fld_names
) as s_cursor:
## for each line
for row in s_cursor:
## get the line segments
## thanks to ArcPy Cafe for this line of code
## https://arcpy.wordpress.com/2014/10/30/split-into-equal-length-features/
segments = [
row[-1].segmentAlongLine(
start_measure = i/num_features,
end_measure = (i+1)/num_features,
use_percentage = True
) for i in range(0, num_features)
]
## for each segment
for seq_id, segment in enumerate(segments, 1):
## get the original attributes and remove the geometry
## add in the new segment geometry and the ORIG_SEQ
record = list(row[:-1]) + [segment, seq_id]
## add the record for that segment.
u_cursor.insertRow(record)
Write the output to disk and clean-up
Once we have all our segments inserted into the temporary feature class, we write the temporary feature class to disk using the ExportFeatures() tool.
And finally we clean up the memory workspace.
arcpy.conversion.ExportFeatures(
in_features = temp_fc,
out_features = out_feature_class
)
arcpy.management.Delete(
in_data = temp_fc
)
Create Custom Tool in ArcGIS Pro
See the video at 06:51
Our YouTube channel is packed with extra content for ArcPy & ArcGIS Pro, and the ArcGIS API for Python & ArcGIS Online. There are also free courses. Make sure to subscribe to be notified about newly released material and added courses. Struggling with something related to Python & ArcGIS? Let us know and we can make a tutorial video. We’re here for your learning!
All the code in one place
You can find the entire code workflow below with links to important components in the documentation that were used.
import arcpy
################################################################################
## Esri Documentation
## https://pro.arcgis.com/en/pro-app/latest/arcpy/functions/getparameterastext.htm
## https://pro.arcgis.com/en/pro-app/latest/arcpy/functions/describe.htm
## https://pro.arcgis.com/en/pro-app/latest/arcpy/functions/listfields.htm
## https://pro.arcgis.com/en/pro-app/latest/tool-reference/data-management/add-field.htm
## https://pro.arcgis.com/en/pro-app/latest/tool-reference/data-management/create-feature-class.htm
## https://pro.arcgis.com/en/pro-app/latest/arcpy/data-access/searchcursor-class.htm
## https://pro.arcgis.com/en/pro-app/latest/arcpy/data-access/updatecursor-class.htm
## https://pro.arcgis.com/en/pro-app/latest/tool-reference/conversion/export-features.htm
## https://pro.arcgis.com/en/pro-app/latest/tool-reference/data-management/delete.htm
##
#################################################################################
#################################################################################
## USER INPUTS
## input linear feature class
in_features = arcpy.GetParameterAsText(0)
## the output linear feature class to create
out_feature_class = arcpy.GetParameterAsText(1)
## how many line segments to chop the line up into
num_features = arcpy.GetParameter(2)
################################################################################
## REQUIRED OBJECTS
## srs id of the in_features so we can assign the output the same
srs_id = arcpy.Describe(in_features).spatialReference.factoryCode
## we need the OID field for the ORIG_ID output_field
oid_fld = [fld.name for fld in arcpy.ListFields(in_features) if fld.type=="OID"][0]
## this list will hold the names of the input fields in order
in_fld_names = [
fld.name for fld in arcpy.ListFields(in_features)
if fld.type not in ("Blob","Geometry","GlobalID","Guid","OID","Raster")
]
## remove Shape_Length
if "Shape_Length" in in_fld_names:
in_fld_names.remove("Shape_Length")
## the field names for the output feature class
## this will be used in an InsertCursor as we create our output lines
out_fld_names = list(in_fld_names) + ["SHAPE@"]
## this will be used in a SearchCursor as we iterate over the
## original input lines
in_fld_names = [oid_fld] + in_fld_names + ["SHAPE@"]
################################################################################
## CREATE TEMP POLYLINE FEATURE CLASS IN MEMORY ################################
## in memory feature class to hold the split lines
temp_fc = arcpy.arcpy.management.CreateFeatureclass(
out_path = "memory",
out_name = "temp_fc",
geometry_type = "POLYLINE",
template = in_features,
has_m = "SAME_AS_TEMPLATE",
has_z = "SAME_AS_TEMPLATE",
spatial_reference = srs_id
)
## add the ORIG_FID field
arcpy.management.AddField(
in_table = temp_fc,
field_name = "ORIG_FID",
field_type="LONG",
field_is_nullable="NULLABLE"
)
## add the ORIG_SEQ field
arcpy.management.AddField(
in_table = temp_fc,
field_name = "ORIG_SEQ",
field_type="LONG",
field_is_nullable="NULLABLE"
)
## adjust our in_fld_names list to account for the added fields.
out_fld_names = ["ORIG_FID"] + out_fld_names + ["ORIG_SEQ"]
################################################################################
## SEGMENT LINES ###############################################################
## insert records with an insert cursor
with arcpy.da.InsertCursor(
in_table = temp_fc,
field_names = out_fld_names
) as u_cursor:
## iterate over every line in the feature class
with arcpy.da.SearchCursor(
in_table = in_features,
field_names = in_fld_names
) as s_cursor:
## for each line
for row in s_cursor:
## get the line segments
## thanks to ArcPy Cafe for this line of code
## https://arcpy.wordpress.com/2014/10/30/split-into-equal-length-features/
segments = [
row[-1].segmentAlongLine(
start_measure = i/num_features,
end_measure = (i+1)/num_features,
use_percentage = True
) for i in range(0, num_features)
]
## for each segment
for seq_id, segment in enumerate(segments, 1):
## get the original attributes and remove the geometry
## add in the new segment geometry and the ORIG_SEQ
record = list(row[:-1]) + [segment, seq_id]
## add the record for that segment.
u_cursor.insertRow(record)
################################################################################
## SAVE TO DISK ################################################################
arcpy.conversion.ExportFeatures(
in_features = temp_fc,
out_features = out_feature_class
)
################################################################################
## CLEAN-UP MEMORY WORKSPACE ###################################################
arcpy.management.Delete(
in_data = temp_fc
)
################################################################################