List domain values for a field

9975
7
04-11-2019 07:59 AM
FugroICT
New Contributor II

I am quite stuck here: how do I list domain values for a field in a feature class?

To give you a broader context: after I got the domain values, I need to compare the values with the one from a shapefile and choose the correct one, strip the numbers from the text of the shapefile and populate the fields in my feature class.

I.e. shapefile value: "100 - apple" corresponds to domain value "abcd100 Apple"

So to begin with, I have so far failed to even list said domain values. Any code snippet would work and thanks for you help in advance

Tags (1)
0 Kudos
7 Replies
Egge-Jan_Pollé
MVP Regular Contributor

Hi Fugro ICT‌,

Domain values in a File Geodatabase (FGDB) are not stored at Feature Class level, but at FGDB level, i.e. a domain can be used in any Feature Class in the FGDB. This is done by assigning the domain to a certain field in a certain Feature Class.

That said, you can list your domain values like this (example taken from the docs: ListDomains—Help | ArcGIS Desktop - replace the Boston.gdb with yours):

import arcpy

domains = arcpy.da.ListDomains("C:/Boston/Boston.gdb")

for domain in domains:
    print('Domain name: {0}'.format(domain.name))
    if domain.domainType == 'CodedValue':
        coded_values = domain.codedValues
        for val, desc in coded_values.items():
            print('{0} : {1}'.format(val, desc))
    elif domain.domainType == 'Range':
        print('Min: {0}'.format(domain.range[0]))
        print('Max: {0}'.format(domain.range[1]))

Is this what you are looking for?

Egge-Jan

FugroICT
New Contributor II

Thanks! I have been staring at this little snippet today for far too long already  It gives me a nice list of all my domains for the gdb but how do I get only the one from my FC and from the specific field?

0 Kudos
DanPatterson_Retired
MVP Emeritus

Field—ArcPy classes | ArcGIS Desktop 

domain
(Read and Write)

The name of the associated domain.

Suggesting you will have to examine your featureclasses (ListFeatureClasses, their fields (ListFields) and check to see if the fields have that property

DAVIDWADSWORTH_LVVWD
New Contributor III

Thanks! I am going to be using this to pull values of a domain, then populate to a list that will be leveraged with updateParameters within the properties of a python script tool as a value list. I couldn't see another way of loading a domain to the script tool gui otherwise. 

0 Kudos
RandyBurton
MVP Alum

When I wanted a count of domain values used in all fields in a feature, I used the following.  It ignores 'Range' type domains, but it might give you some ideas.  To limit it to a specific field, you could use something like:  field.name == 'field_name'.

import arcpy

gdb = r"C:\path\to\file.gdb"
arcpy.env.workspace = gdb # set environment for arcpy
fc = 'feature_name' # a feature in the geodatabase


# gdb domains to dictionary # # # # # # #
domDict = {} # empty dictionary
domains = arcpy.da.ListDomains(gdb)
for domain in domains:
    if domain.domainType == 'CodedValue':
        if domain.name not in domDict:
            vList = [] # empty list
            coded_values = domain.codedValues
            for val, desc in coded_values.items():
                vList.append({val:desc})
        domDict[domain.name] = vList
# print domDict

# read feature's fields and domains information # # # # # # #

fields = arcpy.ListFields(fc)
fldDict = {} # empty dictionary
for field in fields:
    if len(field.domain):
        fldDict[field.name] = field.domain
# print fldDict

# count values in fields with domains # # # # # # #
domCount = {}
fldList = list(fldDict.keys())
with arcpy.da.SearchCursor(fc, fldList) as rows:
    for row in rows:
        for i, f in enumerate(fldList):
            # print i, fldList, row # index, field name, field value
            if fldList not in domCount:
                domCount[fldList] = {}
            if row not in domCount[fldList]:
                domCount[fldList][row] = 1
            else:
                domCount[fldList][row] += 1
del rows # release any locks
# print domCount

# output the results # # # # # # #
for k, v in domCount.iteritems():
    print "Field: {}".format(k)
    print "  Domain: {}".format(fldDict)
    for dLst in domDict[fldDict]:
        for k1, v1 in dLst.iteritems():
            if k1 in v:
                print "    Key:  {} - Description: {} - Count: {}".format(k1, v1, v[k1])
            else:
                print "    Key:  {} - Description: {} - Count: {}".format(k1, v1, 0)

# note, this does not check for invalid codes in domain fields

An example of the output for one field:

Field: Observed
  Domain: YN
    Key:  Y - Description: Yes - Count: 159
    Key:  ? - Description: ? - Count: 121
    Key:  N - Description: No - Count: 255
BarbaraS
Occasional Contributor

Hi Randy,

My apologies for the late reply. I actually got help by a consultant in the end, who worked on that script for a whole day

So this was the situation in our case:

# Classes and functions
 
def get_domain_values(database, domain_name):
    '''
    Get domain values from the database
    '''
    domain_values = {}

    msg = '...Get domain values from the database'
    arcpy.AddMessage(msg)

    in_workspace = database
    out_table = os.path.join("IN_MEMORY", domain_name)

    arcpy.DomainToTable_management(in_workspace=in_workspace,
                                    domain_name=domain_name,
                                    out_table=out_table,
                                    code_field='CODE',
                                    description_field='DESCRIPTION')
    search_fields = ['CODE', 'DESCRIPTION']
    with arcpy.da.SearchCursor(out_table, search_fields) as cursor:
        for row in cursor:
            domain_values[format(row[0])] = format(row[1])

    return domain_values

And then the code:

# Get domain values for symbology code
    domain_name = 'SYMBOL_LINE'
    symbology_code_domain_list = get_domain_values(FGDB_path, domain_name)
    # Read Name field and update symbology_code_field field
    with arcpy.da.UpdateCursor(inmem_SWshape, [symbology_code_field, 'Name']) as cursor:
        geometry_type = 'LINE '
        for row in cursor:
            try:
                IDdesc = row[1].upper()
                if IDdesc.startswith(geometry_type):
                    IDdesc = IDdesc.replace(geometry_type, '')
                domain_code = IDdesc.split(' ')[0]
                if domain_code in symbology_code_domain_list:
                    row[0] = domain_code
                else:
                    row[0] = default_iogp_code
            except:
                row[0] = default_iogp_code
                arcpy.AddMessage("...ERROR: Checking Symbology Code failed")
            cursor.updateRow(row)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

So he figured to export the domain as a table and then read the table in the script. Sounds like a good way to me!

I will surely need your script at a later point as well Randy, so your effort is highly appreciated!

gallgher55
New Contributor II

 

def get_domain_values(feature, field):
    feature_fields = [f for f in arcpy.ListFields(feature)]

    if field.upper() not in [x.name.upper() for x in feature_fields]:
        raise ValueError("Did not find field: {} in {}".format(field, feature))

    gdb = arcpy.Describe(feature).path

    target_field = [f for f in feature_fields if f.name.upper() == field.upper()][0]
    target_field_domain = target_field.domain

    if target_field_domain:
        field_domain = [d for d in arcpy.da.ListDomains(gdb) if d.name == target_field_domain][0]
        domain_values = field_domain.codedValues

        return domain_values

    return False

 

0 Kudos