This walk-through shows how to find features near a location on an ArcGIS Server map. Typically this is used to identify features at a point location, such as when a user clicks on the map. The same approach can be used when you have a location derived from another operation, and want to find map features nearby. For example, if you use either Virtual Earth or ArcGIS Server to locate an address (using geocoding), you can then find features in the neighborhood of the location.

With ArcGIS Server, you can also query using more sophisticated relationships, such as features intersecting or within shapes or other features. See the walk-through Querying features by spatial relationship for an example. 

This walk-through parallels closely the example "Identifying things on the map" in the Interactive SDK. The complete code for the walk-through is at the bottom of this page if you have any questions about where code goes during the steps below.

Identifying features within a distance of a user click

This walk-through searches for features in map layers that are within a specified distance of a point. In this example we get the point from the user by a map click. Other applications might get the point from another operation, such as geocoding (finding the location of an address). We hard-code the distance around the point to search, but the search radius could also come from a user input.

  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 ArcGISVEIdentify1.htm.
  2. Add a reference to the ArcGIS JavaScript client script. This is required for every page to use 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. In the script block in the page header, before the OnPageLoad function, add these variables. We need them at global scope because we'll reuse them in more than one function. The tileUrl specifies the location of the map service that will provide both map display and query service. Obtain the service URL from your system administrator, or if you know the URL of the server, from its Services Directory. The identifyTolerance is the number of pixels around the user click that will be searched for map features.
    CopySome global variables
    var tileUrl = "http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Portland/ESRI_LandBase_WebMercator/MapServer";
    var identifyTolerance = 2; // in pixels
    var identifyTask = null;
    var identifyParameters = null;
  4. Modify the OnPageLoad function as shown below. After the map loads, we do preliminary set-up for the identify operation. We've done this because the user can identify many times, and we only need to do these steps once. The IdentifyParameters determines how the identify search is performed. We set the parameters for the IdentifyParameters using JSON notation. We could also have set the properties of IdentifyParameters individually, for example, by setting identifyParameters.LayerOption in a separate line. The second parameter, for LayerIds, determines which layers are searched. You can use the Services Directory or the original map to determine IDs of layers. The IdentifyParameters are used when we execute the query after the user clicks on the map.

    We create the IdentifyTask here as well and set the URL of the map service. Since we're identifying on the same map service as we displayed earlier, we use the same URL as we'll use next to add the ArcGIS Server map to the display.

    We use the Virtual Earth's AttachEvent method to tell the VE map that when the user clicks on the map, we want to call the getPoint function. We'll add the code for this function shortly.

    Finally, we add the map to the display. This uses the same approach as we used in the walk-through Adding an ArcGIS Server service, except here we're adding the map on startup.
    CopySetting up the query on load
    function OnPageLoad() {
        var centerat = new VELatLong(45.50634690108341, -122.67883300781251);
        map = new VEMap('mymap');
        map.LoadMap(centerat,16,VEMapStyle.Aerial ,false);
    
        var parameters = {"LayerOption":"all", "LayerIds": [2,3,4], "Tolerance": identifyTolerance};
        identifyParameters = new ESRI.ArcGIS.VE.IdentifyParameters(parameters);
        identifyTask = new ESRI.ArcGIS.VE.IdentifyTask();
        identifyTask.Url = tileUrl;
        map.AttachEvent("onclick",getPoint);
    
        var agisve_services = new ESRI.ArcGIS.VE.ArcGISLayerFactory();
        arcgisve_services.CreateLayer(tileUrl, "Parcels", GetMap);            
    }
  5. Just after the close of the OnPageLoad function, add the GetMap function we just referenced. This adds the ArcGIS Server map service to the VE map. See the walk-through Adding an ArcGIS Server service for a discussion of how this works. The new element here is that we set the Opacity of the layer (map service) to 0.5 so that it is semitransparent and we can see the base VE map underneath.
    CopyGetMap function
    function GetMap(tileSourceSpec, resourceInfo) {
        map.HideDashboard(); 
        tileSourceSpec.Opacity=0.5;
        map.AddTileLayer(tileSourceSpec,true);
    }
  6. Next, add the function that gets called when the user clicks on the VE map. We attached this function to the VE map's onclick event in step four above. This function gets the latitude/longitude point clicked, handling both Internet Explorer (which defines the e.latLong) and Firefox (e.mapX and e.mapY are in pixels; we use VE's PixelToLatLong method to transform to geographic coordinates). The clicked point is converted to a VE point (pushpin) shape, and set as the geometry for the identify operation.

    After setting the width, height, and map extent in the IdentifyParameters, the IdentifyTask is executed. We pass it the parameters object we've created, along with the name of the function (addShapes) that will handle the results.
    CopyAdding the getPoint function
    function getPoint(e) {
        var clickPnt = null;
        if (e.latLong) {
            clickPnt = e.latLong;
        } else {
            var clickPixel = new VEPixel(e.mapX,e.mapY);
            clickPnt = map.PixelToLatLong(clickPixel);
        }
        var pushpoint = new VEShape(VEShapeType.Pushpin, clickPnt);
        map.DeleteAllShapes();
        identifyParameters.Geometry = pushpoint;
        var mapDiv =  $get("mymap");
        identifyParameters.Width = mapDiv.clientWidth;
        identifyParameters.height = mapDiv.clientHeight;
        identifyParameters.MapExtent = map.GetMapView();
        identifyTask.Execute(identifyParameters, addShapes); 
    }
  7. Add the function that is called when the results of the identify are returned. The results are an instance of IdentifyResults. The addShapes function checks for errors, then gets the VE shape from the InputGeometry property of the IdentifyResults. The location will be the same as we input for the identify, but it will now have a description that contains information on map features found, if any. The function adds a message if no features are found. The description displays as a pop-up tooltip next to the push-pin that displays on the VE map when the shape is added to the map, which we do as the last step in the function. 

    What if you wanted to show the actual features themselves, such as the outline of a parcel? It's actually easy to do--see the Discussion for information.
    CopyAdding results to the map
    function addShapes(identResults) {
        var err = identResults.Error;
        if (err!=null) 
            alert(err.message);
        else {
            var shape = identResults.InputGeometry;
            if (shape.GetDescription().length==0)
                shape.SetDescription("Unable to identify any map features at the click location.");
            shape.Show();
            map.AddShape(shape);
        }
    }
  8. Save the page, and open it in a browser. It should load the VE map, with the ArcGIS Server map layer with partial transparency on top. Click once on the VE map. After a moment, a pushpin should be added to the map at the click location. If you hover over the pushpin, the attributes of map features found at this location (if any) should pop up next to the cursor. You may click again at other locations to identify additional locations. Note that previous identify locations are removed, which occurs because we used the map.DeleteAllShapes method in the getPoint function (step 6).

