Find features in a spatial table related to features in a non-spatial table.
Use case
The non-spatial tables contained by a map service may contain additional information about sublayer features. Such information can be accessed by traversing table relationships defined in the service.
How to use the sample
Tap the toolbar button to prompt a list of comment data from non-spatial features. Tap on one of the comments to query related spatial features and display the first result on the map.
How it works
Create an AGSArcGISMapImageLayer with the URL of a map service.
Load the tables and layers using loadTablesAndLayers(completion:) and get its first table.
To query the table, create AGSQueryParameters. Set its whereClause to filter the request features.
Use queryFeatures(with:queryFeatureFields:completion:) to get the AGSFeatureQueryResult.
Make AGSFeatureQueryResult iterable using featureEnumerator()and loop through to get each AGSFeature.
To query for related features, get the table's relationship info with AGSServiceFeatureTable.layerInfo.relationshipInfos. This returns an array of AGSRelationshipInfos.
Now create AGSRelatedQueryParameters passing in the AGSRelationshipInfo. To query related features, use AGSServiceFeatureTable.queryRelatedFeatures(for:parameters:completion:).
This returns an array of AGSRelatedFeatureQueryResults, each containing a set of related features.
Relevant API
AGSArcGISFeature
AGSArcGISMapImageLayer
AGSFeature
AGSFeatureQueryResult
AGSQueryParameters
AGSRelatedFeatureQueryResult
AGSRelatedQueryParameters
AGSRelationshipInfo
AGSServiceFeatureTable
About the data
This sample uses the Naperville map service, which is used to collect non-emergency requests for service from the general public.
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
// Copyright 2022 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
classQueryRelatedFeaturesNonSpatialTableViewController: UIViewController{
@IBOutletvar mapView: AGSMapView! {
didSet {
// Assign the map to the map view. mapView.map = makeMap()
// Set the viewpoint. mapView.setViewpoint(AGSViewpoint(latitude: 41.734152, longitude: -88.163718, scale: 2e5))
// Add a graphics overlay to show selected features and add it to the map view. mapView.graphicsOverlays.add(selectedFeaturesOverlay)
}
}
@IBOutletvar queryBarButtonItem: UIBarButtonItem!
/// The map image layer that uses the service URL.let serviceRequestsMapImageLayer =AGSArcGISMapImageLayer(
url: URL(string: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/ServiceRequest/MapServer")! )
/// The (non-spatial) table that contains the service request comments.var commentsTable: AGSServiceFeatureTable?
/// The array to store the possible comments.var comments: [AGSFeature] = []
/// The graphics overlay to add graphics to.let selectedFeaturesOverlay: AGSGraphicsOverlay= {
let overlay =AGSGraphicsOverlay()
overlay.renderer =AGSSimpleRenderer(
symbol: AGSSimpleMarkerSymbol(style: .circle, color: .cyan, size: 14)
)
return overlay
}()
@IBActionfuncqueryFeaturesActions(_sender: UIBarButtonItem) {
// Create an action sheet to display the various comments to choose from.let alertController =UIAlertController(title: "Related Service Requests", message: "Select a comment to view related spatial features on the map.", preferredStyle: .actionSheet)
// Create an action for each comment. comments.forEach { feature in// Extract the "comments" attribute as a string.let title = feature.attributes["comments"] as!String// Create an action with the comments title.let action =UIAlertAction(title: title, style: .default) { _in// Clear the former graphics.self.selectedFeaturesOverlay.graphics.removeAllObjects()
// Disable the query button while the feature loads.self.queryBarButtonItem.isEnabled =false// Cast the selected feature as an AGSArcGISFeature.guardlet selectedFeature = feature as?AGSArcGISFeatureelse { return }
self.queryCommentsTable(feature: selectedFeature)
}
// Add the action to the controller. alertController.addAction(action)
}
// Add "cancel" item.let cancelAction =UIAlertAction(title: "Cancel", style: .cancel)
alertController.addAction(cancelAction)
// Present the controller. alertController.popoverPresentationController?.barButtonItem = queryBarButtonItem
present(alertController, animated: true)
}
/// Make a map for the map view.funcmakeMap() -> AGSMap {
// Create a map with the ArcGIS streets basemap style.let map =AGSMap(basemapStyle: .arcGISStreets)
// Add the layer to the map. map.operationalLayers.add(serviceRequestsMapImageLayer)
// Load the map image layer's tables and layers. serviceRequestsMapImageLayer.loadTablesAndLayers { [weakself] _inself?.queryFeatures()
}
return map
}
/// Query features on the first table in the map image layer.funcqueryFeatures() {
// Create query parameters and set its where clause.let nullCommentsParameters =AGSQueryParameters()
nullCommentsParameters.whereClause ="requestid <> '' AND comments <> ''"// Set the first table from the map image layer. commentsTable = serviceRequestsMapImageLayer.tables.first
// Query features on the feature table with the query parameters and all feature fields. commentsTable?.queryFeatures(with: nullCommentsParameters, queryFeatureFields: .loadAll) { [weakself] result, error inguardletself=selfelse { return }
iflet comments = result?.featureEnumerator().allObjects {
// Show the records from the service request comments table in the list view control.self.comments = comments
// Enable the button after the map and features have been loaded.self.queryBarButtonItem.isEnabled =true } elseiflet error = error {
self.presentAlert(error: error)
}
}
}
/// Query related features for the selected feature.funcqueryCommentsTable(feature: AGSArcGISFeature) {
// Get the relationship that defines related service request features for features in the comments table (this is the first and only relationship).guardlet relationshipInfo = commentsTable?.layerInfo?.relationshipInfos.first else { return }
// Create query parameters to get the related service request for features in the comments table.let relatedQueryParameters =AGSRelatedQueryParameters(relationshipInfo: relationshipInfo)
relatedQueryParameters.returnGeometry =true// Query related features for the selected comment and its related query parameters. commentsTable?.queryRelatedFeatures(for: feature, parameters: relatedQueryParameters) { [weakself] results, error in// Get the first related feature.iflet relatedFeature = results?.first?.featureEnumerator().nextObject() as?AGSArcGISFeature {
// Load the feature and get its geometry to show as a graphic on the map. relatedFeature.load { error inguardletself=selfelse { return }
iflet error = error {
self.presentAlert(error: error)
} elseiflet serviceRequestPoint = relatedFeature.geometry as?AGSPoint {
// Create a graphic to add to the graphics overlay.let graphic =AGSGraphic(geometry: serviceRequestPoint, symbol: nil)
self.selectedFeaturesOverlay.graphics.add(graphic)
// Set the viewpoint to the the related feature.self.mapView.setViewpointCenter(serviceRequestPoint, scale: 150_000)
// Enable the button after the feature has finished loading.self.queryBarButtonItem.isEnabled =true }
}
} else {
// Present an error message is the related feature is not found.self?.presentAlert(title: "Related feature not found. No Feature", message: nil)
}
}
}
overridefuncviewDidLoad() {
super.viewDidLoad()
// Add the source code button item to the right of navigation bar. (self.navigationItem.rightBarButtonItem as?SourceCodeBarButtonItem)?.filenames = ["QueryRelatedFeaturesNonSpatialTableViewController"]
}
}