Overview

You will learn: how to build an app to perform either client-side or server-side SQL and spatial queries to access data.

Applications can perform server-side or client-side SQL, spatial, and statistic queries to access and display data from feature layers. The source data for a feature layer can be hosted onArcGIS OnlineorArcGIS Enterpriseor it can be created from an array on the client.

  • Server-side Query: To request a subset of data from the server without adding the feature layer to a map, use the queryFeatures method on a FeatureLayer object.

  • Client-side Query: To access a subset of data on the client, you have to add the feature layer to a map first, and then use the queryFeatures method on a FeatureLayerView object. Since the data is on the client, client-side queries execute very quickly.

Both client-side and server-side queries can contain a SQL expression and/or a spatial relationship operator. The main difference between client-side and server-side queries is that client-side querying is only possible after the feature layer is added to a map and the attributes are present. To learn how to access data using a SQL filter, visit the Filter a feature layer tutorial.

In this tutorial, you will execute server-side and client-side queries to find trailheads that are within a distance of 1500 meters from the center of the map and where you click.

Click on the map below to execute a query to find features.

Steps

Create a starter app

  1. Open the JavaScript Starter App on CodePen.

  2. In CodePen, click Fork and save the pen as ArcGIS JavaScript Tutorials: Query a feature layer.

Add layers

