1

Disclaimer: I'm new to GIS

Goal: Create a report of how many customers we have in each senate district.

Progress: I found this utility from the state of Missouri which allows me to search for an address and it displays the corresponding district. I did some debugging and found that it looks like it is doing two calls to an ArcGIS server (Using Branson, MO as example):

  1. https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates?SingleLine=branson, mo&f=json&outSR={"wkid":102100,"latestWkid":3857}&outFields=*

  2. https://ogi.oa.mo.gov/ArcGIS/rest/services/LEGIS/legisDistrict/MapServer/identify?f=json&geometry={"x":-10377036.95984281,"y":4389554.630050001,"spatialReference":{"wkid":102100,"latestWkid":3857}}&tolerance=1&returnGeometry=true&mapExtent={"xmin":-10377151.615385238,"ymin":4389439.974507573,"xmax":-10376922.304300383,"ymax":4389669.285592428,"spatialReference":{"wkid":102100}}&imageDisplay=1599,845,96&geometryType=esriGeometryPoint&sr=102100&layers=visible:1

The first call returns a list of candidate objects like this:

{
    "spatialReference": {
        "wkid": 102100,
        "latestWkid": 3857
    },
    "candidates": [
        {
            "address": "Branson, Missouri, United States",
            "location": {
                "x": -10377036.95984281,
                "y": 4389554.630050001
            },
            "score": 100,
            "attributes": {...},
            "extent": {
                "xmin": -10383716.235155277,
                "ymin": 4381233.546051718,
                "xmax": -10370357.896260085,
                "ymax": 4397882.30382274
            }
        }
    ]
}

The second call returns a list of result objects:

{
    "results": [
        {
            "layerId": 1,
            "layerName": "Senate District",
            "displayFieldName": "District",
            "value": "29",
            "attributes": {
                "OBJECTID": "29",
                "District": "29",
                "Population": "181191",
                "webLink": "http://www.senate.mo.gov/15info/members/mem29.htm",
                "photoLink": "http://www.senate.mo.gov/15info/graphics/d29-photo.gif",
                "Name": "David Sater",
                "Shape": "Polygon",
                "District_Number": "29",
                "Shape.area": "8040296221.235743",
                "Shape.len": "505379.224253"
            },
            "geometryType": "esriGeometryPolygon",
            "geometry": {}
        }
    ]
}

attributes.district being the value I want.

As you can see above, the second call sends a parameter called geometry which is a combination of location and spatialReference, as well as mapExtents which is a combination of extent and spatialReference. It also sends some other things.

The point is, this seems to require a lot of object/string manipulation to do both calls. The question is, are both calls necessary? Is there another way to call /identify, perhaps directly with latlng which I already have? I've looked at the documentation and I don't see anything, but to be honest I don't even understand what mapExtent is or why I need it. Why would it be different for every point I query?

  • Where are your customers stored? BTW, if you're doing redistricting, it might be easier just to use Esri's Redistricting Online. – Kirk Kuykendall Dec 29 '15 at 22:35
  • My customer list is in a spreadsheet, unfortunately. We are not redistricting, just running a report – Scott Beeson Dec 29 '15 at 22:54
  • If you have an ArcGIS online subscription, this method might make more sense. You'd need to pull data from your spreadsheet and pass it as a featureset for the pointlayer option. developers.arcgis.com/rest/analysis/api-reference/… – Kirk Kuykendall Dec 30 '15 at 19:52
1

I ended up just doing the string manipulation. I'll vote to close this as "too broad" but I wanted to post my full code here in case it helps anyone.

Excel VBA using VBA-WEB library:

Option Explicit

Sub updateData()
    Dim i As Integer
    On Error Resume Next

    Dim tbl As ListObject
    Dim tRows As Long
    Dim tCols As Long

    Set tbl = ActiveSheet.ListObjects("Participants")

    For i = 1 To tbl.ListRows.Count
        getAddressData (i)
    Next i


