Discover connected features in a utility network using connected, subnetwork, upstream, and downstream traces.
Use case
You can use a trace to visualize and validate the network topology of a utility network for quality assurance. Subnetwork traces are used for validating whether subnetworks, such as circuits or zones, are defined or edited appropriately.
How to use the sample
Tap "Start a new trace" to select the type of trace using the confirmation dialogue. Tap on one or more features while "Start" or "Barrier" is selected. When a junction feature is identified, you may be prompted to select a terminal. When an edge feature is identified, the distance from the tapped location to the beginning of the edge feature will be computed. Tap "Trace" to initiate a trace on the network. Tap "Reset" to clear the trace parameters and start over.
How it works
- Create a
MapView
with aMap
that contains one more utility networks. - Add a
GraphicsOverlay
to hold symbology that distinguishes starting locations from barriers. - Add relevant
FeatureLayer
(s) to the map as operation layers, optionally applying custom styling. - Allow users to choose a trace type from the set of
UtilityTraceParameters.TraceType
(s). - Create
UtilityTraceParameters
with the selected trace type. - Set the
UtilityTraceConfiguration
with the utility tier'sdefaultTraceConfiguration
property. - Use the
onSingleTapGesture
modifier to listen for tap events on the map view. - Identify tapped features on the map and add a
Graphic
that represents its purpose (starting point or barrier) at the tapped location. - Create a
UtilityElement
for the identified feature. - Determine the type of the identified feature using
UtilityNetworkSource.sourceType
. - If the type is
junction
, display a terminal picker when more than one terminal is found. - If the type is
edge
, compute how far along the edge the user tapped usingGeometryEngine.polyline(_:fractionalLengthClosestTo:tolerance:)
. - Add any created
UtilityElement
(s) to the collection of starting locations or barriers in theUtilityTraceParameters
. - Run
UtilityNetwork.trace(using:)
using theUtilityTraceParameters
. - Group the
UtilityElementTraceResult.elements
by theirnetworkSource.name
. - For every
FeatureLayer
in this map with trace result elements, select features by convertingUtilityElement
(s) toArcGISFeature
(s) usingUtilityNetwork.features(for:)
.
Relevant API
- GeometryEngine.polyline(_:fractionalLengthClosestTo:tolerance:)
- ServiceGeodatabase
- UtilityAssetType
- UtilityDomainNetwork
- UtilityElement
- UtilityElementTraceResult
- UtilityNetwork
- UtilityNetworkDefinition
- UtilityNetworkSource
- UtilityTerminal
- UtilityTier
- UtilityTraceConfiguration
- UtilityTraceParameters
- UtilityTraceResult
- UtilityTraceType
- UtilityTraversability
Additional information
The Naperville electrical network feature service, hosted on ArcGIS Online, contains a utility network used to run the subnetwork-based trace shown in this sample.
A UtilityNetworkTrace
toolkit component can be used for various utility network related use cases. For information about setting up the toolkit, as well as code for the underlying component, visit the toolkit repository.
Tags
condition barriers, downstream trace, network analysis, subnetwork trace, toolkit, trace configuration, traversability, upstream trace, utility network, validate consistency
Sample Code
// Copyright 2023 Esri
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import ArcGIS
import SwiftUI
struct TraceUtilityNetworkView: View {
/// The view model for the sample.
@StateObject var model = Model()
var body: some View {
MapViewReader { mapViewProxy in
MapView(
map: model.map,
viewpoint: .initialViewpoint,
graphicsOverlays: [model.points]
)
.onSingleTapGesture { screenPoint, mapPoint in
model.lastSingleTap = (screenPoint, mapPoint)
}
.selectionColor(.yellow)
.onDisappear {
ArcGISEnvironment.authenticationManager.arcGISCredentialStore.removeAll()
}
.overlay(alignment: .center) {
if model.tracingActivity == .traceRunning,
let type = model.pendingTraceParameters?.traceType {
VStack {
Text("Running \(String(describing: type)) trace...")
ProgressView()
.progressViewStyle(.circular)
}
.padding(6)
.background(.thinMaterial)
.clipShape(.rect(cornerRadius: 10))
}
}
.overlay(alignment: .top) {
if let hint = model.hint {
Text(hint)
.frame(maxWidth: .infinity)
.padding(.vertical, 6)
.background(.thinMaterial, ignoresSafeAreaEdges: .horizontal)
}
}
.task {
await model.setup()
}
.task(id: model.lastSingleTap?.mapPoint) {
guard case .settingPoints = model.tracingActivity,
let lastSingleTap = model.lastSingleTap else {
return
}
if let feature = try? await mapViewProxy.identifyLayers(
screenPoint: lastSingleTap.screenPoint,
tolerance: 10
).first?.geoElements.first as? ArcGISFeature {
model.add(feature, at: lastSingleTap.mapPoint)
}
}
.task(id: model.tracingActivity) {
model.updateUserHint()
if model.tracingActivity == .traceRunning {
do {
try await model.trace()
model.tracingActivity = .traceCompleted
} catch {
model.tracingActivity = .traceFailed(description: error.localizedDescription)
}
}
}
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
traceMenu
}
}
}
}
}
extension TraceUtilityNetworkView {
/// The trace types supported for this sample.
var supportedTraceTypes: [UtilityTraceParameters.TraceType] {
return [.connected, .subnetwork, .upstream, .downstream]
}
}
private extension Viewpoint {
/// The initial viewpoint to be displayed when the sample is first opened.
static var initialViewpoint: Viewpoint {
.init(
boundingGeometry: Envelope(
xRange: (-9813547.35557238)...(-9813185.0602376),
yRange: (5129980.36635111)...(5130215.41254146)
)
)
}
}
#Preview {
NavigationStack {
TraceUtilityNetworkView()
}
}