11. Working with Spatial Data with ArcGIS JS API#
In this section, we’ll dive deeper into working with spatial data using the ArcGIS JavaScript API
.
We’ll cover spatial queries
, filtering
, and understanding the data structure
of web maps and feature layers on ArcGIS Online
.
11.1. Performing Spatial Queries Using SQL#
In this section, we’ll explore how to perform spatial queries
using SQL
within a web map using the ArcGIS JavaScript API.
This involves setting up a
feature layer
, definingSQL where clauses
for filtering data, and executing queries to display results on the map.
11.1.1. Feature Layer and Popup Template#
Define the
feature laye
r with apopup
template to display information about each feature.const trailsUrl = "https://services2.arcgis.com/VNo0ht0YPXJoI4oE/arcgis/rest/services/Trials/FeatureServer/0"; const popupTemplate = { title: "Object ID: {OBJECTID}", fieldInfos: [ { fieldName: "trailName", label: "trailName", format: { places: 0, digitSeperator: false }, }, { fieldName: "Shape__Length", label: "Trail_Length", format: { places: 0, digitSeperator: true }, }, { fieldName: "manageOrg", label: "manageOrg", format: { places: 0, digitSeperator: false }, }, { fieldName: "trailStatus", label: "trailStatus", format: { places: 0, digitSeperator: false }, }, ], content: "<b>Trail Name:</b> {trailName} <br>\ <b>Trail Length:</b> {Shape__Length} Meter<br>\ <b>Management Organization:</b> {manageOrg}<br>\ <b>Trail Status:</b> {trailStatus}", }; const featureLayer = new FeatureLayer({ url: trailsUrl, popupTemplate: popupTemplate, }); map.add(featureLayer);
11.1.2. Setting Up the QueryTask and Query#
Define the
QueryTask
andQuery
to executespatial queries
usingSQL
.const qTask = new QueryTask({ url: trailsUrl }); const params = new Query({ returnGeometry: true, outFields: ["*"], });
11.1.3. Defining SQL Where Clauses#
Create an array of
SQL
where clauses for the user to select from.This array will be used to filter features based on different criteria.
const querySQL = [ "Choose a SQL where clause...", "hike = 'No'", "hike = 'Yes'", "bike = 'No'", "bike = 'Yes'", "horse = 'No'", "horse = 'Yes'", ]; let whereClause = querySQL[0]; // Add SQL UI const select = document.createElement("select"); select.setAttribute("class", "esri-widget esri-select"); select.setAttribute( "style", "width: 300px; font-family: 'Avenir Next'; font-size: 1em" ); querySQL.forEach((query) => { let option = document.createElement("option"); option.innerHTML = query; option.value = query; select.appendChild(option); }); view.ui.add(select, "top-right");
11.1.4. Executing the SQL Query#
Listen for changes in the selection and execute the
query
based on the selectedSQL where clause
.select.addEventListener("change", (event) => { whereClause = event.target.value; executeQuery(whereClause); }); function executeQuery(whereClause) { params.where = whereClause; qTask .execute(params) .then((results) => { console.log("Feature count: " + results.features.length); view.graphics.removeAll(); view.graphics.addMany(results.features); }) .catch((error) => { console.error("Error performing query:", error); }); }
11.1.5. Additional Methods and Best Practices#
Setting Up Spatial Relationships
In addition to
filtering by attributes
, you can alsofilter by spatial relationships
, such as within a given area orintersecting
another feature.Here’s an example of setting up a spatial relationship query:
const spatialQueryParams = new Query({ returnGeometry: true, outFields: ["*"], spatialRelationship: "intersects", // Can be intersects, contains, etc. geometry: someGeometry, // Geometry to compare against }); qTask.execute(spatialQueryParams).then((results) => { console.log("Features found: ", results.features); });
Improving Query Performance
For better performance, especially with large datasets:
Limit the
fields
you retrieve usingoutFields
.Use
indexed
fields in yourSQL where clause
.Paginate results if dealing with large numbers of features.
params.outFields = ["OBJECTID", "trailName", "Shape__Length"]; params.where = "Shape__Length > 1000"; // Example where clause to filter long trails qTask.executeForCount(params).then((count) => { console.log("Total features found: ", count); });
11.2. Query a Feature Layer Using Sketch Widgets#
In this tutorial, we will learn how to use the
Sketch widget
to create geometries and performspatial queries
on afeature laye
r using theArcGIS JavaScript API
.This tutorial will guide you through setting up the map, adding a Sketch widget, and performing queries based on
user-drawn geometries
.In this tutorial, we will review again what we learn in pervious sections
11.2.1. Setting Up the Environment#
First, ensure you have the
basic HTML structure
and include the necessary ArcGIS JavaScript and CSS files.<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" /> <title> ArcGIS Maps SDK for JavaScript Tutorials: Query a feature layer (spatial) </title> <style> html, body, #viewDiv { padding: 0; margin: 0; height: 100%; width: 100%; } </style> <link rel="stylesheet" href="https://js.arcgis.com/4.29/esri/themes/light/main.css" /> <script src="https://js.arcgis.com/4.29/"></script> </head> <body> <div id="viewDiv"></div> </body> </html>
11.2.2. Adding the Map and View#
Create the
map
andmap view
with a specified center andzoom level
.require([ "esri/config", "esri/Map", "esri/views/MapView", "esri/widgets/Sketch", "esri/layers/GraphicsLayer", "esri/layers/FeatureLayer", ], function (esriConfig, Map, MapView, Sketch, GraphicsLayer, FeatureLayer) { esriConfig.portalUrl = "https://jsapi.maps.arcgis.com"; const map = new Map({ basemap: "topo-vector", // basemap styles service }); const view = new MapView({ container: "viewDiv", map: map, center: [-118.80543, 34.03], // Longitude, latitude zoom: 13, }); // Continue with the next steps... });
11.2.3. Adding the Feature Layer#
Add the
feature layer
that you want to query to themap
.const parcelLayer = new FeatureLayer({ url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/LA_County_Parcels/FeatureServer/0", }); map.add(parcelLayer);
11.2.4. Adding the Sketch Widget#
Add a
GraphicsLayer
to themap
for thesketch widget
to use.Then, create and configure the
sketch widget
.const graphicsLayerSketch = new GraphicsLayer(); map.add(graphicsLayerSketch); const sketch = new Sketch({ layer: graphicsLayerSketch, view: view, creationMode: "update", // Auto-select }); view.ui.add(sketch, "top-right");
11.2.5. Querying the Feature Layer#
Set up the
event listeners
for thesketch widget
to handle the creation and modification of geometries.These geometries will be used to query the feature layer.
Define the Query Object
The
parcelQuery
object defines theparameters
for querying thefeature layer
. Key properties include:spatialRelationship
: Specifies the spatial relationship to apply. In this case, intersects means we are looking for features that intersect the drawn geometry.geometry
: The geometry drawn by the user, which will be used as thespatial filter
.outFields
: An array ofattribute field names
to include in the query results. This determines which attributes of the features will be returned.returnGeometry
: A boolean that indicates whether the geometry of the features should be returned.
function queryFeaturelayer(geometry) { const parcelQuery = { spatialRelationship: "intersects", // Relationship operation to apply geometry: geometry, // The sketch feature geometry outFields: ["APN", "UseType", "TaxRateCity", "Roll_LandValue"], // Attributes to return returnGeometry: true, };
Executing the Query:
Call
queryFeatures
on theparcelLayer
with theparcelQuery
object.Handle the
promise
returned byqueryFeatures
to process the results or catch any errors.
// Execute the query parcelLayer.queryFeatures(parcelQuery) .then((results) => { console.log("Feature count: " + results.features.length); displayResults(results); }) .catch((error) => { console.log(error); }); }
11.2.6. Displaying the Query Results#
Define a function to display the results of the query.
This function will create graphics for each
feature
returned by the query and add them to themap view
.
Symbol Definition
Define a symbol to style the features. Here, we use a
simple-fill symbol
with asemi-transparent
fill color and a white outline.
function displayResults(results) { const symbol = { type: "simple-fill", color: [150, 10, 100, 0.5], outline: { color: "white", width: 0.5, }, };
Popup Template
Define a
popup template
to display attribute information when a feature is clicked.The
title
andcontent
properties of the template use attribute values.
const popupTemplate = { title: "Parcel {APN}", content: "Type: {UseType} <br> Land value: {Roll_LandValue} <br> Tax Rate City: {TaxRateCity}", };
Assign Symbol and Popup Template, Clear Previous Graphics and Add New Graphics
For each
feature
in the results, assign the definedsymbol
andpopup
template.
// Set symbol and popup results.features.map((feature) => { feature.symbol = symbol; feature.popupTemplate = popupTemplate; return feature; }); // Clear display view.closePopup(); view.graphics.removeAll(); // Add features to graphics layer view.graphics.addMany(results.features);
11.2.7. Final Script#
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
<title>ArcGIS Maps SDK for JavaScript Tutorials: Query a feature layer (spatial)</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.29/esri/themes/light/main.css">
<script src="https://js.arcgis.com/4.29/"></script>
<script>
require([
"esri/config",
"esri/Map",
"esri/views/MapView",
"esri/widgets/Sketch",
"esri/layers/GraphicsLayer",
"esri/layers/FeatureLayer"
], function (esriConfig, Map, MapView, Sketch, GraphicsLayer, FeatureLayer) {
esriConfig.portalUrl = "https://jsapi.maps.arcgis.com";
const map = new Map({
basemap: "topo-vector" // basemap styles service
});
const view = new MapView({
container: "viewDiv",
map: map,
center: [-118.80543, 34.03000], //Longitude, latitude
zoom: 13
});
// Add sketch widget
const graphicsLayerSketch = new GraphicsLayer();
map.add(graphicsLayerSketch);
const sketch = new Sketch({
layer: graphicsLayerSketch,
view: view,
creationMode: "update" // Auto-select
});
view.ui.add(sketch, "top-right");
// Add sketch events to listen for and execute query
sketch.on("update", (event) => {
// Create
if (event.state === "start") {
queryFeaturelayer(event.graphics[0].geometry);
}
if (event.state === "complete") {
graphicsLayerSketch.remove(event.graphics[0]); // Clear the graphic when a user clicks off of it or sketches new one
}
// Change
if (event.toolEventInfo && (event.toolEventInfo.type === "scale-stop" || event.toolEventInfo.type === "reshape-stop" || event.toolEventInfo.type === "move-stop")) {
queryFeaturelayer(event.graphics[0].geometry);
}
});
// Reference query layer
const parcelLayer = new FeatureLayer({
url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/LA_County_Parcels/FeatureServer/0",
});
map.add(parcelLayer);
function queryFeaturelayer(geometry) {
const parcelQuery = {
spatialRelationship: "intersects", // Relationship operation to apply
geometry: geometry, // The sketch feature geometry
outFields: ["APN", "UseType", "TaxRateCity", "Roll_LandValue"], // Attributes to return
returnGeometry: true
};
parcelLayer.queryFeatures(parcelQuery)
.then((results) => {
console.log("Feature count: " + results.features.length)
displayResults(results);
}).catch((error) => {
console.log(error);
});
}
// Show features (graphics)
function displayResults(results) {
// Create a blue polygon
const symbol = {
type: "simple-fill",
color: [150, 10, 100, 0.5],
outline: {
color: "white",
width: .5
},
};
const popupTemplate = {
title: "Parcel {APN}",
content: "Type: {UseType} <br> Land value: {Roll_LandValue} <br> Tax Rate City: {TaxRateCity}"
};
// Set symbol and popup
results.features.map((feature) => {
feature.symbol = symbol;
feature.popupTemplate = popupTemplate;
return feature;
});
// Clear display
view.closePopup();
view.graphics.removeAll();
// Add features to graphics layer
view.graphics.addMany(results.features);
}
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>