Query feature table

View on GitHub

Find features in a feature table which match an SQL query.

Image of query feature table

Use case

Query expressions can be used in ArcGIS to select a subset of features from a feature table. This is most useful in large or complicated data sets. A possible use case might be on a feature table marking the location of street furniture throughout a city. A user may wish to query by a TYPE column to return "benches". In this sample, we query a U.S. state by STATE_NAME from a feature table containing all U.S. states.

How to use the sample

Input the name of a U.S. state into the text field. When you tap "search", a query is performed and the matching features are highlighted or an error is returned.

How it works

  1. Create a ServiceFeatureTable using the URL of a feature service.
  2. Create a QueryParameters with a where clause specified using queryParameters.whereClause.
  3. Perform the query using queryFeatures(queryParameters) on the service feature table.
  4. When complete, the query will return a FeatureQueryResult which can be iterated over to get the matching features.

Relevant API

  • FeatureLayer
  • FeatureQueryResult
  • QueryParameters
  • ServiceFeatureTable

About the data

This sample uses U.S. State polygon features from the USA 2016 Daytime Population feature service.

Tags

query, search

Sample Code

query_feature_table.dart
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
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//
// Copyright 2024 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 'package:arcgis_maps/arcgis_maps.dart';
import 'package:flutter/material.dart';

import '../../utils/sample_state_support.dart';

class QueryFeatureTable extends StatefulWidget {
  const QueryFeatureTable({super.key});

  @override
  State<QueryFeatureTable> createState() => _QueryFeatureTableState();
}

class _QueryFeatureTableState extends State<QueryFeatureTable>
    with SampleStateSupport {
  // Create a controller for the map view.
  final _mapViewController = ArcGISMapView.createController();
  // Create a text editing controller.
  final _textEditingController = TextEditingController();
  // Create a focus node for the search text field.
  final _searchFocusNode = FocusNode();
  // Create an initial viewpoint.
  final _initialViewpoint = Viewpoint.fromCenter(
    ArcGISPoint(
      x: -11000000,
      y: 5000000,
      spatialReference: SpatialReference.webMercator,
    ),
    scale: 100000000,
  );
  // Create a feature table and a feature layer.
  final _featureTable = ServiceFeatureTable.withUri(
    Uri.parse(
      'https://services.arcgis.com/jIL9msH9OI208GCb/arcgis/rest/services/USA_Daytime_Population_2016/FeatureServer/0',
    ),
  );
  late FeatureLayer _featureLayer;

  @override
  void dispose() {
    _textEditingController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      // Create a column with a text field and a map view.
      body: SafeArea(
        child: Column(
          children: [
            // Create a text field for searching states.
            TextField(
              focusNode: _searchFocusNode,
              controller: _textEditingController,
              decoration: InputDecoration(
                hintText: 'Enter a State...',
                prefixIcon: const Icon(Icons.search),
                suffixIcon: IconButton(
                  onPressed: dismissSearch,
                  icon: const Icon(Icons.clear),
                ),
              ),
              onSubmitted: onSearchSubmitted,
            ),
            // Add a map view to the widget tree and set a controller.
            Expanded(
              child: ArcGISMapView(
                controllerProvider: () => _mapViewController,
                onMapViewReady: onMapViewReady,
              ),
            ),
          ],
        ),
      ),
    );
  }

  void onMapViewReady() async {
    // Create a map with the topographic basemap style and set to the map view.
    final map = ArcGISMap.withBasemapStyle(BasemapStyle.arcGISTopographic);

    // Create a feature layer and amend the opacity and max scale properties.
    _featureLayer = FeatureLayer.withFeatureTable(_featureTable)
      ..opacity = 0.8
      ..maxScale = 10000;

    // Create a renderer with a fill symbol and apply to the feature layer.
    final lineSymbol = SimpleLineSymbol(
      style: SimpleLineSymbolStyle.solid,
      color: Colors.black,
      width: 1,
    );
    final fillSymbol = SimpleFillSymbol(
      style: SimpleFillSymbolStyle.solid,
      color: Colors.yellow,
      outline: lineSymbol,
    );
    final renderer = SimpleRenderer(symbol: fillSymbol);
    _featureLayer.renderer = renderer;

    // Add the feature layer to the map and define an initial viewpoint.
    map.operationalLayers.add(_featureLayer);
    map.initialViewpoint = _initialViewpoint;
    // Set the map to the map view.
    _mapViewController.arcGISMap = map;
  }

  void onSearchSubmitted(String value) async {
    // Clear the selection.
    _featureLayer.clearSelection();

    // Create query parameters and set the where clause.
    final queryParameters = QueryParameters();
    final stateName = value.trim();
    queryParameters.whereClause =
        "upper(STATE_NAME) LIKE '${stateName.toUpperCase().sqlEscape()}%'";

    // Query the feature table with the query parameters.
    final queryResult = await _featureTable.queryFeatures(queryParameters);

    // Get the first feature from the query result.
    final iterator = queryResult.features().iterator;
    if (iterator.moveNext()) {
      final feature = iterator.current;
      if (feature.geometry != null) {
        // Set the viewpoint to the feature's extent.
        _mapViewController.setViewpointGeometry(
          feature.geometry!.extent,
          paddingInDiPs: 20.0,
        );
      }
      _featureLayer.selectFeature(feature);
    } else {
      // Show an alert dialog if no matching state is found.
      if (mounted) {
        // Set the viewpoint to the initial viewpoint.
        _mapViewController.setViewpoint(_initialViewpoint);
        showDialog(
          context: context,
          builder: (context) {
            return const AlertDialog(
              content: Text('No matching State found.'),
            );
          },
        );
      }
    }
  }

  void dismissSearch() {
    // Clear the text field and dismiss the keyboard.
    setState(() => _textEditingController.clear());
    FocusManager.instance.primaryFocus?.unfocus();
  }
}

extension on String {
  // Prepare a string so that it can be used as input in a whereClause, which must
  // be valid SQL.
  String sqlEscape() => replaceAll("'", "''");
}

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