Problem: Creating subtypes for feature classes is manual and time-consuming. Doing it for one feature class is annoying, but doing it for multiple feature classes that use the same subtypes is extremely inefficient.
The Data Management tool "Add Subtype" only allows the user to add a single subtype at a time. In batch mode, it allows a batch addition of codes, but not descriptions. Subtypes must be integers, so the description is important.
The Manage Subtypes dialog box also only allows you to manually type in one new subtype at a time. The paste option, which would be another great time saver if you could paste in directly from excel or a domain, does not support pasting more than one cell of data at a time.
Existing Idea (Give it a kudo!): Allow pasting subtypes from one feature class to another
This Idea: My idea is to allow using a domain stored within the geodatabase to populate the subtype codes and descriptions.
Ngl I kind of assumed this functionality was already there. Weird.
Thanks for this Idea @LizAbbey
This functionality is not available as a single click button or GP tool but can be achieved with a few quick lines of python. Below I've included a sample that will allow you to use any integer domain to create subtypes on a feature class. It could be adapted with minor changes to suit non-integer domains as well.
workspace: str = "<insert workspace path here>"
domain_name: str = "<inser name of domain in workspace>"
target_table: str = "<path to target table>"
subtype_field: str = "<name of integer subtype field in target_table>"
st_tbl =arcpy.management.DomainToTable(
workspace, domain_name, 'memory\\temp_out_tbl', 'code', 'desc'
)
arcpy.management.SetSubtypeField(
target_table, subtype_field
)
with arcpy.da.SearchCursor(st_tbl, ['code', 'desc']) as cursor:
for row in cursor:
arcpy.management.AddSubtype(target_table, row[0], row[1])
del cursor
arcpy.management.Delete(st_tbl)
Thanks for the code, @SSWoodward. As written it doesn't prevent the user from nominating a non-integer domain, or from selecting a subtype field that is not an integer type - both scenarios make the script fail.
I was able to save this as a python script inside a toolbox and added in some validation to only allow selecting an integer field for the target subtype field. I wasn't able to limit the domains to integer domains though (limited python knowledge). My workaround is to prefix subtype domain names with "subtype_" so I know they're formatted correctly.
This is an excellent workaround for me and I have posted the entire script with screenshots below.
Parameters:
Execution:
Screenshot
Code:
import arcpy
def add_subtypes_from_domain(workspace: str, domain_name: str, target_table: str, subtype_field: str):
"""
Adds subtypes to a feature class or table based on a coded value domain.
Parameters:
workspace (str): Path to the geodatabase containing the domain.
domain_name (str): Name of the domain to extract codes from.
target_table (str): Path to the feature class or table to update.
subtype_field (str): Name of the integer field to use for subtypes.
"""
arcpy.env.workspace = workspace
temp_table = "memory\\temp_out_tbl"
try:
# Convert domain to table
st_tbl = arcpy.management.DomainToTable(workspace, domain_name, temp_table, 'code', 'desc')
# Set the subtype field
arcpy.management.SetSubtypeField(target_table, subtype_field)
# Add subtypes from the domain table
with arcpy.da.SearchCursor(st_tbl, ['code', 'desc']) as cursor:
for code, desc in cursor:
arcpy.management.AddSubtype(target_table, code, desc)
finally:
# Clean up
if 'cursor' in locals():
del cursor
if arcpy.Exists(temp_table):
arcpy.management.Delete(temp_table)
if __name__ == "__main__":
workspace = arcpy.GetParameterAsText(0)
domain_name = arcpy.GetParameterAsText(1)
target_table = arcpy.GetParameterAsText(2)
subtype_field = arcpy.GetParameterAsText(3)
add_subtypes_from_domain(workspace, domain_name, target_table, subtype_field)
Validation:
Screenshot
Code:
import arcpy
class ToolValidator:
def __init__(self):
self.params = arcpy.GetParameterInfo()
def initializeParameters(self):
return
def updateParameters(self):
# Populate domain names from workspace (no type filtering)
if self.params[0].altered and self.params[0].value:
workspace = self.params[0].valueAsText
arcpy.env.workspace = workspace
try:
domains = arcpy.da.ListDomains(workspace)
domain_names = [d.name for d in domains]
self.params[1].filter.list = domain_names
except Exception:
self.params[1].filter.list = []
# Populate subtype field list with only Integer or SmallInteger fields
if self.params[2].altered and self.params[2].value:
target_table = self.params[2].valueAsText
try:
fields = arcpy.ListFields(target_table)
int_fields = [
f.name for f in fields
if f.type in ["Integer", "SmallInteger"]
]
self.params[3].filter.list = int_fields
except Exception:
self.params[3].filter.list = []
return
def updateMessages(self):
return
@LizAbbey Great improvements!
Adding that validation really takes this to the next level. I'm glad you were able to turn my simple example into a full fledged GP tool. We love to see it.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.