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 on
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 aFeatureLayer
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 aFeatureLayerView
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
Open the JavaScript Starter App on CodePen.
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.
In the
require
statement, add a reference to theFeatureLayer
,GraphicsLayer
andGraphic
modules.require([ "esri/Map", "esri/views/MapView", "esri/layers/FeatureLayer", "esri/layers/GraphicsLayer", "esri/Graphic" ],function(Map, MapView, FeatureLayer, GraphicsLayer, Graphic) {
At the end of the code in the main
function
, create aFeatureLayer
and set theurl
to access and query the trailheads feature layer, and then create aGraphicsLayer
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
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 withremoveAll
. Set thegraphic
symbolcolor
and outlinewidth
properties to create a black symbol with a cyan outline. Add a simplepopupTemplate
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.
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 theaddGraphics
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); }); }
When the
view
is ready, call thequeryFeatureLayer
function and pass in the center of the view,1500
as the distance in meters, andintersects
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"); });
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"); });
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.
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, usewhenLayerView
to ensure the layer is ready and is not being updated. Use thequeryFeatures
method to execute the query. When the features are returned, pass the results to theaddGraphics
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); }); } }); }
Update the
view.when
andview.on
handlers to call thequeryFeatureLayerView
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"); });
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
});
}
}
});
});
});
});