End Sub


Sub getDistrict(rw As Integer, spatialReference As Object, location As Object, extent As Object)
    Dim wkid As String
    wkid = spatialReference("wkid")
    Dim latestWkid As String
    latestWkid = spatialReference("latestWkid")

    Dim x As String
    x = location("x")
    Dim y As String
    y = location("y")

    Dim xmin As String
    xmin = extent("xmin")
    Dim ymin As String
    ymin = extent("ymin")
    Dim xmax As String
    xmax = extent("xmax")
    Dim ymax As String
    ymax = extent("ymax")

    Dim geometry As String
    geometry = "{""x"":" & x & "," & """y"":" & y & ",""spatialReference"":{""wkid"":" & wkid & ",""latestWkid"":" & latestWkid & "}}"

    Dim mapExtent As String
    mapExtent = "{""xmin"":" & xmin & ",""ymin"":" & ymin & ",""xmax"":" & xmax & ",""ymax"":" & ymax & ",""spatialReference"":{""wkid"":" & wkid & ",""latestWkid"":" & latestWkid & "}}"


    Dim arcClient As New WebClient
    arcClient.BaseUrl = "https://ogi.oa.mo.gov/ArcGIS/rest/services/LEGIS/legisDistrict/MapServer/"

    ' Note: querystring params are automatically url-encoded
    Dim Request As New WebRequest
    Request.Resource = "identify"
    Request.AddQuerystringParam "f", "json"
    Request.AddQuerystringParam "geometry", geometry
    Request.AddQuerystringParam "tolerance", "1"
    Request.AddQuerystringParam "returnGeometry", "false"
    Request.AddQuerystringParam "mapExtent", mapExtent
    Request.AddQuerystringParam "imageDisplay", "1599,845,96"
    Request.AddQuerystringParam "geometryType", "esriGeometryPoint"
    Request.AddQuerystringParam "sr", wkid
    Request.AddQuerystringParam "layers", "visible:1"

    ' By default: Request.Method = WebMethod.HttpGet and Request.Format = WebFormat.Json

    Dim Response As WebResponse
    Set Response = arcClient.Execute(Request)

    If Response.StatusCode = WebStatusCode.Ok Then
        Dim district As String
        Dim name As String
        district = Response.Data("results")(1)("attributes")("District")
        name = Response.Data("results")(1)("attributes")("Name")
        Cells(rw, "R").Value = district
        Cells(rw, "S").Value = name
    Else
        MsgBox "Error: " & Response.Content
    End If
End Sub

Sub getAddressData(rw As Integer)
    Dim address As String
    address = Cells(rw, "Q").Value
    Dim arcClient As New WebClient
    arcClient.BaseUrl = "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/"

    ' Note: querystring params are automatically url-encoded
    Dim Request As New WebRequest
    Request.Resource = "findAddressCandidates"
    Request.AddQuerystringParam "SingleLine", address
    Request.AddQuerystringParam "f", "json"
    Request.AddQuerystringParam "outSR", "{""wkid"":102100,""latestWkid"":3857}"
    Request.AddQuerystringParam "outFields", "*"

    ' By default: Request.Method = WebMethod.HttpGet and Request.Format = WebFormat.Json

    Dim Response As WebResponse
    Set Response = arcClient.Execute(Request)

    If Response.StatusCode = WebStatusCode.Ok Then
        Dim spatialReference As Dictionary
        Dim location As Dictionary
        Dim extent As Dictionary
        Set spatialReference = Response.Data("spatialReference")
        Set location = Response.Data("candidates")(1)("location")
        Set extent = Response.Data("candidates")(1)("extent")
        getDistrict rw, spatialReference, location, extent
    Else
        MsgBox "Error: " & Response.Content
    End If

End Sub
  • I forgot I'm not on SO and I don't have enough rep to vote for closure :\ – Scott Beeson Dec 30 '15 at 20:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.