Identify layers

View on GitHub
Sample viewer app

Identify features in all layers in a map.

Image of identify layers

Use case

Identify layers allows users to tap on a map, returning features at that location across multiple layers. Because some layer types have sublayers, the sample recursively counts results for sublayers within each layer.

How to use the sample

Tap to identify features. An alert will show all layers with features at the tapped location, as well as a layer count.

How it works

  1. The tapped position is passed to the AGSGeoView.identifyLayers(atScreenPoint:tolerance:returnPopupsOnly:maximumResultsPerLayer:completion:) method.
  2. For each AGSIdentifyLayerResult in the results, features are counted.

    • Note: there is one identify result per layer with matching features; if the feature count is 0, that means a sublayer contains the matching features.

Relevant API

  • AGSGeoView.identifyLayers(atScreenPoint:tolerance:returnPopupsOnly:maximumResultsPerLayer:completion:)
  • AGSIdentifyLayerResult
  • AGSIdentifyLayerResult.sublayerResults
  • AGSLayerContent.name

Additional information

AGSGeoView supports two methods of identify: identifyLayer, which identifies features within a specific layer, and identifyLayers, which identifies features for all layers in the current view.

Tags

identify, recursion, recursive, sublayers

Sample Code

IdentifyLayersViewController.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
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
// 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

class IdentifyLayersViewController: UIViewController, AGSGeoViewTouchDelegate {
    @IBOutlet var mapView: AGSMapView!

    override func viewDidLoad() {
        super.viewDidLoad()

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

        // Create an instance of a map
        let map = AGSMap(basemapStyle: .arcGISTopographic)

        // Map image layer.
        let mapImageLayer = AGSArcGISMapImageLayer(url: URL(string: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer")!)

        // hide Continent and World layers
        mapImageLayer.load { [weak mapImageLayer] (error: Error?) in
            if error == nil {
                mapImageLayer?.subLayerContents[1].isVisible = false
                mapImageLayer?.subLayerContents[2].isVisible = false
            }
        }
        map.operationalLayers.add(mapImageLayer)

        // Feature table.
        let featureTable = AGSServiceFeatureTable(url: URL(string: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/DamageAssessment/FeatureServer/0")!)

        // Feature layer.
        let featureLayer = AGSFeatureLayer(featureTable: featureTable)

        // Add feature layer add to the operational layers.
        map.operationalLayers.add(featureLayer)

        // Assign map to the map view.
        mapView.map = map

        // Set viewpoint to a specific region.
        mapView.setViewpoint(AGSViewpoint(center: AGSPoint(x: -10977012.785807, y: 4514257.550369, spatialReference: .webMercator()), scale: 68015210))

        // Add self as the touch delegate for the map view.
        mapView.touchDelegate = self
    }

    // MARK: - AGSGeoViewTouchDelegate

    func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
        // Get the geoElements for all layers present at the tapped point.
        self.identifyLayers(screenPoint)
    }

    // MARK: - Identify layers

    private func identifyLayers(_ screen: CGPoint) {
        // Show progress hud.
        UIApplication.shared.showProgressHUD(message: "Identifying")

        self.mapView.identifyLayers(atScreenPoint: screen, tolerance: 12, returnPopupsOnly: false, maximumResultsPerLayer: 10) { (results: [AGSIdentifyLayerResult]?, error: Error?) in
            // Dismiss progress hud.
            UIApplication.shared.hideProgressHUD()

            if let error = error {
                self.presentAlert(error: error)
            } else {
                self.handleIdentifyResults(results!)
            }
        }
    }

    // MARK: - Helper methods

    private func handleIdentifyResults(_ results: [AGSIdentifyLayerResult]) {
        var messageString = ""
        var totalCount = 0
        for identifyLayerResult in results {
            let count = self.geoElementsCountFromResult(identifyLayerResult)
            let layerName = identifyLayerResult.layerContent.name
            messageString.append("\(layerName) :: \(count)")

            // Add new line character if not the final element in array.
            if identifyLayerResult != results.last! {
                messageString.append(" \n ")
            }

            // Update total count.
            totalCount += count
        }

        if totalCount > 0 {
            // If any elements were found, show the results.
            presentAlert(title: "Number of elements found", message: messageString)
        } else {
            // Notify user that no elements were found.
            presentAlert(message: "No element found")
        }
    }

    private func geoElementsCountFromResult(_ result: AGSIdentifyLayerResult) -> Int {
        // Create temp array.
        var tempResults = [result]

        // Using Depth First Search approach to handle recursion.
        var count = 0
        var index = 0

        while index < tempResults.count {
            // Get the result object from the array.
            let identifyResult = tempResults[index]

            // Update count with geoElements from the result.
            count += identifyResult.geoElements.count

            // Check if the result has any sublayer results.
            // If yes then add those result objects in the tempResults
            // array after the current result.
            if !identifyResult.sublayerResults.isEmpty {
                tempResults.insert(contentsOf: identifyResult.sublayerResults, at: index + 1)
            }

            // Update the count and repeat.
            index += 1
        }

        return count
    }
}

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