Use web tools in Python scripts

You can write a Python script to execute and use a web tool in multiple ways. The primary way to execute a script is to use ArcPy. ArcPy has built-in methods to connect to, execute, and handle the result from the service. Alternatively, if you access the service from the ArcGIS Server Services Directory, you can use built-in Python modules to make REST calls using a JSON structure to transfer results. You need to build a client from scratch with Python code to use this. The majority of scripts will connect to and use geoprocessing services through ArcPy.

Note:

The examples here consume the actual ArcGIS Server service. You do not consume the web tool from the portal.

Use ArcPy

A web tool can be accessed through the Python window in ArcGIS Pro, a script tool, or a stand-alone script. The service URL is used to connect to and use a web tool.

Connect to a service using ImportToolbox.

# arcpy.ImportToolbox("http://<hostname>:<port>/arcgis/services;<optional folder>/<service name>","<optional alias>")
arcpy.ImportToolbox("http://degrassi:6080/arcgis/services;GPFolder/BufferService", "PointAlias")

The path used to import the tool is the URL of the web tool. ImportToolbox accepts two parameters: the URL to the service and an optional alias for the toolbox. The URL parameter is broken into two parts separated by a semicolon (;). The first part is the URL (or link) to the service end point, and the second is the service name (optionally, a folder name precedes the service name).

A web tool can execute either synchronously or asynchronously. As a Python scripter, you need to understand how the service executes in order to use it. The IsSynchronous property can be used to determine the execution type of a service. When a service executes synchronously, the results are automatically returned, but no action can be taken until it has completed. For asynchronous services, the current status of the execution must be queried periodically. Once the service has finished executing, the result can be accessed.

The following Python code shows how to connect to an asynchronous geoprocessing service, and. using ArcPy functions, execute it, get the result, and process it further. By setting a result variable when executing the task, a while loop can be used to check the status. The task is finished once a status code of 4 (succeeded) or higher is returned.

Use an asynchronous service to create a buffer and save the result locally.

import arcpy
import time

arcpy.ImportToolbox("http://sampleserver6.arcgisonline.com/ArcGIS/services;Elevation/ESRI_Elevation_World", "viewshedAlias")

result = arcpy.Viewshed_viewshedAlias(r'c:\data\inputPoint.shp', "10000 Kilometers")

while result.status < 4:
	   print(result.status)
	   time.sleep(0.2)
print("Execution Finished")

print(result.getMessages())
arcpy.CopyFeatures_management(result[0], 'localResult.shp')

More information regarding the Result object and status codes, as well as creating raster and map image output, is available in the Using tools in Python help topic.

Use REST

An alternate (but less common) way to use a web tool is by writing a script that makes REST calls, using JSON as a data exchange format. This method requires you to write code to both, send the request, and handle the response.

Sending and receiving REST messages is more involved, as you must handle all aspects of the inputs and outputs syntax yourself. When sending and receiving REST messages, they are returned in a consistent manner. A request can be sent through an HTTP GET or HTTP POST method, and a response can come back structured as JSON. The core Python libraries support both sending a request and functions, which make reading and parsing JSON messages straightforward.

The following example uses a service on the Esri sample servers and demonstrates how you would connect to the service, send a request, and handle a response.

Submit the request

For example, SampleServer6 contains a geoprocessing service to create viewsheds and is accessible from http://sampleserver6.arcgisonline.com/ArcGIS/rest/services/Elevation. This service takes a point and distance as input and returns a feature set. The following inputs can be used to better understand the service:

Parameter NameInput value

Input Observation Point (GPFeatureRecordSetLayer)

{"geometryType" : "esriGeometryPoint",
"spatialReference" : {"wkid" : 54003},
'features':[{'geometry':{'x': -13308192.1956127,
'y':  4221903.58555983}}]}

Viewshed Distance (GPLinearUnit)

{ 'distance' : 8.5, 'units' : 'esriMiles' }

Format (format of the output)

JSON

This request returns the JSON of the visible locations from the service. The full URL used to generate the request can be used in the address bar of a web browser.

