Query with CQL filters

View on GitHubSample viewer app

Query data from an OGC API feature service using CQL filters.

Image of Query with CQL Filters Image of Query with CQL Filters

Use case

CQL (Common Query Language) is an OGC-created query language used to query for subsets of features. Use CQL filters to narrow geometry results from an OGC feature table.

How to use the sample

Configure a CQL query by setting a where clause, the max features count, and a time extent. Tap the "Apply" button to see the query applied to the OGC API features shown on the map.

How it works

  1. Create an AGSOGCFeatureCollectionTable object using a URL to an OGC API feature service and a collection ID.
  2. Create an AGSQueryParameters object.
  3. In the settings page, set the whereClause and the maxFeatures properties. Create Date objects for the start time and end time being queried and set the timeExtent property.
  4. Populate the AGSOGCFeatureCollectionTable using AGSOGCFeatureCollectionTable.populateFromService(with:clearCache:outfields:completion:) with the custom query parameters created in the previous steps.
  5. Set the view point with the feature table's extent to view the newly-queried features.

Relevant API

  • AGSOGCFeatureCollectionTable
  • AGSQueryParameters
  • AGSTimeExtent

About the data

The Daraa, Syria test data is OpenStreetMap data converted to the Topographic Data Store schema of NGA.

Additional information

See the OGC API website for more information on the OGC API family of standards. See the CQL documentation to learn more about the common query language.

Tags

browse, catalog, common query language, CQL, feature table, filter, OGC, OGC API, query, service, web

Sample Code

QueryWithCQLFiltersViewController.swiftQueryWithCQLFiltersViewController.swiftQueryWithCQLFiltersSettingsViewController.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
// Copyright 2021 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
//
//   https://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 QueryWithCQLFiltersViewController: UIViewController {
    // MARK: Storyboard views

    /// The map view managed by the view controller.
    @IBOutlet var mapView: AGSMapView! {
        didSet {
            let map = AGSMap(basemapStyle: .arcGISTopographic)
            map.operationalLayers.add(ogcFeatureLayer)
            mapView.map = map
            // Set the viewpoint to Daraa, Syria.
            mapView.setViewpoint(AGSViewpoint(latitude: 32.62, longitude: 36.10, scale: 20_000))
        }
    }
    /// The button to present the CQL filters settings.
    @IBOutlet var cqlFiltersBarButtonItem: UIBarButtonItem!

    // MARK: Properties

    /// The most recent query job.
    var lastQuery: AGSCancelable?

    /// A feature layer to visualize the OGC API features.
    let ogcFeatureLayer: AGSFeatureLayer = {
        // Note: the collection ID can be accessed later via
        // `featureCollectionInfo.collectionID` property of the feature table.
        let table = AGSOGCFeatureCollectionTable(
            url: URL(string: "https://demo.ldproxy.net/daraa")!,
            collectionID: "TransportationGroundCrv"
        )
        // Set the feature request mode to manual (only manual is currently
        // supported). In this mode, you must manually populate the table -
        // panning and zooming won't request features automatically.
        table.featureRequestMode = .manualCache

        let featureLayer = AGSFeatureLayer(featureTable: table)
        let lineSymbol = AGSSimpleLineSymbol(style: .solid, color: .red, width: 3)
        featureLayer.renderer = AGSSimpleRenderer(symbol: lineSymbol)
        return featureLayer
    }()

    // MARK: Method

    func populateFeaturesFromQuery(queryParameters: AGSQueryParameters) {
        // Cancel if there is an existing query request.
        lastQuery?.cancel()

        // Populate the table with the query. Setting `outFields` to `nil`
        // requests all fields.
        let table = ogcFeatureLayer.featureTable as! AGSOGCFeatureCollectionTable
        lastQuery = table.populateFromService(
            with: queryParameters,
            clearCache: true,
            outfields: nil
        ) { [weak self] result, error in
            guard let self = self else { return }
            self.lastQuery = nil
            if let error = error,
               // Do not display error if user cancelled the request.
               (error as NSError).code != NSUserCancelledError {
                self.presentAlert(error: error)
            } else if let result = result, let extent = self.ogcFeatureLayer.featureTable?.extent {
                // Zoom to the extent of the selected collection.
                self.mapView.setViewpointGeometry(extent, padding: 50)
                self.presentAlert(title: "Query Result", message: "Query returned \(NumberFormatter.localizedString(from: result.featureEnumerator().allObjects.count as NSNumber, number: .decimal)) features.")
            }
        }
    }

    // MARK: UIViewController

    override func viewDidLoad() {
        super.viewDidLoad()
        // Add the source code button item to the right of navigation bar.
        (navigationItem.rightBarButtonItem as? SourceCodeBarButtonItem)?.filenames = [
            "QueryWithCQLFiltersViewController",
            "QueryWithCQLFiltersSettingsViewController"
        ]
        ogcFeatureLayer.load { [weak self] error in
            guard error == nil else { return }
            self?.cqlFiltersBarButtonItem.isEnabled = true
        }
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let navigationController = segue.destination as? UINavigationController,
           let controller = navigationController.topViewController as? QueryWithCQLFiltersSettingsViewController {
            controller.delegate = self
        }
    }
}

// MARK: - QueryWithCQLFiltersSettingsViewControllerDelegate

extension QueryWithCQLFiltersViewController: QueryWithCQLFiltersSettingsViewControllerDelegate {
    func settingsViewController(_ controller: QueryWithCQLFiltersSettingsViewController, didCreate queryParameters: AGSQueryParameters) {
        populateFeaturesFromQuery(queryParameters: queryParameters)
    }
}

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