Create a feature layer for the trailheads so you can execute queries and a graphics layer to draw the features returned.

  1. In the require statement, add a reference to the FeatureLayer, GraphicsLayer and Graphic modules.

        require([
          "esri/Map",
          "esri/views/MapView",
          "esri/layers/FeatureLayer",
          "esri/layers/GraphicsLayer",
          "esri/Graphic"
        ],function(Map, MapView, FeatureLayer, GraphicsLayer, Graphic) {
    
  2. At the end of the code in the main function, create a FeatureLayer and set the url to access and query the trailheads feature layer, and then create a GraphicsLayer and add it to the map. The graphics layer will be used to draw the features returned from the query.

           // Reference the feature layer to query
           var featureLayer = new FeatureLayer({
             url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads_Styled/FeatureServer/0",
           });
    
           // Layer used to draw graphics returned
           var graphicsLayer = new GraphicsLayer();
           map.add(graphicsLayer);
    

Create a function to add graphics

  1. Define an addGraphics function that will be used later to accept the return values from a query and add the results to the graphics layer. Clear the graphics layer each time with removeAll. Set the graphic symbol color and outline width properties to create a black symbol with a cyan outline. Add a simple popupTemplate to each graphic to show some trail information when they are clicked.

          function addGraphics(result) {
            graphicsLayer.removeAll();
            result.features.forEach(function(feature){
              var g = new Graphic({
                geometry: feature.geometry,
                attributes: feature.attributes,
                symbol: {
                 type: "simple-marker",
                  color: [0,0,0],
                  outline: {
                   width: 2,
                   color: [0,255,255],
                 },
                  size: "20px"
                },
                popupTemplate: {
                 title: "{TRL_NAME}",
                 content: "This a {PARK_NAME} trail located in {CITY_JUR}."
                }
              });
              graphicsLayer.add(g);
            });
          }
    

Execute server-side queries and add graphics

Now that the layer is created and we can add graphics, add code to execute the server-side query. Server-side queries can be executed against a feature layer as soon as it is created and the layer does not need to be added to the map.

  1. Define a function that accepts a point, distance to search, spatial relationship operator, and an optional SQL expression and builds a query that will return all of the fields and the geometry from the feature layer. Use the queryFeatures method to execute the query. When the features are returned, pass the results to the addGraphics function. Learn more about the query parameters you can use in the documentation.

          function queryFeatureLayer(point, distance, spatialRelationship, sqlExpression) {
            var query = {
              geometry: point,
              distance: distance,
              spatialRelationship: spatialRelationship,
              outFields: ["*"],
              returnGeometry: true,
              where: sqlExpression
            };
            featureLayer.queryFeatures(query).then(function(result) {
              addGraphics(result, true);
            });
          }
    
  2. When the view is ready, call the queryFeatureLayer function and pass in the center of the view, 1500 as the distance in meters, and intersects as the spatial relationship operator to use. This will search for and display features in the center of the map.

          view.when(function(){
            queryFeatureLayer(view.center, 1500, "intersects");
          });
    
  3. Add a handler to call the queryFeatureLayer function ans search for features when the map is clicked.

          view.on("click", function(event){
            queryFeatureLayer(event.mapPoint, 1500, "intersects");
          });
    
  4. Run the code and click on the map to search and display features that are 1500 meters from the point. Notice that only the features that match the query are displayed.

Execute client-side queries and add graphics

You can perform client-side queries against features that are displayed in the view. To do so, the feature layer must be added to the map and the FeatureLayerView must be ready, in other words, the features and attributes have been loaded and can be queried.

  1. Define another function that accepts a point, distance to search, spatial relationship operator, and an optional SQL expression and builds a query that will return all of the fields and the geometry from the feature layer. Add code to add the featureLayer to the map if it isn't already present. Since features can only be queried after the layer is loaded, use whenLayerView to ensure the layer is ready and is not being updated. Use the queryFeatures method to execute the query. When the features are returned, pass the results to the addGraphics function. Learn more about the parameters you can use in the documentation.

          function queryFeatureLayerView(point, distance, spatialRelationship, sqlExpression) {
            // Add the layer if it is missing
            if (!map.findLayerById(featureLayer.id)) {
              featureLayer.outFields = ["*"];
              map.add(featureLayer,0);
            }
            // Set up the query
            var query = {
              geometry: point,
              distance: distance,
              spatialRelationship: spatialRelationship,
              outFields: ["*"],
              returnGeometry: true,
              where: sqlExpression
            };
            // Wait for the layerview to be ready and then query features
            view.whenLayerView(featureLayer).then(function(featureLayerView) {
              if (featureLayerView.updating) {
                var handle = featureLayerView.watch("updating", function(isUpdating){
                  if (!isUpdating) {
                    // Execute the query
                    featureLayerView.queryFeatures(query).then(function(result) {
                      addGraphics(result)
                    });
                    handle.remove();
                  }
                });
              } else {
                // Execute the query
                featureLayerView.queryFeatures(query).then(function(result) {
                  addGraphics(result);
                });
              }
            });
          }
    
  2. Update the view.when and view.on handlers to call the queryFeatureLayerView function and pass in the same parameters.

          view.when(function(){
            //*** UPDATE ***//
            //queryFeatureLayer(view.center, 1500, "intersects");
            queryFeatureLayerView(view.center, 1500, "intersects");
          });
    
          view.on("click", function(event){
            //*** UPDATE ***//
            //queryFeatureLayer(event.mapPoint, 1500, "intersects");
            queryFeatureLayerView(event.mapPoint, 1500, "intersects");
          });
    
  3. Run the code and click on the map to query and draw features that are 1500 meters from the point. Notice that all of the features draw because the layer is added to the map, but only the features returned from the query are added to the graphics layer.

Congratulations, you're done!

Your app should look something like this.

Challenge

Execute a spatial and attribute query

Beyond spatial queries, you can also perform an attribute or a combination of attribute and spatial search queries with the queryFeatures function. Define a sql variable and update the calls to queryFeatureLayer and queryFeatureLayerView to accept a SQL where clause and run the code again.

      var sql = "TRL_NAME like '%Canyon%'";

      view.when(function(){
        //queryFeatureLayer(view.center, 1500, "intersects")
        //*** UPDATE ***//
        queryFeatureLayerView(view.center, 1500, "intersects", sql)
      });

      view.on("click", function(event){
        //queryFeatureLayer(event.mapPoint, 1500, "intersects")
        //*** UPDATE ***//
        queryFeatureLayerView(event.mapPoint, 1500, "intersects", sql);
      });

Try clicking on the map to see the features selected. Both the SQL filter selected and the spatial query will be applied so you should only see trailheads that have "Canyon" in their name.

Find features and show a pop-up

Another form of querying is to use the hitTest method on the view to find features at a given screen location. After the view and FeatureLayerView are ready, use hitTest to find features and show a pop-up when the cursor is over a feature. hitTest returns features for all visible layers, so it is necessary to filter the results for the layer of interest. Also, add logic to only show a pop-up when a new feature is found.

Add the following code to show a pop-up as the cursor moves.

      //*** ADD ***//
      view.when(function(){
        view.whenLayerView(featureLayer).then(function(featureLayerView) {
          view.on("pointer-move", function(event){
            view.hitTest(event).then(function(response){
              // Only return features for the feature layer
              var feature = response.results.filter(function (result) {
               return result.graphic.layer === featureLayer;
              })[0].graphic;
              if (feature) {
                // Show popup for new features only
                if (!view.popup.features.length || view.popup.features.length && (view.popup.features[0].attributes.FID !== feature.attributes.FID)) {
                  view.popup.open({
                    title: feature.attributes.TRL_NAME,
                    content: "This a " + feature.attributes.PARK_NAME + " trail located in " + feature.attributes.CITY_JUR + ".",
                    location: feature.geometry
                  });
                }
              }
            });
          });
        });
      });

Content