Sunday, April 17, 2016

Domains: Export Geodatabase Domains


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 Export Geodatabase Domains tool exports all ESRI geodatabase domains and associated values to a table.


For column heading prefixes, choose from: "For ETL Source", "For ETL Target" or "Other. This will result in, for example, SourceDomain, TargetDomain, or just Domain. Default is "Other".


Output table columns are:


  1. Domain (domain name)

  2. DomainType ("CodedValue" or "Range")

  3. Code (CodedValue domains only--Code value)

  4. Description (CodedValue domains only--Code description)

  5. RangeMin (Range domains only--Minimum value in the range)

  6. RangeMax (Range domains only--Maximum value in the range)

  7. DomainColType (Domain column type)

  8. join_field (Code and Description in lower case, concatenated with an underscore)

  9. Code_Description (Code and Description, concatenated with an underscore)


Columns are populated as appropriate for each domain type. The last two columns are designed for quality assurance.


Use the output from this tool for verifying and editing domain values, for domain documentation, as well as for a domain loading table (with the Batch Domain Loading tool in this toolbox).


Illustration

Export Geodatabase Domains tool illustration

Usage


Syntax


GdbDomainsToTable_mytools (Input_Workspace, Column_Heading_Prefix_To_Use, Output_Workspace, Output_Table_Name)

Parameter Explanation Data Type
Input_Workspace Dialog Reference

Geodatabase whose domains and values you want to list


Python Reference

Geodatabase whose domains and values you want to list

Workspace
Column_Heading_Prefix_To_Use Dialog Reference

Choice "For ETL Source", "For ETL Target" or "Other" - Default Other.


Python Reference

Choice "For ETL Source", "For ETL Target" or "Other" - Default Other.

String
Output_Workspace Dialog Reference

Output workspace


Python Reference

Output workspace

Workspace
Output_Table_Name Dialog Reference

Output table name


Python Reference

Output table name

String

Code Samples

ExportGeodatabaseDomains.py

See below for tool source code.

import arcpy, traceback, sys, os
arcpy.env.overwriteOutput = True

def insert_rows(in_layer, field_list, rows_to_insert):
    # USES DA.INSERTCURSOR TO INSERT ROWS OF DATA INTO INPUT TABLE.
    # PARAMETERS: INPUT LAYER, LIST OF FIELD NAME STRINGS, LIST OF DATA ROW LISTS.
    cursor = arcpy.da.InsertCursor(in_layer, field_list)
    for row in rows_to_insert:
        cursor.insertRow(row)
    del cursor
    
def unicode2ascii(in_string):
    '''CONVERTS A STRING WITH UNICODE CHARACTERS TO ASCII (TO USE AS STRING).
    REMOVES LEFT AND RIGHT SLANTED SINGLE AND DOUBLE QUOTES AND M-DASHES'''
    s = in_string
    s = unicode(val).replace(u'\u2018', u"'").replace(u'\u2019', u"'").replace(u'\u2013', u'-').replace(u'\u201c', u'"').replace(u'\u201d', u'"')
    s.encode('ascii')
    return s

    
if __name__ == "__main__":

    in_workspace = arcpy.GetParameterAsText(0) # WORKSPACE
    prefix_chosen = arcpy.GetParameterAsText(1) # STRING, FILTER "For ETL Source",
                                                # "For ETL Target", "Other"
    out_workspace = arcpy.GetParameterAsText(2) # WORKSPACE
    out_table_name = arcpy.GetParameterAsText(3) # STRING
    
    try:

        arcpy.AddMessage(" ")

        # CREATE FIELD NAMES
        if prefix_chosen == "For ETL Source":
            n_prefix = "Source"
            s_prefix = "SrcDom"
        elif prefix_chosen == "For ETL Target":
            n_prefix = "Target"
            s_prefix = "TrgDom"
            n_suffix = ""
        else:
            n_prefix = ""
            s_prefix = ""

        col_name = n_prefix + "Domain"
        col_type = n_prefix + "DomainType"
        col_code = n_prefix + "Code"
        col_desc = n_prefix + "Description"
        col_min = s_prefix + "RangeMin"
        col_max = s_prefix + "RangeMax"
        col_ctype = s_prefix + "DomainColType"
        col_join = "join_field"
        col_code_desc = "Code_Description"
        
        arcpy.AddMessage("     Getting domain names from workspace...")
        domains = arcpy.da.ListDomains(in_workspace)
        
        arcpy.AddMessage("     Creating table...")
        out_table = arcpy.CreateTable_management(out_workspace, out_table_name)
        field_names = [col_name, col_type, col_code, col_desc, col_min, col_max, col_ctype, col_join, col_code_desc]
        for fn in field_names:
            arcpy.AddField_management(out_table, str(fn), "text", field_length=255)

        
        arcpy.AddMessage("     Getting domain values from workspace...")
        # CREATE LIST TO HOLD ROWS OF DATA TO INSERT INTO OUTPUT TABLE
        rows_to_insert = []
        # ITERATE DOMAINS
        for domain in domains:
            domain_name = domain.name
            if domain.domainType == 'CodedValue':
                t = []
                # ITERATE DOMAIN VALUES
                for val, desc in domain.codedValues.items(): 
                    # CONVERT UNICODE CHARACTERS TO ASCII TO REMOVE INVALID CHARS
                    asc_val = unicode2ascii(val)
                    asc_desc = unicode2ascii(desc)
                    # CREATE INSERT-ROW LIST AND APPEND TO ROWS-TO-INSERT LIST
                    t = [str(domain_name), str(domain.domainType), str(asc_val), str(asc_desc), None, None, str(domain.type), 
                         str(domain_name.lower()) + "_" + str(asc_val).lower(), str(domain_name) + "_" + str(asc_val)]
                    rows_to_insert.append(t)
            elif domain.domainType == 'Range':
                t = []
                # CREATE INSERT-ROW LIST FOR RANGE DOMAIN AND APPEND TO ROWS-TO-INSERT LIST
                if domain.type in ("Long", "Short"):
                    # MAKE SURE INTEGERS COME OUT AS INTEGERS
                    t = [str(domain_name), str(domain.domainType), None, None, str(int(domain.range[0])), str(int(domain.range[1])), 
                         str(domain.type), str(domain_name.lower()) + "_" + str(int(domain.range[0])) + "_" + str(int(domain.range[1])), 
                         str(domain_name) + "_" + str(int(domain.range[0])) + "_" + str(int(domain.range[1]))]
                else:
                    t = [str(domain_name), str(domain.domainType), None, None, str(domain.range[0]), str(domain.range[1]), str(domain.type), 
                         str(domain_name.lower()) + "_" + str(domain.range[0]) + "_" + str(domain.range[1]), 
                         str(domain_name) + "_" + str(domain.range[0]) + "_" + str(domain.range[1])]
                rows_to_insert.append(t)
 
        # POPULATE OUTPUT TABLE
        arcpy.AddMessage("     Creating output table...")
        insert_rows(out_table, field_names, rows_to_insert)
        
        # SET OUTPUT PARAMETER
        arcpy.SetParameter(4, out_table) # TABLEVIEW
                
        arcpy.AddMessage(" ")
        
    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:
        del in_workspace, prefix_chosen, out_workspace, out_table_name
        del domains, rows_to_insert, out_table
        del col_name, col_type, col_code, col_desc, col_min, col_max

Tags


geodatabase, domain, Describe, CreateTable, ListDomains, InsertCursor, AddField

Credits


Ruth Bowers, 2015, 2016.


Use limitations


ESRI geodatabases only. Tested as ArcGIS 10.2.2 python script tool in custom toolbox.