List related features

View on GitHubSample viewer app

List features related to the selected feature.

List related features sample

Use case

Related features are useful for managing relational information, like what you would store in a relational database management system (RDBMS). You can define relationship between records as one-to-one, one-to-many, or many-to-one. For example, you could model inspections and facilities as a many-to-one relationship. Then, for any facility feature, you could list related inspection features.

How to use the sample

Tap on a feature to select it. The related features will be displayed in a list.

How it works

  1. With an AGSArcGISFeature, call AGSArcGISFeatureTable.queryRelatedFeatures(for:completion:) on the feature's feature table.
  2. Iterate over the result's collection of AGSRelatedFeatureQueryResult objects to get the related features and add them to a list.

Relevant API

  • AGSArcGISFeature
  • AGSArcGISFeatureTable
  • AGSArcGISFeatureTable.queryRelatedFeatures
  • AGSFeatureQueryResult
  • AGSRelatedFeatureQueryResult

Tags

features, identify, query, related, relationship, search

Sample Code

ListRelatedFeaturesViewController.swiftListRelatedFeaturesViewController.swiftRelatedFeaturesListViewController.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
155
156
157
158
// Copyright 2017 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 ListRelatedFeaturesViewController: UIViewController, AGSGeoViewTouchDelegate {
    @IBOutlet var mapView: AGSMapView!

    private var parksFeatureLayer: AGSFeatureLayer!
    private var parksFeatureTable: AGSServiceFeatureTable!
    private var selectedPark: AGSArcGISFeature!
    private var screenPoint: CGPoint!

    private var results = [AGSRelatedFeatureQueryResult]()

    override func viewDidLoad() {
        super.viewDidLoad()

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

        // Initialize map with a topographic basemap style.
        let map = AGSMap(basemapStyle: .arcGISTopographic)

        // add self as the touch delegate for map view
        // we will need to be notified when the user taps with the map
        self.mapView.touchDelegate = self

        // create feature table for the parks layer, the origin layer in the relationship
        self.parksFeatureTable = AGSServiceFeatureTable(url: URL(string: "https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/arcgis/rest/services/AlaskaNationalParksPreservesSpecies_List/FeatureServer/1")!)

        // feature layer for parks
        let parksFeatureLayer = AGSFeatureLayer(featureTable: self.parksFeatureTable)

        // add parks feature layer to the map
        map.operationalLayers.add(parksFeatureLayer)

        // Feature table for related Preserves layer
        let preservesFeatureTable = AGSServiceFeatureTable(url: URL(string: "https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/arcgis/rest/services/AlaskaNationalParksPreservesSpecies_List/FeatureServer/0")!)

        // Feature table for related Species layer
        let speciesFeatureTable = AGSServiceFeatureTable(url: URL(string: "https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/arcgis/rest/services/AlaskaNationalParksPreservesSpecies_List/FeatureServer/2")!)

        // add these to the tables on the map
        // to query related features in a layer, the layer must either be added as a feature
        // layer in operational layers or as a feature table in tables on map
        map.tables.addObjects(from: [preservesFeatureTable, speciesFeatureTable])

        // assign map to the map view
        mapView.map = map
        let point = AGSPoint(x: -16507762.575543, y: 9058828.127243, spatialReference: .webMercator())
        mapView.setViewpoint(AGSViewpoint(center: point, scale: 36764077))

        // set selection color
        mapView.selectionProperties.color = .yellow

        // store the feature layer for later use
        self.parksFeatureLayer = parksFeatureLayer
    }

    // MARK: - AGSGeoViewTouchDelegate

    func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
        // unselect previously selected park
        if let previousSelection = self.selectedPark {
            self.parksFeatureLayer.unselectFeature(previousSelection)
        }

        // show progress hud
        UIApplication.shared.showProgressHUD(message: "Identifying feature")

        // identify features at the tapped location
        self.mapView.identifyLayer(self.parksFeatureLayer, screenPoint: screenPoint, tolerance: 12, returnPopupsOnly: false) { [weak self] (result: AGSIdentifyLayerResult) in
            // dismiss progress hud
            UIApplication.shared.hideProgressHUD()

            if let error = result.error {
                // dismiss progress hud
                self?.presentAlert(error: error)
            }
            // Check if a feature is identified
            else if let feature = result.geoElements.first as? AGSArcGISFeature {
                // store as selected park to use for querying
                self?.selectedPark = feature

                // select feature on layer
                self?.parksFeatureLayer.select(feature)

                // store the screen point for the tapped location to show popover at that location
                self?.screenPoint = screenPoint

                // query for related features
                self?.queryRelatedFeatures()
            }
        }
    }

    // query for related features given the origin feature
    private func queryRelatedFeatures() {
        // show progress hud
        UIApplication.shared.showProgressHUD(message: "Querying related features")

        // query for related features
        self.parksFeatureTable.queryRelatedFeatures(for: self.selectedPark) { [weak self] (results: [AGSRelatedFeatureQueryResult]?, error: Error?) in
            // dismiss progress hud
            UIApplication.shared.hideProgressHUD()

            guard let self = self else { return }

            if let error = error {
                // display error
                self.presentAlert(error: error)
            } else if let results = results {
                // Show the related features found in popover
                if !results.isEmpty {
                    self.results = results
                    self.showRelatedFeatures()
                } else {  // else notify user
                    self.presentAlert(message: "No related features found")
                }
            }
        }
    }

    // show related features in a table view as popover
    private func showRelatedFeatures() {
        // perform popover segue
        self.performSegue(withIdentifier: "RelatedFeaturesSegue", sender: self)
    }

    // MARK: - Navigation

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "RelatedFeaturesSegue",
            let navController = segue.destination as? UINavigationController,
            let controller = navController.viewControllers.first as? RelatedFeaturesListViewController {
            // set results from related features query
            controller.results = results
        }
     }

    // to hide popover controller on rotation
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        self.dismiss(animated: true)
    }
}

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