The symbology property of a script tool parameter allows you to associate a single layer file (.lyrx) with an output parameter. When the script tool is run, the output is added to the display using the symbology from 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, and associate the correct layer file based on the output geometry type. The following code snippet demonstrates this.
# Set the symbology of the output.
output = self.params[1].value
if output:
desc = arcpy.Describe(output)
if desc.shapeType == "Polygon":
self.params[2].symbology = "c:/Tools/Extractor/ToolData/polygon.lyr"
elif desc.shapeType == "Polyline":
self.params[2].symbology = "c:/Tools/Extractor/ToolData/polyline.lyr"
else:
self.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:
- Create a layer file that will symbolize each possible output.
- Based on logic in your script, determine which layer file should be used and set it using the symbology property of the parameter.
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 unnecessarily lengthens 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:
- Using __file__ to retrieve the full path to the script file
- Using the Python os module to create paths to data
- Using the CreateScratchName function to create a scratch feature class
# 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
import os
arcpy.env.overwriteOutput = True
try:
# This tool uses a system folder with a Scripts and ToolData subfolder.
# You can discover the pathname of this folder using the Python __file__
# attribute, which is the pathname to the script
# (example: 'E:\examples\symbology\scripts\ExtractData.py'.) You
# then use the toolSharePath variable to create paths to your
# shapefile data and layer files ('E:\examples\symbology\ToolData\points.lyr').
scriptPath = __file__
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 {} ...'.format(arcpy.GetParameterAsText(0)))
output = arcpy.Clip_analysis(arcpy.GetParameterAsText(0), scrname,
arcpy.GetParameterAsText(2))
# Delete the 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 Exception as err:
arcpy.AddError('An error occurred: {}'.format(err))