List features related to the selected feature.
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
- With an
AGSArcGISFeature
, callAGSArcGISFeatureTable.queryRelatedFeatures(for:completion:)
on the feature's feature table. - 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
// 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)
}
}