7.3 ArcGIS Server: Querying ArcGIS Server Layers

Often times you will want to query a point data ArcGIS map service to return only points that fall within a buffer. In this tutorial, we will create a search box in your maproom that will allow users to query ArcGIS server for points inside a buffer around a zip code.

Setting up

Let’s start with an empty maproom layout.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<title>Advanced GIS Toggle Tutorial</title>
<style type="text/css">
html { height: 100% }
body { height: 100%; margin: 0px; padding: 0px }
#map_canvas { height: 100% }
</style>

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="http://yohman.bol.ucla.edu/jquery-1.5.2.min.js"></script>

<script type="text/javascript">
//declare namespace
var yoh = {};

//global variables
var map;
var bounds;
var buffer;
var busstop = [];

function trace(message)
{
if (typeof console != 'undefined')
{
console.log(message);
}
}

//Function that gets run when the document loads
yoh.initialize = function()
{
var latlng = new google.maps.LatLng(34.070264, -118.4440562);
var myOptions = {
zoom: 13,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
}
</script>
</head>
<body onload="yoh.initialize()">

<!-- side panel div container -->
<div style="position:absolute; width:380px; height: 100%;  overflow:auto; float:left; padding-left:10px; padding-right:10px;">
<h1>7.3 Querying ArcGIS Server Layers</h1>
<br>
</div>
<!-- map div container -->
<div id="map_canvas" style="height:100%; margin-left:400px;"></div>

</body>
</html>

Creating the Input Box

Now we will create the input box with the ID “address” which the users will use to input zip codes. This input box div should be inserted under the H1 tag.

1
2
3
4
5
6
<!-- search box -->
<div style="border:1px solid #ccc; background:#e5e5e5; padding:10px;">
<span style="width:50px; display: inline-block;">Find:</span>
<input type="text" id="address" value="90095">
<input type="button" value="find" onClick="yoh.geocode()">
</div>

Setting up the Geocoder Function

First, declare a geocoder variable in the global space

1
2
//declare geocoder
var geocoder;

Next, add the Geocoder function to your code. This function will take the user input from the input box we created previously. If the geocoder is successful it will center the map on the zip code and invoke the drawMetroWithinBuffer function which we will create next.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//function to geocode
yoh.geocode = function()
{
    var address = $('#address').val();
    geocoder.geocode( { 'address': address}, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK)
        {
            map.setCenter(results[0].geometry.location);
           
            //second parameter is buffer distance in Kilometers
            yoh.drawMetroWithinBuffer(results[0].geometry.location,2);
        }
        else
        {
            alert("Geocode was not successful for the following reason: " + status);
        }
    });
}

Remember to add the geocoder in your initialize function:

1
2
//set the geocoder
geocoder = new google.maps.Geocoder();

Setting up the Draw Metro Stops within Buffer Function

Let’s create the drawMetroWithinBuffer function. This function takes two inputs: center and radius to draw a buffer. It uses the lat/long from the geocoder function, and a hard coded radius in order to draw this buffer. It then queries the ArcGIS server layer to return all points within that buffer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//function to draw the metro stops within the given radius (in kilometers)
yoh.drawMetroWithinBuffer = function(center, radius)
{
    //erase any existing circles
    if (buffer)
    {
        buffer.setMap(null);
    }
   
    //erase any existing stops
    if (busstop)
    {
        $.each(busstop,function(i,item)
        {
            busstop[i].setMap(null);
        });
    }
   
    //reset the bounds
    bounds = new google.maps.LatLngBounds();
   
    //number of nodes the circle polygon will have... the more, the more accurate but time consuming
    var nodes = 30;
    var latConv = center.distanceFrom(new google.maps.LatLng(center.lat()+0.1, center.lng()))/100;
    var lngConv = center.distanceFrom(new google.maps.LatLng(center.lat(), center.lng()+0.1))/100;
   
    //the json variable for the circle polygon that will be fed to ArcGIS Server to query the metro layer
    var circlepolygon = '{"rings":[[[';

    var step = parseInt(360/nodes)||10;
    var count = 0;
    for(var i=0; i<=360; i+=step)
    {
        var point = new google.maps.LatLng(center.lat() + (radius/latConv * Math.cos(i * Math.PI/180)), center.lng() +
                (radius/lngConv * Math.sin(i * Math.PI/180)));
               
        bounds.extend(point);
        map.fitBounds(bounds);

        circlepolygon +=  point.lng()+',';
       
        //if on the last node, finish the json correctly
        if (count == nodes)
        {
            circlepolygon +=  point.lat()+']]]}';
        }
        else
        {
            circlepolygon +=  point.lat()+'],[';
        }
        count = count + 1;
    }
   
    //
    yoh.getStops(circlepolygon);

    //optionally, draw a circle to see the buffer
    var bufferOptions = {
        strokeColor: "#FF0000",
        strokeOpacity: 0.4,
        strokeWeight: 0,
        fillColor: "#FF0000",
        fillOpacity: 0.3,
        map: map,
        center: center,
        radius: radius*1000
      };
     
      buffer = new google.maps.Circle(bufferOptions);
}

This function uses two other functions while running so let's create those now.

Creating the supporting functions

Add the code below for the distance calculator function:

1
2
3
4
5
6
7
8
9
10
11
12
13
//Function to calculate distance from 2 points 
google.maps.LatLng.prototype.distanceFrom = function(p2) {
    var R = 6378137; // earth's mean radius in meters (this was a parameter in V2)
    var rad = function(x) {return x*Math.PI/180;}
    var dLat = rad(p2.lat() - this.lat());
    var dLong = rad(p2.lng() - this.lng());
   
    var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(rad(this.lat())) * Math.cos(rad(p2.lat())) * Math.sin(dLong/2) * Math.sin(dLong/2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    var d = R * c;
   
    return d.toFixed(3);
}

The next function we need to create is the getStops function. This function queries ArcGIS server for the stops that are within the buffer zone.

1
2
3
4
5
6
7
8
9
10
11
12
yoh.getStops = function(polygon)
{
    var url = 'http://whippet.ats.ucla.edu/ArcGIS/rest/services/up206b/metro/MapServer/1/query?text=&amp;geometry=' + polygon + '&amp;geometryType=esriGeometryPolygon&amp;returnGeometry=true&amp;outFields=&amp;f=pjson&amp;callback=?';
    $.getJSON(url,function(data)
    {
        $.each(data.features,function(i,item)
        {
            yoh.createStop(i,item.geometry.y,item.geometry.x,item.attributes.STOPNAME);
        });
       
    });
}

After finding stops within the buffer zone, the getStops function calls the createStop function which creates the markers on your map. The code for the createStop function is below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Function to create busstop
yoh.createStop = function(i,latitude,longitude,title)
{  
    var markerLatLng = new google.maps.LatLng(latitude,longitude);  

    //extent bounds for each stop and adjust map to fit to it
    bounds.extend(markerLatLng);
    map.fitBounds(bounds);

    busstop[i] = new google.maps.Marker({
        position: markerLatLng,
        map: map,
        title: title,
        icon: 'http://yohman.bol.ucla.edu/images/bus.png'
    });
}

Final Code Sample

Leave a Reply