# How to create line segments and add sequential ids

2188
3
11-20-2016 09:45 AM
New Contributor

I’m fairly inexperienced with python, but have been attempting to use it to automate a geoprocessing workflow. I’ve pieced together most of it, but I’m stuck with one section…

I have a shapefile with transect lines, and attribute fields TransNa (transect name) and surveydate (date of survey). I want to split each transect line into segments based on a user specified distance. The output file would have a number of segments representing each transect line. I also want to add a few fields to the output file.

Interval – sequential record number for each segment of a transect line.  First segment of each transect line would be 1.

Segment – Sort records by surveydate, TransNa and Interval fields, then add a sequential record number for all records.

Mid_x – x  coordinate for segment midpoint

Mid_y – y coordinate for segment midpoint

Any guidance on how to achieve this would be much appreciated!

Thanks

3 Replies
MVP Esteemed Contributor

A few questions first:

1. version of software, and do you have an Advanced (ArcInfo) license?
2. are your transects all one segment, or several?,
1. and if several, does the distance need to be split across the segments.  For example, if first part is 450m and second is 550m, and your segments will be 1km (1000m) each, do you need the 5th segment to include 500m from the first, and 500m from the second

How I name my transect segments if, lets say the transect number i 101, the segments are 101.1, 101.2, 101.3.  The next transect would be 102.1, 102.2, etc.  We had a project that has transects that were 25-25k that I had to re-segment into 1k lengths to I could buffer.  Because the flight line transect are over several segments (after removing "off-trans" segments), I had to create the transects as "routes" and use the geometry to product the exact length needed).

If they are all one segment, the process is much easier, including some ArcGIS tools or see this Feature Line Split

This probably will not run, as is (for one, you would have to supply the input attributes) and I would already have my segments sorted and converted to routes (I have 2 prior scripts that do that), but depending on how far along you are will determine if any of this will help.

EDIT: cleaned up the code a little...

``````'''
Purpose:
to re-segment the flown-and-cleaned transects into new segments, e.g. 1k
and then rerun the buffers.

Requirements for inputs:
- Sorted transect FC, with all attributes
(including TRANSNO, SEGMENT, VIEWSIDE, TransSeg (?), sortSegs, distFrom, distTo)
- Route FC created from the above based on TRANSNO, and two fields: distFrom, distTo
Input parameters for this script:
- workspace, i.e. the FGDB to read/write from
- sorted transects FC
- routes
- new segment distance
Outputs:
- temp files (overwritten for each segment):
- freq (frequency table)
- Points  (the points used for splitting)
- zTransExp (the individual exported sorted trans)
- zTransSplit  (the individual split transects)
- zTranFinal   (last individual FC before append)
- reSegment<newLength>  (final, all transects appended)

###################################################################################
'''
import os, sys
import time
import arcpy
#from gpdecorators import *

def myMsgs(message):
print(message)

myMsgs("Running against: {}".format(sys.version))

# catch_errors decorator must preceed a function using the @ notation.
#@catch_errors
def main():

'''
Main function to resegment (retrofit) the flown transect to new segment length
'''

#setup variables
arcpy.env.overwriteOutput = True

# set parameter for script source, for template location
theScriptPath = arcpy.GetParameterAsText(0)
if not theScriptPath:
theScriptPath = r"\_data\_scripts"
theTemplatePath = os.path.join(theScriptPath,"Reports\RetroFit\ReSegment.gdb")

# set parameter for project directory
theProjectDir = arcpy.GetParameterAsText(1)
if not theProjectDir:
theProjectDir = r"\_data\_ProjectName"
else:
myMsgs(theProjectDir)
theProjectDir = os.path.realpath(theProjectDir)

# project folder, create other
fdrReports = os.path.join(theProjectDir, "Reports")

fdrRetroFit = arcpy.GetParameterAsText(6)
if not fdrRetroFit:
fdrRetroFit = "RetroFit"
else:
myMsgs(fdrRetroFit)

fdrRetroFit = os.path.join(fdrReports, fdrRetroFit)
outFGDB = os.path.join(fdrRetroFit, "ReSegment.gdb")
arcpy.env.workspace = outFGDB

inputSort = arcpy.GetParameterAsText(2)
if not inputSort:
inputSort =  "transSorted"   # Sorted transects

inputRoutes = arcpy.GetParameterAsText(3)
if not inputRoutes:
inputRoutes = "transRoutes"  # ROUTES

newLength = arcpy.GetParameter(4)
if not newLength:
newLength = 1000
newLength= int(newLength)