http://sampleserver6.arcgisonline.com/ArcGIS/rest/services/Elevation/ESRI_Elevation_World/GPServer/Viewshed/execute?Input_Observation_Point={%22features%22%3A[{%22geometry%22%3A{%22x%22%3A-13308192.1956127%2C%22y%22%3A4221903.58555983}}]}&Viewshed_Distance={+%27distance%27+%3A+8.5%2C+%27units%27+%3A+%27esriMiles%27+}&env%3AoutSR=&env%3AprocessSR=&f=pjson

The URL can be submitted using the Python module urllib or urllib2. The following Python code would execute the above request in a similar way to using the Service Directory or copying the link into the address bar of a web browser.

# Requires Python 3+
import urllib.request as urlopen
import urllib.parse as urlencode
import urllib.request as request
import json

inPts = {"geometryType" : "esriGeometryPoint",
         "spatialReference" : {"wkid" : 54003},
         'features':[{'geometry': {'x': -13308192.1956127, 'y': 4221903.58555983}}]}
dist = {'distance':8.5,'units':'esriMiles'}

data = {'Input_Observation_Point': inPts,
        'Viewshed_Distance': dist,
        'f': 'pjson'}

URL = 'http://sampleserver6.arcgisonline.com/ArcGIS/rest/services/Elevation/ESRI_Elevation_World/GPServer/Viewshed/execute'

req = request.Request(URL, urlencode.urlencode(data).encode('UTF-8'))
response = urlopen.urlopen(req)
response_bytes = response.read()
print(json.loads(response_bytes.decode('UTF-8')))

The Result object is returned as a string. There are a number of different methods that can be used to make a request and parse a result; the above example is just one method. The JSON returned from a web tool can be placed into a dictionary by using json.loads() as shown in the previous example. Depending on the output of your web tool, this may be the best technique, or you may need to explore other options to handle the output when consumed from Python through REST.

Note:

If working with result features, be sure that using the actual x,y pairs makes sense for your workflow, as you will not be receiving the actual features in a format that supports geometry in ArcGIS.

A service that executes asynchronously requires that you periodically ask the status of a task to see if it has finished, similar to the above example using ArcPy. Your Python code could look for the phrase esriJobSucceeded or esriJobFailed in the jobStatus status message, which is returned when checking the job status. The following code sample demonstrates one technique for working with an asynchronous web tool (using submitJob):

import urllib.request as urlopen
import urllib.parse as urlencode
import urllib.request as request
import json
import time

def sendReq(URL, data=None):

    req = request.Request(URL, urlencode.urlencode(data).encode('UTF-8'))
    response = urlopen.urlopen(req)
    response_bytes = response.read()
    return json.loads(response_bytes.decode('UTF-8'))

inPts = {"geometryType": "esriGeometryPoint",
         "spatialReference": {"wkid" : 54003},
         'features': [{'geometry': {'x': -13308192.1956127, 'y': 4221903.58555983}}]}
dist = {'distance': 8.5, 'units': 'esriMiles'}

data = {'Input_Observation_Point': inPts,
        'Viewshed_Distance': dist,
        'f': 'pjson'}

taskUrl = "http://localhost:6080/arcgis/rest/services/WorldViewshed/GPServer/viewshed"
submitUrl = taskUrl + "/submitJob"

submitJson = sendReq(submitUrl, data)

if 'jobId' in submitJson:
    jobID = submitJson['jobId']
    status = submitJson['jobStatus']
    jobUrl = taskUrl + "/jobs/" + jobID

    while status == "esriJobSubmitted" or status == "esriJobExecuting":
        print("checking to see if job is completed...")
        time.sleep(1)

        jobJson = sendReq(jobUrl, {"f": "json"})

        if 'jobStatus' in jobJson.keys():
            status = jobJson['jobStatus']

            if status == "esriJobSucceeded":
                if 'results' in jobJson:
                    resultsJson = jobJson['results']
                    for paramName in resultsJson.keys():
                        resultsUrl = jobUrl + "/" + resultsJson[paramName]['paramUrl']
                        resultJson = sendReq(resultsUrl, {"f": "json"})
                        print(resultJson)

            if status == "esriJobFailed":
                if 'messages' in jobJson.keys():
                    print(jobJson['messages'])

else:
    print("no jobId found in the response")

Related topics


In this topic
  1. Use ArcPy
  2. Use REST