Skip To Content ArcGIS for Developers Sign In Dashboard

Trace a utility network

On the ArcGIS platform, utility networks offer a framework for modeling utility systems, such as, electric, gas, water, storm water, wastewater, and telecommunications. Each utility network demonstrates how features are connected and how dynamic devices are configured. For more information about utility networks, see ArcGIS Pro's help topic What is a utility network?. This topic describes how to build ArcGIS Runtime apps that can trace the flow of resources, such as gas, water, and electricity, through its network. You can explore how a network is affected by real-world events such as storms, outages, or equipment failure, by asking certain questions, for example,

  • How is your network connected?
  • How does electricity/gas/water reach your house?
  • If a device is disabled, what section of the network will be out of power?

To help answer these questions, ArcGIS Runtime supports upstream, downstream, subnetwork, and connected utility traces. To trace a utility network you need to:

  1. Access the utility network
  2. Define the trace parameters
    1. Decide which trace to perform
    2. Define the starting and barrier locations
    3. Specify the trace configuration
  3. Execute the trace
  4. Examine the results

Access the utility network

Utility networks are implemented in service-based geodatabases as network controller datasets. These datasets contain a network's service feature tables along with the network's domains, sources, tiers, assets, terminals, rules and associations. This utility network is accessible via these service feature tables stored in a single feature service.

You can display and share a complete utility network with a user via a web map if the map includes one feature layer for every network source.

Diagram of objects loaded when loading a utility network with a map with layers for all network sources.

To display and share the utility network, create a utility network object from a feature service URL and a web map that contains all the layers that participate in the utility network.

UtilityNetwork utilityNetwork = new UtilityNetwork(featureServiceURL, map);


The feature service provides access to the topological network in the utility network. So, you could provide a map that contains just a subset of the feature layers, for a specific workflow. Any tracing would be performed using the full topological network provided by the feature service. If you need to add additional utility network layers you can create them from the individual network sources, as required.

You can also access a utility network and run a trace completely without a map. Just provide the feature service URL when you create the utility network. If needed, you can create a completely new map by creating feature layers from the network sources.

UtilityNetwork utilityNetwork = new UtilityNetwork(featureServiceURL);


ArcGIS Runtime supports Utility Network version 2 and later that is provided from ArcGIS Pro 2.2 onwards. For details see utility network upgrade history.

Load the utility network

The utility network follows the loadable pattern for asynchronous resources. Loading the utility network loads the entire utility network schema (information about the datasets that participate in the network). Once loaded, your app can navigate this network schema to discover the domain networks it contains and any further details about the network.

