Mobile map (search and route)

View on GitHub
Sample viewer app

Display maps and use locators to enable search and routing offline using a mobile map package.

Image of mobile map search and route 1 Image of mobile map search and route 2

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 an AGSMobileMapPackage from its path.
  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

  • AGSGeocodeResult
  • AGSMobileMapPackage
  • AGSReverseGeocodeParameters
  • AGSRoute
  • AGSRouteParameters
  • AGSRouteResult
  • AGSRouteTask
  • AGSTransportationNetworkDataset

Tags

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

Sample Code

MapPackageCell.swiftMapPackagesListViewController.swiftMobileMapViewController.swift
                                                                                                           
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
//
// Copyright 2016 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
//
//   http://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 UIKit
import ArcGIS

protocol MapPackageCellDelegate: AnyObject {
    func mapPackageCell(_ mapPackageCell: MapPackageCell, didSelectMap map: AGSMap)
}

class MapPackageCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegate {
    @IBOutlet var titleLabel: UILabel!
    @IBOutlet var collectionView: UICollectionView!

    @IBOutlet var collectionViewHeightConstraint: NSLayoutConstraint!
    @IBOutlet var collectionViewTopConstraint: NSLayoutConstraint!

    weak var delegate: MapPackageCellDelegate?

    var isCollapsed = true {
        didSet {
            isCollapsed ? self.collapseCell() : self.expandCell()
        }
    }

    var mapPackage: AGSMobileMapPackage! {
        didSet {
            self.loadMapPackage()
        }
    }

    func loadMapPackage() {
        self.mapPackage.load { [weak self] (error: Error?) in
            if let error = error {
                print(error.localizedDescription)
            } else {
                // update title label
                self?.titleLabel.text = self?.mapPackage.item?.title

                self?.collectionView.reloadData()
            }
        }
    }

    func expandCell() {
        self.collectionViewTopConstraint.constant = 8
        self.collectionViewHeightConstraint.constant = 110
    }

    func collapseCell() {
        self.collectionViewTopConstraint.constant = 0
        self.collectionViewHeightConstraint.constant = 0
    }

    // MARK: - UICollectionViewDataSource

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return self.mapPackage?.maps.count ?? 0
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MobileMapCell", for: indexPath)

        // label
        let label = cell.viewWithTag(11) as! UILabel
        label.text = "Map \(indexPath.item + 1)"

        // border
        cell.layer.borderColor = UIColor.black.cgColor
        cell.layer.borderWidth = 1

        // search image view
        let searchImageView = cell.viewWithTag(12) as! UIImageView
        searchImageView.isHidden = (self.mapPackage.locatorTask == nil)

        let map = self.mapPackage.maps[indexPath.item]
        // route image view
        let routeImageView = cell.viewWithTag(13) as! UIImageView
        routeImageView.isHidden = map.transportationNetworks.isEmpty

        // thumbnail
        let imageView = cell.viewWithTag(14) as! UIImageView
        imageView.image = map.item?.thumbnail?.image
        imageView.layer.borderColor = UIColor.gray.cgColor
        imageView.layer.borderWidth = 1

        return cell
    }

    // MARK: - UICollectionViewDelegate

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        self.delegate?.mapPackageCell(self, didSelectMap: self.mapPackage.maps[indexPath.item])
    }
}

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