I have a process that I'm trying to streamline. On a regular basis I have to produce ArcPad base maps for low end GPS units. The area that I need to cover is very large (ca. 7 million acres). I've found that if I strip the attribute tables down to just the necessary fields, I can make the maps work, even with the memory limitations on Windows mobile devices. The process currently goes like this...

I have a base map (mxd) connected to the corporate data sources. I use xTools to export all of the data in the table of contents to shapefiles and also create a new mxd pointing to the new shape files. This maintains any symbology, definition queries and labels.

Here's the part that I want to streamline:

I edit each shapefile and delete all fields that are not used for symbology, definition queries or labels.

I would like to be able to script this part of the process. I know that some layer properties like definition queries can be accessed with ArcPy. What I'd like to do is create a list of attribute fields for each shapefile to delete, but exclude any attributes that are being used for symbology, definition queries or labels. Most of this is easy to figure out. What I can't figure out is how to get the information about which attributes are being used.

  • 1
    You'd need layer properties to decide what attributes to keep, but it/they wouldn't be the only critical values. Python (arcpy) could certainly do this, but Python coding questions are expected to contain Python code (initial effort, up until the point where you got stuck). – Vince Mar 9 '16 at 20:00
  • Sorry about not including code. I didn't realize it was it was a requirement. I hadn't fleshed out the code because I wasn't sure how to access the layer properties in question. – kkovacs Mar 9 '16 at 21:35

This code prints each layer's data source followed by a list of attributes that are used in the layer's definition query, labels, or symbology. Note that a single data source may be behind multiple layers. Also, not that I couldn't figure out a way to identify the fields used in "unique values, many fields" or "dot density" symbology.

import arcpy, os

mxd = arcpy.mapping.MapDocument(r'C:\temp\Untitled.mxd')

for layer in arcpy.mapping.ListLayers(mxd):
    print layer
    # If you can't identify the layer's source data (e.g., web services, group
    #   layers), then skip it
    if not layer.supports('dataSource'):
    ds = layer.dataSource
    # Fields in the layer
    fields = [i.name for i in arcpy.ListFields(ds)]
    fields.sort(key=lambda s: len(s), reverse=True)

    # A list of fields used in the map
    fieldsUsed = list()

    # Get the fields used in its symbology; note that this will only work
    # for layers that have basic "Unique Values" symbology. Dot Density symbology
    # and "unique values, many fields" symbology won't return the fields used.
    if hasattr(layer, 'symbology'):
        sym = layer.symbology
        if hasattr(sym, 'valueField'):
            field = sym.valueField
        if hasattr(sym, 'normalization'):
            field = sym.normalization

    # Get the fields used in its definition query
    if hasattr(layer, 'definitionQuery'):
        dq = layer.definitionQuery
        for field in fields:
            if field in dq:
                dq = dq.replace(field, '')

    if hasattr(layer, 'labelClasses'):
        # Get the fields used in the labels
        lcs = layer.labelClasses
        for lc in lcs:
            lcSQL = lc.SQLQuery
            lcExp = lc.expression
            for field in fields:
                if field in lcSQL:
                    lcSQL = lcSQL.replace(field, '')
                if field in lcExp:
                    lcExp = lcExp.replace(field, '')

    # Remove repeated field names
    fieldsUsed = list(set(fieldsUsed))

    # Print out the fields used
    print '{}:'.format(ds)
    for f in fieldsUsed:
        print '\t{}'.format(f)

EDIT: Updated the code to be more versatile and not crash in the face of rasters, group layers, and feature services.

Here are details on the layer properties. For the symbology properties, see the paragraph that begins "Depending on the symbology type". As it mentions there, the "OTHER" symbologyType "represents an unsupported layer symbology class". Unfortunately, this includes the many fields symbology. However, tweaking the work-around presented here, you might be able to pull it off.

Also, note that this script may over-select fields. For example, if you have field named 'F1' and you have a definition query or a label class query like "MyTextField = 'Blah blah and F1 blah'". Then, F1 would be returned, even though the literal string 'F1' is being used in the query and not the field name. It turns out that these types of scenarios are extremely difficult to account for, and based on a google search, no one has been able to accomplish the feat. That said, you could address many query scenarios with some relatively simple regex.

EDIT: Updated the code to prevent conflation of fields named, e.g., 'F1', 'F11', 'F111'. In the previous version, the appearance of F111 in a definition query or label query would have yielded all three field names in the output.

| improve this answer | |
  • That gets me 90% there. I do have one layer that uses unique values, many fields. So I'll have to sort that out, but thanks for the info on accessing the layer properties. – kkovacs Mar 9 '16 at 21:37
  • How can I get a list of attributes for objects like layer.symbology or layer.definitionQuery? I want to dig deeper into this but can't find any documentation. – kkovacs Mar 9 '16 at 22:25
  • @kkovacs, I've added a paragraph to my answer to direct you to more detail. – Tom Mar 9 '16 at 22:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.