Select to view content in your preferred language

Issues with using Python to update field visibility in .aprx map document layers

360
2
01-16-2025 10:10 AM
MollyMoore
Frequent Contributor

I am upgrading a script that heavily relied on the arcpy.mapping package to work with ArcGIS Pro. I have gotten everything working okay EXCEPT for updating the visible fields in the map layers. I have tried a bunch of different suggestions, but none that have worked. The simplest approach seems to be using Python cim access: https://pro.arcgis.com/en/pro-app/latest/arcpy/mapping/python-cim-access.htm

My code runs okay, but when I open my .aprx, none of the fields have been hidden that are supposed to be hidden. Anyone have suggestions?

visible_fields = ['list', 'of', 'fields']

aprx = arcpy.mp.ArcGISProject(aprx_path)
map_doc = aprx.listMaps(map_frame_name)[0]

for lyr in map_doc.listLayers():
    if arcpy.Describe(lyr).dataType == "FeatureLayer":
        print(lyr.name)
        # Get the layer's CIM definition - using Pro 2.9
        cim_lyr = lyr.getDefinition('V2')

        # Make changes to field properties
        for fd in cim_lyr.featureTable.fieldDescriptions:
            if fd.fieldName not in visible_fields:
                fd.visible = False  # Do not display this field

        # Push the changes back to the layer object
        lyr.setDefinition(cim_lyr)

aprx.save()
2 Replies
HaydenWelch
MVP Regular Contributor

You wrote everything correctly, I can't really see a better way to do it. Went ahead and just added some hinting and an overridden print function so you can monitor exactly what the code is doing depending on where you're running it from:

from builtins import print as _print
from typing import Literal
from _typeshed import SupportsWrite

from arcpy import AddMessage
from arcpy.mp import ArcGISProject
from arcpy._mp import Map, Layer
from arcpy.cim import CIMFeatureLayer, CIMFeatureTable, CIMFieldDescription

# Define a custom print function that also writes to the ArcGIS Pro messages
def print(
    *values: object,
    sep: str | None = " ",
    end: str | None = "\n",
    file: SupportsWrite[str] | None = None,
    flush: Literal[False] = False,
) -> None:
    _print(*values, sep=sep, end=end, file=file, flush=flush)
    AddMessage(sep.join(map(str, values)) + end if end != '\n' else '')

def update_field_visibility(
    aprx_path: str, 
    map_frame_name: str, 
    *, 
    visible_fields: tuple[str]= (), 
    use_alias: bool = False):
    
    # Open the project and get the layers
    aprx = ArcGISProject(aprx_path)
    map_doc: Map = aprx.listMaps(map_frame_name)[0]
    layers: list[Layer] = [layer for layer in map_doc.listLayers() if layer.isFeatureLayer]
    
    # Update the field visibility for each layer
    for layer in layers:
        # Get CIM objects
        cim_lyr: CIMFeatureLayer = layer.getDefinition('V2')
        cim_feature_table: CIMFeatureTable = cim_lyr.featureTable
        cim_field_descriptions: list[CIMFieldDescription] = cim_feature_table.fieldDescriptions
        
        # Update field visibility
        for fd in cim_field_descriptions:
            name = (use_alias and fd.alias) or fd.fieldName
            
            if name not in visible_fields and fd.visible:
                fd.visible = False
                print(f"Field {name} for {layer.name} is now hidden")
            
            elif name in visible_fields and not fd.visible:
                fd.visible = True
                print(f"Field {name} for {layer.name} is now visible")
        
        # Set the definition
        layer.setDefinition(cim_lyr)
    
    # Save the project
    aprx.save()
    
if __name__ == "__main__":
    aprx = r"Path\To\APRX"
    map_frame_name = "Map"
    visible_fields = ("Field1", "Field2", "Field3")
    
    update_field_visibility(aprx, map_frame_name, visible_fields=visible_fields)

 

That should at least tell you what fields are getting switched on and off. I've used this exact method before to great success, so I'm not sure exactly what could be happening.

JeffBarrette
Esri Regular Contributor

@MollyMoore is it possible that the field properties were never modified?  In other words, if you add a new data source as a layer and try to use Python CIM Access to modified the field properties, it won't work.  The reason is that because NO properties were previously altered.  The layer JSON only persists changes.  If no changes are made, then you won't see the properties.  All you have to do is modify one property for one layer, save, and then you will see everything.

Here is a basic example that checks to see if the field descriptions properties exist in the CIM.  If not, it uses a temporary makeFeatureLayer work around to force those properties into the CIM.

 

def updateCIMFields(l, cimLyr):
    fList = ["SQKM", "POP2001", "Shape_Length", "Shape_Area"]

    for fd in cimLyr.featureTable.fieldDescriptions:
        if fd.fieldName in fList:
            fd.numberFormat.roundingOption = "esriRoundNumberOfDecimals"
            fd.numberFormat.roundingValue = 0
            fd.numberFormat.zeroPad = True         
    l.setDefinition(cimLyr)
    return cimLyr


   
p = arcpy.mp.ArcGISProject('current')
m = p.listMaps()[0]

lyr = m.listLayers('Provinces')[0]
lyr_cim = lyr.getDefinition('V3')

if len(lyr_cim.featureTable.fieldDescriptions) == 0:  #NO CIM field info present
    print('No CIM Field Info')

    mkLyr = arcpy.management.MakeFeatureLayer(lyr)[0]
    mkLyr_cim = mkLyr.getDefinition('V3')
    mkLyr_cim = updateCIMFields(mkLyr, mkLyr_cim)

    #Paste CIM information and remove temporary layer
    lyr.pasteProperties(mkLyr, 'FIELD_PROPERTIES')   
    m.removeLayer(mkLyr)

else:                                                 #CIM field info present
    print('CIM Field Info Pre-Exists')
    lyr_cim = updateCIMFields(lyr, lyr_cim)
    lyr.setDefinition(lyr_cim) 

 

Jeff - arcpy.mp team

0 Kudos