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.

  1. 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.
  2. 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>
  3. 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 startup
    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: Intersects");
        map.AddShape(polygon); 
    }
  4. 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 button
    Find 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>
  5. 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.
    Then we create the QueryTask, and set its URL to the map service layer to query. The "2" in the URL indicates the ID of the map layer. See the information on the Services Directory for obtaining layer information. Then we execute the task using the Query parameters. The second argument in the execute method is the function that will be called once the result returns from the server.
    CopyExecuting the query
    function 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";
    }
  6. 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 results
    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.";
        }
    }
  7. 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:

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

CopyExample: Querying by spatial and attribute criteria
<!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>