Find closest facility to an incident (interactive)

View on GitHubSample viewer app

Find a route to the closest facility from a location.

Find closest facility to an incident interactive

Use case

Quickly and accurately determining the most efficient route between a location and a facility is a frequently encountered task. For example, a paramedic may need to know which hospital in the vicinity offers the possibility of getting an ambulance patient critical medical care in the shortest amount of time. Solving for the closest hospital to the ambulance's location using an impedance of "travel time" would provide this information.

How to use the sample

Tap near any of the hospitals and a route will be displayed from that tapped location to the nearest hospital.

How it works

  1. Create a new AGSClosestFacilityTask using a URL from an online network analysis service.
  2. Get AGSClosestFacilityParameters from the task.
  3. Add facilities to the parameters.
  4. Add an incident (as an AGSPoint) to the parameters.
  5. Get AGSClosestFacilityResult by solving the task with the parameters.
  6. Get the indexed list of closet facilities to the incident.
  7. Get the index of the closest facility.
  8. Get closest facility route from the facility result.
  9. Display the route on the AGSMapView as an AGSGraphic on an AGSGraphicsOverlay.

Relevant API

  • AGSClosestFacilityParameters
  • AGSClosestFacilityResult
  • AGSClosestFacilityRoute
  • AGSClosestFacilityTask
  • AGSFacility
  • AGSGraphic
  • AGSGraphicsOverlay
  • AGSIncident

Tags

incident, network analysis, route, routing & directions, search

Sample Code

FindClosestFacilityInteractiveViewController.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
//
// Copyright © 2020 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

class FindClosestFacilityInteractiveViewController: UIViewController {
    @IBOutlet var mapView: AGSMapView! {
        didSet {
            // Initialize the map.
            let map = AGSMap(basemapStyle: .arcGISStreets)
            mapView.map = map
            mapView.setViewpoint(AGSViewpoint(latitude: 32.727, longitude: -117.1750, scale: 144447.638572))
            mapView.touchDelegate = self

            // Create symbols and graphics to add to the graphic overlays.
            mapView.graphicsOverlays.add(makeFacilitiesOverlay())
            mapView.graphicsOverlays.add(incidentGraphicsOverlay)
        }
    }

    // Create a closest facility task from the network service URL.
    private let closestFacilityTask: AGSClosestFacilityTask = {
        let networkServiceURL = URL(string: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/ClosestFacility")!
        return AGSClosestFacilityTask(url: networkServiceURL)
    }()

    // Create a graphic overlay to the map.
    private var incidentGraphicsOverlay = AGSGraphicsOverlay()

    // Create graphics to represent the route.
    private let routeSymbol = AGSSimpleLineSymbol(style: .solid, color: .blue, width: 2.0)

    // Create an array of facilities in the area.
    private var facilities = [
        AGSFacility(point: AGSPoint(x: -1.3042129900625112E7, y: 3860127.9479775648, spatialReference: .webMercator())),
        AGSFacility(point: AGSPoint(x: -1.3042193400557665E7, y: 3862448.873041752, spatialReference: .webMercator())),
        AGSFacility(point: AGSPoint(x: -1.3046882875518233E7, y: 3862704.9896770366, spatialReference: .webMercator())),
        AGSFacility(point: AGSPoint(x: -1.3040539754780494E7, y: 3862924.5938606677, spatialReference: .webMercator())),
        AGSFacility(point: AGSPoint(x: -1.3042571225655518E7, y: 3858981.773018156, spatialReference: .webMercator())),
        AGSFacility(point: AGSPoint(x: -1.3039784633928463E7, y: 3856692.5980474586, spatialReference: .webMercator())),
        AGSFacility(point: AGSPoint(x: -1.3049023883956768E7, y: 3861993.789732541, spatialReference: .webMercator()))
    ]

    // Create the graphics and add them to the graphics overlay
    func makeFacilitiesOverlay() -> AGSGraphicsOverlay {
        let facilitySymbolURL = URL(string: "https://static.arcgis.com/images/Symbols/SafetyHealth/Hospital.png")!
        let facilitySymbol = AGSPictureMarkerSymbol(url: facilitySymbolURL)
        facilitySymbol.height = 30
        facilitySymbol.width = 30

        let graphicsOverlay = AGSGraphicsOverlay()
        graphicsOverlay.graphics.addObjects(from: facilities.map { AGSGraphic(geometry: $0.geometry, symbol: facilitySymbol) })
        return graphicsOverlay
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Add the source code button item to the right of navigation bar.
        (self.navigationItem.rightBarButtonItem as! SourceCodeBarButtonItem).filenames = ["FindClosestFacilityInteractiveViewController"]
    }
}

// MARK: - AGSGeoViewTouchDelegate
extension FindClosestFacilityInteractiveViewController: AGSGeoViewTouchDelegate {
    func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
        // Find the closest facilities with the default parameters.
        closestFacilityTask.defaultClosestFacilityParameters { [weak self] (parameters, error) in
            guard let self = self else { return }
            if let parameters = parameters {
                parameters.setFacilities(self.facilities)

                // Create a symbol for the incident.
                let incidentSymbol = AGSSimpleMarkerSymbol(style: .cross, color: .black, size: 20)

                // Remove previous graphics.
                self.incidentGraphicsOverlay.graphics.removeAllObjects()

                // Create a point and graphics for the incident.
                let graphic = AGSGraphic(geometry: mapPoint, symbol: incidentSymbol)
                self.incidentGraphicsOverlay.graphics.add(graphic)

                // Set the incident for the parameters.
                parameters.setIncidents([AGSIncident(point: mapPoint)])
                self.closestFacilityTask.solveClosestFacility(with: parameters) { [weak self] (result, error) in
                    guard let self = self else { return }
                    if let result = result {
                        // Get the ranked list of closest facilities.
                        let rankedList = result.rankedFacilityIndexes(forIncidentIndex: 0)

                        // Get the facility closest to the incident.
                         let closestFacility = rankedList?.first as! Int

                        // Calculate the route based on the closest facility and chosen incident.
                        let route = result.route(forFacilityIndex: closestFacility, incidentIndex: 0)

                        // Display the route graphics.
                        self.incidentGraphicsOverlay.graphics.add(AGSGraphic(geometry: route?.routeGeometry, symbol: self.routeSymbol))
                    } else if let error = error {
                        self.presentAlert(error: error)
                    }
                }
            } else if let error = error {
                self.presentAlert(error: error)
            }
        }
    }
}

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