Learn how to execute a SQL query to return features from a feature layer based on spatial and attribute criteria.
A feature layer can contain a large number of features stored in ArcGIS. You can query a layer to access a subset of its features using any combination of spatial and attribute criteria. You can control whether or not each feature's geometry is returned, as well as which attributes are included in the results. Queries allow you to return a well-defined subset of your hosted data for analysis or display in your ArcGIS Runtime app.
In this tutorial, you'll write code to perform SQL queries that return a subset of features in the LA County Parcel feature layer (containing over 2.4 million features). Features that meet the query criteria are selected in the map.
Prerequisites
Before starting this tutorial:
-
You need an ArcGIS Location Platform or ArcGIS Online account.
-
Your system meets the system requirements.
Steps
Open the Xcode project
-
To start the tutorial, complete the Display a map tutorial or download and unzip the solution.
-
Open the
.xcodeproj
file in Xcode. -
If you downloaded the solution, get an access token and set the API key.
An API Key gives your app access to secure resources used in this tutorial.
-
Go to the Create an API key tutorial to obtain a new API key access token. Ensure that the following privilege is enabled: Location services > Basemaps > Basemap styles service. Copy the access token as it will be used in the next step.
-
In Xcode, in the Project Navigator, click AppDelegate.swift.
-
In the editor, set the
API
property on theKey AGS
with your access token.ArcGIS Runtime Environment AppDelegate.swiftUse dark colors for code blocks func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { AGSArcGISRuntimeEnvironment.apiKey = "YOUR_ACCESS_TOKEN" return true }
-
Set selection properties
Define the selection color via the map view's AGS
to distinguish selected features in the map view.
-
In Xcode, in the Project Navigator, click ViewController.swift.
-
Update the
setup
method to set map view's selection color to be yellow. This step is optional, and if you don't set a selection color, cyan is used as the default.Map() ViewController.swiftUse dark colors for code blocks private func setupMap() { mapView.map = map mapView.setViewpoint( AGSViewpoint( latitude: 34.02700, longitude: -118.80500, scale: 72_000 ) ) // Set selection color. mapView.selectionProperties.color = .yellow }
Add a parcels layer to the map
Access the LA parcels feature service and create a new layer to display those features in the map.
-
In ViewController.swift, create a property for an
AGSServiceFeatureTable
to access LA parcels after themap
property declaration.View ViewController.swiftUse dark colors for code blocks @IBOutlet var mapView: AGSMapView! private let map = AGSMap(basemapStyle: .arcGISTopographic) let featureTable = AGSServiceFeatureTable(url: URL(string: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/LA_County_Parcels/FeatureServer/0")!)
-
Create a new method,
setup
. Create anLayer() AGSFeatureLayer
and add it to the map's list of operational layers to view the parcels.ViewController.swiftUse dark colors for code blocks private func setupLayer() { // Add feature layer to map's operational layers. let parcelsFeatureLayer = AGSFeatureLayer(featureTable: featureTable) mapView.map?.operationalLayers.add(parcelsFeatureLayer) }
-
Update the
view
method with a call toDid Load() setup
.Layer() ViewController.swiftUse dark colors for code blocks override func viewDidLoad() { super.viewDidLoad() setupMap() setupLayer() }
-
Press Command + R to run the app.
If you are using the Xcode simulator your system must meet these minimum requirements: macOS Big Sur 11.3, Xcode 13, iOS 13. If you are using a physical device, then refer to the system requirements.
The app loads with the map centered on the Santa Monica Mountains in California with the parcels feature layer displayed.
Create a UI control to display predefined query strings
To make performing a query on a feature layer flexible, add a UI control to present a list of predefined attribute queries for the parcels dataset.
-
Create a
UI
and define its properties.Button ViewController.swiftUse dark colors for code blocks @IBOutlet var mapView: AGSMapView! private let map = AGSMap(basemapStyle: .arcGISTopographic) let featureTable = AGSServiceFeatureTable(url: URL(string: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/LA_County_Parcels/FeatureServer/0")!) let optionsButton: UIButton = { let button = UIButton() button.backgroundColor = .lightGray button.setTitleColor(.blue, for: .normal) button.setTitle("Choose a SQL 'where' clause", for: .normal) button.translatesAutoresizingMaskIntoConstraints = false return button }()
-
Create a new method,
setup
, and add the button as aButton() sub
of the mainView view
. This displays the button when the app is run.ViewController.swiftUse dark colors for code blocks private func setupButton() { // Add button as subview to the view. view.addSubview(optionsButton) }
-
Next, add layout constraints to define the position of the button relative to the main
view
andsafe
of the device. This ensures the button looks as expected in different devices and orientations.Area ViewController.swiftUse dark colors for code blocks private func setupButton() { // Add button as subview to the view. view.addSubview(optionsButton) // Add layout constraints defining the position of the button optionsButton.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true optionsButton.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true optionsButton.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true optionsButton.heightAnchor.constraint(equalToConstant: 60 + self.view.safeAreaInsets.bottom).isActive = true }
-
Next, add a target for the button with the method to execute when it is tapped. This method,
populate
, which you'll create in the next section, populates and displays a list of query options.Options() ViewController.swiftUse dark colors for code blocks private func setupButton() { // Add button as subview to the view. view.addSubview(optionsButton) // Add layout constraints defining the position of the button optionsButton.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true optionsButton.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true optionsButton.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true optionsButton.heightAnchor.constraint(equalToConstant: 60 + self.view.safeAreaInsets.bottom).isActive = true // Define method to call when button is tapped. optionsButton.addTarget(self, action: #selector(populateOptions), for: .touchUpInside) }
-
Update the
view
method with a call toDid Load() setup
.Button() ViewController.swiftUse dark colors for code blocks override func viewDidLoad() { super.viewDidLoad() setupMap() setupLayer() setupButton() }
You can also create and set up the UI
using storyboards.
Create predefined query expressions
You'll now create and present a list of query options for the user to choose from when the button is tapped.
-
To populate the UI with a predefined list of
where
clauses for the query, create a new method calledpopulate
.Options() ViewController.swiftUse dark colors for code blocks @objc func populateOptions() { }
-
Create an
UI
with aAlert Controller title
and of styleaction
. Create an array of strings to populate theSheet where
clauses. Create an alert action for eachwhere
clause, which calls the method to execute the query when chosen. You'll implement the methodexecute
later.Query(where Clause :) ViewController.swiftUse dark colors for code blocks @objc func populateOptions() { // Populate query options. let alert = UIAlertController(title: "Choose a SQL where clause.", message: nil, preferredStyle: .actionSheet) let whereClauses = ["UseType = 'Government'", "UseType = 'Residential'", "UseType = 'Irrigated Farm'", "TaxRateArea = 10853", "TaxRateArea = 10860", "Roll_LandValue > 1000000", "Roll_LandValue < 1000000"] whereClauses.forEach { whereClause in let action = UIAlertAction(title: whereClause, style: .default) { [unowned self] _ in executeQuery(whereClause: whereClause) } alert.addAction(action) } }
-
Add code to present the options to the user.
ViewController.swiftUse dark colors for code blocks @objc func populateOptions() { // Populate query options. let alert = UIAlertController(title: "Choose a SQL where clause.", message: nil, preferredStyle: .actionSheet) let whereClauses = ["UseType = 'Government'", "UseType = 'Residential'", "UseType = 'Irrigated Farm'", "TaxRateArea = 10853", "TaxRateArea = 10860", "Roll_LandValue > 1000000", "Roll_LandValue < 1000000"] whereClauses.forEach { whereClause in let action = UIAlertAction(title: whereClause, style: .default) { [unowned self] _ in executeQuery(whereClause: whereClause) } alert.addAction(action) } // Present query options. alert.popoverPresentationController?.sourceView = optionsButton alert.popoverPresentationController?.sourceRect = optionsButton.frame alert.preferredContentSize = CGSize(width: 300, height: 200) present(alert, animated: true) }
Add a method to execute the query
In this step, create a new method that queries the parcels
AGSFeatureLayer
using both attribute and spatial criteria. After clearing any currently selected features, a new query will be executed to find features in the map's current extent that meet the selected attribute expression. The features in the
AGSFeatureQueryResult
will be selected in the parcels layer.
-
Add the
execute
function to query parcels using a provided where expression.Query ViewController.swiftUse dark colors for code blocks func executeQuery(whereClause: String) { // Get the parcels layer from the map guard let parcelsFeatureLayer = map.operationalLayers.firstObject as? AGSFeatureLayer else { //... handle error return } // Clear any existing selection. parcelsFeatureLayer.clearSelection() // Create the query parameters using the where expression and extent passed in. let queryParams = AGSQueryParameters() queryParams.whereClause = whereClause queryParams.geometry = mapView.visibleArea?.extent queryParams.returnGeometry = true featureTable.queryFeatures(with: queryParams) { queryResult, error in if let _ = error { //... handle error } else if let features = queryResult?.featureEnumerator().allObjects as? [AGSArcGISFeature] { parcelsFeatureLayer.select(features) } } }
Run the app
-
Press Command + R to run the app.
If you are using the Xcode simulator your system must meet these minimum requirements: macOS Big Sur 11.3, Xcode 13, iOS 13. If you are using a physical device, then refer to the system requirements.
-
The app loads with the map centered on the Santa Monica Mountains in California with the parcels feature layer displayed. Choose an attribute expression, and parcels in the current extent that meet the selected criteria will display in the specified selection color.
What's next?
Learn how to use additional API features, ArcGIS location services, and ArcGIS tools in these tutorials: