Do you work on the IS side of GIS, in ESRI geodatabases? The day may come when you are faced with culling domain values from what seems like a gazillion tables, organizing and verifying the values, and loading them into multiple domains.
Sorting through domain values by visually inspecting each column and loading domains one by one can get tiresome. It may be that you decide that the best thing to do is to learn how to script and automate much of the domain value sorting, validating and loading process.
Whether you are just starting out and want some tips, or whether you don't like scripting and just want something that works--this blog is for you.
This series of posts on domains will put you on your way. Each post describes a python script tool that can be added to an ArcGIS custom toolbox. Collectively, the tools automate the domain workflow, reduce errors, and save time.
Summary Of Tool
The Batch Domain
Loading tool loads values to multiple geodatabse coded
value domains from a single input table. Each domain code value must be
in a separate row in the loading table.
The following columns must exist in the domain
loading table:
Domain name
Code (value)
Description
Domain column type, i.e., Long, Double, Text, etc.
Domain values are only loaded if the domain already exists (guards against misspellings).
With the APPEND option, if an existing domain value matches a value to be loaded, the existing value is deleted and replaced.
Domain values are sorted by code within each domain after being loaded.
This tool supports all levels of geodatabases.
Illustration
Usage
There is no usage for this tool.
Syntax
BatchTableToDomain_mytools
(Domain_Value_Table, Code_Column, Description_Column,
Domain_Column_Type_Column, Domain_Name_Column, Input_Workspace,
Update_Option)
Parameter | Explanation | Data Type |
---|---|---|
Domain_Value_Table |
Dialog Reference Domain loading table ___________________ Python Reference Doamin loading table |
Table View |
Code_Column |
Dialog Reference Coded domain value column, obtained from Domain_Values_Table ___________________ Python Reference Coded domain value column, obtained from Domain_Values_Table |
Field |
Description_Column |
Dialog Reference Coded domain description column, obtained from Domain_Values_Table ___________________ Python Reference Coded domain description column, obtained from Domain_Values_Table |
Field |
Domain_Column_Type_Column |
Dialog Reference Domain column type field, obtained from Domain_Values_Table ___________________ Python Reference Domain column type field, obtained from Domain_Values_Table |
Field |
Domain_Name_Column |
Dialog Reference Domain name column, obtained from Domain_Values_Table ___________________ Python Reference Domain name column, obtained from Domain_Values_Table |
Field |
Input_Workspace |
Dialog Reference Target workspace for domain values ___________________ Python Reference Target workspace for domain values |
Workspace |
Update_Option |
Dialog Reference APPEND or REPLACE target domain values ___________________ Python Reference APPEND or REPLACE target domain values |
String |
Code Samples
BatchDomainLoading.py
See below for tool source code.
import arcpy, traceback, sys arcpy.env.overwriteOutput = True def get_distinct_col_val(in_layer, in_col): # RETURNS A LIST OF THE UNIQUE VALUES CONTAINED IN THE INPUT COLUMN. l = [row[0] for row in arcpy.da.SearchCursor(in_layer, in_col)] list_of_distinct_values = set(l) return list_of_distinct_values def dict_from_2cols(in_layer, col_1, col_2): # CREATES A DICTIONARY FROM TWO COLUMNS; COL 1 IS KEY, COL 2 IS VALUE. d = {} with arcpy.da.SearchCursor(in_layer, [col_1, col_2]) as c: for r in c: d[r[0]] = r[1] return d def dict_from_coded_value_domains(input_wksp): # CREATES A DICTIONARY FROM CODED VALUE DOMAINS. KEY=DOMAIN NAME; VALUE=DOMAIN VALUES. d = {} doms = arcpy.da.ListDomains(input_wksp) for dom in doms: if dom.domainType == 'CodedValue': d[dom.name] = dom.codedValues.items() return d def get_delimited_field(in_layer, fld): # RETURNS A DELIMITED FIELD NAME STRING FOR INPUT FIELD d = arcpy.Describe(in_layer) dCatPath = d.catalogPath fld_delimited = arcpy.AddFieldDelimiters(dCatPath, fld) return fld_delimited def get_sel_count(in_layer): # RETURNS SELECTION COUNT AS INTEGER selCnt = int(arcpy.GetCount_management(in_layer).getOutput(0)) return selCnt if __name__ == "__main__": domain_value_table = arcpy.GetParameterAsText(0) # TABLEVIEW code_field = arcpy.GetParameterAsText(1) # FIELD, OBTAINED FROM DOMAIN_VALUE_TABLE desc_field = arcpy.GetParameterAsText(2) # FIELD, OBTAINED FROM DOMAIN_VALUE_TABLE type_field = arcpy.GetParameterAsText(3) # FIELD, OBTAINED FROM DOMAIN_VALUE_TABLE domain_name_field = arcpy.GetParameterAsText(4) # FIELD, OBTAINED FROM DOMAIN_VALUE_TABLE input_wksp = arcpy.GetParameterAsText(5) # WORKSPACE update_option = arcpy.GetParameterAsText(6) # STRING # FILTER, APPEND OR REPLACE target_ddesc = "" try: arcpy.AddMessage(" ") # RETRIEVE LIST OF SOURCE DOMAIN NAMES FROM DICTIONARY src_dn_list = get_distinct_col_val(domain_value_table, domain_name_field) # CREATE DICTIONARY OF TARGET CODED-VALUE DOMAIN NAMES AND VALUES targ_dn_dict = dict_from_coded_value_domains(input_wksp) targ_dn_list = targ_dn_dict.keys() # GET DELIMITED SOURCE DOMAIN NAME FIELD domain_name_field_delim = get_delimited_field(domain_value_table, domain_name_field) # ITERATE SOURCE DOMAIN NAMES... for src_dn in src_dn_list: domain_exists = False # SELECT SOURCE DOMAIN VALUES IN LOADING TABLE FOR THIS DOMAIN expr = domain_name_field_delim + " = '" + str(src_dn) + "'" arcpy.SelectLayerByAttribute_management(domain_value_table, "CLEAR_SELECTION") etl_tab_sel = arcpy.SelectLayerByAttribute_management(domain_value_table, "NEW_SELECTION", expr) cnt = get_sel_count(domain_value_table) # ASSUME TARGET DOMAIN NAME IS SAME AS SOURCE target_dn = src_dn # IF ANYTHING IS SELECTED... if cnt > 0: # IF TARGET DOMAIN EXISTS... (DON'T WANT TO LOAD MISSPELLED DOMAIN) domain_exists = target_dn in targ_dn_list if domain_exists == True: # OUTPUT SELECTED SOURCE DOMAIN VALUES IN LOADING TABLE TO TEMP TABLE arcpy.Delete_management("in_memory") arcpy.TableToTable_conversion(domain_value_table, "in_memory", "src_dom_val") # ASSUMES DOMAIN COLUMN TYPE IS TEXT TO START WITH dom_col_type = "Text" # SET DOMAIN COLUMN TYPE TO DOUBLE IF APPROPRIATE with arcpy.da.SearchCursor("in_memory/src_dom_val", type_field) as type_cur: for row in type_cur: if row[0] == "Double": dom_col_type = "Double" break # IF DOMAIN COLUMN TYPE IS DOUBLE, # ADD A CODE_DBL COLUMN AND POPULATE IT. if dom_col_type == "Double": arcpy.AddField_management("in_memory/src_dom_val", "code_dbl", field_type="Double", field_alias="code_dbl") arcpy.CalculateField_management("in_memory/src_dom_val", "code_dbl", "!" + code_field + "!", "PYTHON_9.3") # IF UPDATE OPTION IS REPLACE, RUN TABLE TO DOMAIN TOOL if update_option == "REPLACE": arcpy.AddMessage(" Replacing %s domain with domain and values from %s domain..." % (target_dn, src_dn)) if dom_col_type == "Double": arcpy.TableToDomain_management("in_memory/src_dom_val", "code_dbl", desc_field, input_wksp, target_dn, target_ddesc, update_option) else: arcpy.TableToDomain_management("in_memory/src_dom_val", code_field, desc_field, input_wksp, target_dn, target_ddesc, update_option) # ELSE IF UPDATE OPTION IS APPEND... else: arcpy.AddMessage(" Appending %s domain with domain and values from %s domain..." % (target_dn, src_dn)) # CREATE LIST OF ALL TARGET DOMAIN VALUES val_to_del = [] all_target_val = [] all_target_items = targ_dn_dict[target_dn] for val, desc in all_target_items: all_target_val.append(val) # CREATE LIST OF TARGET DOMAIN VALUES THAT MATCH SOURCE # DOMAIN VALUES TO BE APPENDED. THESE WILL BE DELETED. with arcpy.da.SearchCursor("in_memory/src_dom_val", code_field) as temp_cur: for row in temp_cur: if row[0] in all_target_val: val_to_del.append(row[0]) arcpy.AddMessage(" Replacing value " + row[0]) # DELETE EXISTING VALUES if len(val_to_del)>0: arcpy.DeleteCodedValueFromDomain_management(input_wksp, target_dn, val_to_del) # RUN TABLE TO DOMAIN TOOL if dom_col_type == "Double": arcpy.TableToDomain_management("in_memory/src_dom_val", "code_dbl", desc_field, input_wksp, target_dn, target_ddesc, update_option) else: arcpy.TableToDomain_management("in_memory/src_dom_val", code_field, desc_field, input_wksp, target_dn, target_ddesc, update_option) # SORT DOMAIN BY CODE arcpy.AddMessage(" Sorting domain...") arcpy.SortCodedValueDomain_management(input_wksp, target_dn, "CODE", "ASCENDING") else: arcpy.AddMessage(" Target domain %s not found. Source domain %s values not added." % (src_dn, target_dn)) # ELSE LET USER KNOW DOMAIN VALUES WERE NOT FOUND else: arcpy.AddMessage(" No values found for source domain %s, nothing added to target domain %s" % (src_dn, target_dn)) arcpy.SelectLayerByAttribute_management(domain_value_table, "CLEAR_SELECTION") arcpy.AddMessage(" ") # SET OUTPUT PARAMETER arcpy.SetParameter(7, input_wksp) # WORKSPACE except: # PRINT ERROR MESSAGES tb = sys.exc_info()[2] tbinfo = traceback.format_tb(tb)[0] pymsg = tbinfo + "\n" + str(sys.exc_type)+ ": " + str(sys.exc_value) arcpy.AddError("Python Messages: " + pymsg + " GP Messages: " + arcpy.GetMessages(2)) finally: arcpy.SelectLayerByAttribute_management(domain_value_table, "CLEAR_SELECTION") del domain_value_table, code_field del desc_field, domain_name_field, input_wksp, update_option arcpy.Delete_management("in_memory")
Tags
Domain, geodatabase, TableToDomain, dictionary,
DeleteCodedValueFromDomain, TableToTable, SortCodedValueDomain,
AddField, SearchCursor, CalculateField, SelectLayerByAttribute,
Describe, ListDomains, GetCount, AddFieldDelimiters
Credits
Ruth Bowers, 2015
Use limitations
Tested as script tool in ArcGIS 10.2.2
No comments:
Post a Comment