if (utilityNetwork.getLoadStatus() == LoadStatus.LOADED) {
  utilityNetwork.addDoneLoadingListener(() -> {
    System.out.println("The Utility Network schema version is " + utilityNetwork.getDefinition().getSchemaVersion());
} else {
  new Alert(Alert.AlertType.ERROR, "Error loading Utility Network.").show();

Define the trace parameters

Trace parameters define how the trace analysis proceeds across the utility network. These are the essential trace parameters:

  1. Trace type
  2. Start and barrier locations
  3. Trace configuration

Trace type

This version of the ArcGIS Runtime SDK supports four different types of utility network trace:

Upstream trace

In a source-based network (gas or electric), an upstream trace is against the flow and toward the source, such as a circuit breaker or generator (subnetwork controller). In a sink-based network (sewer or storm water), an upstream trace is against the flow and away from the sink such as a sewage treatment (subnetwork controller).

For more details see upstream trace.

Downstream trace

In a source-based network (gas or electric), a downstream trace is with the flow and away from the source, such as a circuit breaker or generator (subnetwork controller). In a sink-based network (sewer or storm water), a downstream trace is with the flow and toward the sink such as a sewage treatment (subnetwork controller).

For more details see downstream trace.

Subnetwork trace

A subnetwork trace discovers all features participating in a subnetwork. This trace type is useful for validating whether subnetworks, such as circuits or zones, are defined or edited appropriately.

The trace begins at one or more starting points and spans outward along connected features to find subnetwork controllers that are traversable. A subnetwork trace stops when it encounters a barrier, a feature that is not traversable, or when there are no more connected features.

For more details see subnetwork trace.

Connected trace

A connected trace begins at one or more starting points and spans outward radially along connected features. A trace stops when a barrier is encountered or there are no more connected features. This type of trace is useful for validating newly edited features are connected as expected.

For more details see find connected features.

Use the utility trace type to create the parameters

Create a set of UtilityTraceParameters by providing a UtilityTraceType of UPSTREAM, DOWNSTREAM, SUBNETWORK, or CONNECTED, along with a collection of starting locations (if known at this stage).

UtilityTraceParameters utilityTraceParameters = new UtilityTraceParameters(UtilityTraceType.UPSTREAM, startingLocations);

Start and barrier locations

Each trace requires a start location from which to initiate the trace. You can create a start location from a specific feature, as follows:

  1. Get the network source for the feature.
    1. Get the ArcGIS feature table for the feature.
    2. Get the network source represented by the ArcGIS feature table using the table name.

    ArcGISFeatureTable featureTable =((ArcGISFeature)feature).getFeatureTable();
    UtilityNetworkSource utilityNetworkSource = utilityNetwork.getDefinition().getNetworkSource(featureTable.getTableName());

  2. Create a utility element for the feature. You must adopt different code patterns, depending on whether the utility network source is a junction or an edge.
    1. If the utility network source is a junction, find the available terminals for that feature using the utility network definition. If more than one terminal is found, select a terminal to use. Create the utility element from the feature and set the terminal.

      UtilityElement utilityElement = utilityNetwork.createElement(arcGISFeature);
      // create the utility element from the feature
      if (utilityNetworkSource.getSourceType() == UtilityNetworkSource.Type.JUNCTION) {
        UtilityTerminalConfiguration terminalConfiguration = utilityElement.getAssetType().getTerminalConfiguration();
        if (terminalConfiguration != null) {
          List<UtilityTerminal> utilityTerminals = terminalConfiguration.getTerminals();
          if (!utilityTerminals.isEmpty()) {
            UtilityTerminal utilityTerminal = utilityTerminals.get(0);
      } else {
        System.out.println("SourceType is not a JUNCTION");

    2. If the utility network source is an edge feature, create the utility element from the feature. Call the setfractionAlongEdge method to set a value representing the location of the starting point or barrier along that edge.

      // create the element from the feature
      UtilityElement utilityElement = utilityNetwork.createElement(arcGISFeature, null);
      if (utilityNetworkSource.getSourceType() == UtilityNetworkSource.Type.EDGE) {
        Geometry geometry = arcGISFeature.getGeometry();
        // determine how far the starting point or barrier is located along the edge
        if (geometry instanceof Polyline) {
          Polyline polyline = (Polyline) GeometryEngine.removeZ(geometry);
          double fraction = GeometryEngine.fractionAlong(polyline, mapPoint, -1.0);
          if (!Double.isNaN(fraction )) {
          else System.out.println("fractionAlong(geometry) does not return a number");
      } else {
        System.out.println("SourceType is not an EDGE");

  3. Add the utility element to the utility trace parameters’ list of starting locations, which is returned by getStartingLocations .


  4. If you need to add a barrier, complete the steps 1 and 2 above and add the utility element to the utility trace parameters’ list of barriers, which is returned by getBarriers.

Trace configuration

You can enhance a trace as using the trace configuration that is set on the UtilityTraceParameters. With the trace configuration you could:

  • stop the trace at protective devices if they are open. For example, the flow of electricity in a network will be stopped if a fuse is open
  • control the types of features traced (pipe diameter greater than 6 inches)

Each trace configuration manages basic properties such as:

  • Include barriers in trace results
  • Include containers in trace results
  • Include content in trace results
  • Include structures in trace results
  • Ignore barriers if they are the starting points
  • Domain network
  • Source tier
For more advanced properties, such as traversability, propagators and target tiers see the advanced trace configuration section.

When a utility network administrator creates a new tier in ArcGIS Pro, a subnetwork trace configuration is created and populated as described in Configure a trace.

You can choose if your app uses the trace configuration as defined by an administrator, you modify this configuration, or whether you create your own trace configuration.

Use a trace configuration defined in a utility network tier

To obtain the trace configuration from a utility network tier, you need to know the name of the domain network and the tier, as follows:

  1. Obtain the utility network definition from the utility network.
  2. Get the domain network from the utility network definition.
  3. Obtain the tier from the domain network.
  4. Pass the tier's trace configuration to the utility trace parameters.
  5. Modify any of these properties as required.

    if (utilityNetwork.getLoadStatus() == LoadStatus.LOADED) {
        utilityNetwork.addDoneLoadingListener(() -> {
          // get the domain network called "ElectricDistribution"
          UtilityDomainNetwork utilityDomainNetwork = utilityNetwork.getDefinition().getDomainNetwork("ElectricDistribution");
          if (utilityDomainNetwork != null){
            // get the tier called "Medium Voltage Radial"
            UtilityTier utilityTier = utilityDomainNetwork.getTier("Medium Voltage Radial");
            if (utilityTier != null){
              // get the tier's trace configuration and then set the configuration on the trace parameters
              UtilityTraceConfiguration utilityTraceConfiguration = utilityTier.getTraceConfiguration();
            else  System.out.println("Specified utility tier not found");
          else System.out.println("Specified utility network domain not found");

Create your own trace configuration

You can create your own trace configuration.

  1. Create a utility trace configuration.
  2. If you are running an upstream, downstream, or subnetwork trace then you must set the domain network as follows:
    1. Obtain the domain network from the utility network definition.
    2. Pass the domain network to the utility trace configuration.
  3. Modify any of the other properties, as required
  4. Pass the utility trace configuration to the utility trace parameters.

    if (utilityNetwork.getLoadStatus() == LoadStatus.LOADED) {
      utilityNetwork.addDoneLoadingListener(() -> {
        // create the utility trace configuration
        UtilityTraceConfiguration traceConfiguration = new UtilityTraceConfiguration();
        // set the network domain called "ElectricDistribution" on the utility trace configuration
        UtilityNetworkDefinition utilityNetworkDefinition = utilityNetwork.getDefinition();
        UtilityDomainNetwork utilityDomainNetwork = utilityNetworkDefinition.getDomainNetwork("ElectricDistribution");
        if (utilityDomainNetwork != null) {
          // modify other parameters, as required, here...
          // and then set the trace configuration on the utility trace parameters
        } else System.out.println("Specified utility domain network not found.");

Execute the trace

Run the trace by calling the trace method on the utility network object. Use the utility trace parameters defined in the previous section. In the current release of ArcGIS Runtime SDK, all utility trace results are returned as utility element trace results.

ListenableFuture<List<UtilityTraceResult>> utilityTraceResultsListenableFuture;
utilityTraceResultsListenableFuture = utilityNetwork.traceAsync(utilityTraceParameters);
utilityTraceResultsListenableFuture.addDoneListener(() -> {
  try {
    List<UtilityTraceResult> utilityTraceResults = utilityTraceResultsListenableFuture.get();
    if (utilityTraceResults.get(0) != null) {
      if (utilityTraceResults.get(0) instanceof UtilityElementTraceResult) {
        UtilityElementTraceResult utilityElementTraceResult = (UtilityElementTraceResult) utilityTraceResults.get(0);
        List<UtilityElement> elements = utilityElementTraceResult.getElements();
        if (!elements.isEmpty()) {
          // check if there are any elements in the first trace result
          System.out.println("Count of utility elements in first trace result is: " + elements.size());
          // process trace results here

If the trace fails you can examine why. For example, failure could be due to dirty areas in the network topology.

Examine the results

To display the results of this trace on a map, or to analyze them further, you need to obtain the corresponding features for these elements. Do this as follows:

  1. Filter the utility element trace results to find those that are part of the map, and group them by network source name.
  2. For every group (network source with utility elements), make sure there is a layer in the map for the features. Next find the features corresponding to the utility elements.

    // get the first result of the utility trace and then the elements in that result
    utilityElementTraceResult = (UtilityElementTraceResult) utilityTraceResults.get(0);
    utilityElements = utilityElementTraceResult.getElements();
    // group together utility elements that have the same network source
    List<UtilityElement> utilityElementsWithSameNetworkSource;
    if (!utilityElements.isEmpty()) {
      // make a HashMap of key-value pairs where key is the name of a UtilityNetworkSource and
      // value is a list of utility elements in that UtilityNetworkSource
      HashMap<String, List<UtilityElement>> hashMap = new HashMap<>();
      for (UtilityElement utilityElement : utilityElements) {
        String utilityNetworkSourceName = utilityElement.getNetworkSource().getName();
        // if utilityNetworkSourceName is already a key, get the corresponding list of elements
        if (hashMap.containsKey(utilityNetworkSourceName)) {
          utilityElementsWithSameNetworkSource = hashMap.get(utilityNetworkSourceName);
        // if utilityNetworkSourceName is not a key, create a new list of elements
        else {
          utilityElementsWithSameNetworkSource = new ArrayList<>();
        // add current utility element to the list of elements
        // update the hashMap with the new/modified key-value pair
        hashMap.put(utilityNetworkSourceName, utilityElementsWithSameNetworkSource);
      // ensure that the features' layer exists in the map
      for (Map.Entry<String, List<UtilityElement>> entry : hashMap.entrySet()) {
        FeatureLayer layer = (FeatureLayer) map.getOperationalLayers().get(0);
        if (layer.getFeatureTable().getTableName().equals(entry.getKey())) {
          // get the features that correspond to the elements
          ListenableFuture<List<ArcGISFeature>> arcGISFeatureListListenableFuture;
          arcGISFeatureListListenableFuture = utilityNetwork.fetchFeaturesForElementsAsync(entry.getValue());
          arcGISFeatureListListenableFuture.addDoneListener(() -> {
            try {
              List<ArcGISFeature> arcGISFeatureList = arcGISFeatureListListenableFuture.get();
            } catch (InterruptedException | ExecutionException e) {
    } else
      System.out.println("First trace result is empty");

  3. Select the features that correspond to the trace result or process as required.

    arcGISFeatureListListenableFuture = utilityNetwork.fetchFeaturesForElementsAsync(entry.getValue());
    arcGISFeatureListListenableFuture.addDoneListener(() -> {
      try {
        List<ArcGISFeature> arcGISFeatureList = arcGISFeatureListListenableFuture.get();
        // select features to highlight result
        for (ArcGISFeature arcGISFeature : arcGISFeatureList){
      } catch (InterruptedException | ExecutionException e) {

Advanced trace configuration

The trace configuration has a few advanced properties that will be discussed here:


As you trace a topological network you can examine a number of constraints or conditions that could stop the trace. For example, you can stop tracing the network if:

  • a water valve is closed
  • an electric switch is open
  • a distance of 1000 m is reached
  • if the gas pipe is made of plastic
The ability for a trace to traverse the topological network is defined by the getTraversability and setTraversability methods in theUtilityTraceConfiguration class. You can set conditions or constraints to this trace using barriers and function barrier properties.


Set up a trace barrier by comparing the value of an asset's attribute or by examining the existence of a UtilityCategory. You can compare them individually or combine them with boolean And / Or operations into complex filters.

  • Use the UtilityNetworkAttributeComparison class to compare an element's network attribute. For example, compare an attribute value to:
    • a specific value (for example, "DeviceStatus not equal to 4"), and/or
    • another network attribute on the same element (for example, "SeasonalDeviceStatus" <> "NormalDeviceStatus")

    UtilityTraceConfiguration traceConfiguration = new UtilityTraceConfiguration();
    UtilityNetworkDefinition utilityNetworkDefinition = utilityNetwork.getDefinition();
    UtilityNetworkAttribute lifecycleNetworkAttribute = utilityNetworkDefinition.getNetworkAttribute("Lifecycle");
    // create a network attribute comparison that stops traversal if Lifecycle != 4
    UtilityNetworkAttributeComparison inDesignNetworkAttributeComparison = new UtilityNetworkAttributeComparison(lifecycleNetworkAttribute, UtilityAttributeComparisonOperator.NOT_EQUAL, 4);
    // create a network attribute comparison to stop traversal if Lifecycle != 8
    UtilityNetworkAttributeComparison inServiceNetworkAttributeComparison = new UtilityNetworkAttributeComparison(lifecycleNetworkAttribute, UtilityAttributeComparisonOperator.NOT_EQUAL, 8);
    // combine these two comparison together with AND
    UtilityTraceAndCondition lifecycleFilter = new UtilityTraceAndCondition(inDesignNetworkAttributeComparison, inServiceNetworkAttributeComparison);
    // final condition stops traversal if Lifecycle != 4 and Lifecycle != 8
    UtilityTraversability utilityTraversability = traceConfiguration.getTraversability();
    if (utilityTraversability != null)

  • Check a utility element's asset type to see whether that asset type (and thus the element) is included in a specific category using the UtilityCategoryComparison class.

Function barriers

You can create a function barrier to terminate network traversal whenever a function expression evaluates to true. The function barrier compares the current results of a function and a given value. For example, you can use the function barrier to stop traversal after the trace traverses 1000 m along the network.

// create a network attribute object for the Shape length network attribute from the utility network definition
UtilityNetworkDefinition utilityNetworkDefinition = utilityNetwork.getDefinition();
UtilityNetworkAttribute shapeLengthNetworkAttribute = utilityNetworkDefinition.getNetworkAttribute("Shape length");

// create a function that adds up shape length
UtilityTraceFunction lengthFunction = new UtilityTraceFunction(UtilityTraceFunctionType.ADD, shapeLengthNetworkAttribute);

// create a function barrier that stops traversal after 1000 meters
UtilityTraceFunctionBarrier distanceBarrier = new UtilityTraceFunctionBarrier(lengthFunction, UtilityAttributeComparisonOperator.GREATER_THAN, 1000.0);

UtilityTraceConfiguration traceConfiguration = new UtilityTraceConfiguration();

// set this function barrier
UtilityTraversability utilityTraversability = traceConfiguration.getTraversability();
if (utilityTraversability != null)

For more information see traversability.


The UtilityTraversabilityScope property determines whether these conditions are evaluated on edges, junctions, or both.


A propagator defines the propagation of a network attribute along a traversal and provides a filter to stop traversal. Propagators are only applicable to subnetwork-based traces (subnetwork, upstream, or downstream). One example is electric phase propagation, where open devices along the network will restrict some phases from continuing along the trace.

// get a network attribute object for the "Phases Normal" attribute from the utility network definition
UtilityNetworkDefinition utilityNetworkDefinition = utilityNetwork.getDefinition();
UtilityNetworkAttribute networkAttribute = utilityNetworkDefinition.getNetworkAttribute("Phases Normal");

// create a propagator to propagate the attribute using a Bitwise And function
UtilityPropagator propagator;
propagator = new UtilityPropagator(networkAttribute, UtilityPropagatorFunctionType.BITWISE_AND, UtilityAttributeComparisonOperator.INCLUDES_ANY, 7);

UtilityTraceConfiguration traceConfiguration = new UtilityTraceConfiguration();

// assign the propagator to the trace configuration

For more information see propagators.

Target tiers

All upstream and downstream traces can operate across the current tier (source tier). If you want your upstream or downstream trace to continue into another tier, you call the setTargetTier method on the UtilityTraceConfiguration.

Tracing considerations

Dirty areas

All tracing operations rely on a topological network that is built from the utility network. If the network has been edited but the topological network is out of date then it can contain dirty areas. For more information see validate the network topology. If the topological network has dirty areas you can adopt a different approach depending on your app's purpose:

  • If the app must trace with the latest data (for example, an outage management app), an error should be returned to the user if it encounters a dirty area.
  • If the app can trace with a network that is out of sync with the feature (for example, a pole inspection app), then you should consider whether to call setValidateConsistency with false in the UtilityTraceConfiguration. You can optionally display a warning to the user if a dirty area is encountered.

Get associated utility elements

Associations model three types of relationships between features:

  • Connectivity associations model the connectivity between two junctions that don't have geometric coincidence (are not in the same x, y and z location).
  • Structural attachment associations model equipment attached to structures.
  • Containment associations model features encased within other features.
Each association is defined between two UtilityElement objects. You can identify which UtilityElements are associated with a given UtilityElement using the getAssociationsAsync method on the UtilityNetwork.

// get the elements associated with a single utility element (using containment)
ListenableFuture<List<UtilityAssociation>> utilityAssociationListListenableFuture;
utilityAssociationListListenableFuture = utilityNetwork.getAssociationsAsync(utilityElement, UtilityAssociationType.CONTAINMENT);
utilityAssociationListListenableFuture.addDoneListener(() -> {
  try {
    List<UtilityAssociation> utilityAssociations;
    utilityAssociations = utilityAssociationListListenableFuture.get();
    if (!utilityAssociations.isEmpty() ) {
      for (UtilityAssociation association : utilityAssociations) {
        UtilityElement fromElement = association.getFromElement();
        UtilityElement toElement = association.getToElement();
  } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }

For more information see ArcGIS Pro's help topicAssociations.