Hello everyone,
I’m currently working on automating some map production tasks using Arcpy, and I’m facing a challenge when it comes to customizing the appearance of a legend.
I want to create a legend that has:
I’ve checked the ESRI documentation, but I can’t seem to find a straightforward way to achieve this using Arcpy. Additionally, I haven’t been able to find information on how to import an existing legend style.
Could anyone guide me on how to customize a legend’s appearance to have a white background and borders and hide the layer names and headings in the legend, or import a legend style via Arcpy.
Any advice or code examples would be greatly appreciated!
Thanks in advance for your help!
Solved! Go to Solution.
Unfortunately, there isn't a straightforward way; you have to use CIM.
Python CIM access—ArcGIS Pro | Documentation
This is a very useful addin to help you view the CIM and see exactly what you're doing.
Solved: CIM Viewer for ArcPro 3.0 - Esri Community
The biggest problem here is that CIM is absolutely miserable to work with. Granted, I don't work with it frequently, but this took me longer than I'd like to admit to work out.
Anyway, this code gave me a legend with a white background, black border (each with a gap of 5 pts), with no headings or layer names.
aprx = arcpy.mp.ArcGISProject("CURRENT")
lyt= aprx.listLayouts()[0]
lytcim = lyt.getDefinition('V3')
# Grab only the legend elements.
# Probably a more elegant way to do this.
lgds = [e for e in lytcim.elements if "CIMLegend" in str(type(e))]
for lgd in lgds:
frm = lgd.graphicFrame
# Set background color
bckgnd = frm.backgroundSymbol.symbol.symbolLayers[0]
bckgnd.color = [255,255,255, 255] # Red, Green, Blue, Alpha
# Set Border color
brdr = frm.borderSymbol.symbol.symbolLayers[0]
brdr.color = [0,0,0, 255] # Red, Green, Blue, Alpha
# Add gaps for looks
frm.backgroundGapX = 5
frm.backgroundGapY = 5
frm.borderGapX = 5
frm.borderGapY = 5
# Iterate through legend items to turn off headings and layer names.
for item in lgd.items:
# item.showGroupLayerName = False
item.showHeading = False
item.showLayerName = False
#Save changes
lyt.setDefinition(lytcim)
This assumes that you're not getting too fancy with the symbols, so it just works on the first symbol level it finds. That means that if you want your stroke layer to look like this, you're going to have to alter that code a bit.
Hope this helps!
You could use StyleItem to import an existing legend style?
Oh, this would also work. I've never used this before and didn't think to look haha.
for lgnd in lyt.listElements('LEGEND_ELEMENT'):
lgnd.applyStyleItem("your styleitemhere")
I've never used it before, but I came up with this. One would have to make the .stylx.
# Define the path to your project and style file
aprx = arcpy.mp.ArcGISProject("CURRENT")
style_file = r"C:\Path\To\Your\StyleFile.stylx"
# Add the style file to the project
aprx.updateStyles(style_file)
# Get the layout and the legend element
layout = aprx.listLayouts("Your Layout Name")[0]
legend = layout.listElements("LEGEND_ELEMENT", "Your Legend Name")[0]
# List the style items in the style file
style_items = aprx.listStyleItems("LEGEND", style_file)
# Apply the first style item found (you can refine this to select a specific style)
if style_items:
legend.applyStyleItem(style_items[0])
Thank you very much! I couldn’t get updateStyles to work because I hadn’t added the style file to the project’s style list. So, I made a small change to append it before updating, and it worked! I also realized that when you import a style, it doesn’t exactly match how you saved it (e.g., layer names and headings weren’t hidden).
if not style_file in styleItemList:
styleItemList.append(style_file)
projet.updateStyles(styleItemList)
Unfortunately, there isn't a straightforward way; you have to use CIM.
Python CIM access—ArcGIS Pro | Documentation
This is a very useful addin to help you view the CIM and see exactly what you're doing.
Solved: CIM Viewer for ArcPro 3.0 - Esri Community
The biggest problem here is that CIM is absolutely miserable to work with. Granted, I don't work with it frequently, but this took me longer than I'd like to admit to work out.
Anyway, this code gave me a legend with a white background, black border (each with a gap of 5 pts), with no headings or layer names.
aprx = arcpy.mp.ArcGISProject("CURRENT")
lyt= aprx.listLayouts()[0]
lytcim = lyt.getDefinition('V3')
# Grab only the legend elements.
# Probably a more elegant way to do this.
lgds = [e for e in lytcim.elements if "CIMLegend" in str(type(e))]
for lgd in lgds:
frm = lgd.graphicFrame
# Set background color
bckgnd = frm.backgroundSymbol.symbol.symbolLayers[0]
bckgnd.color = [255,255,255, 255] # Red, Green, Blue, Alpha
# Set Border color
brdr = frm.borderSymbol.symbol.symbolLayers[0]
brdr.color = [0,0,0, 255] # Red, Green, Blue, Alpha
# Add gaps for looks
frm.backgroundGapX = 5
frm.backgroundGapY = 5
frm.borderGapX = 5
frm.borderGapY = 5
# Iterate through legend items to turn off headings and layer names.
for item in lgd.items:
# item.showGroupLayerName = False
item.showHeading = False
item.showLayerName = False
#Save changes
lyt.setDefinition(lytcim)
This assumes that you're not getting too fancy with the symbols, so it just works on the first symbol level it finds. That means that if you want your stroke layer to look like this, you're going to have to alter that code a bit.
Hope this helps!
Thank you very much, I heard about CIM but I couldn't figure out how to use it well. This is exactly what I want !
Thank you !
It's a bit easier to work with CIM bi-directionally. I usually make what I want, save it as a layer file, then read the layer file to build the CIM. That way you aren't trying to parse the CIM definition in your head and are instead just translating the one you made visually into python code.
There is also some sort of stub in the CIM module for importing a json definition of a CIM object using json.loads(), but I can't get it to work. Might try to get around to writing a version that actually works when I finish my "cim with type hinting" side project lol.
I was using the CIM viewer when I wrote this and found that a lot of the keys are not exactly the same as what they are when you're actually setting them. Haven't compared to the LYRX; it's probably closer? Another issue I had is that setting symbology for a legend symbol is NOT the same as setting symbology for a layer, which I had just done the other week and was hoping would help me out. It didn't lol.
One thing that I do actually recommend for puzzling out CIM is the use of the Python Window. Like yeah, it's super annoying, but it has helpful hinting if you go line by line. Versus typing it out in a notebook or something.