Setting output symbology in scripts

The symbology property of a script tool parameter allows you to associate a single layer file (.lyr) with an output parameter. When the script tool is run, the output is added to the display using the symbology found in the layer file. You can also set the symbology property in the script's symbology code.

In both cases, you can associate one and only one layer file with the output parameter. Having only one layer file works well if the output is well-defined. But what if your output isn't well defined? For example, you know your output is a feature class, but you don't know if it will contain point, polyline, or polygon features until the tool is run. Layer files are dependent on geometry type, which means you cannot have one layer file that will symbolize multiple feature types. In this case, you need to have three layer files, one for each geometry type, then associate the correct layer file based on the output geometry type. The following code snippet demonstrates this.

# Set the symbology of the output. 
    #   output    = the output value
    #   params[2] = the output parameter
    #
    params = arcpy.GetParameterInfo()
    desc = arcpy.Describe(output)
    if desc.shapeType == "Polygon":
        params[2].symbology = "c:/Tools/Extractor/ToolData/polygon.lyr"
    elif desc.shapeType == "Polyline":
        params[2].symbology = "c:/Tools/Extractor/ToolData/polyline.lyr"
    else:
        params[2].symbology = "c:/Tools/Extractor/ToolData/point.lyr"

The logic in the above script is fairly simple: test the geometry (shape) type and set the layer file accordingly. Even if you have more complex logic, the pattern remains the same:

Setting symbology in a script versus the ToolValidator class

If you're familiar with programming tool validation logic in a ToolValidator class, you can see that the above code snippet could be rewritten for use in the updateParameters method. In fact, if you only need to reference one layer file, you should do so either in the script tool's properties or in the initializeParameters method. But if you need to set symbology to any one of several layer files, you do so in the script tool. Putting such logic in the ToolValidator class bloats your code with logic that has nothing to do with tool validation, and in some cases, you may not know which layer file to use until tool execution.

Example script

The script below creates features based on their distance from parks in the city of Portland. There are three parameters: the input features, the distance, and the output features. The output features are symbolized so that they are easily distinguishable from other features on the map (something other than the default symbology, which can be difficult to distinguish). Since the input features can be either point, polyline, or polygon, three different layer files are needed.

This script also demonstrates several coding techniques for portability. The portability techniques used are as follows:

# ExtractData.py
# Description: Script that will extract features from an input layer within a specified
#              distance from a park.
# Parameters:
#  0 - input features
#  1 - distance from parks (linear units)
#  2 - output feature class

import arcpy
from arcpy import env
import os
import sys

env.overwriteOutput = True

try:
    # This tool makes use of a system folder with a Scripts and ToolData subfolder. 
    #  We can discover the pathname of this folder by examining the
    #  first argument to the script, which is the pathname to the script
    #  (example: "E:\examples\symbology\scripts\ExtractData.py".)  We
    #  then use this toolSharePath variable to create pathnames to our 
    #  shapefile data and layer files ("E:\examples\symbology\ToolData\points.lyr").
    #
    scriptPath      = sys.argv[0]
    toolSharePath   = os.path.dirname(os.path.dirname(scriptPath))
    dataPath        = os.path.join(toolSharePath, "ToolData")
    parkPath        = os.path.join(dataPath, "PortlandParks.shp")
    pointLyrPath    = os.path.join(dataPath, "point.lyr")
    polygonLyrPath  = os.path.join(dataPath, "polygon.lyr")
    polylineLyrPath = os.path.join(dataPath, "polyline.lyr")
    
    # Buffer the parks by the specified distance.  The output is a scratch
    #  feature class in the same workspace as the output feature class
    #
    arcpy.SetProgressorLabel("Buffering parks ...")
    scrname = arcpy.CreateScratchName("xxx", "", "featureclass", 
                                      os.path.dirname(arcpy.GetParameterAsText(2)))
    arcpy.Buffer_analysis(parkPath, scrname, arcpy.GetParameterAsText(1))

    # Clip the defined layer with the buffered parks
    #
    arcpy.SetProgressorLabel("Clipping " + arcpy.GetParameterAsText(0) + " ..." )
    output = arcpy.Clip_analysis(arcpy.GetParameterAsText(0), scrname, 
                                 arcpy.GetParameterAsText(2))

    # Delete intermediate dataset
    #
    try:
        arcpy.Delete_management(scrname)
    except:
        pass

    # Set the symbology of the output. 
    #
    params = arcpy.GetParameterInfo()
    desc = arcpy.Describe(output)
    if desc.shapeType == "Polygon":
        params[2].symbology = polygonLyrPath
    elif desc.shapeType == "Polyline":
        params[2].symbology = polylineLyrPath
    else:
        params[2].symbology = pointLyrPath

except:
    arcpy.AddError("An error occurred. " + arcpy.GetMessages(2))
3/3/2014