Error setting field for UniqueValueRenderer

2109
9
05-09-2023 06:20 AM
TorbjørnDalløkken2
Frequent Contributor

Hi. 

I'm trying to change the symbology of one of my layers in a python script. I've found the given layer 

 

# Get layer "Vegteig" from active map
aprx = arcpy.mp.ArcGISProject("CURRENT")
m = aprx.activeMap
fl_vegteig = m.listLayers("Vegteiger")[0]

 

Then I'm trying to define a new UniqueValueRenderer for the layer:

 

symb = fl_vegteig.symbology
symb.updateRenderer("UniqueValueRenderer")
symb.renderer.fields = ['TILKNYTTET']

 

 When I try to run this, I get this error:

The attribute 'fields' is not supported on this instance of UniqueValueRenderer

Does anyone know what might be wrong? I'm using almost the same code in another script (same toolbox), which works perfectly.

9 Replies
by Anonymous User
Not applicable

There is one post on SO that mentions that the layers datasource was broken and he was getting this error.  I'm sure you worked that out already so maybe it would be helpful to report this to esri? No too long ago there was another question on here about changing values and the method to do so in the docs were not working so maybe there is something really wrong with the UniqueValueRenderer class.

0 Kudos
DuncanHornby
MVP Notable Contributor

Hi,

Did you find a solution to this? I'm developing some code in Arcpro 3.1.3 and I too get the same vague error. I've tried running it in the python command line window as well as notebook. I've also saved the project, re-opened it with the layer in it and tried running the code below. The layer exists, so its not a broken layer,there is sensible data in it and SubNetID field exists. If I go to the Symbology panel in ArcPro and set it to unique value, subnetid, then it all uniquely colours so I know the dataset is OK. Dare I say it that ESRI have somehow broken this with an update?

My code is simply:

 

 

aprx = arcpy.mp.ArcGISProject("Current")
aprxMap = aprx.listMaps("Map")[0]
layer = aprxMap.listLayers('Sub1000_3')[0]  # This layer was initially loaded into the map as a single symbol
aSymbology = layer.symbology
aSymbology.updateRenderer('UniqueValueRenderer')
aSymbology.renderer.fields = ["SubNetID"]
p = arcpy.mp.ArcGISProject("CURRENT")
cr = p.listColorRamps("Basic Random")[0]
aSymbology.colorRamp = cr
layer.symbology = aSymbology

 

 

It fails on line 6 with 

NameError: The attribute 'fields' is not supported on this instance of UniqueValueRenderer.

 Trying to think out of the box, you could argue the renderer has not been given any values to render but it seems to be a chicken and egg situation, how can I give it values when the first step is to say which field you want to render on, but its that line it fails on!

I've tagged a few of python gurus into this to see if they can identify the issue: @DanPatterson_Retired , @curtvprice , @XanderBakker , @jcarlson , @JoshuaBixby 

0 Kudos
curtvprice
MVP Alum

The first code sample in the help does it exactly the way you're doing it, so I'm kind of mystified too.

0 Kudos
DuncanHornby
MVP Notable Contributor

I've been exploring this problem and it appears to be an issue with the number of unique values. I need to symbolise 13,000 values and this could easily be more.  The GUI handles it but when I set it to be an UniqueValueRenderer in arcpy it blows up with a misleading error message. The property sampleSize does not appear when accessing the renderer properties (that sees like a cosmetic bug to me) as I can set it, but it has no affect.

I wondered about going down the CIM route but  because the object model is not clear and there appears to be zero examples anywhere on the internet of setting the symbology of a layer to be unique values using CIM in arcpy it is impossible for me to test my code as EVERYTIME I try something ArcPro crashes throwing me out into Windows...

It would be good if esri released some code samples of using CIM to create an UniqueValueRenderer in arcpy. All I have seen on the internet are c# solutions.

Quite frustrating.

0 Kudos
makitahideki
New Contributor

Recently, I had to change the layer symbology using CIM. I didn't use UniqueValues, but I hope this helps.

