Use .NET to Query ArcGIS Features

QUERY AND FILTER ARCGIS FEATURES WITH .NET RUNTIME

by

The ArcGIS Runtime SDK for .NET has a robust set of capabilities, including the ability to query, select, and filter features from a map using C#. This tutorial will teach you how to use a SQL query to filter the data displayed by your GIS mapping solution.

Selecting Data from Complete Set

In this section, you will learn how to select and highlight a sub-set of a feature service’s data. The tutorial will build off the WPF application you created in the previous tutorial.

In the MapViewModel.cs, the class initializer method will simply call a single method, InitializeMap().

public MapViewModel()
{
    InitializeMap();
}

The InitializeMap() method will populate the map and set the initial viewpoint. Start with the same code you used in the previous tutorial.

private async void InitializeMap()
{
    Map newMap = new Map(Basemap.CreateStreets());
    FeatureLayer featureLayer = new FeatureLayer(new Uri("https://arcgis.atlantaregional.com/arcgis/rest/services/OpenData/FeatureServer/146"));

    await featureLayer.LoadAsync();

    newMap.OperationalLayers.Add(featureLayer);
    newMap.InitialViewpoint = new Viewpoint(featureLayer.FullExtent);

    Map = newMap;
}

The above code will load the entire data set and display all of its features on the map. Suppose you want to select those features which are subway routes operated by the Metropolitan Atlanta Rapid Transit Authority (MARTA). Recall that the application’s MapView is bound to the Map property of the MapViewModel. It is when the Map property is updated that the program’s UI is refreshed and the map is populated. When you perform a query on feature layer that has been added to the map, the Map property gets updated.

To query against the feature table, you will define the SQL query as part of a QueryParameters object.

QueryParameters queryParams = new QueryParameters
{
    WhereClause = "agency_id = 'MARTA' and rte_type = 'subway, metro'"
};

Next, execute the query and await the results. Save the list of features that match the query as a FeatureQueryResult type.

FeatureQueryResult queryResult = await featureLayer.FeatureTable.QueryFeaturesAsync(queryParams);

Now, all the features that match the query string are saved as a variable. You can use these results to perform functions on the selected features. For example, you could highlight each those features that match the query string:

private async void InitializeMap()
{
    Map newMap = new Map(Basemap.CreateStreets());
    FeatureLayer featureLayer = new FeatureLayer(new Uri("https://arcgis.atlantaregional.com/arcgis/rest/services/OpenData/FeatureServer/146"));

    await featureLayer.LoadAsync();

    newMap.OperationalLayers.Add(featureLayer);
    newMap.InitialViewpoint = new Viewpoint(featureLayer.FullExtent);

    QueryParameters queryParams = new QueryParameters
    {
        WhereClause = "agency_id = 'MARTA' and rte_type = 'subway, metro'"
    };

    FeatureQueryResult queryResult = await featureLayer.FeatureTable.QueryFeaturesAsync(queryParams);
    foreach (Feature feature in queryResult)
    {
        featureLayer.SelectFeature(feature);
    }

    Map = newMap;
}

Filtering and Displaying Subset of Data

Suppose you want to display only those features that match a given query. In this case, you should load the feature service as a Feature Table. This will allow you to edit the table, keeping only those results that are of interest. Finally, you will load the modified table as a feature layer and add it to your map.

For this example, create a private field to hold the values of the feature table and feature layer. This will make it easier to pass them to various methods in your C# class.

private ServiceFeatureTable _featureTable;
private FeatureLayer _featureLayer;

Next, construct the InitializeMap() method. It will look similar to the previous example, except you will load the feature service as the private ServiceFeatureTable you created earlier, instead of loading it directly as a FeatureLayer.

private async void InitializeMap()
{
    Map newMap = new Map(BasemapType.Streets, 33.78506, -84.37366, 11);

    _featureTable = new ServiceFeatureTable(new Uri("https://arcgis.atlantaregional.com/arcgis/rest/services/OpenData/FeatureServer/146"))
    {
        FeatureRequestMode = FeatureRequestMode.ManualCache
    };

    _featureTable.Loaded += OnTableLoaded;
    _featureLayer = new FeatureLayer(_featureTable);

    newMap.OperationalLayers.Add(_featureLayer);

    Map = newMap;
}

Line 22 creates a map with a given basemap, and it sets the initial viewpoint to the Atlanta region. Next, you loaded the Atlanta transit routes feature service as a feature table and set the ServiceFeatureTable.FeatureRequestMode property to ManualCache. This ensures that the map will not automatically be populated by features from the service. Instead, the map will only be populated manually by calling the PopulateFromServiceAsync() method.

One Line 29, you have referenced a method, OnTableLoaded(), that will be called in response to the Loaded event of the ServiceFeatureTable being raised. For a primer, see the tutorial on event-driven programming.

The OnTableLoaded() method will be your filter method. This method should query the table and update it to show only results that match the query terms.

private async void OnTableLoaded(object sender, EventArgs e)
{
    QueryParameters queryParams = new QueryParameters
    {
        WhereClause = "agency_id = 'MARTA' and rte_type = 'subway, metro'"
    };

    string[] outputFields = { "*" };

    try
    {
        FeatureQueryResult queryResult = await _featureTable.PopulateFromServiceAsync(queryParams, true, outputFields);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString(), "Error");
    }
}

Similar to the example above, you have started by defining SQL query parameters to search for subways operated by MARTA. The outputFields variable is used when populating from the service to define which fields are loaded. In this example, all fields will be loaded. This is useful for displaying a callout or popup to show data to the user, such as the subway route number, etc. You could also limit the fields that are returned to only those your application will use.

Finally, Lines 46-53 include a try-catch block that executes a call to the PopulateFromServiceAsync() method based on the query parameters and the fields you wish to return. The try-catch provides basic error handling in case the sql string is improperly formatted, for example.

The second argument of the PopulateFromServiceAsync() is a bool parameter which indicates whether the cache should be cleared before the table is populated. When this value is set to true , as in this example, the table will be cleared and replaced with the query results. If it were set to false , the results would be appended to the table.

The Bottom Line

In this tutorial, you learned a technique for querying and selecting features from an ArcGIS feature service. You also learned how to filter a feature table to show a map that contains only the results that match a specific query string. These techniques form the building blocks to more advanced GIS applications. If you have any questions, feel free to ask in the comments!


Don't stop learning!

There is so much to discover about C#. That's why I am making my favorite tips and tricks available for free. Enter your email address below to become a better .NET developer.


Did you know?

Our beautiful, multi-column C# reference guides contain more than 150 tips and examples to make it even easier to write better code.

Get your cheat sheets