Discussion

You could use other shape types for the identify query, such as a line or polygon. We used a point in step 6, but any VEShapeType can be used when constructing the VEShape. This would enable a more sophisticated application where, for instance, the user searches for a parcel by entering an address, the code queries the parcel layer to obtain a polygon, and then the polygon's shape used to find the schools within some distance of the parcel, all in a single step.

If you want to draw the features found, rather than just the original user click point, it's easy to do. The IdentifyResults (step 7) has a ToVEShapeLayer method, which is a quick way to add all features found. If you only want to show features in certain layers, or otherwise work with individual features, the the Results property of IdentifyResults will contain an array of IdentifyResult objects, which has in its Feature property a GraphicFeature. The Shapes property of the GraphicFeature has an array of VEShapes that may be added directly to the VE map, using the same AddShape method we used in step 7. This approach is used to add shapes in the walk-through Querying features by spatial relationship.

Complete code for this walkthrough

CopyExample: Identifying features within a distance of a user click
<!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>Identify features in 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 tileUrl = "http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Portland/ESRI_LandBase_WebMercator/MapServer"; 
        var identifyTolerance = 2; // in pixels 
        var identifyTask = null; 
        var identifyParameters = null;

        function OnPageLoad() {
            var centerat = new VELatLong(45.50634690108341, -122.67883300781251);
            map = new VEMap('mymap');
            map.LoadMap(centerat,16,VEMapStyle.Aerial,false);

            var agisve_services = new ESRI.ArcGIS.VE.ArcGISLayerFactory();
            agisve_services.CreateLayer(tileUrl, "Parcels", GetMap);

            var parameters = {"LayerOption":"all", "LayerIds": [2,3,4], "Tolerance": identifyTolerance};
            identifyParameters = new ESRI.ArcGIS.VE.IdentifyParameters(parameters);
            identifyTask = new ESRI.ArcGIS.VE.IdentifyTask();
            identifyTask.Url = tileUrl;
            map.AttachEvent("onclick",getPoint);
        }

        function GetMap(tileSourceSpec, resourceInfo) {
            map.HideDashboard(); 
            tileSourceSpec.Opacity=0.5;
            map.AddTileLayer(tileSourceSpec,true);
        }

        function getPoint(e) {
            var clickPnt = null;
            if (e.latLong) {
                clickPnt = e.latLong;
            } else {
                var clickPixel = new VEPixel(e.mapX,e.mapY);
                clickPnt = map.PixelToLatLong(clickPixel);
            }
            var pushpoint = new VEShape(VEShapeType.Pushpin, clickPnt);
            map.DeleteAllShapes();
            identifyParameters.Geometry = pushpoint;
            var mapDiv =  $get("mymap");
            identifyParameters.Width = mapDiv.clientWidth;
            identifyParameters.height = mapDiv.clientHeight;
            identifyParameters.MapExtent = map.GetMapView();
            identifyTask.Execute(identifyParameters, addShapes); 
        }

        function addShapes(identResults) {
            var err = identResults.Error;
            if (err!=null) 
                alert(err.message);
            else {
                var shape = identResults.InputGeometry;
                if (shape.GetDescription().length==0)
                    shape.SetDescription("Unable to identify any map features at the click location.");
                shape.Show();
                map.AddShape(shape);
            }
         }       
    </script>
</head>
<body onload="OnPageLoad()" >
    <form action="" >
    <div id='mymap' style="position:relative; width: 750px; height: 500px;"></div>
    Click on the map to find features within two pixels of the map click.
    </form>
</body>
</html>