Select to view content in your preferred language

ArcPy Automatic Legend Formatting Errors

229
0
03-05-2025 09:23 AM
YahyaMasri
Emerging Contributor

Hello everyone,

I’m working on automating the legend, but I keep encountering formatting issues. My goal is to position the legend to the right of the map and have it displayed vertically from top to bottom. Since the number of legend items can vary, the legend height should not be a fixed value.

In lines 47 and 48, I limit the legend to a single column and set it to adjust the font size automatically. Additionally, I ensure sufficient vertical space by setting the legend height to 15 (line 34). However, as shown in the attached image, the legend gets cut off at a certain point.

The issue is not with the symbols but rather with the legend formatting, which I haven't been able to resolve. I also attempted to set the fittingStrategy (line 47) to 'AdjustColumnsAndFont' and commented out columnCount (line 48), but this caused the legend to be cut off horizontally instead.

I've attached examples below for reference. Any insights or suggestions would be greatly appreciated!

 

 

 

                def MakeRec_LL(llx, lly, w, h):
                    xyRecList = [
                        [llx, lly],
                        [llx, lly + h],
                        [llx + w, lly + h],
                        [llx + w, lly],
                        [llx, lly]
                    ]
                    array = arcpy.Array([arcpy.Point(*coords) for coords in xyRecList])
                    return arcpy.Polygon(array)

                def mapLayout(m_title):
                    p = arcpy.mp.ArcGISProject(new_aprx_path)
                    lyt = p.createLayout(17, 11, 'INCH')
                    m = p.listMaps("Map")[0]
                    
                    # Map Frame Configuration
                    map_frame_width, map_frame_height = 13, 7.5
                    map_frame_x = ((17 - map_frame_width) / 2) - 1.5
                    map_frame_y = (11 - map_frame_height) / 2
                    mf = lyt.createMapFrame(
                        MakeRec_LL(map_frame_x, map_frame_y, map_frame_width, map_frame_height),
                        m,
                        "New Map Frame"
                    )

                    lyt = p.listLayouts('Layout')[0]
                    mf = lyt.listElements('MAPFRAME_ELEMENT', 'New Map Frame')[0]

                    # Legend Configuration
                    legend_x = 13.5  # Adjusted X position
                    legend_y = 8    # Lower starting Y position
                    legend_width = 3  # Narrow width
                    legend_height = 15   # Increased height to allow vertical expansion

                    # Define the legend style
                    legend_style = p.listStyleItems('ArcGIS 2D', 'LEGEND', 'Legend 1')[0]

                    # Create the legend
                    legend = lyt.createMapSurroundElement(
                        arcpy.Point(legend_x, legend_y), 'LEGEND', mf, legend_style, 'Map Legend'
                    )

                    # Configure legend appearance and behavior
                    legend.elementWidth = legend_width
                    legend.elementHeight = legend_height
                    legend.fittingStrategy = 'AdjustFontSize'
                    legend.columnCount = 1  # Force single column (vertical)
                    legend.showTitle = False

                    # Valid categories to display in the legend
                    valid_categories_in_map = [
                        "Damage or destruction, looting, or theft of cultural heritage",
                        "Damage or destruction of civilian critical infrastructure",
                        "Enslavement",
                        "Extrajudicial killing",
                        "Forced disappearance",
                        "Gender-based or other conflict-related sexual violence",
                        "Human trafficking",
                        "Indiscriminate use of weapons",
                        "Kidnapping",
                        "Mass execution",
                        "Military operations (battle, shelling)",
                        "Movement of military, paramilitary, or other troops and equipment",
                        "Persecution based on political, racial, ethnic, gender, or sexual orientation",
                        "Torture or indications of torture",
                        "Unlawful detention",
                        "Violent crackdowns on protesters/opponents/civil rights abuse",
                        "Willful killing of civilians"
                    ]

                    # Use CIM to refine the legend further
                    lyt_cim = lyt.getDefinition('V3')
                    for elm in lyt_cim.elements:
                        if elm.name == "Map Legend":
                            legend_cim = elm

                            # Remove the background frame/outline
                            legend_cim.frame = None

                            # Customize text properties
                            for item in legend_cim.items:
                                item.showHeading = False
                                item.showLabels = False
                                
                                # Adjust text symbol properties if possible
                                if hasattr(item, 'textSymbol'):
                                    item.textSymbol.fontSize = 7  # Slightly larger for readability
                                    item.textSymbol.fontFamily = 'Arial'
                                    item.textSymbol.fontStyle = 'Regular'

                            # Update the definition
                            lyt.setDefinition(lyt_cim)

                    # Filter legend items
                    for item in legend.items[:]:  # Create a copy of the list to safely modify
                        try:
                            if item.name not in valid_categories_in_map:
                                legend.removeItem(item)
                        except Exception as e:
                            print(f"Error processing legend item {item}: {e}")

                    # Debug information
                    print(f"Total legend items after filtering: {len(legend.items)}")
                    for item in legend.items:
                        print(f"Remaining legend item: {item.name}")

                    # Finalize legend positioning
                    legend.elementPositionX = legend_x
                    legend.elementPositionY = legend_y

                    remaining code...

 

 

 

 

0 Kudos
0 Replies