I have points marking tagged fish locations along a river. How do I measure the distance between each of those successive points?
You don't specify the format of your data...but lets assume that the points are in sequential order in something like a shapefile's table. You can create a new field in your table (ie Dist type double etc) and use one of the code blocks specifying a Python parser and copying
dist_between(!Shape!) or dist_cumu(!Shape!)
depending upon whether you need inter-point distance or cumulative distance. (Note...haven't tested in a long time)
EDIT this will give you the point to point distance, if you points form part of the polyline, convert the polyline to points first
otherwise you will get the Euclidean distance between the points and not the points along the polyline
for interpoint distance
I have added a function to produce points along a polyline based upon an increment if that will help
''' input shape field: returns distance between points dist_between(!Shape!) #enter into the expression box''' x0 = 0.0; y0 = 0.0; distance = 0.0 def dist_between(shape): global x0; global y0; global distance x = shape.firstpoint.X; y = shape.firstpoint.Y if x0 == 0.0 and y0 == 0.0: x0 = x; y0 = y distance = math.sqrt((x - x0)**2 + (y - y0)**2) x0 = x; y0 = y return distance
for cumulative distance
''' input shape field: returns cumulative distance between points
dist_cumu(!Shape!) #enter into the expression box'''
x0 = 0.0; y0 = 0.0; distance = 0.0
def dist_cumu(shape):
global x0; global y0; global distance
x = shape.firstpoint.X; y = shape.firstpoint.Y
if x0 == 0.0 and y0 == 0.0:
x0 = x; y0 = y
distance += math.sqrt((x - x0)**2 + (y - y0)**2)
x0 = x; y0 = y
return distance
# create points along polyline example
''' input shape field, value (distance or decimal fraction, 0-1), use_fraction (True/False), XorY (X or Y):
returns a point x meters or x decimal fraction along a line, user specifies whether X or Y coordinates
pnt_along(!Shape!, 100, False, 'X') #eg. calculate X coordinate 100 m from start point '''
def pnt_along(shape, value, use_fraction, XorY):
XorY = XorY.upper()
if use_fraction and (value > 1.0):
value = value/100.0
if shape.type == "polygon":
shape = shape.boundary()
pnt = shape.positionAlongLine(value,use_fraction)
if XorY == 'X': return pnt.centroid.X
else: return pnt.centroid.Y
I appreciate your comments, Dan.
I have a point shapefile of tagged fish locations. I snapped those points to a polyline shapefile of the rivers that the fish migrated on. Should I convert the river (polyline) shapefile to a point file and calculate your code on a distance field in that newly converted point file?
Thank you,
Jody
Did you try the code I posted? It does not require you to snap your points to the river...
No...as indicated, the interpoint distance examples I suggested would give you the Euclidean distance between the points, I think you want points to be along the polyline after they are snapped, then distance determined along the polyline...I would go with Xander's solution...just change the input files and run his script in a script editor
Hi Dan
What should I do if the polyline is complicated (ie: a grid) and want to measure the shortest distance between 2 points along that grid? I am using ArcGIS 10.2 and cannot use Nework Analyst.
If these points located along river line you can use linear referenceing tool to measure the route length along river
In case you have a featureclass with the river (1 line) and a point featureclass with the fishing locations, then you should snap those points to the line, obtain the position of the snapped points on the line, define the distance from the start of the river, sort the location on distance from start and determine the distance between consecutive points. In python this would look like this:
import arcpy fc_pnt = r"C:\Forum\Fishing\test.gdb\fishingpoints" fc_line = r"C:\Forum\Fishing\test.gdb\river" fld_diststart = "DistStart" fld_distprevious = "DistPrev" # add fields to point featureclass in case they do not exist add_flds = [fld_diststart, fld_distprevious] for fld in add_flds: if len(arcpy.ListFields(fc_pnt, wild_card=fld)) == 0: arcpy.AddField_management(fc_pnt, fld, "DOUBLE") # get first (only?) polyline from lines featureclass polyline = arcpy.da.SearchCursor(fc_line, ("SHAPE@",)).next()[0] dct = {} flds = ("SHAPE@", "OID@", fld_diststart) with arcpy.da.SearchCursor(fc_pnt, flds) as curs: for row in curs: pnt = row[0] oid = row[1] diststart = polyline.queryPointAndDistance(pnt, False) dct[oid] = diststart[1] del curs, row # sort on distance and determine distance between points dct2 = {} cnt = 0 for oid, diststart in sorted(dct.items(), key=lambda x: x[1]): cnt += 1 if cnt == 1: distbetween = 0 distprev = diststart else: distbetween = diststart - distprev distprev = diststart dct2[oid] = distbetween # update the points fc flds = ("OID@", fld_diststart, fld_distprevious) with arcpy.da.UpdateCursor(fc_pnt, flds) as curs: for row in curs: oid = row[0] row[1] = dct[oid] row[2] = dct2[oid] curs.updateRow(row) del curs, row
The result will look like this:
This looks like a good script for my needs right now. I have many points that are associated with many different lines. They don't necessarily fall on the lines but I know which line they should be associated with by a unique line ID. It seems I can use your script above but I need to somehow find the right line for the right points. Seems like maybe I should set up a cursor first for the lines that accesses the unique line ID field, and below set up a cursor for the points that accesses the unique line ID field that is attached to the points, and when those values match, then do the distance calculations? The data is multidimensional, where I have groups of points associated with a vehicle, and there are time values for each point as well, that essentially represent trips along each line. I've already sorted on the vehicle ID, the line ID, the time and the coords and now want to calculate the distance, and ultimately the speed of the vehicle between each point.
Your are wrong on how you should approach this problem. Embedded cursors are virtually never the solution to any problem. Use a dictionary to make the match between the line IDs and the points and then process one cursor normally. The larger your datasets get the embedded cursors slow down exponentially, while Dictionaries have a linear performance impact. So for 10000 points on 100 lines embedded cursors will perform at least 100 times slower than a dictionary lookup.
Although I have not tested the code below, I believe it will work. The main part I am unsure of is the sort of dct that creates dct2. In any case, once all bugs are worked out this code will perform wonderfully.
import arcpy fc_pnt = r"C:\Forum\Fishing\test.gdb\fishingpoints" fc_line = r"C:\Forum\Fishing\test.gdb\river" fld_diststart = "DistStart" fld_distprevious = "DistPrev" # add fields to point featureclass in case they do not exist add_flds = [fld_diststart, fld_distprevious] for fld in add_flds: if len(arcpy.ListFields(fc_pnt, wild_card=fld)) == 0: arcpy.AddField_management(fc_pnt, fld, "DOUBLE") fields = ["LINE_ID", "SHAPE@"] # Create a dictionary of lines with their geometry valueDict = {r[0]:(r[1:]) for r in arcpy.da.SearchCursor(fc_line, fields)} dct = {} flds = ["LINE_ID", "SHAPE@", "OID@", fld_diststart] with arcpy.da.SearchCursor(fc_pnt, flds) as curs: for row in curs: line = row[0] if line in valueDict: polyline = valueDict[line] pnt = row[1] oid = row[2] diststart = polyline.queryPointAndDistance(pnt, False) dct[(line, oid)] = diststart[1] del curs, row # sort on distance and determine distance between points dct2 = {} cnt = 0 prevline = -1 for line_oid, diststart in sorted(dct.items(), key=lambda x: x[1]): cnt += 1 if cnt == 1: distbetween = 0 distprev = diststart prevline = line_oid[0] elif line_oid[0] != prevline: distbetween = 0 distprev = diststart prevline = line_oid[0] else: distbetween = diststart - distprev distprev = diststart dct2[line_oid] = distbetween # update the points fc flds = ("LINE_ID", "OID@", fld_diststart, fld_distprevious) with arcpy.da.UpdateCursor(fc_pnt, flds) as curs: for row in curs: line = row[0] oid = row[1] if (line, oid) in dct: row[2] = dct[(line, oid)] row[3] = dct2[(line, oid)] curs.updateRow(row) del curs, row