Using an arcpy Python toolbox (.pyt) as a Print Service in ArcGIS for Server

A Python Toolbox

Python toolboxes in ArcGIS are great. They’re even better then regular Python script in Toolboxes. Let me explain why and show you how to use an Python toolbox in a Print Service in ArcGIS for Server.

Since many years now, when you do any geoprocessing in ArcGIS for Desktop, you are able to incorporate a script in an toolbox in ArcMap or ArcCatalog. This enables you to do some more customizing if compared to the “drag-and-drop” modelling in toolboxes.

You can recognize a scripted toolbox by the icon of a … well … script (see screenshot below).

Icon of scripttool and regular toolbox model

A Python toolbox is different. It is actually a toolbox, containing one or more tools. You can recognize it by a different icon (see screenshot below, showing the toolbox “Print Tools” with the tool “PrintTool”).

Python toolbox

You build the toolbox independently from ArcGIS, for example in Aptana. This means that you don’t have to start up ArcMap or ArcCatalog (which can save a lot of time). It also means that you can use full and debugging capabilities of your IDE of choice. The only thing you need ArcMap or ArcCatalog for is publishing your toolbox if you want to use it as a geoprocessing service.

In this example, I’m going to show you how to use this technique to set up a geoprocessing service that can be used as a Print Service (for example when using the javascript API of ArcGIS). If you want to see an example in action, check out https://developers.arcgis.com/en/javascript/jssamples/widget_print_webmap.html.

The Toolbox

The Python Toolbox is a python script (so, a text file). Let’s start at the beginning.

  • Include any packages you might need, but at least arcpy:
import arcpy
  • Define the class “Toolbox”. You cannot choose another name. The class has some members, defining label, alias and the tools that will be inside the toolbox. This last member is a list of classes, that we will define a little bit later in the same python script. In this case there will be one tool, called MyPrintTool:
class Toolbox(object):
def __init__(self):
“””Define the toolbox (the name of the toolbox is the name of the .pyt file).”””
self.label = “Print Tools”
self.alias = “”
# List of tool classes associated with this toolbox
self.tools = [MyPrintTool]
  • Define the class “MyPrintTool”. It also has a number of prescribed members and functions, giving access to the properties of the tool and providing ArcGIS with the necessary functionality to run the tool. For now I left out some of the code, I will explain that later:
class MyPrintTool(object):

def __init__(self):

“””Define the tool (tool name is the name of the class).”””
self.label = “PrintTool”
self.alias = “PrintTool”
self.description = “My first python print tool”
self.canRunInBackground = False

def getParameterInfo(self):
(…)
def isLicensed(self):

“””Set whether tool is licensed to execute.”””
return True

def updateParameters(self, parameters):

“””Modify the values and properties of parameters before internal
validation is performed. This method is called whenever a parameter
has been changed.”””
return

def updateMessages(self, parameters):

“””Modify the messages created by internal validation for each tool
parameter. This method is called after internal validation.”””
return

def execute(self, parameters, messages):

(…)

  • Let’s look at the Parameters of the tool. These are defined in the member getParameterInfo. The parameters are of the type arcpy.Parameter. They have members like name, datatype, parameterType (required parameter or not) and direction (input or output parameter). In the case of a Print Tool, the number, names and other specifications of the parameters are fixed. They are shown below:
def getParameterInfo(self):
 
“””Define parameter definitions”””
 
Web_Map_as_JSON = arcpy.Parameter(
displayName=”Web_Map_as_JSON”,
name=”Web_Map_as_JSON”,
datatype=”String”,
parameterType=”Required”,
direction=”Input”)

Format = arcpy.Parameter(

displayName=”Format”,
name=”Format”,
datatype=”String”,
parameterType=”Optional”,
direction=”Input”)
 
Format.value = ‘PDF’
 
Layout_Template = arcpy.Parameter(
displayName=”Layout_Template”,
name=”Layout_Template”,
datatype=”String”,
parameterType=”Optional”,
direction=”Input”)
 
