I know you are not asking for a Python solution, but that is the best way I know of to tackle this problem (but I'm sure there are better ways using Network Analyst). I made a very simple example and it worked for me. It does make some assumptions though:
- Every line segment will have only 1 source and 1 sink that intersect it
- The lines have an ID field of LONG type (can just field calculate the object ID, I called my field
"ST_ID"
)
- You are using ArcGIS 10.2+
- The points are snapped to the stream network
You can copy and paste the following code into the Python window, make sure you import os
first though:
import os
def distanceBetweenPointsAlongLines(lines, stream_id, sources, sinks, out_table):
"""Find distance along lines between sources and sinks
This tool makes the following assumptions:
1. Every line segment will have only 1 source and 1 sink that intersect it
2. The lines have an ID field of LONG type (can just field calculate the object ID)
Required:
lines -- input lines
stream_id -- field with unique IDs (must be LONG)
sources -- source or origin points
sinks -- sink points
out_table -- full path to output table
"""
# step 1, spatial join the lines to both points1 & points2
tmp_sources = r'in_memory\tmp_sources'
tmp_sinks = r'in_memory\tmp_sinks'
arcpy.analysis.SpatialJoin(sources, lines, tmp_sources)
arcpy.analysis.SpatialJoin(sinks, lines, tmp_sinks)
# step 2, get line geometries in a dictionary
with arcpy.da.SearchCursor(lines, [stream_id, 'SHAPE@']) as rows:
ld = dict(r for r in rows)
# step 3, cycle through points and store in dictionaries
with arcpy.da.SearchCursor(tmp_sources, [stream_id, 'OID@', 'SHAPE@']) as rows:
source_d = {r[0]: r[1:] for r in rows}
with arcpy.da.SearchCursor(tmp_sinks, [stream_id, 'OID@', 'SHAPE@']) as rows:
sink_d = {r[0]: r[1:] for r in rows}
# step 4, create new table
path, name = os.path.split(out_table)
arcpy.management.CreateTable(path, name)
id_fields = ['Line_ID', 'Source_ID', 'Sink_ID']
for idf in id_fields:
arcpy.management.AddField(out_table, idf, 'LONG')
arcpy.management.AddField(out_table, 'Distance', 'DOUBLE')
# step 5, see where there are both sources in sinks in each line ID and fill out table
with arcpy.da.InsertCursor(out_table, id_fields + ['Distance']) as irows:
for line_id, source_atts in sorted(source_d.iteritems()):
if line_id in sink_d:
# we have a line that intersects points from both layers, find distance
source_id, source_geom = source_atts
sink_id, sink_geom = sink_d[line_id]
line_geom = ld[line_id]
# use segment along line to get segment in between points
seg = line_geom.segmentAlongLine(line_geom.measureOnLine(source_geom), line_geom.measureOnLine(sink_geom))
irows.insertRow([line_id, source_id, sink_id, seg.length])
return out_table
And then to run it, you just make a variable for an output table and call the function:
out_tab = r'C:\TEMP\Test.gdb\Lengths'
distanceBetweenPointsAlongLines("streams", "ST_ID", "MacroInvertebrates", "low_pop", out_tab)
In my case, the lines layer in the ArcMap TOC is called "streams"
, my source points is called "MacroInvertebrates"
, and the sinks are called "low_pop"
. The second parameter is a stream_id
field of the type LONG
, I just created a field called "ST_ID"
where I just field calculated the OBJECTID
field. If you do something similar this should work.
The function will make an output table that can be used to join to whichever layer with a Line_ID
(matching the stream_id
param), a Source_ID
(matching your source point's OBJECTID
/FID
field, a Sink_ID
(matching your sink point's OBJECTID
/FID
field, and finally a Distance
field, which reports the distance between source and sink points along the route (in units of the line features). Here is what my output and map looked like: