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.
Steps
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 <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_URI", // 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 <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_URI", // 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>
Add references to ArcGIS REST JS
-
In the
<head
element, reference the> feature-service
andrequest
packages from ArcGIS REST JS.<script src="https://cesium.com/downloads/cesiumjs/releases/1.125/Build/Cesium/Cesium.js"></script> <link href="https://cesium.com/downloads/cesiumjs/releases/1.125/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>
-
In the
<body
, create an> arcgis
using your access token to authenticate requests to the feature service.Rest. Api Key Manager /* 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_URI", // The redirect URL registered in your OAuth credentials // portal: "YOUR_PORTAL_URL" // Your portal URL // }) // const accessToken = session.token; Cesium.ArcGisMapService.defaultAccessToken = accessToken; const authentication = arcgisRest.ApiKeyManager.fromKey(accessToken);
Create a drawing mode selector
-
Create a
<select
HTML element to provide a list of spatial operations for drawing features such as points, lines, or polygons.> <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>
-
Style the
<select
element so that it displays properly in your application.> <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; } </style>
Add a feature layer
Reference the LA County Parcels hosted feature layer in your application to perform a spatial query against. The layer contains ±2.4 million features. The spatial query returns all of the parcels that intersect the sketched feature.
-
Copy the URL of the LA County Parcels feature layer.
const layerName = "LA_County_Parcels"; const layerURL = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/" + layerName + "/FeatureServer/0";
-
Add the data attribution for the feature layer source.
- Go to the LA County Parcels item.
- Scroll down to the Credits (Attribution) section and copy its value.
- Create an
attribution
property and paste the attribution value from the item.// 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));
Create drawing tools
A spatial query is performed by passing a geometry object to a hosted feature layer. Add tools that allow the user of your app to draw point, line, and polygon geometries on the map.
-
Add methods to draw points, lines, and polygons on the map. The methods will be called based on the drawing mode the user selects.
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; }
-
Set up event handlers to initiate drawing in the selected mode (point, polyline, or polygon) when the user clicks. Dynamically update the shape based on mouse clicks and movements.
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)) { // Points if (drawingMode === "Point") { activeShapePoints.push(earthPosition); createPoint(earthPosition); performQuery() } // Lines and polygons 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);
-
Add code to terminate the drawing when the user double clicks.
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); // 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(); }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); const select = document.getElementById("mode-select"); select.addEventListener("change", () => { terminateShape(); viewer.entities.removeAll(); drawingMode = select.value; })
-
Create a helper function that converts the resulting geometries into ArcGIS format. This will be used to query the feature layer.
// 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; }
Query the feature layer
-
Create an
execute
function that usesQuery arcgis
to query theRest.query Features L
feature layer with a geometry. Set theA County Parcels spatial
parameter of the request toRel esri
to find parcels that intersect the geometry.Spatial Rel Intersects function executeQuery(geometry) { arcgisRest.queryFeatures({ url: layerURL, authentication, f: "geojson", geometry: geometry, geometryType: "esriGeometry" + drawingMode, inSR: 4326, spatialRel: "esriSpatialRelIntersects", returnGeometry: true }) }
-
Call
execute
using the drawn geometry when the user is finished drawing.Query } 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);
-
Load the resulting features onto the map as a
Cesium.
.Geo Json Data Source 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); }) }) }
Run the app
Run the app.
You should be able to draw features on the map and perform spatial queries against the feature layer.What's next?
Learn how to use additional ArcGIS location services in these tutorials: