Find route in transport network

View on GitHub

Solve a route on-the-fly using offline data.

Image of find route in transport network

Use case

You can use an offline network to enable routing in disconnected scenarios. For example, you could provide offline location capabilities to field workers repairing critical infrastructure in a disaster when network availability is limited.

How to use the sample

Tap near a road to start adding a stop to the route. Tap again to place it on the map. A number graphic will show its order in the route. After adding at least 2 stops, a route will display. Choose "Fastest" or "Shortest" to control how the route is optimized. The route will update on-the-fly while adding stops.

How it works

  1. Create the map's Basemap from a local tile package using a TileCache and ArcGISTiledLayer.
  2. Create a RouteTask with an offline locator geodatabase.
  3. Get the RouteParameters using RouteTask.makeDefaultParameters().
  4. Create Stops and add them to the route task's parameters with setStops(_:).
  5. Solve the Route using RouteTask.solveRoute(using:).
  6. Create a graphic with the route's geometry and a SimpleLineSymbol and display it on another GraphicsOverlay.

Relevant API

  • RouteParameters
  • RouteResult
  • RouteTask
  • Stop
  • TravelMode

Offline data

The data used by this sample is available on ArcGIS Online.

About the data

This sample uses a pre-packaged sample dataset consisting of a geodatabase with a San Diego road network and a tile package with a streets basemap.

Tags

connectivity, disconnected, fastest, locator, navigation, network analysis, offline, routing, shortest, turn-by-turn

Sample Code

FindRouteInTransportNetworkView.swiftFindRouteInTransportNetworkView.swiftFindRouteInTransportNetworkView.Model.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
// 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 FindRouteInTransportNetworkView: View {
    /// The view model for the sample.
    @StateObject private var model = Model()

    /// The current travel mode selection of the picker.
    @State private var travelModeSelection: TravelModeOption = .fastest

    /// A Boolean value indicating whether the user is currently long pressing on the map.
    @State private var longPressing = false

    /// A Boolean value indicating whether the reset button is currently disabled.
    @State private var resetDisabled = true

    var body: some View {
        MapView(
            map: model.map,
            graphicsOverlays: [model.routeGraphicsOverlay, model.stopGraphicsOverlay]
        )
        .onSingleTapGesture { _, mapPoint in
            // Add a route stop when the on single tap.
            model.addRouteStop(at: mapPoint)
            resetDisabled = false
        }
        .onLongPressAndDragGesture { mapPoint in
            // Add and update a route stop on long press.
            model.addRouteStop(at: mapPoint, replacingLast: longPressing)
            longPressing = true
            resetDisabled = false
        } onEnded: {
            longPressing = false
        }
        .overlay(alignment: .top) {
            Text(model.routeInfo.label ?? "Tap to add a point.")
                .frame(maxWidth: .infinity, alignment: .center)
                .padding(8)
                .background(.thinMaterial, ignoresSafeAreaEdges: .horizontal)
        }
        .toolbar {
            ToolbarItemGroup(placement: .bottomBar) {
                Spacer()

                Picker("Travel Mode", selection: $travelModeSelection) {
                    Text("Fastest").tag(TravelModeOption.fastest)
                    Text("Shortest").tag(TravelModeOption.shortest)
                }
                .pickerStyle(.segmented)
                .onChange(of: travelModeSelection) { newMode in
                    model.updateTravelMode(to: newMode)
                }

                Spacer()

                Button("Reset") {
                    model.reset()
                    resetDisabled = true
                }
                .disabled(resetDisabled)
            }
        }
        .task {
            // Load the default route parameters from the route task when the sample loads.
            model.routeParameters = try? await model.routeTask.makeDefaultParameters()
        }
    }
}

private extension MapView {
    /// Sets a closure to perform when the map view recognizes a long press and drag gesture.
    /// - Parameters:
    ///   - action: The closure to perform when the gesture is recognized.
    ///   - onEnded: The closure to perform when the gesture ends.
    /// - Returns: A new `View` object.
    func onLongPressAndDragGesture(
        perform action: @escaping (Point) -> Void,
        onEnded: @escaping () -> Void
    ) -> some View {
        self
            .onLongPressGesture { _, mapPoint in
                action(mapPoint)
            }
            .gesture(
                LongPressGesture()
                    .simultaneously(with: DragGesture())
                    .onEnded { _ in
                        onEnded()
                    }
            )
    }
}

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