Using ArcGIS Pro, a utility network can be created and configured manually using the suite of geoprocessing tools available in the Utility Network Tools toolbox. The Trace Configuration toolset in that toolbox contains tools to create named trace configurations in your utility network. Named trace configurations allow you to configure and store the properties of complex traces. Your ArcGIS Maps SDK for Kotlin app can read named trace configurations when performing traces with a utility network.
While you can create trace configurations on demand (programmatically), these are typically configured ahead of time and stored as a named trace configuration. A common workflow is to read a named trace configuration from the utility network and update its properties as needed for a particular trace.
Trace configuration from a tier
When the administrator configures the subnetwork trace, a default trace configuration can be obtained from a utility network tier with the names of the domain network and the tier.
// Get the domain network called "ElectricDistribution".
val domainNetwork = utilityNetwork.definition?.getDomainNetwork("ElectricDistribution")
// Get the tier called "Medium Voltage Radial".
val mediumVoltageTier = domainNetwork?.getTier("Medium Voltage Radial")
// Get the tier's trace configuration and then set the configuration on the trace parameters.
val utilityTraceConfiguration = mediumVoltageTier?.getDefaultTraceConfiguration()
utilityTraceParameters.traceConfiguration = utilityTraceConfiguration
Named trace configurations
Support for named trace configurations is available in ArcGIS Enterprise 10.9 or later. The same set of configurations can be made available to all desktop, web, and mobile users. Administrators can also choose to share a subset of these with a web map to provide configurations specific to those contents.
// Perform a query for specific named trace configurations.
val queryParameters = UtilityNamedTraceConfigurationQueryParameters()
queryParameters.creators.add("unadmin")
queryParameters.tags.add("electric")
val queryResult = utilityNetwork.queryNamedTraceConfigurations(queryParameters = queryParameters)
.getOrElse { error -> return@launch showError("Failed to get results for the named trace configuration: ${error.message.toString()}") }
if (queryResult.isNotEmpty()) {
val traceParameters = UtilityTraceParameters(
namedTraceConfiguration = queryResult.first(),
startingLocations = utilityElementStartingLocations
)
val traceResultList = utilityNetwork.trace(traceParameters).getOrElse { error ->
return@launch showError("Failed to get trace result list: ${error.message}")
}
}
An online utility network from a web map source may return a smaller subset of named trace configurations published for this utility network and applicable only to this map.
val namedTraceConfigurationList = map.getNamedTraceConfigurationsFromUtilityNetwork(utilityNetwork).getOrElse { error ->
return showError("Failed to get named trace configurations from the map: ${error.message}")
}
val namedTraceConfiguration = namedTraceConfigurationList.first { it ->
it.name == "SubnetworkTraceWithFunctions"
}
val traceParameters = UtilityTraceParameters(
namedTraceConfiguration = namedTraceConfiguration,
startingLocations = utilityElementStartingLocations
)
Trace configuration properties
A trace configuration can define things like:
-
The domain network on which the trace will run
-
Traversability barriers
-
A filter for the type of assets included in the results
-
Whether the results should include things like content in containers, structure features, and feature or condition barriers
-
Functions to compute while performing the trace
Trace configuration settings are exposed as properties on the UtilityTraceConfiguration
class.
Traversability
As you trace a topological network, you can examine several 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
- The gas pipe is made of plastic
The ability for a trace to traverse the topological network is defined by the UtilityTraversability
in the UtilityTraceConfiguration
class. You can set conditions or constraints for this trace using barriers and function barrier properties.
Barriers and function barriers used as traversability constraints are defined using a condition, such as a valve being closed, encountering a particular material type, reaching a specified threshold along the trace, and so on. This contrasts with barriers that are defined with trace parameters using utility network elements, often selected interactively by the user.
Barriers
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" or "NormalDeviceStatus")
- 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.
// Get the "Lifecycle" network attribute.
val lifecycleNetworkAttribute = utilityNetwork.definition?.getNetworkAttribute("Lifecycle")
lifecycleNetworkAttribute?.let {
val traceCondition = UtilityTraceConfiguration().apply {
// Create a network attribute comparison that stops traversal if Lifecycle <> 4).
val inDesignNetworkAttributeComparison = UtilityNetworkAttributeComparison(
networkAttribute = lifecycleNetworkAttribute,
comparisonOperator = UtilityAttributeComparisonOperator.NotEqual,
value = 4
)
// Create a network attribute comparison to stop traversal if Lifecycle <> 8.
val inServiceNetworkAttributeComparison = UtilityNetworkAttributeComparison(
networkAttribute = lifecycleNetworkAttribute,
comparisonOperator = UtilityAttributeComparisonOperator.NotEqual,
value = 8
)
// Final condition stops traversal if Lifecycle <> 4 and Lifecycle <> 8.
traversability?.barriers = UtilityTraceAndCondition(
leftExpression = inDesignNetworkAttributeComparison,
rightExpression = inServiceNetworkAttributeComparison
)
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 meters along the network.
// Create a network attribute object for the Shape length network attribute from the utility network definition.
utilityNetwork.definition?.getNetworkAttribute(networkAttributeName = "Shape length")?.let { shapeLengthNetworkAttribute ->
val traceConfiguration = UtilityTraceConfiguration().apply {
// Create a function that adds up shape length.
val lengthFunction = UtilityTraceFunction(
functionType = UtilityTraceFunctionType.Add,
networkAttribute = shapeLengthNetworkAttribute
)
// Create a function barrier that stops traversal after 1000 meters.
val distanceBarrier = UtilityTraceFunctionBarrier(
traceFunction = lengthFunction,
comparisonOperator = UtilityAttributeComparisonOperator.GreaterThan,
value = 1000.0
)
// Set this function barrier.
traversability?.functionBarriers?.add(distanceBarrier)
}
Trace filters
Filters are a mechanism to stop tracing when returning results. They do not stop traversability to the controller.
A trace filter and traversability both have properties for defining barriers, function barriers, and scope. These properties work the same way in both classes. What makes them different is when they are applied and how they affect the trace.
While both traversability and trace filter can terminate a trace, they have slightly different use cases. In upstream and downstream traces, traversability is considered first because it determines the subnetwork controller and the flow direction of tracing. If the controller's type is source-based, the flow direction is away from the identified subnetwork controller. If the controller's type is sink-based, the flow direction is toward the controller. Once the starting location, subnetwork controller, and flow direction are established, the features that are traversable are then evaluated against the trace filter criteria.
If you want a trace to find the next upstream protective device in an electrical network, for example, create a UtilityCategoryComparison
where 'Protective Device' category exists. If you set this barrier on traversability, the trace will fail; it will be unable to find a subnetwork controller to determine which direction is upstream. Instead, use a trace filter.
Trace filter barriers can be used to configure isolation traces. An isolation trace allows you to isolate a portion of the network using filter barriers. An upstream trace configured to use isolation valves as filter barriers will determine the boundary of the isolation zone, from which you can determine which customers are affected (out of service).
// Define a starting location utility element.
val networkSource = utilityNetwork.definition?.getNetworkSource("Gas Line")
val assetGroup = networkSource?.getAssetGroup("Distribution Pipe")
val assetType = assetGroup?.getAssetType("Bare Steel") ?: return showError("Null asset type")
val startingLocation = utilityNetwork.createElementOrNull(assetType,
Guid("{B903103D-8933-4383-8C7B-A77FC705F32D}")
) ?: return showError("Null utility element.")
// Create a category comparison ("IsolationDevice").
val category = utilityNetwork.definition?.categories?.firstOrNull { category ->
category.name == "IsolationDevice"
} ?: return showError("Null category")
val categoryComparison = UtilityCategoryComparison(
category = category,
comparisonOperator = UtilityCategoryComparisonOperator.Exists
)
// Create a network attribute comparison ("Accessible == 1").
val networkAttribute = utilityNetwork.definition?.getNetworkAttribute("Accessible")
?: return showError("Null network attribute")
val networkAttributeComparison = UtilityNetworkAttributeComparison(
networkAttribute = networkAttribute,
comparisonOperator = UtilityAttributeComparisonOperator.Equal, value = 1
)
// Create a trace condition that uses both comparisons defined above.
val condition = UtilityTraceAndCondition(
leftExpression = categoryComparison,
rightExpression = networkAttributeComparison
)
// Create trace parameters with 'isoloation' trace type and the starting location.
val parameters = UtilityTraceParameters(
traceType = UtilityTraceType.Isolation,
startingLocations = listOf(startingLocation)
).apply {
traceConfiguration = UtilityTraceConfiguration().apply {
// Create a trace filter that uses the condition.
filter = UtilityTraceFilter().apply {
barriers = condition
}
includeIsolatedFeatures = true
}
}
// Execute the trace with these parameters.
coroutineScope.launch {
// Run the utility trace and get the results.
utilityNetwork.trace(parameters).onFailure { error ->
showError("Error tracing utility network: ${error.message}")
}.onSuccess { utilityTraceResults: List<UtilityTraceResult> ->
Bitset network attributes
Bitset network attributes are only applicable to upstream and downstream trace types. They can be used to add special logic during a trace so the trace is more reflective of real world scenarios.
There are cases where traces need to be aware that a network attribute is a bitset that controls traversability. For example, you might have an electrical network where phase is represented as a bitset network attribute (one bit per phase), and overhead electrical devices are represented with one device per phase. Use a bitset network attribute to ensure that trace results include valid paths specified in the network attribute, not all paths.
Nearest neighbor
The nearest neighbor filter, UtilityNearestNeighbor
, allows you to return a specified number of features from the starting location of the trace. When assigned to a UtilityTraceFilter
, it will return a number of features of a certain type within a given distance.
A network attribute that represents travel cost is used to define the distance, which is typically shape length. Other attributes may be more useful depending on circumstances. For example, if you are searching for the "nearest" vault in an underground structure network, you may prefer a geographically distant vault that is connected via a duct bank rather than a closer one through a direct-buried trench (since excavating the trench is more costly). In this case a different attribute that represents the cost per segment should be used.
The type of features to return can be specified by utility category, asset type, or both. A valid UtilityNearestNeighbor
therefore, will have a cost network attribute, a count greater than 0, and at least one specified category or asset type.
Output filters
Output filters allow you to filter the type of elements returned as the result of a trace. When performing a trace on a utility network, you can limit the results to a specific asset type or output condition. An output filter screens every feature encountered during a trace to check if it meets the filter criteria. Only elements that satisfy these criteria are included in the trace result. For example, returning only protective devices from a trace of an electrical network.
There are two types of output filters available with the UtilityTraceConfiguration
class:
-
Output asset types—Returns only the specified asset types in the trace results. For example, to create a pole report for an electric network, use an output asset type filter to only return poles in the results.
-
Output conditions—Supports network categories and network attributes. In an output condition, the name of the network attribute, an operator, and a value that is valid for the network attribute are specified. Returns only elements that fulfill a condition. This is the same type of condition used in traversability, and can perform comparisons against network attributes, categories, or both.
Trace output filters are applied after all other tracing calculations, including traversal, filters, and function calculation. Features that don't meet the filter criteria are still traced but are not included in the results. Both asset type and output condition filters can be used for the same trace. As an example, you could create an output filter to return only elements that belong to the "Plastic PE pipe" asset type, have a diameter of 12 inches, and a pressure greater than 80 PSI.
Propagators
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.
utilityNetwork.definition?.getNetworkAttribute("Phases Normal")?.let { phasesNormalNetworkAttribute ->
// Create a propagator to propagate the attribute using a Bitwise And function.
val propagator = UtilityPropagator(
networkAttribute = phasesNormalNetworkAttribute,
propagatorFunctionType = UtilityPropagatorFunctionType.BitwiseAnd,
comparisonOperator = UtilityAttributeComparisonOperator.IncludesAny,
value = 7
)
// Assign the propagator to the trace configuration.
propagators.add(propagator)
}
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 can specify the target tier on UtilityTraceConfiguration
.
Tracing considerations
Dirty areas
All tracing operations rely on a topological index that is built from the utility network. If the network has been edited but the topology is out of date, it may contain dirty areas. For more information, see ArcGIS Pro's discussion 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 toggling the validate consistency setting to
false
on theUtilityTraceConfiguration
. You can optionally display a warning to the user if a dirty area is encountered.