Table of Contents
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.
## 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")]
## add field for accessing geometry and length
in_fld_names.append(["SHAPE@"])
## the field names for the output feature class
out_fld_names = list(in_fld_names)
## 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]
in_fld_names.insert(0, oid_fld)
## srs id of the in_features so we can assign the output the same
srs_id = arcpy.Describe(in_features).spatialReference.factoryCode
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 points
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.insert(0, "ORIG_FID")
out_fld_names.append("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
)
At Final Draft Mapping we provide comprehensive courses for automating tasks within ArcGIS Pro and ArcGIS Online with ArcPy and the ArcGIS API for Python. Courses range from beginner to advanced workflows and all paid courses provide extra support where you can ask questions. Automation within ArcGIS is a highly sought after skill, by adding these skills to your arsenal you are placing yourself at the forefront of that demand.
We appreciate our blog readers, you can get 25% off any (non-sale) course at any time with the code FDMBLOG25
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
##
## ArcGIS Pro Version 3.2.0
##
#################################################################################
#################################################################################
## USER INPUTS
## input linear feature class
in_features = "INPUT_FC_PATH"
## output feature class containg the ticks
out_feature_class = r"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 OBJECTS
## 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")]
## add field for accessing geometry and length
in_fld_names.append(["SHAPE@"])
## the field names for the output feature class
out_fld_names = list(in_fld_names)
## 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]
in_fld_names.insert(0, oid_fld)
## srs id of the in_features so we can assign the output the same
srs_id = arcpy.Describe(in_features).spatialReference.factoryCode
################################################################################
## CREATE TEMP POLYLINE FEATURE CLASS IN MEMORY ################################
## in memory feature class to hold the points
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.insert(0, "ORIG_FID")
out_fld_names.append("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
)
################################################################################