Learn how to execute a spatial query to access polygon features from a feature service.
A feature layer can contain a large number of features stored in ArcGIS. To access a subset of these features, you can execute an SQL or spatial query, either together or individually. The results can contain the attributes, geometry, or both for each record. SQL and spatial queries are useful when a feature layer is very large and you want to access only a subset of its data.
In this tutorial, you sketch a feature on the map and use ArcGIS REST JS to perform a spatial query against the LA County Parcels hosted feature layer. The layer contains ±2.4 million features. The spatial query returns all of the parcels that intersect the sketched feature.
Prerequisites
An ArcGIS Location Platform or ArcGIS Online account.
Get the starter app
Select a type of authentication below and follow the steps to create a new application.
Set up authentication
Create developer credentials in your portal for the type of authentication you selected.
Set developer credentials
Use the API key or OAuth developer credentials created in the previous step in your application.
Get a Cesium ion access token
All Cesium applications must use an access token provided through Cesium ion. This token allows you to access assets such as Cesium World Terrain in your application.
-
Go to your Cesium ion dashboard to generate an access token. Copy the key to your clipboard.
-
Create a
cesium
variable and replaceAccess Token YOUR
with the access token you copied from the Cesium ion dashboard._CESIUM _ACCESS _TOKEN Use dark colors for code blocks <script> /* Use for API key authentication */ const accessToken = "YOUR_ACCESS_TOKEN"; // or /* Use for user authentication */ // const session = await arcgisRest.ArcGISIdentityManager.beginOAuth2({ // clientId: "YOUR_CLIENT_ID", // Your client ID from OAuth credentials // redirectUri: "YOUR_REDIRECT_URL", // The redirect URL registered in your OAuth credentials // portal: "YOUR_PORTAL_URL" // Your portal URL // }) // const accessToken = session.token; Cesium.ArcGisMapService.defaultAccessToken = accessToken; const cesiumAccessToken = "YOUR_CESIUM_ACCESS_TOKEN"; </script>
-
Configure
Cesium.
with the Cesium access token to validate the application.Ion.default Access Token Use dark colors for code blocks <script> /* Use for API key authentication */ const accessToken = "YOUR_ACCESS_TOKEN"; // or /* Use for user authentication */ // const session = await arcgisRest.ArcGISIdentityManager.beginOAuth2({ // clientId: "YOUR_CLIENT_ID", // Your client ID from OAuth credentials // redirectUri: "YOUR_REDIRECT_URL", // The redirect URL registered in your OAuth credentials // portal: "YOUR_PORTAL_URL" // Your portal URL // }) // const accessToken = session.token; Cesium.ArcGisMapService.defaultAccessToken = accessToken; const cesiumAccessToken = "YOUR_CESIUM_ACCESS_TOKEN"; Cesium.Ion.defaultAccessToken = cesiumAccessToken; </script>
Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CesiumJS: Query a feature layer (spatial)</title>
<!-- Include the CesiumJS JavaScript and CSS files -->
<script src="https://cesium.com/downloads/cesiumjs/releases/1.121/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.121/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<script src="https://unpkg.com/@esri/arcgis-rest-request@4/dist/bundled/request.umd.js"></script>
<script
src="https://unpkg.com/@esri/arcgis-rest-feature-service@4/dist/bundled/feature-service.umd.js"></script>
<script src="https://unpkg.com/@terraformer/arcgis@2.0.7/dist/t-arcgis.umd.js"></script>
<style>
html,
body,
#cesiumContainer {
width: 100%;
height: 100%;
padding: 0px;
margin: 0px;
}
#mode-select {
position: absolute;
top: 8px;
left: 8px;
padding: 4px 8px;
font-size: 16px;
border-radius: 0;
/* Causes more uniform appearance across browsers. */
}
</style>
</head>
<body>
<div id="cesiumContainer"></div>
<select id="mode-select">
<option value="">Choose a type of feature to draw...</option>
<option value="Point">Draw point</option>
<option value="Polyline">Draw line</option>
<option value="Polygon" selected>Draw polygon</option>
</select>
<script>
/* Use for API key authentication */
const accessToken = "YOUR_ACCESS_TOKEN";
// or
/* Use for user authentication */
// const session = await arcgisRest.ArcGISIdentityManager.beginOAuth2({
// clientId: "YOUR_CLIENT_ID", // Your client ID from OAuth credentials
// redirectUri: "YOUR_REDIRECT_URL", // The redirect URL registered in your OAuth credentials
// portal: "https://www.arcgis.com/sharing/rest" // Your portal URL
// })
// const accessToken = session.token;
Cesium.ArcGisMapService.defaultAccessToken = accessToken;
const authentication = arcgisRest.ApiKeyManager.fromKey(accessToken);
const cesiumAccessToken = "YOUR_CESIUM_ACCESS_TOKEN";
Cesium.Ion.defaultAccessToken = cesiumAccessToken;
const arcGisImagery = Cesium.ArcGisMapServerImageryProvider.fromBasemapType(Cesium.ArcGisBaseMapType.SATELLITE, {
enablePickFeatures: false
});
const viewer = new Cesium.Viewer("cesiumContainer", {
baseLayer: Cesium.ImageryLayer.fromProviderAsync(arcGisImagery),
timeline: false,
animation: false,
geocoder: false
});
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(-118.80624, 34.008, 3000),
orientation: {
heading: Cesium.Math.toRadians(0.0),
pitch: Cesium.Math.toRadians(-70.0),
}
});
// Add Esri attribution
// Learn more in https://esriurl.com/attribution
const poweredByEsri = new Cesium.Credit("Powered by <a href='https://www.esri.com/en-us/home' target='_blank'>Esri</a>", true)
viewer.creditDisplay.addStaticCredit(poweredByEsri);
const layerName = "LA_County_Parcels";
const layerURL = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/" + layerName + "/FeatureServer/0";
// Attribution text retrieved from https://arcgis.com/home/item.html?id=a6fdf2ee0e454393a53ba32b9838b303
viewer.creditDisplay.addStaticCredit(new Cesium.Credit("County of Los Angeles Office of the Assessor", false));
// Draw features
//
if (!viewer.scene.pickPositionSupported) {
window.alert("This browser does not support pickPosition.");
}
viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
);
function createPoint(worldPosition) {
const point = viewer.entities.add({
position: worldPosition,
point: {
color: Cesium.Color.WHITE,
pixelSize: 5,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
},
});
return point;
}
let drawingMode;
function drawShape(positionData) {
let shape;
if (drawingMode === "Polyline") {
shape = viewer.entities.add({
polyline: {
positions: positionData,
clampToGround: true,
width: 3,
},
});
}
else if (drawingMode === "Polygon") {
shape = viewer.entities.add({
polygon: {
hierarchy: positionData,
outline: true,
outlineWidth: 10,
material: new Cesium.ColorMaterialProperty(
Cesium.Color.WHITE.withAlpha(0.4)
),
},
});
}
return shape;
}
let activeShapePoints = [];
let activeShape;
let floatingPoint;
let newShape = true;
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
handler.setInputAction(function (event) {
if (!drawingMode) return;
if (newShape) {
viewer.entities.removeAll();
newShape = false;
}
const earthPosition = viewer.camera.pickEllipsoid(event.position);
// `earthPosition` will be undefined if our mouse is not over the globe.
if (Cesium.defined(earthPosition)) {
if (drawingMode === "Point") {
activeShapePoints.push(earthPosition);
createPoint(earthPosition);
performQuery()
}
else {
if (activeShapePoints.length === 0) {
floatingPoint = createPoint(earthPosition);
activeShapePoints.push(earthPosition);
const dynamicPositions = new Cesium.CallbackProperty(function () {
if (drawingMode === "Polygon") {
return new Cesium.PolygonHierarchy(activeShapePoints);
}
return activeShapePoints;
}, false);
activeShape = drawShape(dynamicPositions);
}
activeShapePoints.push(earthPosition);
createPoint(earthPosition);
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
handler.setInputAction(function (event) {
if (Cesium.defined(floatingPoint)) {
const newPosition = viewer.camera.pickEllipsoid(event.endPosition);
if (Cesium.defined(newPosition)) {
floatingPoint.position.setValue(newPosition);
activeShapePoints.pop();
activeShapePoints.push(newPosition);
}
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
//
// Process features
//
function getArcGISFeature(points) {
//convert each point to cartesian latlng
const degrees = []
for (point of points) {
const cartographic = Cesium.Cartographic.fromCartesian(point);
degrees.push([Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude)]);
}
let coords;
let type = drawingMode;
if (drawingMode === "Point") {
coords = degrees[0];
}
else if (drawingMode === "Polyline") {
type = "LineString";
coords = degrees;
}
else {
coords = [degrees];
}
const result = Terraformer.geojsonToArcGIS({
"coordinates": coords,
"type": type
})
return result;
}
function executeQuery(geometry) {
arcgisRest.queryFeatures({
url: layerURL,
authentication,
f: "geojson",
geometry: geometry,
geometryType: "esriGeometry" + drawingMode,
inSR: 4326,
spatialRel: "esriSpatialRelIntersects",
returnGeometry: true
})
.then((response) => {
Cesium.GeoJsonDataSource.load(response).then((data) => {
viewer.dataSources.add(data);
})
})
}
function performQuery() {
viewer.dataSources.removeAll();
const geometry = getArcGISFeature(activeShapePoints);
executeQuery(geometry);
drawShape(activeShapePoints);
terminateShape()
}
// Redraw the shape so it's not dynamic and remove the dynamic shape.
function terminateShape() {
viewer.entities.remove(floatingPoint);
viewer.entities.remove(activeShape);
floatingPoint = undefined;
activeShape = undefined;
activeShapePoints = [];
newShape = true;
}
// Event listeners
handler.setInputAction(function (event) {
if (drawingMode === "Point" || !drawingMode) return;
activeShapePoints.pop();
performQuery();
}, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
const select = document.getElementById("mode-select");
select.addEventListener("change", () => {
terminateShape();
viewer.entities.removeAll();
drawingMode = select.value;
})
const startCoords = [[[-118.80364, 34.02090], [-118.79641, 34.01991], [-118.79818, 34.01585], [-118.80347, 34.01620]]];
drawingMode = 'Polygon';
// Draw initial shape
const startPoints = []
for (coord of startCoords[0]) {
startPoints.push(Cesium.Cartesian3.fromDegrees(coord[0], coord[1]))
}
drawShape(startPoints)
// Query initial shape
const startShape = Terraformer.geojsonToArcGIS({
"coordinates": startCoords,
"type": drawingMode
})
executeQuery(startShape);
terminateShape();
</script>
</body>
</html>
What's next?
Learn how to use additional ArcGIS location services in these tutorials: