Tutorial: Building the Census Regions Tool
This tutorial walks you through building a real ArcGIS Pro geoprocessing tool using Python and the arcsmith helper library. Rather than presenting finished code to copy, it guides you through the decisions behind each piece: what to write, why it works that way, and how the sections connect. By the end you will have a working tool and a clear mental model for building others from scratch.
What Is a Python Toolbox?
A Python toolbox is a .pyt file that ArcGIS Pro reads as source code. Inside it you define one Toolbox class that registers the toolbox and lists which tools it contains, then one class per tool. Each tool class contains a set of methods that ArcGIS calls at different moments: when the dialog opens, when the user changes a value, when the user clicks Run, and so on.
What makes .pyt files powerful is that they are plain text. You open them in any editor, save them, and ArcGIS picks up the changes immediately after a refresh. There is no compilation step, no separate dialog designer, and no XML to hand-edit. The interface, the validation logic, and the geoprocessing code all live in one readable file.
The tool you will build, Census Regions, takes a US Census county feature class, lets the user pick a set of states, trims the data to only the needed fields, and produces three output layers: county polygons for the selected states, state boundaries dissolved from those counties, and a single region boundary dissolving all selected states into one polygon.
What Each Method Does
Every tool class can define the following methods. Only __init__ and execute are required. The others are optional but most real tools use at least getParameterInfo and updateParameters.
| Method | When ArcGIS calls it |
|---|---|
__init__ |
When the toolbox is loaded |
getParameterInfo |
When the tool dialog opens |
isLicensed |
When ArcGIS checks whether the tool should be enabled |
updateParameters |
Every time the user changes any parameter value |
updateMessages |
After ArcGIS runs its own internal validation |
execute |
When the user clicks Run |
postExecute |
After execute finishes and outputs are added to the map |
You do not write these in isolation. A common workflow is to define a parameter in getParameterInfo, immediately write the interactive behavior for it in updateParameters, test it, and then come back to getParameterInfo to define the next parameter. This tutorial follows that back-and-forth pattern rather than asking you to define all parameters up front and wire them up later.
Starter Template
Save the following as census_tutorial.pyt inside your source_files/ folder. Every new toolbox starts from something like this.
# -*- coding: utf-8 -*-
import arcpy
class Toolbox:
def __init__(self):
"""Define the toolbox (the name of the toolbox is the name of the
.pyt file)."""
self.label = "Toolbox"
self.alias = "toolbox"
# List of tool classes associated with this toolbox
self.tools = [Tool]
class Tool:
def __init__(self):
"""Define the tool (tool name is the name of the class)."""
self.label = "Tool"
self.description = ""
def getParameterInfo(self):
"""Define the tool parameters."""
params = None
return params
def isLicensed(self):
"""Set whether the 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):
"""The source code of the tool."""
return
def postExecute(self, parameters):
"""This method takes place after outputs are processed and
added to the display."""
return
Rename the Toolbox label and alias and replace the Tool class with your tool class. The template above already includes a postExecute stub, which ArcGIS Pro does not generate by default but is worth including from the start.
Project Layout
The tutorial data and arcsmith are pre-packaged in source_files/. Its layout looks like this:
source_files/
├── census_tutorial.pyt
├── census_tutorial.gdb/
│ └── CensusCounties
├── arcsmith/
│ └── ...
└── lyrx/
├── county_symb.lyrx
├── state_boundary.lyrx
└── region_boundary.lyrx
When ArcGIS Pro runs a .pyt file, Python automatically adds that file's directory to sys.path. Because arcsmith/ sits in the same folder as the .pyt, writing import arcsmith resolves to the local copy without any installation. Keeping tools structured this way means the whole folder can be zipped and sent to a colleague and it will work immediately after unzipping on any machine.
Understanding Parameters Before You Start
Before writing any code, it helps to understand how parameters flow through a toolbox, because the same concept appears in every method you write.
Inside getParameterInfo you create each parameter as an arcpy.Parameter object, collect them into a Python list in the order you want them displayed, and return that list. ArcGIS reads the list and builds the tool dialog from it.
Every other method receives that same list back as an argument called parameters. You access individual parameters by their position: parameters[0] is the first one you added to the list, parameters[1] is the second, and so on. That index is the only thing connecting a parameter across getParameterInfo, updateParameters, execute, and postExecute. If you lose track of which index is which, things break in confusing ways.
This tutorial names each parameter variable with a zero-padded number prefix during definition: p_00, p_01, p_02, and so on. The number directly corresponds to the parameter's position in the returned list, so when you later see parameters[3] inside execute you immediately know it refers to p_03 without having to count. Zero-padding is not just style. Without it, a tool with ten parameters would have p_1 sort between p_10 and p_2 in most editors rather than in list order.
For the full list of valid datatype strings you can pass to arcpy.Parameter, see the ArcGIS Pro documentation on geoprocessing data types.
Now update your template: rename the class to CensusRegions, set the label and description, and add the import arcsmith line at the top. The skeleton you are working toward looks like this:
# -*- coding: utf-8 -*-
import arcpy
import arcsmith
class Toolbox:
def __init__(self):
self.label = "Census Tutorial Toolbox"
self.alias = "censustutorial"
self.tools = [CensusRegions]
class CensusRegions:
def __init__(self):
self.label = "Census Regions"
self.description = (
"Filter US Census county data to selected states, "
"trim fields, and produce county, state, and region boundary layers."
)
def getParameterInfo(self): ...
def isLicensed(self): return True
def updateParameters(self, parameters): ...
def updateMessages(self, parameters): return
def execute(self, parameters, messages): ...
def postExecute(self, parameters): ...
Each ... is a section you will fill in. Now open the tool dialog in ArcGIS Pro by right-clicking the .pyt in the Catalog pane and selecting Refresh, then double-clicking the tool. It will open with no parameters yet. Keep the dialog open as you work. Each time you save the file and refresh the toolbox in the Catalog pane the dialog will update to reflect your changes.
p_00: The Input Feature Class
Start getParameterInfo with the input feature class. This is always the foundation: every other parameter in this tool depends on knowing which feature class the user is working with.
Add this to your getParameterInfo method:
def getParameterInfo(self):
p_00 = arcpy.Parameter(
displayName="National County Feature Class",
name="county_fc",
datatype="DEFeatureClass",
parameterType="Required",
direction="Input",
)
p_00.value = r"C:\source_files\census_tutorial.gdb\CensusCounties"
params = [p_00]
return params
datatype="DEFeatureClass" tells ArcGIS to show a file browser that accepts feature classes on disk. The name argument, "county_fc", is the internal identifier you will use later in parameterDependencies. Setting .value gives the tool a development default so the dialog opens pre-filled. Update the path to match where you placed source_files/ on your machine.
Save the file, refresh the toolbox, and open the tool dialog. You should see one parameter: a feature class browser pre-filled with the data path. Confirm it shows the CensusCounties layer before moving on.
p_01: The State Field — Definition and Interactivity Together
The next parameter is a field picker that lets the user choose which field contains state abbreviations. Add the parameter definition to getParameterInfo, then immediately wire up its interactive behavior in updateParameters. This is the back-and-forth workflow mentioned in the introduction: define a parameter, make it work, then come back for the next one.
Define p_01 in getParameterInfo
Update your getParameterInfo to add p_01 and include both parameters in the returned list:
p_01 = arcpy.Parameter(
displayName="State Name Field",
name="state_field",
datatype="Field",
parameterType="Required",
direction="Input",
)
p_01.parameterDependencies = ["county_fc"]
p_01.filter.list = ["Text"]
p_01.value = "ST_STUSPS"
params = [p_00, p_01]
return params
datatype="Field" creates a field picker dropdown. On its own a field picker is empty because it does not know which feature class to read from. Setting parameterDependencies = ["county_fc"] points it at p_00 by name. ArcGIS reads the field list from whatever the user has set for county_fc and populates the dropdown automatically. The value in that list must match the name property of the target parameter, not its displayName or its index.
filter.list = ["Text"] restricts the dropdown to string fields only. State abbreviations are strings, so numeric and date fields are not useful here.
The default "ST_STUSPS" is the abbreviation field in the Census data, so most users will not need to change this parameter at all.
Wire up the state dropdown in updateParameters
Now move to updateParameters. When the user picks a state field you want to read every unique value from that field and load them into the next parameter's checklist. Write this logic now, even though the checklist parameter does not exist yet:
def updateParameters(self, parameters):
fc_in = parameters[0]
state_field = parameters[1]
if fc_in.value:
fc_in_path = arcsmith.param.to_path(fc_in)
if arcsmith.param.state(state_field) == "pending":
states = arcsmith.flds.unique_values(fc_in_path, state_field.valueAsText)
# parameters[2] will be the States to Include checklist — defined next
# arcsmith.param.drop_populate(parameters[2], states)
return
Leave the drop_populate call commented out for now. It references parameters[2] which does not exist yet. You will uncomment it after defining the next parameter.
Two things in this code need explanation.
Why arcsmith.param.to_path instead of .valueAsText? When the user selects a layer from the map Contents pane rather than browsing to a file, .valueAsText returns the display name shown in the map (for example, "CensusCounties"), not the underlying file path. arcsmith.param.to_path calls arcpy.Describe on the parameter value object and returns catalogPath, which is always the resolvable absolute file path regardless of how the user provided the input.
What does arcsmith.param.state return? ArcGIS tracks two booleans on every parameter: altered (whether the user has changed it) and hasBeenValidated (whether ArcGIS has run validation on it). arcsmith combines these into four named states that are easier to reason about than the raw booleans:
| State | What it means |
|---|---|
fresh |
Never touched and not yet validated |
pending |
Just changed by the user, not yet validated |
settled |
Not changed, but has been validated (initial load with a default) |
confirmed |
Changed and validated |
Checking for "pending" means the unique-value scan only runs when the user actively picks a new state field. Without this guard, the scan would fire on every pass through updateParameters, including every time the user types a character in an unrelated text box.
Save the file and refresh. The tool dialog should now show two parameters. Changing the feature class path should update the field picker's list automatically.
p_02: States to Include
Now add the multi-value checklist that will hold the available states. Update getParameterInfo again:
p_02 = arcpy.Parameter(
displayName="States to Include",
name="states",
datatype="GPString",
parameterType="Required",
direction="Input",
multiValue=True,
)
params = [p_00, p_01, p_02]
return params
multiValue=True changes the control from a single text box to a checklist. The filter list for this parameter is intentionally not set here. Hardcoding a list of state abbreviations would make the tool brittle and specific to this dataset. Instead, the list is built dynamically from the actual values present in whichever field the user chose.
Now go back to updateParameters and finish what you started. Uncomment the drop_populate line and add a cascade_clear call above it:
def updateParameters(self, parameters):
fc_in = parameters[0]
state_field = parameters[1]
selected_states = parameters[2]
if fc_in.value:
fc_in_path = arcsmith.param.to_path(fc_in)
arcsmith.param.cascade_clear(state_field, [selected_states])
if arcsmith.param.state(state_field) == "pending":
states = arcsmith.flds.unique_values(fc_in_path, state_field.valueAsText)
arcsmith.param.drop_populate(selected_states, states)
return
The cascade_clear call clears selected_states whenever state_field is pending or fresh. Without it, if the user picked FL, GA, AL using one field and then switched to a different field, those three selections would silently persist in the checklist even though they may not be valid values in the new context.
Save and refresh. Change the feature class input, then change the state field. The States to Include checklist should repopulate with the actual unique values from that field.
p_03: Fields to Keep
Add a multi-value field picker that lets the user select which columns to carry through to the outputs:
p_03 = arcpy.Parameter(
displayName="Fields to Keep",
name="fields_to_keep",
datatype="Field",
parameterType="Required",
direction="Input",
multiValue=True,
)
p_03.parameterDependencies = ["county_fc"]
params = [p_00, p_01, p_02, p_03]
return params
Like p_01, this parameter depends on county_fc and ArcGIS will populate the field list automatically when the input changes. No type filter is applied here because the user may want to keep fields of any type.
Making this parameter Required is a deliberate design choice that simplifies the code in execute. If it were Optional and the user left it blank, .valueAsText would return None, and calling .split(";") on None raises an error. By requiring at least one field, that situation never occurs and no defensive code is needed.
This parameter does not need any updateParameters logic. ArcGIS handles the field list population automatically through parameterDependencies.
The two fields to use for this tutorial
For the Census data, select NAMELSAD (the county name) and ST_STUSPS (the state abbreviation). These are the only two fields needed for the outputs this tool produces.
p_04: Output Geodatabase
Add the destination for the output feature classes:
p_04 = arcpy.Parameter(
displayName="Output Geodatabase",
name="output_gdb",
datatype="DEWorkspace",
parameterType="Required",
direction="Input",
)
params = [p_00, p_01, p_02, p_03, p_04]
return params
datatype="DEWorkspace" opens a browser that accepts geodatabases and folders. This parameter needs no updateParameters logic.
p_05 and p_06: A Checkbox Controlling a Text Field
These two parameters work together. A checkbox (p_05) controls whether a text field (p_06) is active. When the checkbox is unchecked, the text field is greyed out and ignored. When it is checked, the text field becomes active and its value is prepended to the output names.
Add both parameters to getParameterInfo:
p_05 = arcpy.Parameter(
displayName="Add Results Prefix",
name="add_prefix",
datatype="GPBoolean",
parameterType="Optional",
direction="Input",
)
p_05.value = False
p_06 = arcpy.Parameter(
displayName="Output Prefix",
name="output_prefix",
datatype="GPString",
parameterType="Optional",
direction="Input",
)
params = [p_00, p_01, p_02, p_03, p_04, p_05, p_06]
return params
Now go to updateParameters and add one line at the bottom to handle the relationship between them:
The full updateParameters method now looks like this:
def updateParameters(self, parameters):
fc_in = parameters[0]
state_field = parameters[1]
selected_states = parameters[2]
add_prefix = parameters[5]
output_prefix = parameters[6]
if fc_in.value:
fc_in_path = arcsmith.param.to_path(fc_in)
arcsmith.param.cascade_clear(state_field, [selected_states])
if arcsmith.param.state(state_field) == "pending":
states = arcsmith.flds.unique_values(fc_in_path, state_field.valueAsText)
arcsmith.param.drop_populate(selected_states, states)
arcsmith.param.checkbox_dependence(add_prefix, output_prefix, hidden_value=None)
return
arcsmith.param.checkbox_dependence handles the full cycle automatically. When add_prefix is unchecked it disables output_prefix and sets its value to None. When the user checks it, output_prefix is enabled. When the state is stable (neither just checked nor just unchecked), the call is a no-op so any text the user has typed is preserved.
Save and refresh. Check and uncheck the Add Results Prefix checkbox and confirm that the Output Prefix field greys in and out accordingly.
p_07: Apply Symbology
Add the last parameter, a checkbox that controls whether .lyrx symbology is applied to the output layers:
p_07 = arcpy.Parameter(
displayName="Apply Symbology",
name="apply_symbology",
datatype="GPBoolean",
parameterType="Optional",
direction="Input",
)
p_07.value = True
params = [p_00, p_01, p_02, p_03, p_04, p_05, p_06, p_07]
return params
The default is True because a styled result is more useful than an unstyled one in most cases. This parameter needs no updateParameters logic.
With all eight parameters defined, save and refresh one more time. The tool dialog should now display all parameters in order, with the prefix field greying correctly based on the checkbox.
execute: Running the Tool
execute runs when the user clicks Run. All parameters have been validated before this method is called, so you can read values directly.
Reading parameter values
Start by pulling every value out of the parameters list at the top of the method. Grouping all the value-reading in one place makes the rest of the method easier to follow:
def execute(self, parameters, messages):
fc_in_path = arcsmith.param.to_path(parameters[0])
state_field = parameters[1].valueAsText
states_list = parameters[2].valueAsText.split(";")
fields_list = parameters[3].valueAsText.split(";")
out_gdb = parameters[4].valueAsText
add_prefix = parameters[5].value
output_prefix = parameters[6].valueAsText
apply_sym = parameters[7].value
A few things are worth noting about how the values are read.
arcsmith.param.to_path(parameters[0]) is used for the feature class for the same reason as in updateParameters. It returns the actual file path whether the input came from the map or the catalog browser.
For multi-value parameters (p_02 and p_03), .valueAsText returns selections as a semicolon-delimited string such as "FL;GA;AL". This is documented arcpy behavior: multi-value parameters always use semicolons as the delimiter when accessed as text. Calling .split(";") converts that into a Python list.
parameters[5].value and parameters[7].value use .value rather than .valueAsText for the boolean checkboxes. .value returns Python True or False. .valueAsText on a boolean returns the strings "true" or "false", and since non-empty strings are always truthy in Python, the else branch of any if statement would be unreachable if you used .valueAsText.
Building the outputs
prefix = f"{output_prefix}_" if add_prefix else ""
where_states = arcsmith.fc.build_where_in(fc_in_path, state_field, states_list)
census_lite = arcsmith.fc.copy_w_fields(
fc_in_path,
f"{out_gdb}/{prefix}census_lite",
fields_list,
keep=True,
where_clause=where_states,
)
state_boundaries = str(arcpy.management.Dissolve(
census_lite,
f"{out_gdb}/{prefix}state_boundaries",
state_field,
))
region_boundary = str(arcpy.management.Dissolve(
census_lite,
f"{out_gdb}/{prefix}region_boundary",
))
arcsmith.fc.build_where_in builds a SQL IN clause from the list of selected states. It inspects the field type on the feature class and handles quoting automatically, producing something like:
arcsmith.fc.copy_w_fields runs FeatureClassToFeatureClass with both a field filter and the where clause in one call. The result is a lean feature class containing only the selected states and only the columns the user chose.
arcpy.management.Dissolve returns an arcpy Result object rather than a string path. Wrapping it in str() extracts the output path, which is what arcsmith.lyr.add expects. Both dissolves operate on census_lite so they inherit the state selection automatically without needing a second where clause.
Adding layers to the map
aprx = arcpy.mp.ArcGISProject("CURRENT")
active_map = aprx.activeMap
if apply_sym:
arcsmith.lyr.add(
census_lite, active_map,
lyr_name="Counties",
lyrx_src=r"C:\source_files\lyrx\county_symb.lyrx",
)
arcsmith.lyr.add(
state_boundaries, active_map,
lyr_name="States",
lyrx_src=r"C:\source_files\lyrx\state_boundary.lyrx",
)
arcsmith.lyr.add(region_boundary, active_map, lyr_name="Region")
else:
arcsmith.lyr.add(census_lite, active_map, lyr_name="Counties")
arcsmith.lyr.add(state_boundaries, active_map, lyr_name="States")
arcsmith.lyr.add(region_boundary, active_map, lyr_name="Region")
return
Notice that Counties and States receive their .lyrx symbology here inside execute, but Region is added without one. Its symbology is applied separately in postExecute. The next section explains why.
postExecute: Applying Symbology After Outputs Are Ready
postExecute runs after execute completes and after ArcGIS has finished adding all outputs to the Contents pane. This timing distinction matters for symbology.
When you add a layer inside execute and immediately try to apply complex symbology to it, you may find that the layer is not fully committed to the map yet. Attribute-based classification, graduated colors, and unique value renderers that need to read field values to build their class breaks can all fail silently in this situation, producing a layer with no visible style and no error message to diagnose. Moving symbology application to postExecute eliminates this problem because the outputs are fully present before the method is called.
The tradeoff is that postExecute is harder to troubleshoot. It runs silently and ArcGIS suppresses errors that occur inside it, so a broken .lyrx path or a mismatched layer name will produce no feedback. Using arcsmith's apply_lyrx here keeps things manageable because it raises a Python ValueError if the named layer is not found, which at least surfaces the failure in the geoprocessing messages pane.
def postExecute(self, parameters):
apply_sym = parameters[7].value
if apply_sym:
aprx = arcpy.mp.ArcGISProject("CURRENT")
active_map = aprx.activeMap
arcsmith.lyr.apply_lyrx(
active_map,
r"C:\source_files\lyrx\region_boundary.lyrx",
lyr_name="Region",
)
return
arcsmith.lyr.apply_lyrx finds every layer in the Contents pane whose TOC name matches "Region" and applies the symbology from the .lyrx file to each one. It uses arcpy.mp to assign the symbology directly to the layer object, which is the approach the arcpy documentation recommends for postExecute.
Symbology on a Single-Output Tool: An Alternative Approach
This tool produces three output layers and adds them manually inside execute, so applying symbology through arcsmith makes sense. If you were building a tool that produces a single output feature class and declared it as a proper output parameter, arcpy gives you a shorter path: you can attach a .lyrx file directly to the output parameter and ArcGIS will attempt to apply it automatically.
Here is what that looks like on the parameter:
p_out = arcpy.Parameter(
displayName="Output Feature Class",
name="output_fc",
datatype="DEFeatureClass",
parameterType="Derived",
direction="Output",
)
p_out.symbology = r"C:\source_files\lyrx\my_style.lyrx"
Setting parameterType="Derived" tells ArcGIS that this output is produced by the tool rather than entered by the user. Setting .symbology to a .lyrx path tells ArcGIS to apply that style when the output is added to the map.
This approach works well for simple symbology: a single fill color, a single stroke style, or any renderer that does not need to read field values to build class breaks. For anything more complex — graduated colors, unique value renderers, or any style that depends on actual data in the output — the automatic application can fail silently and produce an unstyled layer with no error message to explain why.
Applying symbology manually through arcpy.mp is also verbose. Getting a reference to the project, finding the active map, locating the right layer, loading the .lyrx file, pulling the symbology off it, and assigning it to the target layer takes six or more lines of code, every one of which is a place something can go wrong quietly. arcsmith.lyr.apply_lyrx wraps all of that into a single call and raises a ValueError if the named layer is not found in the map, so failures surface instead of disappearing. For anything beyond the simplest fill-and-stroke styles, use arcsmith.lyr.apply_lyrx in postExecute instead of relying on the parameter's .symbology property.
Complete Tool File
# -*- coding: utf-8 -*-
import arcpy
import arcsmith
class Toolbox:
def __init__(self):
self.label = "Census Tutorial Toolbox"
self.alias = "censustutorial"
self.tools = [CensusRegions]
class CensusRegions:
def __init__(self):
self.label = "Census Regions"
self.description = (
"Filter US Census county data to selected states, "
"trim fields, and produce county, state, and region boundary layers."
)
def getParameterInfo(self):
p_00 = arcpy.Parameter(
displayName="National County Feature Class",
name="county_fc",
datatype="DEFeatureClass",
parameterType="Required",
direction="Input",
)
p_00.value = r"C:\source_files\census_tutorial.gdb\CensusCounties"
p_01 = arcpy.Parameter(
displayName="State Name Field",
name="state_field",
datatype="Field",
parameterType="Required",
direction="Input",
)
p_01.parameterDependencies = ["county_fc"]
p_01.filter.list = ["Text"]
p_01.value = "ST_STUSPS"
p_02 = arcpy.Parameter(
displayName="States to Include",
name="states",
datatype="GPString",
parameterType="Required",
direction="Input",
multiValue=True,
)
p_03 = arcpy.Parameter(
displayName="Fields to Keep",
name="fields_to_keep",
datatype="Field",
parameterType="Required",
direction="Input",
multiValue=True,
)
p_03.parameterDependencies = ["county_fc"]
p_04 = arcpy.Parameter(
displayName="Output Geodatabase",
name="output_gdb",
datatype="DEWorkspace",
parameterType="Required",
direction="Input",
)
p_05 = arcpy.Parameter(
displayName="Add Results Prefix",
name="add_prefix",
datatype="GPBoolean",
parameterType="Optional",
direction="Input",
)
p_05.value = False
p_06 = arcpy.Parameter(
displayName="Output Prefix",
name="output_prefix",
datatype="GPString",
parameterType="Optional",
direction="Input",
)
p_07 = arcpy.Parameter(
displayName="Apply Symbology",
name="apply_symbology",
datatype="GPBoolean",
parameterType="Optional",
direction="Input",
)
p_07.value = True
params = [p_00, p_01, p_02, p_03, p_04, p_05, p_06, p_07]
return params
def isLicensed(self):
return True
def updateParameters(self, parameters):
fc_in = parameters[0]
state_field = parameters[1]
selected_states = parameters[2]
add_prefix = parameters[5]
output_prefix = parameters[6]
if fc_in.value:
fc_in_path = arcsmith.param.to_path(fc_in)
arcsmith.param.cascade_clear(state_field, [selected_states])
if arcsmith.param.state(state_field) == "pending":
states = arcsmith.flds.unique_values(fc_in_path, state_field.valueAsText)
arcsmith.param.drop_populate(selected_states, states)
arcsmith.param.checkbox_dependence(add_prefix, output_prefix, hidden_value=None)
return
def updateMessages(self, parameters):
return
def execute(self, parameters, messages):
fc_in_path = arcsmith.param.to_path(parameters[0])
state_field = parameters[1].valueAsText
states_list = parameters[2].valueAsText.split(";")
fields_list = parameters[3].valueAsText.split(";")
out_gdb = parameters[4].valueAsText
add_prefix = parameters[5].value
output_prefix = parameters[6].valueAsText
apply_sym = parameters[7].value
prefix = f"{output_prefix}_" if add_prefix else ""
where_states = arcsmith.fc.build_where_in(fc_in_path, state_field, states_list)
census_lite = arcsmith.fc.copy_w_fields(
fc_in_path,
f"{out_gdb}/{prefix}census_lite",
fields_list,
keep=True,
where_clause=where_states,
)
state_boundaries = str(arcpy.management.Dissolve(
census_lite,
f"{out_gdb}/{prefix}state_boundaries",
state_field,
))
region_boundary = str(arcpy.management.Dissolve(
census_lite,
f"{out_gdb}/{prefix}region_boundary",
))
aprx = arcpy.mp.ArcGISProject("CURRENT")
active_map = aprx.activeMap
if apply_sym:
arcsmith.lyr.add(
census_lite, active_map,
lyr_name="Counties",
lyrx_src=r"C:\source_files\lyrx\county_symb.lyrx",
)
arcsmith.lyr.add(
state_boundaries, active_map,
lyr_name="States",
lyrx_src=r"C:\source_files\lyrx\state_boundary.lyrx",
)
arcsmith.lyr.add(region_boundary, active_map, lyr_name="Region")
else:
arcsmith.lyr.add(census_lite, active_map, lyr_name="Counties")
arcsmith.lyr.add(state_boundaries, active_map, lyr_name="States")
arcsmith.lyr.add(region_boundary, active_map, lyr_name="Region")
return
def postExecute(self, parameters):
apply_sym = parameters[7].value
if apply_sym:
aprx = arcpy.mp.ArcGISProject("CURRENT")
active_map = aprx.activeMap
arcsmith.lyr.apply_lyrx(
active_map,
r"C:\source_files\lyrx\region_boundary.lyrx",
lyr_name="Region",
)
return
Running the Tool
- Open ArcGIS Pro and connect to your
source_files/folder in the Catalog pane. - Right-click
census_tutorial.pytand choose Refresh. - Expand the toolbox and double-click Census Regions.
- The National County Feature Class should be pre-filled. Confirm it points to
CensusCountiesincensus_tutorial.gdb. - Leave State Name Field set to
ST_STUSPSand click into the States to Include field. The checklist should populate with state abbreviations from the data. - Pick the states you want.
- In Fields to Keep, select
NAMELSADandST_STUSPS. - Set Output Geodatabase to an existing
.gdbon your machine. - Optionally check Add Results Prefix and type a short prefix.
- Leave Apply Symbology checked, then click Run.
What Each Output Contains
| Layer | How it is produced | Contents |
|---|---|---|
census_lite |
Filtered copy of CensusCounties | County polygons for the selected states, trimmed to chosen fields |
state_boundaries |
Dissolve by state field | One polygon per state, county boundaries dissolved away |
region_boundary |
Full dissolve | Single polygon covering all selected states combined |
arcsmith Functions Used in This Tutorial
| Function | What it does |
|---|---|
arcsmith.param.to_path(param) |
Resolves a parameter to its absolute catalog path, safe for map-layer inputs |
arcsmith.param.state(param) |
Returns 'fresh', 'pending', 'settled', or 'confirmed' for a parameter |
arcsmith.param.cascade_clear(trigger, dependents) |
Clears dependent parameters when the trigger changes |
arcsmith.param.drop_populate(param, values) |
Sets a dropdown filter list from a Python list |
arcsmith.param.checkbox_dependence(checkbox, dependents) |
Enables and disables dependent parameters based on a checkbox |
arcsmith.flds.unique_values(fc, field) |
Returns sorted unique values present in a field |
arcsmith.fc.build_where_in(fc, field, values) |
Builds a SQL IN clause for a list of values |
arcsmith.fc.copy_w_fields(fc, output, fields, keep, where_clause) |
Copies a feature class with field and row filters in one call |
arcsmith.lyr.add(src, map, lyr_name, lyrx_src) |
Adds a data source to a map, optionally applying symbology |
arcsmith.lyr.apply_lyrx(map, lyrx, lyr_name) |
Applies symbology from a .lyrx file to named layers in the map |