Trace utility network

View on GitHub

Discover connected features in a utility network using connected, subnetwork, upstream, and downstream traces.

Image of trace utility network

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

  1. Create a MapView with a Map that contains one more utility networks.
  2. Add a GraphicsOverlay to hold symbology that distinguishes starting locations from barriers.
  3. Add relevant FeatureLayer(s) to the map as operation layers, optionally applying custom styling.
  4. Allow users to choose a trace type from the set of UtilityTraceParameters.TraceType(s).
  5. Create UtilityTraceParameters with the selected trace type.
  6. Set the UtilityTraceConfiguration with the utility tier's defaultTraceConfiguration property.
  7. Use the onSingleTapGesture modifier to listen for tap events on the map view.
  8. Identify tapped features on the map and add a Graphic that represents its purpose (starting point or barrier) at the tapped location.
  9. Create a UtilityElement for the identified feature.
  10. Determine the type of the identified feature using UtilityNetworkSource.sourceType.
  11. If the type is junction, display a terminal picker when more than one terminal is found.
  12. If the type is edge, compute how far along the edge the user tapped using GeometryEngine.polyline(_:fractionalLengthClosestTo:tolerance:).
  13. Add any created UtilityElement(s) to the collection of starting locations or barriers in the UtilityTraceParameters.
  14. Run UtilityNetwork.trace(using:) using the UtilityTraceParameters.
  15. Group the UtilityElementTraceResult.elements by their networkSource.name.
  16. For every FeatureLayer in this map with trace result elements, select features by converting UtilityElement(s) to ArcGISFeature(s) using UtilityNetwork.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

TraceUtilityNetworkView.swiftTraceUtilityNetworkView.swiftTraceUtilityNetworkView.Enums.swiftTraceUtilityNetworkView.Model.swiftTraceUtilityNetworkView.Views.swift
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// 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 = TraceUtilityNetworkView.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)
                    .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)
            )
        )
    }
}

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.