0

I am trying to write a script to use in the ArcGIS Pro Python console which copies an initial layout from an APRX (Layout1) below, then uses this to create multiple layouts which use other maps within the APRX (so Layout 2 will be based off Map 2, Layout 3 off Map 3 and so on). Layout1 uses dynamic text, so the idea is that the exported layouts will automatically edit the text elements to match the relevant details (map name etc.).

I have got the below script but I am receiving errors, the most current is:

'AttributeError: 'function' object has no attribute 'create'' at line 33.

I'm very much new to script writing.

import arcpy 
# Set the workspace 
workspace = r"workspace_location"
# Define map and layout template names 
map_names = ["Map1", "Map2", "Map3", "Map4"]
layout_template_name = "Layout1"
# Open ArcGIS project
aprx = arcpy.mp.ArcGISProject(workspace)
# Find the layout template by name
layout_template = None
for layout in aprx.listLayouts():
if layout.name == layout_template_name:
    layout_template = layout
    break
if layout_template is None:
print(f"Layout template '{layout_template_name}' not found in the project.")
print("Available layout templates:")
for layout in aprx.listLayouts():
    print(layout.name)
exit()
# create a new layout based on the template for each map
for map_name in map_names:
# Get the corresponding map from the project
map = aprx.listMaps(map_name)[0]

# Create a new layout
new_layout = aprx.listLayouts.create(map_name)

# Copy elements from the layout template to the new layout
for element in layout_template.listElements("TEXT_ELEMENT"):
    new_text_element = arcpy.mp.TextElement(element.text)
    new_text_element.elementPositionX = element.elementPositionX
    new_text_element.elementPositionY = element.elementPositionY
    new_layout.listElements("GRAPHIC_ELEMENT").append(new_text_element)

# Find the map frame element in the layout and update its map reference
for element in new_layout.listElements("MAPFRAME_ELEMENT"):
    element.map = map

# Set layout scale based on map frame
new_layout.zoomToMapElements()
print("Completed")
2
  • 1
    Please Edit the Question to report the error(s) and where they occur.
    – Vince
    May 10, 2023 at 12:59
  • 1
    Few things that stands out while skimming your code: You try to use the method "listLayouts" like a property which results in the error you get in line 33. In addtion, there is no create method for a layout object. What you can try to do is copy an existing layout (aprx.CopyItem()) and alter the layouts elements (like the map etc.). As a first step I would make myself more familiar with the arcpy.mp module.
    – Kai
    May 10, 2023 at 14:17

1 Answer 1

0

Regarding the error you have received for this line

# Create a new layout
new_layout = aprx.listLayouts.create(map_name)

There is indeed no "create" method for layouts.... yet, see below.

Additionally, at your "# Copy elements" for loop, this won't achieve what you're looking for as you can't append a new element to the listElements() method (you also switch to filtering by Graphic Elements, rather than Text Elements?). Currently, there is no way to copy elements from one Layout to another, and there is no way to create new elements on a Layout either.

Also, zoomToMapElements() does not exist, the closest you can get currently is layout.openView(), which will open and activate the chosen layout within ArcGIS Pro.

Lastly, you can't use "map" as a variable name as this is the name of an in-built function in Python.

Currently, you could copy the template x times, which would copy all Elements within also. After the Map Frame source is updated, the Dynamic text would update too - making most of your original code unnecessary:

# Copy and modify the template for each Map
for map_name in map_names:
    # Get the corresponding Map from the Project
    map_ = aprx.listMaps(map_name)[0]
    # Copy the template Layout
    new_layout = aprx.copyItem(layout_template, map_.name)
    # Modify the Map Frame(s) on the new copy
    # Loop is not necessary if there's only one Map Frame on each Layout
    for idx, element in enumerate(new_layout.listElements("MAPFRAME_ELEMENT")):
        template_mf = layout_template.listElements("MAPFRAME_ELEMENT")[idx]
        element.map = map_ # Source
        element.camera.setExtent(template_mf.camera.getExtent()) # Extent
    print(f"Layout for {map_.name} complete")
print("All Layouts complete.")

However, as of ArcGIS Pro 3.2 (coming next month) the arcpy.mp module will be expanded to include such functionality, and your code could probably look something like this:

# ...

# Create a new Layout based on the template for each Map
for map_name in map_names:
    # Get the corresponding Map from the Project
    map_ = aprx.listMaps(map_name)[0]
    # Create a new Layout using new ArcGISProject function
    new_layout = aprx.createLayout(map_name)

    # Copy the template elements
    for element in layout_template.listElements("TEXT_ELEMENT"):
        # Create a new blank Text Element
        new_text = new_layout.createTextElement() # Properties of new Text Element may be passed as parameters here?
        # Apply Text Element properties from template
        new_text.text = element.text
        new_text.elementPositionX = element.elementPositionX
        new_text.elementPositionY = element.elementPositionY

    # Create Map Frame Element(s) in the new Layout and update source, properties and extent
    # Loop is not necessary if there's only one Map Frame on each Layout
    for element in layout_template.listElements("MAPFRAME_ELEMENT"):
        new_mf = new_layout.createMapFrame() # Properties of new Map Frame may be passed as parameters here?
        new_mf.map = map_ # Source
        properties = ['elementHeight', 'elementPositionX', 'elementPositionY', 'elementRotation', 'elementWidth']
        for p in properties: # Unsure if this would work, but you get the idea
            new_mf.p = element.p # Properties
        new_mf.camera.setExtent(element.getExtent()) # Extent
    print(f"Layout for {map_.name} complete")
print("All Layouts complete.")

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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