Table of Contents
Introduction
You have made a selection based on attributes or location in ArcGIS Pro (or via a script or tool) and you want to set a definition query that only shows the selected records. Let’s walk through making that tool in ArcGIS Pro with ArcPy
ArcGIS Pro Definition Query Masterclass with ArcPy & Python
Unlock the power of Definition Queries in ArcGIS Pro through this comprehensive course that dives deep into the intricacies of managing and utilizing definition queries with spatial data using ArcPy and Python scripting. Definition Queries provide a dynamic way to filter and display specific subsets of your data, enabling you to create more insightful maps and analyses. In this course, we will focus exclusively on Definition Queries, covering their creation, modification, and application through hands-on exercises and real-world scenarios. Accredited by the Association for Geographic Information (AGI) for 1 CPD point.
Custom Tool Syntax
The syntax for the Definition Query from Selection tool is as follows…
defQueryFromSelection(
in_table,
fld,
{delete_dq}
)
The in_table parameter represents the layer or table in ArcGIS Pro. fld is the field to base the definition query on, and delete_dq is optional and will delete any previous definition queries for the in_table.
Import the ArcPy module
ArcPy is a Python site package that enables automation, analysis, and management of geographic data within the ArcGIS software environment.
import arcpy
Define the parameters
We want three user input parameters; in_table as a Feature Layer or Table View, fld as a Field name (currently this will only be the OID field), and the delete_dq as a Boolean gives the user a choice to delete any previous definition queries set on the layer/table or to simply add a new definition query.
## input feature layer or table from the APRX
in_table = arcpy.GetParameter(0)
## the field to base the definition query on
fld = arcpy.GetParameterAsText(1)
## delete any previous def queries on the layer/table or not
delete_dq = arcpy.GetParameterAsText(2)
Get Unique Values function
We’ll create a function that returns a unique list of values from the fld input using the Search Cursor and set comprehension. At the moment we are only catering for the OID field which is always unique but this function will give us the option to expand for other unique field types in the future or create more complex definition queries based on selections.
def getUniqueValues(table, field):
"""
Get a list of unique values from a single field in a table
Args:
table (str): table or feature class in a geodatabase
field (str): field to get unique values from
Returns:
a sorted set of unique entries
"""
with arcpy.da.SearchCursor(table, [field]) as cursor:
return sorted({row[0] for row in cursor if row[0] != None})
Required Python object for the tool
We have a few required objects to help with the workflow. We create an ArcGISProject object for the open APRX. We then check whether the input from our first parameter (in_table) is a Layer or a Table and use that ArcGISProject object to access the layer or table object via the active map. Last, we need the field type from the fld input parameter, it will be OID for now and we will limit this in the parameters validation of the tool in ArcGIS Pro.
## access the APRX
aprx = arcpy.mp.ArcGISProject("CURRENT")
## figure out if input is a layer or table and access the layer or table
## in the active map
if isinstance(in_table, arcpy._mp.Layer):
in_table = aprx.activeMap.listLayers(in_table.longName)[0]
elif isinstance(in_table, arcpy._mp.Table):
in_table = aprx.activeMap.listTables(in_table.longName)[0]
## get the field type from the fld parameter
fld_type = [field.type for field in arcpy.ListFields(in_table) if field.name == fld][0]
Get Unique Values
We then call our getUniqueValues() function
## get unique values to base definition query on
values = getUniqueValues(in_table, fld)
Delete Definition Queries - Optional Parameter
If the user selects to delete any previous definition queries, we will remove them before adding the new query.
if delete_dq == "true":
in_table.updateDefinitionQueries(None)
Create Definition Query
All there is left to do is create the new definition query. We’ll add a warning if a field type was selected that we do not handle just yet, this won’t come into play as we will limit input to an OID field type but we will look at upgrading this tool in the future.
## only handles OID fields for now
if fld_type in ("OID") :
in_table.definitionQuery = "{0} IN ({1})".format(fld, ",".join(str(x) for x in values))
else:
arcpy.AddWarning("Field type not supported at this time")
ArcGIS Pro Definition Query Masterclass with ArcPy & Python
Unlock the power of Definition Queries in ArcGIS Pro through this comprehensive course that dives deep into the intricacies of managing and utilizing definition queries with spatial data using ArcPy and Python scripting. Definition Queries provide a dynamic way to filter and display specific subsets of your data, enabling you to create more insightful maps and analyses. In this course, we will focus exclusively on Definition Queries, covering their creation, modification, and application through hands-on exercises and real-world scenarios. Accredited by the Association for Geographic Information (AGI) for 1 CPD point.
Create the tool in ArcGIS Pro
Save your script and open up ArcGIS Pro. Right-click on your toolbox/toolset of choice and select New > Script. The New Script window will appear. In the General tab set Name to defQueryFromSelection, Label to Add Definition Query based on Selection, and the Description as per below or any description you feel is apt.
In the Parameters tab set as below. Set the Data Type for the Input Layer or Table to Feature Layer, Table View. Set the Filter for the Query Field to OID field only, and set the Dependency to the first parameter in_table. Set the Type to Optional for the Delete Previous Definition Queries and the Default to true.
In the Execution tab, click the folder icon in the top-right corner and add your saved Python script.
In the Validation tab we want set an error message if a selection has not been made on the layer/table as we do not want to ever create a definition query for the entire dataset.
def updateMessages(self):
# Modify the messages created by internal validation for each tool
# parameter. This method is called after internal validation.
if self.params[0].altered:
desc = arcpy.Describe(self.params[0].value)
if desc.FIDSet:
self.params[0].clearMessage()
else:
self.params[0].setErrorMessage("There must be a selection on the layer/table")
return
You can download the tool and other custom tools over on this page. This tool is in the Custom Tools on a Basic License with ArcPy section.
All the code in one place
You can find the entire code workflow below with links to important components in the documentation that were used.
import arcpy
################################################################################
## Documentation Links:
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/functions/getparameterastext.htm
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/data-access/searchcursor-class.htm
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/mapping/arcgisproject-class.htm
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/mapping/map-class.htm
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/functions/listfields.htm
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/classes/field.htm
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/mapping/layer-class.htm
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/functions/addmessage.htm
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/functions/addwarning.htm
##
## ArcGIS Pro 3.2.0
##
################################################################################
################################################################################
## USER INPUTS #################################################################
## input feature layer or table from the APRX
in_table = arcpy.GetParameter(0)
## the field to base the definition query on
fld = arcpy.GetParameterAsText(1)
## delete any previous def queries on the layer/table or not
delete_dq = arcpy.GetParameterAsText(2)
################################################################################
## FUNCTIONS ###################################################################
def getUniqueValues(table, field):
"""
Get a list of unique values from a single field in a table
Args:
table (str): table or feature class in a geodatabase
field (str): field to get unique values from
Returns:
a sorted set of unique entries
"""
with arcpy.da.SearchCursor(table, [field]) as cursor:
return sorted({row[0] for row in cursor if row[0] != None})
################################################################################
## REQUIRED OBJECTS ############################################################
## access the APRX
aprx = arcpy.mp.ArcGISProject("CURRENT")
## figure out if input is a layer or table and access the layer or table
## in the active map
if isinstance(in_table, arcpy._mp.Layer):
in_table = aprx.activeMap.listLayers(in_table.longName)[0]
elif isinstance(in_table, arcpy._mp.Table):
in_table = aprx.activeMap.listTables(in_table.longName)[0]
## get the field type from the fld parameter
fld_type = [field.type for field in arcpy.ListFields(in_table) if field.name == fld][0]
################################################################################
## GET UNIQUE VALUES ###########################################################
arcpy.AddMessage("Getting unique values")
## get unique values to base definition query on
values = getUniqueValues(in_table, fld)
################################################################################
## DELETE DEF QUERIES ##########################################################
if delete_dq == "true":
arcpy.AddMessage("Deleting previous definition queries")
in_table.updateDefinitionQueries(None)
################################################################################
## UPDATE DEFINITION QUERY #####################################################
arcpy.AddMessage("Updating Definition Query")
## only handles OID fields for now
if fld_type in ("OID") :
in_table.definitionQuery = "{0} IN ({1})".format(fld, ",".join(str(x) for x in values))
else:
arcpy.AddWarning("Field type not supported at this time")
################################################################################