thePts = "Point"
#createNewFinal = True

#if createNewFinal == True:
finalFC = "reSegA" + str(newLength)
if arcpy.Exists(finalFC):
myMsgs("deleting " + finalFC)
arcpy.Delete_management(finalFC)
if arcpy.Exists(finalFC):
myMsgs("still exists")
else:
myMsgs("doesnt exist")
spatialRef = arcpy.SpatialReference(3338) #arcpy.Describe(theTranFinal).spatialReference
myMsgs(" Creating/replacing the final FC: " + finalFC)
myMsgs(theTemplatePath)

# Use Search Cursor to process one TRANSNO and geometry at a time
sCur = arcpy.da.SearchCursor(inputRoutes, ["TRANSNO", "SHAPE@"])
for aRoute in sCur:
pointList = []              # initialize point array
theTransID = aRoute[0]      # get TRANSNO string value
geo = aRoute[1]             # get all transect geometry
lenTrans = geo.length       # get the full length of the route

myMsgs("-->> ReSegmenting {0}".format(theTransID) +
"; total initial length: " + str(int(lenTrans)) + " ###")

# Calculate the number of transect at full newLength
calcSegs = int(lenTrans/newLength)

# Determine the length of the remainder segment
lastSeg =  lenTrans - (newLength * calcSegs)    # remainder length
#myMsgs ("No. New Segments: " + str(calcSegs))

# initialize the starting distance to point to beginning of line
startPtDist = 0
# initialze counter for creating new segments
newSeg = 0
# Determine whether last segment should be added to previous or standalone
if lastSeg < (newLength/2):
# if < newLength/2, added to previous segment --- else stands alone
calcSegs = calcSegs - 1      # number of new point pairs needed
# Collect points along route to be used for spliting
while newSeg < calcSegs:
startPtDist = startPtDist + newLength         # 1st split point
newPT = geo.positionAlongLine(startPtDist)
pointList.append(newPT)
#myMsgs ("Distance along line: " + str(startPtDist))
newSeg = newSeg + 1

# Creating Split Point feature class, create and calc ptID field
# #myMsgs("         Creating point FC for transect: " + theTransID)
arcpy.CopyFeatures_management(pointList, thePts)
#myMsgs("         ..calcing ptID = Objectid")    # to help with the re-sorting process
#arcpy.CalculateField_management(thePts,"ptID","[OBJECTID]","VB","#")
arcpy.CalculateField_management(thePts,"ptID","!OBJECTID!","PYTHON_9.3","#")
'''
#################################################################################
Done creating the list of points to be used for splitting
#################################################################################
'''

# define an Expression for reselecting individual transect for processing
exprTran = arcpy.AddFieldDelimiters(inputSort, "TRANSNO") + " = " + "'%s'" %theTransID

# create a sorted Feature Layer of each individual transect
# #myMsgs("         Making new temp trans layer for the transect...")
#  "trans_fl"   the second arg below if want to be tmp, first arg in SJ
# -->>arcpy.MakeFeatureLayer_management(inputSort, "trans_fl", exprTran)
zTransExp = "in_memory/zTransExp"
arcpy.Select_analysis(inputSort, zTransExp, exprTran)  # makes FC not layer

# Clean up the output be deleting fields with bogus data for the new segments
lstFieldsToDelete = "LENGTH;LATITUDE;LONGITUDE;XCOORD;YCOORD;PLANEDIST;TRANDIST;STARTTIME;ENDTIME;LENGTH_meters"
arcpy.DeleteField_management(zTransExp,lstFieldsToDelete)

# create new Feature Layer split by the points
#myMsgs("            ....splitting temp trans layer by Point to zTransSplit")
zTransSplit = "in_memory/zTransSplit"
arcpy.SplitLineAtPoint_management(zTransExp,thePts, zTransSplit,"5 Meters")

# add field for a new

# creating a Frequency table of segments for use in cursors
freqFC = "freq"
arcpy.Frequency_analysis(zTransSplit, freqFC, "tmpSortID")

# Search cursor of segment numbers
sCur = arcpy.da.SearchCursor(freqFC, ["tmpSortID"])
for aRec in sCur:
freqSort = aRec[0]
#myMsgs("        Segment block: " + str(freqSort))
cntSegs = 0

# Update cursor of the segments

uCur = arcpy.da.UpdateCursor(zTransSplit, ["tmpSortID", "SHAPE@"])
for row in uCur:
geo = row[1]
firstPt = geo.firstPoint
firstPtX = int(firstPt.X)
firstPtY = int(firstPt.Y)
#myMsgs("X: " + str(firstPtX) + "  Y: " + str(firstPtY))

