I figured out how to prevent the downstream edge from being returned in the upstream trace. My mistake was simply copying my selected junction to a separate flag feature class. To make this work, I also have to add and populate a couple of fields that you'll notice in the TN_Temp_Starting_Points feature class in your default geodatabase that stores the flags you create interactively (via Data tab on ribbon), namely, SOURCEID and FEATUREGLOBALID. The SOURCEID refers to the identifier given to the junction layer in the trace network. The FEATUREGLOBALID is the GLOBALID of the junction feature where you want the starting point placed. I wish this information had been in a white paper that I had access to.
Here's code that demonstrates how to trace upstream given a junction layer.
import arcpy
def get_source_id(network, network_layer):
d = arcpy.Describe(network_layer)
fc_name = d.featureClass.name
d = arcpy.Describe(network)
for s in d.sources:
if s.name == fc_name:
return s.sourceID
arcpy.AddError(
'Could not determine network source ID for ' + network_layer.name)
raise arcpy.ExecuteError
def point_to_flag(shape_xy, spatial_ref, source_id, global_id):
flag_fc = arcpy.CreateFeatureclass_management(
out_path='memory',
out_name='flag',
geometry_type='POINT',
has_m='ENABLED',
has_z='ENABLED',
spatial_reference=spatial_ref)[0]
flag_fc = arcpy.management.AddField(flag_fc, 'SOURCEID', 'LONG')[0]
flag_fc = arcpy.management.AddField(flag_fc, 'FEATUREGLOBALID', 'GUID')[0]
fields = ['SHAPE@XY', 'SOURCEID', 'FEATUREGLOBALID']
with arcpy.da.InsertCursor(flag_fc, fields) as cursor:
cursor.insertRow((shape_xy, source_id, global_id))
return flag_fc
arcpy.env.overwriteOutput = True
network = arcpy.GetParameter(0)
junction_layer = arcpy.GetParameter(1)
spatial_ref = arcpy.Describe(junction_layer).spatialReference
source_id = get_source_id(network, junction_layer)
with arcpy.da.SearchCursor(junction_layer, ['SHAPE@XY', 'GLOBALID']) as cursor:
for i, row in enumerate(cursor):
flag = point_to_flag(row[0], spatial_ref, source_id, row[1])
Updated_Trace_Network = arcpy.tn.Trace(
in_trace_network=network,
trace_type='UPSTREAM',
starting_points=flag)[0]
arcpy.SetParameter(2, junction_layer)
So, until Pro 2.7 is released in which the trace tools return selection layers, the keys to iterating traces are:
- Create a point feature class for your starting point(s), and populate SOURCEID and FEATUREGLOBALID attributes in that feature class.
- Return aggregated geometry, and use that geometry to select features from your network layer(s).
- If you're updating a network layer's attributes while iterating, when you call arcpy.tn.Trace remember to set validate_consistency='DO_NOT_VALIDATE_CONSISTENCY' so the tool executes even as you update features.
- Run arcpy.ValidateNetworkTopology_tn when you need to validate the network after updating it.
I've implemented this via Python. It seems like it should also work via ModelBuilder iteration, but ModelBuilder iteration confuses me, so I haven't verified it works there. My id_to_edges.py script, and the utils.py script it relies on, are attached in case folks find it helpful.