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
You need an ArcGIS Location Platform or ArcGIS Online account.
Steps
Get the starter app
Select a type of authentication and follow the steps to create a new app.
Choose API key authentication if you:
- Want the easiest way to get started.
- Want to build public applications that access ArcGIS Location Services and secure items.
- Have an ArcGIS Location Platform or ArcGIS Online account.
Choose user authentication if you:
- Want to build private applications.
- Require application users to sign in with their own ArcGIS account and access resources their behalf.
- Have an ArcGIS Online account.
To learn more about both types of authentication, go to Authentication.
Set up authentication
Set developer credentials
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
cesiumvariable and replaceAccess Token YOURwith 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_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 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_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
<headelement, reference the> feature-serviceandrequestpackages from ArcGIS REST JS.Use dark colors for code blocks <script src="https://cesium.com/downloads/cesiumjs/releases/1.135/Build/Cesium/Cesium.js"></script> <link href="https://cesium.com/downloads/cesiumjs/releases/1.135/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> arcgisusing your access token to authenticate requests to the feature service.Rest. Api Key Manager Use dark colors for code blocks /* 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
<selectHTML element to provide a list of spatial operations for drawing features such as points, lines, or polygons.> Use dark colors for code blocks <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
<selectelement so that it displays properly in your application.> Use dark colors for code blocks <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.
Use dark colors for code blocks 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 Acknowledgments section and copy its value.
- Paste the copied value to the
creditproperty.Use dark colors for code blocks // 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.
Use dark colors for code blocks 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.
Use dark colors for code blocks 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(); } 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.
Use dark colors for code blocks 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.
Use dark colors for code blocks // 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
executefunction that usesQuery arcgisto query theRest.query Features Lfeature layer with a geometry. Set theA County Parcels spatialparameter of the request toRel esrito find parcels that intersect the geometry.Spatial Rel Intersects Use dark colors for code blocks function executeQuery(geometry) { arcgisRest.queryFeatures({ url: layerURL, authentication, f: "geojson", geometry: geometry, geometryType: "esriGeometry" + drawingMode, inSR: 4326, spatialRel: "esriSpatialRelIntersects", returnGeometry: true }) } -
Call
executeusing the drawn geometry when the user is finished drawing.Query Use dark colors for code blocks } 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 Use dark colors for code blocks 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 location services in these tutorials:


