I found a way! I used a couple of different sources and manipulated the script to fit my needs (mostly from gotanuki on StackExchange). A variation of this is already on the site, but the of data that I was using is different. In case someone else runs into this issue, this is the solution I used:
# Define orientation (start, mid, end) and draw Matchlines function
# This function is defined before it is called
def orientMatchlines(feat,ix,iy):
# If the line is horizontal or vertical, the slope and
# negative reciprocal calculations will fail, so do this instead
if starty==endy or startx==endx:
if starty == endy:
y1 = iy + matchDist
y2 = iy - matchDist
x1 = ix
x2 = ix
if startx == endx:
y1 = iy
y2 = iy
x1 = ix + matchDist
x2 = ix - matchDist
else:
# Get slope of line
m = float((starty - endy)/(startx - endx))
# Get negative reciprocal
negativereciprocal = -1*((startx - endx)/(starty - endy))
# For all values of slope, calculate perpendicular line
# with length = matchDist
if m > 0:
if m >= 1:
y1 = negativereciprocal*(matchDist)+ iy
y2 = negativereciprocal*(-matchDist) + iy
x1 = ix + matchDist
x2 = ix - matchDist
if m < 1:
y1 = iy + matchDist
y2 = iy - matchDist
x1 = (matchDist/negativereciprocal) + ix
x2 = (-matchDist/negativereciprocal)+ ix
if m < 0:
if m >= -1:
y1 = iy + matchDist
y2 = iy - matchDist
x1 = (matchDist/negativereciprocal) + ix
x2 = (-matchDist/negativereciprocal)+ ix
if m < -1:
y1 = negativereciprocal*(matchDist)+ iy
y2 = negativereciprocal*(-matchDist) + iy
x1 = ix + matchDist
x2 = ix - matchDist
point1.X = x1
point1.Y = y1
point2.X = x2
point2.Y = y2
lineArray.add(point1)
lineArray.add(point2)
del x1
del x2
del y1
del y2
# Create search cursor in inLine
rows = arcpy.SearchCursor(inLine)
# Get number of records in inLine
numRecords = int(arcpy.GetCount_management(inLine).getOutput(0))
# Create new point files and array to collect values
point1 = arcpy.Point()
point2 = arcpy.Point()
lineArray = arcpy.Array()
# Define counter
counter = 0
# Loop over rows in outLine
for row in rows:
# Create the geometry object
feat = row.Shape
# Get coordinate values as lists
firstpoint = feat.firstPoint
lastpoint = feat.lastPoint
midpoint = feat.centroid
# Get X and Y values for each point
startx = firstpoint.X
starty = firstpoint.Y
endx = lastpoint.X
endy = lastpoint.Y
midx = midpoint.X
midy = midpoint.Y
m = ((starty - endy)/(startx - endx+.0001))
# For all points besides the last one
if counter < numRecords - 1:
orientMatchlines(feat,startx,starty)
# For the last point
else:
orientMatchlines(feat,endx,endy)
#Create insert cursor
cur = arcpy.InsertCursor(matchlines)
#Insert new row from array
feat = cur.newRow()
#feat.slope = m
feat.shape = lineArray
cur.insertRow(feat)
lineArray.removeAll()
del cur
# Increase counter by 1 and start again
counter = counter + 1
del row
del rows
Linear reference should do the job, but can be bulky. This is why I am using this script:
# Import arcpy module
import arcpy, os, traceback, sys,time
from arcpy import env
env.overwriteOutput = True
infc = arcpy.GetParameterAsText(0)
routeid = arcpy.GetParameterAsText(1)
outfc=arcpy.GetParameterAsText(2)
fields = [f for f in arcpy.ListFields(infc)]
fieldNames = [f.name for f in arcpy.ListFields(infc)]
# define number of lines
result=arcpy.GetCount_management(infc)
nF=int(result.getOutput(0))
d=arcpy.Describe(outfc)
SR_p=d.spatialReference
d=arcpy.Describe(infc)
SR=d.spatialReference
if SR_p.name<>SR.name:
arcpy.AddError("\nDifferent projections. Quit\n")
raise SystemExit('Quit...')
## initial point layer clean-up
fieldsP = [f for f in arcpy.ListFields(outfc)]
fieldNamesP = [f.name for f in arcpy.ListFields(outfc)]
dToLine,dAlong="D_TO_LINE","D_ALONG"
for ent in [routeid,dToLine,dAlong]:
if ent in fieldNamesP:
arcpy.DeleteField_management(outfc, ent)
outFolder, theFile="in_memory","intLines"
env.workspace = outFolder
try:
def showPyMessage():
arcpy.AddMessage(str(time.ctime()) + " - " + message)
def CalcChainage():
pC,lineID,dToLine,Chainage=theRow
shp=dictFeatures[lineID]
theRow[2]=pC.distanceTo(shp)
theRow[3]=shp.measureOnLine(pC)
return
with arcpy.da.SearchCursor(infc, ("SHAPE@",routeid)) as rows:
dictFeatures = {}
for feat,theID in rows:
dictFeatures[theID]=(feat)
del rows
arcpy.SpatialJoin_analysis(outfc, infc, "theJoin", "JOIN_ONE_TO_ONE", "KEEP_ALL", "", "CLOSEST","",dToLine)
arcpy.AddField_management("theJoin", dAlong, "DOUBLE")
## calculate chainage
result=arcpy.GetCount_management("theJoin")
nF=int(result.getOutput(0))
arcpy.AddMessage("\nComputing chainage...")
arcpy.SetProgressor("step", "", 0, nF,1)
with arcpy.da.UpdateCursor("theJoin", ("SHAPE@",routeid,dToLine,dAlong)) as rows:
for theRow in rows:
CalcChainage()
rows.updateRow(theRow)
arcpy.SetProgressorPosition()
del rows
##defining the type of route id field
n=fieldNames.index(routeid)
fType=fields[n].type
fLength=fields[n].length
try:
if fType=="String":
arcpy.AddField_management(outfc, routeid, "TEXT","","",fLength)
if fType=="Integer":
arcpy.AddField_management(outfc, routeid, "LONG")
if fType=="SmallInteger":
arcpy.AddField_management(outfc, routeid, "SHORT")
except: pass
try:
arcpy.AddField_management(outfc, dToLine, "DOUBLE")
arcpy.AddField_management(outfc, dAlong, "DOUBLE")
except: pass
arcpy.AddMessage("Transferring calcs to points...\n")
vFT=arcpy.da.TableToNumPyArray("theJoin",(routeid,dToLine,dAlong))
arcpy.SetProgressor("step", "", 0, nF,1)
with arcpy.da.UpdateCursor(outfc,(routeid,dToLine,dAlong )) as rows:
n=0
for theRow in rows:
theRow=vFT[n]
rows.updateRow(theRow)
arcpy.SetProgressorPosition()
n+=1
del vFT, rows
except NameError, theMessage:
arcpy.AddMessage (theMessage)
except:
message = "\n*** PYTHON ERRORS *** "; showPyMessage()
message = "Python Traceback Info: " + traceback.format_tb(sys.exc_info()[2])[0]; showPyMessage()
message = "Python Error Info: " + str(sys.exc_type)+ ": " + str(sys.exc_value) + "\n"; showPyMessage()
Script expects these parameters:
To get this:
Perhaps you'd like to merge (dissolve) your polylines into single line. Make sure it won't create multipart shape, i.e. ends of segments must be snapped.
Script tested on shapefiles.
Best Answer
you can use linear referencing to extract the position of the points along the lines ("locate along route" tool).
Then you sort your point using these values and compute an incremental integer.