Use geoprocessing services in Python scripts

You can write a Python script to run and use a geoprocessing service in multiple ways. The primary way to run a script is to use ArcPy. ArcPy has built-in methods to connect to, run, 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 must build a client from scratch with Python code to use this. The majority of scripts will connect to and use geoprocessing services through ArcPy.

Use ArcPy

A geoprocessing service 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 geoprocessing service.

Connect to a service using ImportToolbox or AddToolbox.

# arcpy.ImportToolbox("https://machine.domain.com/webadaptor/services;<optional folder>/<service name>;{username};{password}")
arcpy.ImportToolbox("https://machine.domain.com/webadaptor/services;GPFolder/BufferService;serverusername;serverpassword")

The path used to import the tool is the URL of the web tool. ImportToolbox and AddToolbox accept the information of the server, which includes four parts separated by a semicolon (;). The first part is the URL (or link) to the service end point, the second is the service name (optionally, a folder name precedes the service name), the third is the optional server username, and the last is the optional server password. Provide the username and password if you do not have the permission to access the web tool.

A geoprocessing service can run either synchronously or asynchronously. As a Python scripter, you must understand how the service runs to use it. The IsSynchronous property can be used to determine whether the service runs synchronously or asynchronously. When a service runs synchronously, the results are automatically returned, but no action can be taken until it has completed. When a service runs asynchronously, the current status must be queried periodically. Once the service has finished running, you can access the result.

The following Python code shows how to connect to an asynchronous geoprocessing service and use ArcPy functions to run it, get the result, and process it further. By setting a result variable when running 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://machine.domain.com/webadaptor/services;Elevation/viewshedAlias;serverusername;serverpassword")

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 Use tools in Python help topic.

Use REST

An alternate (but less common) way to use a geoprocessing service 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 demonstrates how you would connect to the service, send a request, and handle a response.

Submit the request

For example, a geoprocessing service to create viewsheds is accessible from https://machine.domain.com/webadaptor/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.

https://machine.domain.com/webadaptor/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 runs 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 = 'https://machine.domain.com/webadaptor/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. A variety of methods can be used to make a request and parse a result; the above example is just one method. The JSON returned from a geoprocessing service can be placed into a dictionary by using json.loads(), as shown in the previous example. Depending on the output of your geoprocessing service, 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 you're working with result features, ensure 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 runs 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 search 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 geoprocessing service (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 = "https://machine.domain.com/webadaptor/rest/services/Elevation/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")

In this topic
  1. Use ArcPy
  2. Use REST