Note: The value of "val['renderer']" was obtained from the .lyrx file (to get the symbology settings, I had to change the file extension to '.json').

 

    for item, val in dict_feat.items():
            layer = mapFrame.map.addDataFromPath(val['shape'])
            layer.name = item
            cim_lyr = layer.getDefinition('V2')
            cim_lyr.renderer = val['renderer']
            layer.setDefinition(cim_lyr)
0 Kudos
TorbjørnDalløkken2
Frequent Contributor

@DuncanHornby @curtvprice Did you guys find any solutions? I can't figure this out. I have a couple of python scripts which after some updates sets a uniquevaluerenderer as described in the first post. In one of the scripts (using the same layer) it works, and in the other script it fails. Unfortunatly this error seems to be happening in ArcGIS Pro 3.2 as well. 

0 Kudos
DuncanHornby
MVP Notable Contributor

No, I came to the conclusion its some sort of bug based upon number of unique values, so my code just traps the error and fails gracefully.

0 Kudos
mattkramer
Frequent Contributor

Adding on to note that I have experienced this kind of bug when attempting to set a field on a layer with 0 features. Seems to error out on both extremes (too many records, or too few).

0 Kudos
Samy_BoumaNgock
Emerging Contributor

Just a note that I have a working script as part of a broader implementation:

  • Feature class is of type Point (e.g. Centroid of countries)
  • Field name must be the internal field name (e.g. Country_Flag), not alias name (e.g. Country Flag). It is also case sensitive
  • Country_Flag field contains 238 unique values
  • Step 1: set default renderer with default 'Shape Marker' symbol, based on Country_Flag field values
  • Step 2: modify each symbol with their corresponding 'Picture Marker', based on images of country flags stored in a folder on disk. Each image's filename corresponds to exactly a value name in the Country_Flag field
 
    def set_defaultuniquevaluesrenderer(featurelayer=None, fieldname=None):
        """Set a default unique values renderer for the specified field in the feature layer."""
        if featurelayer is None:
            arcpy.AddError("Layer not found. Symbology not updated.")
        
        sym = featurelayer.symbology
        sym.updateRenderer('UniqueValueRenderer')
        sym.renderer.fields = [fieldname]
        featurelayer.symbology = sym
        arcpy.AddMessage(f"Symbology updated to UniqueValueRenderer on field: {fieldname}")

    def set_updatesymbology(featurelayer=None, fieldname=None, imagefolder=None):
        """Update only the point symbols as picture markers with corresponding images."""
        if featurelayer is None:
            arcpy.AddError("Layer not found. Symbology not updated.")

        # Get the CIM (Cartographic Information Model) definition of the layer
        cim = featurelayer.getDefinition('V3')
        renderer = cim.renderer

        # Iterate through each class in the renderer's first group
        for classDef in renderer.groups[0].classes:
            # Get the value for the symbol (usually the flag image filename)
            try:
                value = classDef.values[0].fieldValues[0]
            except (AttributeError, IndexError, TypeError):
                arcpy.AddWarning(f"Skipping class with no valid value for symbol: {classDef.name}")
                continue
            
            # Build the full path to the image file
            imagePath = os.path.join(imagefolder, value)
            if arcpy.Exists(imagePath):
                # Read and encode the image as base64
                with open(imagePath, "rb") as imgFile:
                    encoded = base64.b64encode(imgFile.read()).decode("utf-8")
                # Define a CIMPictureMarker using the encoded image
                pictureMarker = {
                    "type": "CIMPictureMarker",
                    "enable": True,
                    "size": 10,
                    "url": f"data:image/png;base64,{encoded}",
                }
                # Create a CIMPointSymbol with the picture marker
                pointSymbol = {
                    "type": "CIMPointSymbol",
                    "symbolLayers": [pictureMarker]
                }
                # Create a CIMSymbolReference for the class symbol
                symbolRef = {
                    "type": "CIMSymbolReference",
                    "symbol": pointSymbol
                }
                # Assign the new symbol to the class definition
                classDef.symbol = symbolRef

        # Apply the updated CIM definition back to the feature layer
        featurelayer.setDefinition(cim)
        arcpy.AddMessage("Point symbols updated with corresponding flag images.")

0 Kudos