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 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.
Open the JavaScript Starter App on CodePen.
In CodePen, click Fork and save the pen as ArcGIS JavaScript Tutorials: Query a feature layer
.
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 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) {
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);
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);
});
}
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 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);
});
}
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");
});
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.
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, 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);
});
}
});
}
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");
});
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.
Your app should look something like this.
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.
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
});
}
}
});
});
});
});