3

I need to find the distance between points in separate layers along a stream network. I have source sites for macro-invertebrates and sites where they are lacking. I need to find the distance between the source sites and the sites where they are not through the stream network.

I tried the linear referencing toolbox but the locate features along route tool can only use one layer of points and has not way to differentiate between sources and not source sites.

Any ideas?

I am using ArcMap 10.3?

1
  • Have a look at www. Rivex.co.uk , this is a river network processing tool which does not require any extension.
    – Hornbydd
    Commented Jun 19, 2016 at 15:04

2 Answers 2

1

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:

  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, I called my field "ST_ID")
  3. You are using ArcGIS 10.2+
  4. 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:

enter image description here

0

The linear referencing tools are probably the "by the book" way I would do this, but the answer from crmackey is probably a better solution for you. If the linear referencing toolbar allowed you to use you stream lines that tells me the feature is m-aware (routed line), but that doesn't necessarily mean the value was populated or that it was populated in the way you might expect. The simplest case here you would just merge your point files together and add a field to differentiate the types (source/not source), do a spatial join to the nearest line to get the Route ID field then run the locate along route, then use that output to create a line event. But this is assuming your sources/not sources are on the same route, which seems unlikely to me.

In my experience setting up routed networks is very efficient for particular tasks once it has been set up, but it is a lot of work on the front end.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.