Layout_Template.filter.type = “ValueList”
Layout_Template.filter.list = [“A3_landscape”,”A3_portrait”]
Layout_Template.value = “A3_landscape”
 
Output_File = arcpy.Parameter(
displayName=”Output_File”,
name=”Output_File”,
datatype=”File”,
parameterType=”Derived”,
direction=”Output”)
 
params = [Web_Map_as_JSON,Format,Layout_Template,Output_File]
return params

So what happens here? In the code above, you see the four instances are created of the class Parameter. The names of these instances are Web_Map_as_JSON, Format, Layout_Template and Output_File. Each instance gets a name, datatype, parameterType and direction. Then all four instances are put in a list called params and that list is the result of the function getParameterInfo of the Tool.

As you can see, the Format parameter gets a value (“Format.value = ‘PDF'”). The same goes for Layout_Template (“A3_landscape”). These are the default values for these parameters, which can be overriden when the user or application is invoking the tool. Maybe you can also see that the parameter Layout_Template has a ValueList from which the user can choose the valid options.

The parameter Web_Map_as_JSON also needs some attention. This parameter contains the definition of the webmap. The webmap is used by the Javascript API (and other online APIs) to define the specifications of the map (layers, titles, extents, etc). The Print Tool takes the definition of the webmap as input. If you use the Print Dijit in your Javascript application, the API will do this for you.

  • And now the most important part: the execution of the tool. In this example, the actual printing (that is: creating a PDf or JPG) is done by calling the correct arcpy statements. These are used to open a MXD that is present on the server, zoom to the same extent as the webmap, and exporting the image. Like so:
def execute(self, parameters, messages):
“””The source code of the tool.”””
# Input WebMap json
Web_Map_as_JSON = parameters[0].valueAsText
webmap_obj = json.loads(Web_Map_as_JSON)

folderToolRoot = r”C:\somewhere\on\your\machine\MyPythonProject”

# The template location in the server data store, template_project.mxd
templateMxd = os.path.join(folderToolRoot,’template_project.mxd’)

# Convert the WebMap to a map document
result = arcpy.mapping.ConvertWebMapToMapDocument(Web_Map_as_JSON)
mxd_web = result.mapDocument
mxd_print = arcpy.mapping.MapDocument(templateMxd)

# Reference the data frame that contains the webmap and apply extent to template extent
# Note: ConvertWebMapToMapDocument renames the active dataframe in the template_mxd to “Webmap”
df = arcpy.mapping.ListDataFrames(mxd_web, “Webmap”)[0]
ext_web = df.extent
dfp = arcpy.mapping.ListDataFrames(mxd_print)[0]
dfp.extent = ext_web

# Use the uuid module to generate a GUID as part of the output name
# This will ensure a unique output name
output = ‘WebMap_{}.pdf’.format(str(uuid.uuid1()))
Output_File = os.path.join(arcpy.env.scratchFolder, output)

# Export the WebMap
arcpy.mapping.ExportToPDF(mxd_print, Output_File)

# Set the output parameter to be the output file of the server job
arcpy.SetParameterAsText(3, Output_File)

# Clean up – delete the map document reference
filePath = mxd_web.filePath
del mxd_web, result, mxd_print
os.remove(filePath)
return

And that’s it! Add the toolbox to ArcMap, run it once succesfully and publish it to your ArcGIS for Server.

Testing during development

One last note on testing your toolbox. It is rather cumbersome to have to add the toolbox the ArcMap each time, so it is much easier to do the following. Create a separate python script containing something like this:

import arcpy
arcpy.AddToolbox(r”C:\somewhere\on\your\machine\MyPythonProject\MyToolbox.pyt”)
s = ‘{ …… }’ # JSON serialization of webmap
result = arcpy.MyPrintTool(s, ‘PDF’, ‘A3_landscape’)
resultValue = result.getOutput(0)
print resultValue # path to resulting image
 

Resources

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s