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 Qt 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 utility network definition from the utility network.
UtilityNetworkDefinition* utilityNetworkDefinition = utilityNetwork->definition();
// Get the utility domain network from the utility network definition for the name "Gas".
UtilityDomainNetwork* utilityDomainNetwork = utilityNetworkDefinition->domainNetwork("Gas");
// Get the utility tier from the utility domain network for the tier name
// "Pipe Distribution System".
UtilityTier* utilityTier = utilityDomainNetwork->tier("Pipe Distribution System");
// For the utility trace parameters, set the trace configuration to the utility tier's
// default trace configuration.
utilityTraceParameters->setTraceConfiguration(utilityTier->defaultTraceConfiguration());
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.
The following code shows how to get named trace configurations from a specific creator with a particular tag and then run a trace with a configuration matching this search criteria.
// Create a new utility named trace configuration query parameters.
UtilityNamedTraceConfigurationQueryParameters* utilityNamedTraceConfigurationQueryParameters = new
UtilityNamedTraceConfigurationQueryParameters(this);
// Get the QStringList of creators from the utility named trace configuration query
// parameters and add the creator named "unadmin".
QStringList qstringlistCreators = utilityNamedTraceConfigurationQueryParameters->creators();
qstringlistCreators.append("unadmin");
// Get the QStringList of tags from the utility named trace configuration query
// parameters and add the tag named "electric".
QStringList qstringlistTags = utilityNamedTraceConfigurationQueryParameters->tags();
qstringlistTags.append("electric");
// Get the QList of utility named trace configurations from the utility network via the
// queryNamedTraceConfigurationsAsync method (QFuture).
utilityNetwork->queryNamedTraceConfigurationsAsync(utilityNamedTraceConfigurationQueryParameters,
this).then (this, [utilityNetwork, startingUtilityElement, this]
(const QList<UtilityNamedTraceConfiguration*> utilityNamedTraceConfigurations)
{
// Get the first utility named trace configuration.
UtilityNamedTraceConfiguration* utilityNamedTraceConfiguration =
utilityNamedTraceConfigurations.first();
// Create a QList of utility elements and add the startingUtilityElement.
QList<UtilityElement*> utilityElements;
utilityElements.append(startingUtilityElement);
// Create a new utility trace parameters from the utility named trace
// configuration and QList of utility elements.
UtilityTraceParameters* utilityTraceParameters = new UtilityTraceParameters(
utilityNamedTraceConfiguration, utilityElements, this);
// Get the QList of utility trace results from the utility network via the
// traceAsync method (QFuture).
utilityNetwork->traceAsync(utilityTraceParameters, this).then(this,[]
(const QList<UtilityTraceResult*> utilityTraceResults)
{
// Process the utility trace results ...
});
});
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.
// Call the function to get the list of all utility network trace configurations in
// the utility network's map.
map->namedTraceConfigurationsFromUtilityNetworkAsync(utilityNetwork)
.then (this, [this, aUtilityElement, utilityNetwork]
(const QList<UtilityNamedTraceConfiguration*>& namedTraceConfigurationsResults)
{
// Obtain the first utility named trace configuration from the results.
UtilityNamedTraceConfiguration* utilityNamedTraceConfiguration =
namedTraceConfigurationsResults.first();
// Get the name of the utility named trace configuration.
const QString utilityNamedTraceConfigurationName = utilityNamedTraceConfiguration->name();
// Only process further unless the name is "SubnetworkTraceWithFunctions".
if (utilityNamedTraceConfigurationName == "SubnetworkTraceWithFunctions")
{
// Create a QList of utility elements and add the aUtilityElement.
QList<UtilityElement*> utilityElements;
utilityElements.append(aUtilityElement);
// Create a utility trace parameters from the utility named trace configuration
// and QList of utility elements.
UtilityTraceParameters* utilityTraceParameters = new UtilityTraceParameters(
utilityNamedTraceConfiguration, utilityElements, this);
// Get the QList of utility trace results from the utility network via the
// traceAsync method (QFuture).
utilityNetwork->traceAsync(utilityTraceParameters, this)
.then(this, [] (const QList<UtilityTraceResult*> utilityTraceResults)
{
// Process the utility trace results ...
});
}
});
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 utility network definition from the utility network.
UtilityNetworkDefinition* utilityNetworkDefinition = utilityNetwork->definition();
// Get the "Lifecycle" utility network attribute from the utility network definition.
UtilityNetworkAttribute* lifecycleUtilityNetworkAttribute = utilityNetworkDefinition->
networkAttribute("Lifecycle");
// End the function if the "Lifecycle" utility network attribute is NULL.
if (!lifecycleUtilityNetworkAttribute) {return;}
// Create a utility network attribute comparison that stops traversal
// if "Lifecycle" <> 4).
UtilityNetworkAttributeComparison* inDesignUtilityNetworkAttributeComparison = new
UtilityNetworkAttributeComparison
(
lifecycleUtilityNetworkAttribute, // utility network attribute
UtilityAttributeComparisonOperator::NotEqual, // utility attribute comparison operator
4, // value to compare the network attribute against - integer or double
this // parent
);
// Create a utility network attribute comparison to stop traversal if "Lifecycle" <> 8.
UtilityNetworkAttributeComparison* inServiceUtilityNetworkAttributeComparison = new
UtilityNetworkAttributeComparison
(
lifecycleUtilityNetworkAttribute, // utility network attribute
UtilityAttributeComparisonOperator::NotEqual, // utility attribute comparison operator
8, // value to compare the network attribute against - integer or double
this // parent
);
// Combine the two utility network attribute comparisons together with "And" to make a
// new utility trace and condition.
UtilityTraceAndCondition* lifecycleUtilityTraceAndCondition = new UtilityTraceAndCondition(
inDesignUtilityNetworkAttributeComparison, inServiceUtilityNetworkAttributeComparison, this);
// Get the utility transversability from the utility trace configuration.
UtilityTraversability* utilityTraversability = utilityTraceConfiguration->traversability();
// Final condition stops traversal if Lifecycle <> 4 and Lifecycle <> 8.
utilityTraversability->setBarriers(lifecycleUtilityTraceAndCondition);
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.
// Get the utility network definition from the utility network.
UtilityNetworkDefinition* utilityNetworkDefinition = utilityNetwork->definition();
// Get the "Shape length" utility network attribute from the utility network definition.
UtilityNetworkAttribute* shapelengthUtilityNetworkAttribute = utilityNetworkDefinition->
networkAttribute("Shape length");
// End the function if the "Shape length" utility network attribute is NULL.
if (!shapelengthUtilityNetworkAttribute) {return;}
// Create a utility trace function using the utility trace function type of "Add" and the
// "Shape length" utility network attribute.
UtilityTraceFunction* shapelengthUtilityTraceFunction = new UtilityTraceFunction(
UtilityTraceFunctionType::Add, shapelengthUtilityNetworkAttribute, this);
// Create a utility trace function barrier that stops traversal after 1000 meters.
UtilityTraceFunctionBarrier* utilityTraceFunctionBarrier = new UtilityTraceFunctionBarrier
(
shapelengthUtilityTraceFunction, // trace function
UtilityAttributeComparisonOperator::GreaterThan, // comparison operator
1000.0, // value to compare against - QVariant - double
this // parent
);
// Get the utility transversability from the utility trace configuration.
UtilityTraversability* utilityTraversability = utilityTraceConfiguration->traversability();
// Get the utility trace function barrier list model from the utility transversability.
UtilityTraceFunctionBarrierListModel* utilityTraceFunctionBarrierListModel =
utilityTraversability->functionBarriers();
// Add the utility trace function barrier to the utility trace function barrier list model.
utilityTraceFunctionBarrierListModel->append(utilityTraceFunctionBarrier);
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).
// Get the utility network definition from the utility network.
UtilityNetworkDefinition* utilityNetworkDefinition = utilityNetwork->definition();
// Get the utility network source from the utility network definition of "Gas Line".
UtilityNetworkSource* networNetworkSource = utilityNetworkDefinition->networkSource("Gas Line");
// Get the utility asset group from the utility network source of "Distribution Pipe".
UtilityAssetGroup* utilityAssetGroup = networNetworkSource->assetGroup("Distribution Pipe");
// Get the utility asset type from the utility asset group of "Bare Steel".
UtilityAssetType* utilityAssetType = utilityAssetGroup->assetType("Bare Steel");
// Define the GUID for the utility element.
QUuid quuid("{B903103D-8933-4383-8C7B-A77FC705F32D}");
// Get the utility element from the utility network with utility asset type and
// utiity element GUID.
UtilityElement* utilityElement = utilityNetwork->
createElementWithAssetType(utilityAssetType, quuid);
// Create a QList of utility elements and add the utility element.
QList<UtilityElement*> utilityElements;
utilityElements.append(utilityElement);
// Get the QList of utility categories from the utility network definition.
const QList<UtilityCategory*> utilityCategories = utilityNetworkDefinition->categories();
// Obtain the utility category with the name "IsolationDevice" from the QList
// of utility categories.
auto resultIterator = std::find_if(utilityCategories.begin(), utilityCategories.end(),
[](UtilityCategory* c) {return c->name() == "IsolationDevice";});
if (resultIterator == std::end(utilityCategories))
return;
UtilityCategory* foundUtilityCategory = (*resultIterator);
// Create a utility category comparison using the found utility category, and the
// utility cagegory comparison operator of "Exists".
UtilityCategoryComparison* utilityCategoryComparison = new UtilityCategoryComparison(
foundUtilityCategory, UtilityCategoryComparisonOperator::Exists, this);
// Get the utility network attribute from the utility network definition where the network
// attribute is "Accessible".
UtilityNetworkAttribute* utilityNetworkAttribute = utilityNetworkDefinition->
networkAttribute("Accessible");
// Create a utility network attribute comparison using the utility network attribute,
// utility attribute comparision operator of "Equal", and the value of 1 for the node
// in the trace.
UtilityNetworkAttributeComparison* utilityNetworkAttributeComparison = new
UtilityNetworkAttributeComparison
(
utilityNetworkAttribute, // utility network attribute
UtilityAttributeComparisonOperator::Equal, // utility attribute comparison operator
1, // value to compare the network attribute against - QVariant - integer - double
this // parent
);
// Create a utility trace and condition using the utility category comparision, and the utility
// network attribute comparison.
UtilityTraceAndCondition* utilityTraceAndCondition = new UtilityTraceAndCondition(
utilityCategoryComparison, utilityNetworkAttributeComparison, this);
// Create a utility trace filter and set the barriers to the utility trace and condition.
UtilityTraceFilter* utilityTraceFilter = new UtilityTraceFilter(this);
utilityTraceFilter->setBarriers(utilityTraceAndCondition);
// Create a utility trace configuration and set the filer to the utility trace filter
// and set the include isolated features to `true`.
UtilityTraceConfiguration* utilityTraceConfiguration = new UtilityTraceConfiguration(this);
utilityTraceConfiguration->setFilter(utilityTraceFilter);
utilityTraceConfiguration->setIncludeIsolatedFeatures(true);
// Create a utility trace parameters using the utility trace type of "Isolation".
UtilityTraceParameters* utilityTraceParameters = new UtilityTraceParameters(
UtilityTraceType::Isolation, utilityElements, this);
// Get the QList of utility trace results from the utility network via the
// traceAsync method (QFuture).
utilityNetwork->traceAsync(utilityTraceParameters, this).then(this, []
(const QList<UtilityTraceResult*> utilityTraceResults)
{
// Process the utility trace results ...
});
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 the utility network definition from the utility network.
UtilityNetworkDefinition* utilityNetworkDefinition = utilityNetwork->definition();
// Get the utility network attribute from the utility network definition of "Phase Normal".
UtilityNetworkAttribute* utilityNetworkAttribute = utilityNetworkDefinition->
networkAttribute("Phases Normal");
// Create a new utility propagator using the utility network attribute, utility
// propagator function type of "BitwiseAnd", and a value (7) for comparision
// when executing the trace.
UtilityPropagator* utilityPropagator = new UtilityPropagator
(
utilityNetworkAttribute, // network attribute
UtilityPropagatorFunctionType::BitwiseAnd, // utility propagator function type
UtilityAttributeComparisonOperator::IncludesAny, // utility attribute comparison operator
7, // value that is compared against the propagated value - QVariant - int
this // parent
);
// Get the utility propagator list model from the utility trace configuration.
UtilityPropagatorListModel* utilityPropagatorListModel = utilityTraceConfiguration->propagators();
// Add the utlity propagator to the utility propagator list model.
utilityPropagatorListModel->append(utilityPropagator);
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.