Find route in mobile map package

View on GitHub

Display maps and use locators to enable search and routing offline using a Mobile Map Package.

Image of find route in mobile map package Image of find route in mobile map package

Use case

Mobile map packages make it easy to transmit and store the necessary components for an offline map experience including: transportation networks (for routing/navigation), locators (address search, forward and reverse geocoding), and maps.

A field worker might download a mobile map package to support their operations while working offline.

How to use the sample

A list of maps from a mobile map package will be displayed. If the map contains transportation networks, the list item will have a navigation icon. Tap on a map in the list to open it. If a locator task is available, tap on the map to reverse geocode the location's address. If transportation networks are available, a route will be calculated between geocode locations.

How it works

  1. Create a MobileMapPackage using MobileMapPackage(fileURL:).
  2. Get a list of maps inside the package using the maps property.
  3. If the package has a locator, access it using the locatorTask property.
  4. To see if a map contains transportation networks, check each map's transportationNetworks property.

Relevant API

  • GeocodeResult
  • MobileMapPackage
  • ReverseGeocodeParameters
  • Route
  • RouteParameters
  • RouteResult
  • RouteTask
  • TransportationNetworkDataset

Offline data

This sample uses the San Francisco mobile map package and the Yellowstone mobile map package. Both are downloaded from ArcGIS Online automatically.

Tags

disconnected, field mobility, geocode, network, network analysis, offline, routing, search, transportation

Sample Code

FindRouteInMobileMapPackageView.swiftFindRouteInMobileMapPackageView.swiftFindRouteInMobileMapPackageView.Models.swiftFindRouteInMobileMapPackageView.MobileMapView.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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// 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
import UniformTypeIdentifiers

struct FindRouteInMobileMapPackageView: View {
    /// The view model for the view.
    @StateObject private var model = Model()

    /// A Boolean value indicating whether the file importer interface is showing.
    @State private var fileImporterIsShowing = false

    /// The file URL to a local "mmpk" file imported from the file importer.
    @State private var importedFileURL: URL?

    var body: some View {
        List {
            // Create a list section for each map package.
            ForEach(model.mapPackages.enumeratedArray(), id: \.offset) { (offset, mapPackage) in
                Section {
                    MobileMapListView(mapPackage: mapPackage)
                } header: {
                    Text(mapPackage.item?.title ?? "Mobile Map Package \(offset + 1)")
                }
            }
        }
        .toolbar {
            // The button used to import mobile map packages.
            ToolbarItem(placement: .bottomBar) {
                Button("Add Package") {
                    fileImporterIsShowing = true
                }
                .fileImporter(
                    isPresented: $fileImporterIsShowing,
                    allowedContentTypes: [.mmpk]
                ) { result in
                    switch result {
                    case .success(let fileURL):
                        importedFileURL = fileURL
                    case .failure(let error):
                        model.error = error
                    }
                }
            }
        }
        .task {
            // Load the San Francisco mobile map package from the bundle when the sample loads.
            guard model.mapPackages.isEmpty else { return }
            await model.addMapPackage(from: .sanFranciscoPackage)
        }
        .task(id: importedFileURL) {
            // Load the new mobile map package when a file URL is imported.
            guard let importedFileURL else { return }
            await model.importMapPackage(from: importedFileURL)
            self.importedFileURL = nil
        }
        .errorAlert(presentingError: $model.error)
    }
}

private extension FindRouteInMobileMapPackageView {
    /// A list of the maps in a given map package.
    struct MobileMapListView: View {
        /// The mobile map package containing the maps.
        @State var mapPackage: MobileMapPackage

        var body: some View {
            // Create a list row for each map in the map package.
            ForEach(mapPackage.maps.enumeratedArray(), id: \.offset) { (offset, map) in
                let mapName = map.item?.name ?? "Map \(offset + 1)"

                // The navigation link to the map.
                NavigationLink {
                    Group {
                        if let locatorTask = mapPackage.locatorTask {
                            MobileMapView(map: map, locatorTask: locatorTask)
                        } else {
                            MapView(map: map)
                        }
                    }
                    .navigationTitle(mapName)
                } label: {
                    HStack {
                        // The image of the map for the row.
                        Image(uiImage: map.item?.thumbnail?.image ?? UIImage(
                            systemName: "questionmark"
                        )!)
                        .resizable()
                        .scaledToFit()
                        .frame(height: 50)

                        VStack(alignment: .leading, spacing: 2) {
                            Text(mapName)

                            HStack {
                                // The symbol indicating whether the map can geocode.
                                if mapPackage.locatorTask != nil {
                                    HStack(spacing: 2) {
                                        Image(systemName: "mappin.circle")
                                        Text("Geocoding")
                                    }
                                }

                                // The symbol indicating whether the map can route.
                                if !map.transportationNetworks.isEmpty {
                                    HStack(spacing: 2) {
                                        Image(systemName: "arrow.triangle.turn.up.right.circle")
                                        Text("Routing")
                                    }
                                }
                            }
                            .font(.caption2)
                            .foregroundStyle(.secondary)
                        }
                    }
                }
            }
        }
    }
}

private extension Collection {
    /// Enumerates a collection as an array of (n, x) pairs, where n represents a consecutive integer
    /// starting at zero and x represents an element of the collection.
    /// - Returns: An array of pairs enumerating the collection.
    func enumeratedArray() -> [(offset: Int, element: Self.Element)] {
        return Array(self.enumerated())
    }
}

private extension UTType {
    /// A type that represents a mobile map package file.
    static let mmpk = UTType(filenameExtension: "mmpk")!
}

private extension URL {
    /// The URL to the local San Francisco mobile map package file.
    static var sanFranciscoPackage: URL {
        Bundle.main.url(forResource: "SanFrancisco", withExtension: "mmpk")!
    }
}

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