# Search cursor of the split points
sCur = arcpy.da.SearchCursor(thePts, ["ptID", "SHAPE@"])
for rowPt in sCur:
geoPt = rowPt[1]
aPt = geoPt.firstPoint
aPtX = int(aPt.X)
aPtY = int(aPt.Y)
theID = rowPt[0]
#myMsgs("    x: " + str(aPtX) + "  y: " + str(aPtY))
if cntSegs == 0:
theNewPtID = 0
elif ((firstPtX == aPtX) and (firstPtY == aPtY)):  #lastPT == ptXY:
#myMsgs("hello")
theNewPtID = theID
cntSegs = cntSegs + 1

#myMsgs(str(theNewPtID))
row[0] = theNewPtID
uCur.updateRow(row)

#  overwrites the same file but not before appending to a very final FC
theTranFinal = "zTranFinal"
#myMsgs("           ...resorting")
arcpy.Sort_management(zTransSplit,theTranFinal,"sortSegs ASCENDING;tmpSortID ASCENDING","UR")

'''
############################################################################
IF ALL SEGS IN PROPER ORDER, will assign the new numbers and related numbers
############################################################################
'''
# Adding a field for the new Segment numbers, with decimal for related segments
#myMsgs("     -->  Adding a new field to relate short segments")

# Creating new segment numbers for the new segments.
#   If segment is final or "full" length, number is #.1, e.g. 1.1, 2.1, etc
#   If multiple to make new length, decimal is incremented, e.g. 5.1, 5.2, etc
# #myMsgs("         Assigning the new (final) segment numbers")
newSegID = 1.1
prevLength = 0
currLength = 0
uCursor = arcpy.da.UpdateCursor(theTranFinal, ["finalSortID", "SHAPE@"])
for aRow in uCursor:
currLength = aRow[1].length
if currLength < (newLength - 1):
aRow[0] = newSegID
newSegID = (newSegID + .1)
prevLength = currLength + prevLength
if prevLength > (newLength - 1):
newSegID = (int(newSegID) + 1.1)
prevLength = 0
else:
aRow[0] = newSegID
newSegID = (int(newSegID) + 1.1)
prevLength = 0
uCursor.updateRow(aRow)

#arcpy.CalculateField_management(theTranFinal,"finalSortInt","Int ( [finalSortID] )","VB","#")
arcpy.CalculateField_management(theTranFinal,"finalSortInt","int(!finalSortID!)","PYTHON_9.3","#")

#  ---> Move the create to another location....check to see
#      if it exists....othrwise, create from the zfinalSort
#      created below

if not arcpy.Exists(finalFC):
arcpy.CreateFeatureclass_management(outFGDB, finalFC, "POLYLINE",theTranFinal, "SAME_AS_TEMPLATE", "SAME_AS_TEMPLATE", spatialRef)
#else:
#arcpy.CreateFeatureclass_management(outFGDB, finalFC, "POLYLINE", theTranFinal, "SAME_AS_TEMPLATE", "SAME_AS_TEMPLATE", spatialRef)
#createNewFinal = False

# append all the individual files to one.  This will feed into FlipAndBuffer tool
myMsgs("          ...Appending to " + finalFC +  " ###")
arcpy.Append_management(theTranFinal, finalFC, "TEST", "#", "#")

# add final uniqID field for final matchup of new segments to bear group locations
arcpy.AddField_management(finalFC, "newUniqID", "SHORT", 6, 0, 6)
arcpy.CalculateField_management(finalFC, "newUniqID", "!OBJECTID!","PYTHON_9.3","#")

# Clean up the output by deleting fields with bogus data for the new segments
lstFieldsToDelete = "sortSegs;distFrom;distTo;tmpSortID"
arcpy.DeleteField_management(finalFC,lstFieldsToDelete)
myMsgs("  Done deleting fields bogus fields")

myMsgs("  Creating a Feature Layer and adding it to the TOC")
fullFinalPath = os.path.join(outFGDB, finalFC)
arcpy.SetParameterAsText(5, fullFinalPath)

myMsgs('!!! Success !!!')

# End main function

if __name__ == '__main__':
main()
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍``````

If you look at the UpdateCursor area, you can see that I am working with the first and last point of the segments. It is just as easy to grab mid point geometry.

I you have code you want to post, make sure it is formatted correctly.  To add to a comment here, at the menu at the top,     .... More > Syntax Highlighter > Python    then paste you code in the box.

Hope this helps.

MVP Esteemed Contributor