POST
|
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 discovered this when I noticed that traces initiated from a copy of TN_Temp_Starting_Points worked. I deleted fields one by one and ran traces to see which fields were actually required, leading me to just the two fields I mentioned above. I do not know if that works in all cases. It would be nice if an Esri guru could comment on how to properly make a starting flag feature class. 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)
... View more
12-02-2020
02:08 PM
|
0
|
1
|
854
|
POST
|
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.
... View more
12-02-2020
02:03 PM
|
0
|
2
|
3542
|
POST
|
I've got 2.6.0 currently. My Pro instance can't connect to the update server, so I'm unable to upgrade to 2.6.3.
... View more
12-02-2020
12:11 PM
|
1
|
0
|
1546
|
POST
|
I have a trace network with edges (NHDFlowline) and junctions (WRProxyMZ) that has been validated. When I trace downstream from a point along an edge (green circle in figures below), using all WRProxyMZ points as barriers, with Include Barrier Features checked, the barrier that stops the trace is not returned in the results. It seems like it should be returned if Include Barrier Features is checked. See the two screenshots below. The first shows the map layers and starting flag which I set interactively. The second shows the Trace tool inputs (no advanced options set) and the resulting map selection after running the trace. Example data and Pro project are attached. Starting flag (green) and junctions used as barriers (orange) Trace results -- note no downstream junction selected
... View more
11-22-2020
08:49 AM
|
0
|
6
|
1575
|
POST
|
I'm thinking the only recourse until 2.7 is released, is to output an aggregated feature and then use a spatial filter to select the appropriate edges That's what I wound up doing. I used Python rather than ModelBuilder because I find it easier to follow. Some example code is below. I also heard Arc Hydro is using trace networks now, so I will peak at their code later. For this to work, I have to deal with the fact that when I copy a given junction to a new feature class to use as a starting point, when I perform an upstream trace, the trace tends to also include the edge just downstream of that junction. Before running the code below, I precompute length downstream to the network outlet for each junction, and also from the downstream end of each edge. If an edge has a length downstream value less than the junction, then it is downstream from the junction. I only want upstream edges. Updated_Trace_Network = arcpy.tn.Trace(
in_trace_network=network,
trace_type='UPSTREAM',
starting_points=start_pt,
validate_consistency='DO_NOT_VALIDATE_CONSISTENCY', ## proceed even if we've updated other features
condition_barriers=[['HydroID', 'DOES_NOT_EQUAL', 'SPECIFIC_VALUE', hydro_id, 'OR']],
traversability_scope='JUNCTIONS_ONLY',
result_types=['AGGREGATED_GEOMETRY'],
trace_name=str(hydro_id),
aggregated_lines=agg_lines)[0]
if Updated_Trace_Network:
# Select the edges corresponding to the aggregated trace result
lyr = arcpy.SelectLayerByLocation_management(
edge_layer, 'SHARE_A_LINE_SEGMENT_WITH', agg_lines)[0]
edge_count = int(arcpy.GetCount_management(lyr)[0])
if edge_count > 0:
# Sometimes downstream edges are selected. Remove them.
arcpy.SelectLayerByAttribute_management(
lyr, 'SUBSET_SELECTION', 'LengthDown >= ' + str(length_down))
edge_count = int(arcpy.GetCount_management(lyr)[0])
if edge_count > 0:
arcpy.AddMessage('Upstream edge count: ' + str(edge_count))
arcpy.CalculateField_management(lyr, 'JunctionID', hydro_id)
else:
arcpy.AddMessage('No upstream edges found')
else:
arcpy.AddMessage('No upstream edges found')
... View more
11-07-2020
02:57 PM
|
0
|
0
|
3572
|
POST
|
hydro_id is a number, like 1392. It's used to create a whereclause like "HydroID = 1392". I use hydro_id to uniquely identify each feature in my feature class. It's unique like ObjectID, but I have control over it. In line 24, whereclause is one of the optional arguments in the FeatureClassToFeatureClass function that is called on line 20. There is a closing paren on line 24 that completes the function. Geoprocessing tools can return one or more outputs, so the [0] is grabbing the first output, which is the output feature class. The same thing goes for the [0] bracket at the end of the arcpy.tn.Trace call that starts on line 25. I think condition barriers is right. I created that code by exporting it as a Python script from a working GP Model in ModelBuilder, and then tweaking it to run as a script tool. Also, you can have multiple conditions, and each condition has a list of parameters, so the conditions argument is a list of conditions, i.e., a list of lists. In my case, I only have one condition, so the outer list only has one element in it (the inner list), and I can see how that looks funny. Even if those condition barriers were wrong, if it didn't cause an error, the worst it would do is to make all upstream edges be selected instead of just the ones between the starting point and the nearest upstream barriers. However, even downstream edges are included in that search cursor on line 32, so it's like the layer isn't yet seeing the selection from the network trace. I observed that as the tool executes, I do see the selections from each trace happening in the map, and when the tool finishes, the selection from the last trace is still there. But, the edge layer as seen by the code doesn't see that selection; ditto when run within an iterator in ModelBuilder.
... View more
11-06-2020
02:03 PM
|
0
|
1
|
3572
|
POST
|
I attempted the same task in a script tool and found the same result: The input edge layer does not have the trace results as a selection when arcpy.tn.Trace completes. I'm thinking the only recourse until 2.7 is released, is to output an aggregated feature and then use a spatial filter to select the appropriate edges, which will be very slow. I'm also worried about geometries not matching exactly since exported junctions don't seem to return the same results as an interactive starting point. import arcpy
arcpy.env.overwriteOutput = True
network = arcpy.GetParameter(0)
junction_layer = arcpy.GetParameter(1)
edge_layer = arcpy.GetParameter(2)
# Trace network overwrites selections, so grab HydroIDs before tracing
hydro_ids = []
with arcpy.da.SearchCursor(junction_layer, ['HydroID']) as cursor:
for row in cursor:
hydro_ids.append(row[0])
for hydro_id in hydro_ids:
# Using junction layer even with filter applied results in all points used,
# so send the junction to a temporary feature class for use in tracing
start_pt = arcpy.conversion.FeatureClassToFeatureClass(
in_features=junction_layer,
out_path='memory',
out_name='current_junction',
where_clause='HydroID = ' + str(hydro_id))[0]
Updated_Trace_Network = arcpy.tn.Trace(
in_trace_network=network,
trace_type='UPSTREAM',
starting_points=start_pt,
condition_barriers=[['HydroID', 'DOES_NOT_EQUAL', 'SPECIFIC_VALUE', hydro_id, 'OR']],
traversability_scope='JUNCTIONS_ONLY',)[0]
if Updated_Trace_Network:
with arcpy.da.SearchCursor(edge_layer, ['JunctionID']) as cursor:
count = 0
for row in cursor:
count += 1
# Should be a selected subset, but we're getting the entire feature class
arcpy.AddMessage('Edges: ' + str(count))
arcpy.SetParameter(3, edge_layer)
... View more
11-06-2020
09:22 AM
|
0
|
4
|
3572
|
POST
|
I don't think there is an output destination in _SingleJunctionoEdges. The input to that tool is a layer, and the tool calculates a field on the layer, so the output labeled Output Feature Class is actually the input layer. Or maybe I'm misunderstanding. I put the geodatabase and toolbox here in case you want to have a look: NetworkTest.zip - Box To test: 1. Add the Hydro network in the geodatabase to a map. 2. Select a single WRProxyMZ point (or more, but it just takes longer). 3. In the toolbox, run Junctions To Edges Iterator, with the Hydro network, WRProxyMZ, and NHDFlowline as inputs. The tool's job is to trace upstream from each selected point, using all other WRProxyMZ points as barriers. For each upstream edge found between the starting point and the upstream points, assign the JunctionID attribute of the edges to be equal to the HydroID of the starting point. The freshly unzipped geodatabase has all JunctionIDs as -1. When the tool finishes, the edges in the trace results for the final iteration are selected in the map. However, the calculate field task ran on the entire layer and not just the selection, as evidenced by all NHDFlowlines having the same JunctionID, e.g., 1392.
... View more
11-06-2020
07:25 AM
|
0
|
5
|
3572
|
POST
|
In theory, the trace network would serve your needs. I'm actually trying to do something similar. The general procedure would be to build a trace network with your snapped cities and rivers. Then you would iterate through each city, and do the following: 1. Set the city as a starting point. 2. Trace upstream. 3. Collect and names of the upstream cities that were returned in the trace. They would be returned in the form of a feature selection on the city layer. 4. Concatenate the names in a comma separated list and write to the Cities Upstream field in the city layer. 5. Trace downstream. 6. Collect the names downstream. 7. Write the names to the Cities Downstream field. (In fact, if you iterate through all cities and trace upstream, you can use that information to also build the list of cities downstream, but conceptually it's just easier to do steps 5, 6, and 7, albeit probably slower from a scripting standpoint). However, in practice I'm struggling to get the iteration working in ModelBuilder. I may wait a few days to see if any experts have solutions. Things may be better in Pro 2.7, but my client will be at 2.6 for a while so I'm stuck for now. If a ModelBuilder solution doesn't present itself, I'll try Python instead.
... View more
11-05-2020
12:49 PM
|
0
|
0
|
467
|
POST
|
I have a GP model that performs a trace and then assigns a long integer stored in the HydroID variable to a field in the edge layer. Calculate Field has the Updated Trace Network as a precondition, so it waits until the trace is finished and there is a selection. This works fine when I run the model. The model is shown below, and is named "_Single Junction To Edges". The problem arises when I encapsulate this model in another model with an iterator. I want to iterate through a selected set of junctions, and for each one, perform the trace and calculate the field. However, calculate field appears to be running on the entire edge layer rather than the selection. As the model runs, I can see the selections being made in the map, but the tool ignores them. The model with the iterator, which includes "_Single Junction To Edges" as one of the tools, is shown below. What do I need to do to get the iterator version to work like the standalone version with respect to using feature selections when it comes time to calculate the field? I do acknowledge that outputting a group layer with the selection from the Trace tool in Pro 2.7, as discussed in this thread on working with trace results, may be the solution. But it seems like the single model was working correctly, so I wonder if I'm just missing something that will make it work in the iterator. I'm on Pro 2.6.
... View more
11-05-2020
12:27 PM
|
0
|
10
|
3914
|
POST
|
As a workaround for the Trace tool not honoring feature selections, I am trying the following: 1. In Pro 2.6, select the junction feature I'm interested in. 2. Export the feature to a new feature class named Flag in my default geodatabase. Note that my trace network is in another geodatabase. 3. Clear the selection. 4. Run an upstream trace, using Flag as the Starting Points with all other parameters at defaults. However, in addition to the expected upstream edges, the resulting feature selection includes the edge just downstream of the junction (orange circle in image below). When I instead use the ribbon to interactively set a starting point at the desired junction (I made sure it snapped to that junction feature before clicking), and then run an upstream trace (initiated from the ribbon, so it's using the TN_Temp_Starting_Points and all defaults), I get the desired result, which is just the upstream set of edges. Why is my exported junction causing a downstream edge to be returned from an upstream trace? Note that interactively setting starting points isn't an option for me, because I'm trying to automate this for thousands of junctions via ModelBuilder.
... View more
11-05-2020
11:55 AM
|
0
|
2
|
886
|
POST
|
I have a trace network built from a point feature class and a line feature class. Traces initiated using locations set by the Trace Locations functionality of the ribbon work fine. I'd like to be able to use a feature selection as my starting points instead of interactively clicking each point. I clear all trace locations via the ribbon. However, even if I select just a single point in my point layer, when I run a downstream trace and set the point layer as the Starting Points (leaving everything else as default), the trace completes using all the points in the layer as starting points rather than the selected point. I was under the impression that geoprocessing tools honor feature selections, but the Trace tool does not appear to be doing so. I'm in Pro 2.6.
... View more
11-05-2020
09:48 AM
|
0
|
1
|
541
|
POST
|
I made a Python toolbox with a simple tool for testing arcpy's charting functionality in Pro. I'm trying to plot a line chart from these data, which I imported into a geodatabase as a table: x,y 1,4 2,2 3,4 I have a map named "Map" with the above table as the first table in the map. Here's the tool class's execute method: def execute(self, parameters, messages):
aprx = arcpy.mp.ArcGISProject("current")
map = aprx.listMaps("Map")[0]
table = map.listTables()[0]
c = arcpy.Chart('MyChart')
c.type = 'line'
c.xAxis.field = 'x'
c.yAxis.field = 'y'
c.addToLayer(table)
return When I run the tool, a chart is added as shown below the table in the Table of Contents, but when I open the chart, instead of a chart I see "Select Variable(s) in the Chart Properties pane to begin". But the chart properties pane does show the variables selected. If I uncheck and then re-check "y" in the numeric fields list and click Apply, nothing happens. If I right-click the table to manually create a new chart, the chart previously created by my GP tool disappears.
... View more
05-11-2020
11:56 AM
|
1
|
0
|
480
|
POST
|
Hi Uma, It sounds like I'll have to take that into consideration regarding tool activation, and I'll just notify users of the crash fix in 2.6. Thanks for the info! Tim
... View more
04-23-2020
04:10 PM
|
0
|
0
|
549
|
Title | Kudos | Posted |
---|---|---|
1 | 05-11-2020 11:56 AM | |
3 | 06-03-2021 09:16 AM | |
2 | 12-13-2020 12:16 PM | |
1 | 12-02-2020 12:11 PM | |
1 | 06-05-2019 06:55 AM |
Online Status |
Offline
|
Date Last Visited |
a week ago
|