This walk-through shows how to find features based on a spatial relationship. A common use of this approach is to find features that intersect a shape. The shape can be a point, line or polygon. For instance, you could find the features in a layer of store locations that intersect an input shape of a sales territory. The shapes you search with may be ones you have in advance, or they might be obtained through a separate query done in the page before the spatial query.
The QueryTask used in this walk-through can also query based on attribute values, by setting a Where clause in the Query used in the task. The QueryTask can query by spatial location, by attributes of features, or both. In this walk-though we will show the option of querying by both location and attributes.
This walk-through parallels the example "Searching for things within an area" in the Interactive SDK, with some changes to illustrate functionality in the API. As usual, the complete code appears at the end of the walk-through.
Querying features that intersect a polygon
This walk-through searches for features in a map layer that intersect a polygon. In this example we hard-code the polygon geometry, but in real applications you might get the shape from the user by clicking on the map, or from another operation such as an identify, query or geocoding (finding the location of an address) operation.
- Create a web page with a basic Virtual Earth map. You may use the walk-through in this Help, Creating a base map with Virtual Earth. Update the <title> of the page as desired. Save the file, for example to ArcGISVESpatialQuery1.htm.
- Add a reference to the ArcGIS JavaScript client script. This is required
for every page that uses the ArcGIS JavaScript Extension for Virtual Earth. Add this in the header section,
just below the reference to the Virtual Earth script.
CopyArcGIS JavaScript Extension for Virtual Earth script reference<script src="http://serverapi.arcgisonline.com/jsapi/ve/?v=1.4" type="text/javascript"></script>
- Create the polygon we will use for the query, and add it to the map for visual purposes.
Modify the existing script block that contains the OnPageLoad function so that it
has the following format. Declare the polygon outside the function, since we'll
reuse it to perform the query. We create the polygon with a series of VELatLong
items. We add a description that appears when the user hovers over the pushpin that
VE adds in the middle of the polygon. Then we add the polygon to the map. Notice that we also change the starting location and zoom
level.
CopyAdd polygon to map on startupvar map = null; var polygon = null; function OnPageLoad() { var centerat = new VELatLong(33.5230788089042, -116.50); map = new VEMap('mymap'); map.LoadMap(centerat,7,VEMapStyle.Aerial,false); // create query shape and add it to the map map.EnableShapeDisplayThreshold(false); polygon = new VEShape(VEShapeType.Polygon, [new VELatLong(32.28713263261637, -118.0478515625), new VELatLong(33.73347670599253, -118.17993164062501), new VELatLong(34.41145445426761, -117.010009765625011), new VELatLong(34.28906131584994, -114.268798828125), new VELatLong(32.53755174676899, -114.36767578125003) ]); polygon.SetTitle("Selection Geometry"); polygon.SetDescription("Geometry: Polygon<br/>Spatial Relationship: Intersects"); map.AddShape(polygon); }
- In the body of the page, add a button and an empty div element, which we'll use
to display messages during the query. The user will click the button to perform the search.
In the onclick of the button, we call a function that we will code next.
CopyAdding find buttonFind counties that intersect the polygon and being with "San": <input type="button" id="doQuery" onclick="ExecuteTask()" style="width:75px;" value="Find" /> <div id="job"></div>
- In the script block, just after the close of the existing OnPageLoad function, add
the ExecuteTask function. In this function, create a new
Query object and set its
properties:
- Geometry - set to the polygon we created when the page loaded.
- SpatialRelationship - use esriSpatialRelIntersects to indicate that we want to find features that intersect the polygon at any location. This property may be any of the esriSpatialRelXxxxx values listed in in the API reference for SpatialRelationship. See the Discussion for more information.
- Where - we also add an attribute criterion: counties must begin with "San". The % is the wildcard SQL character.
- OutFields - we want the values for these fields returned in the results. Remember you can find information on a layer's fields in the Services Directory.
CopyExecuting the queryfunction ExecuteTask() { var spatialQueryParams = new ESRI.ArcGIS.VE.Query(); spatialQueryParams.Geometry = polygon; spatialQueryParams.SpatialRelationship = "esriSpatialRelIntersects"; spatialQueryParams.Where = "NAME like 'San%25'"; spatialQueryParams.OutFields = ["NAME","STATE_NAME","AREA","SQMI","POP2000","POP2007","POP00_SQMI"]; spatialQueryTask = new ESRI.ArcGIS.VE.QueryTask(); spatialQueryTask.Url ="http://serverapi.arcgisonline.com/arcgis/rest/services/USAData/CensusData/MapServer/2"; spatialQueryTask.Execute(spatialQueryParams, addShapes); var div = $get("job"); div.innerHTML = 'Matching features within shape . . .'; div.style.cursor = "wait"; }
- Add the function that will receive the results of the query. The addShapes
function
takes the results as its argument. The results are an instance of the
FeatureSet class. After checking for errors, the function retrieves the
Features returned,
which are an array of GraphicFeature
objects. We loop through the features and get the
Shapes of each, which contains one or more VEShapes. We add each to the
map after setting a line and fill color.
CopyFunction handling query resultsfunction addShapes(data) { var div = $get("job"); div.style.cursor = "default"; var err = data.Error; if (err!=null) div.innerHTML = err.message; else { var rs = data.Features; var color = new VEColor(255,0,0,.5); var lineColor = new VEColor(0,0,0,.5); if(rs) { for(var i = 0; i < rs.length; i++) { var result = rs[i]; var shapes = result.Shapes; for (ss=0;ss<shapes.length;ss++) { var shape = shapes[ss]; shape.SetLineColor(lineColor); shape.SetFillColor(color); shape.Show(); map.AddShape(shape); } } } div.innerHTML = "Completed."; } }
- Save the page, and open it in a browser. It should load the VE map, with the polygon displaying in the area of southern California. Click the Find button. After a few moments, you should see two counties highlighted: San Diego and San Bernardino. Hover over each one's pushpin to view the attributes retrieved for each.
Discussion
With ArcGIS Server, you can query using sophisticated spatial relationships, including:
- Intersection (esriSpatialRelIntersects): geometry of features intersect in any way; for example, find the road segments that cross a river, or the zip codes that intersect a sales territory.
- Touches (esriSpatialRelTouches): features share geometry or border; for example, find the states that border a specified state.
- Contains (esriSpatialRelContains): features are completely within the search geometry; for example, find the lakes that are wholly within a county (excludes lakes extending outside the county).
For discussion and illustrations on spatial operators, see the ESRI Developer Network page on esriSpatialRelEnum Constants, and also Using Select By Location in the ArcGIS Desktop Help.
Note that the QueryTask does not support searching around the input geometry--it only searches for features touching, intersecting or within the geometry. If you need to expand the search to the area around the point(s), line or polygon shape, you have two options: either use the IdentifyTask, which supports a tolerance around the geometry (see the walk-through Identify or find features near a location), or use the Geometry service to create a buffer polygon around the shape, and then use the buffer polygon as the input to the QueryTask. See the sample "Querying data using a buffer" in the Interactive SDK for an example of this second approach. The second approach allows you to simultaneously search by location and by attributes, where the location is a distance around a point, line or polygon.
Complete code for this walkthrough
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>Spatial query of an ArcGIS Server map</title> <script src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2" type="text/javascript"></script> <script src="http://serverapi.arcgisonline.com/jsapi/ve/?v=1.4" type="text/javascript"></script> <script language="javascript" type="text/javascript" > var map = null; var polygon = null; function OnPageLoad() { var centerat = new VELatLong(33.5230788089042, -116.50); map = new VEMap('mymap'); map.LoadMap(centerat,7,VEMapStyle.Aerial ,false); // create query shape and add it to the map map.EnableShapeDisplayThreshold(false); polygon = new VEShape(VEShapeType.Polygon, [new VELatLong(32.28713263261637, -118.0478515625), new VELatLong(33.73347670599253, -118.17993164062501), new VELatLong(34.41145445426761, -117.010009765625011), new VELatLong(34.28906131584994, -114.268798828125), new VELatLong(32.53755174676899, -114.36767578125003) ]); polygon.SetTitle("Selection Geometry"); polygon.SetDescription("Geometry: Polygon<br/>Spatial Relationship: Contains"); map.AddShape(polygon); } function ExecuteTask(theForm) { var spatialQueryParams = new ESRI.ArcGIS.VE.Query(); spatialQueryParams.Geometry = polygon; spatialQueryParams.SpatialRelationship = "esriSpatialRelIntersects"; spatialQueryParams.Where = "NAME like 'San%25'"; spatialQueryParams.OutFields = ["NAME","STATE_NAME","AREA","SQMI","POP2000","POP2007","POP00_SQMI"]; spatialQueryTask = new ESRI.ArcGIS.VE.QueryTask(); spatialQueryTask.Url ="http://serverapi.arcgisonline.com/arcgis/rest/services/USAData/CensusData/MapServer/2"; spatialQueryTask.Execute(spatialQueryParams, addShapes); var div = $get("job"); div.innerHTML = 'Matching features within shape . . .'; div.style.cursor = "wait"; } function addShapes(data) { var div = $get("job"); div.style.cursor = "default"; var err = data.Error; if (err!=null) div.innerHTML = err.message; else { var rs = data.Features; var color = new VEColor(255,0,0,.5); var lineColor = new VEColor(0,0,0,.5); if(rs) { for(var i = 0; i < rs.length; i++) { var result = rs[i]; var shapes = result.Shapes; for (ss=0;ss<shapes.length;ss++) { var shape = shapes[ss]; shape.SetLineColor(lineColor); shape.SetFillColor(color); shape.Show(); map.AddShape(shape); } } } div.innerHTML = "Completed."; } } </script> </head> <body onload="OnPageLoad()" > <form action="" > <div id='mymap' style="position:relative; width: 750px; height: 500px;"></div> Find counties that intersect the polygon and begin with "San": <input type="button" id="doQuery" onclick="ExecuteTask()" style="width:75px;" value="Find" /> <div id="job"></div> </form> </body> </html>