Display OGC API collection

View on GitHubSample viewer app

Display an OGC API feature collection and query features while navigating the map view.

Image of Display OGC API collection

Use case

When panning the map view, it may be necessary to query the OGC API feature table for additional features within the new visible extent.

How to use the sample

Pan the map and see how new features are loaded from the OGC API feature service.

How it works

  1. Create an AGSOGCFeatureCollectionTable object using a URL to an OGC API feature service and a collection ID.
  2. Set the feature table's featureRequestMode property to manualCache.
  3. Create an AGSFeatureLayer using the feature table and add it to the map view.
  4. Create a default AGSQueryParameters object.
    • Set the parameter's spatialRelationship property to intersects.
    • Set the maxFeatures property to 5_000 (some services have a low default value for maximum features).
  5. Every time the map view navigation completes:
    • Set the parameter's geometry to the current extent of the map view.
    • Call AGSOGCFeatureCollectionTable.populateFromService(with:clearCache:outfields:completion:) using the query parameters.

Relevant API

  • AGSOGCFeatureCollectionTable
  • AGSQueryParameters

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.

Tags

feature, feature layer, feature table, OGC, OGC API, service, table, web

Sample Code

DisplayOGCAPICollectionViewController.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
// 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 DisplayOGCAPICollectionViewController: UIViewController {
    // MARK: Instance properties

    /// 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 last query job.
    var lastQuery: AGSCancelable?
    /// The KVO on navigating status of the map view.
    var navigatingObservation: NSKeyValueObservation?
    /// The base query parameters to populate features from the OGC API service.
    let visibleExtentQueryParameters: AGSQueryParameters = {
        let queryParameters = AGSQueryParameters()
        queryParameters.spatialRelationship = .intersects
        // Set a limit of 5000 on the number of returned features per request,
        // because the default on some services could be as low as 10.
        queryParameters.maxFeatures = 5_000
        return queryParameters
    }()

    /// 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: .blue, width: 3)
        featureLayer.renderer = AGSSimpleRenderer(symbol: lineSymbol)
        return featureLayer
    }()

    // MARK: Method

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

        // Set the query's geometry to the current visible extent.
        visibleExtentQueryParameters.geometry = mapView.visibleArea?.extent

        // Populate the table with the query. Setting `outFields` to `nil`
        // requests all fields.
        let table = ogcFeatureLayer.featureTable as! AGSOGCFeatureCollectionTable
        lastQuery = table.populateFromService(
            with: visibleExtentQueryParameters,
            clearCache: false,
            outfields: nil
        ) { [weak self] _, error in
            if let error = error,
               // Do not display error if user simply cancelled the request.
               (error as NSError).code != NSUserCancelledError {
                self?.presentAlert(error: error)
            }
        }
    }

    // MARK: UIViewController

    override func viewDidLoad() {
        super.viewDidLoad()
        // Add the source code button item to the right of navigation bar.
        (navigationItem.rightBarButtonItem as? SourceCodeBarButtonItem)?.filenames = ["DisplayOGCAPICollectionViewController"]
        // Query for features whenever the user stops interacting with the map
        // view.
        navigatingObservation = mapView.observe(\.isNavigating, options: .initial) { mapView, _ in
            guard !mapView.isNavigating else { return }
            DispatchQueue.main.async { [weak self] in
                self?.populateFeaturesFromQuery()
            }
        }
    }
}

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