I have a arcgis pro project that has 3 layouts each is references (linked or whatever the terminology may be) to one of the 3 maps that this project also have.
I tried to write code that uses a for loop to loop over the layout names and over the map name that associated to each layout. code below.
However i am able to get a list of all layouts and print that list of strings (keysList) but the map names that link to them is not returning. I read there a MapFrame element within a layout object that to my understanding contains the map name (among other elements, but I don't find how to call it and loop over it to access this attribute.
Any help is appriciates.
import arcpy
keysList = []
valuesList = []
aprx = arcpy.mp.ArcGISProject("CURRENT")
for lyt in aprx.listLayouts():
for m in lyt.listElements():
keysList.append(lyt.name)
valuesList.append(m.name)
print(keysList)
print(valuesList)
Solved! Go to Solution.
You were very close, just needed to filter the list.
import arcpy
keysList = []
valuesList = []
aprx = arcpy.mp.ArcGISProject("CURRENT")
for lyt in aprx.listLayouts():
#Filter down the list to only map frames
for m in lyt.listElements("MAPFRAME_ELEMENT"):
#You want the name of the map in map frame, not the map frame itself
keysList.append(lyt.name)
valuesList.append(m.map.name)
print(keysList)
print(valuesList)
'''Result'''
#['Layout', 'Layout']
#['Extent', 'Grey-scale']
It might be cleaner to make a dictionary:
import arcpy
mapDict= {}
aprx = arcpy.mp.ArcGISProject("CURRENT")
for lyt in aprx.listLayouts():
#Filter down the list to only map frames
for m in lyt.listElements("MAPFRAME_ELEMENT"):
if lyt.name in mapDict:
#You want the name of the map in map frame, not the map frame itself
mapDict[lyt.name].append(m.map.name)
else:
mapDict[lyt.name]=[m.map.name]
print(mapDict)
'''Result'''
#{'Layout': ['Extent', 'Grey-scale']}
Helpful pages:
Hope this helps!
You were very close, just needed to filter the list.
import arcpy
keysList = []
valuesList = []
aprx = arcpy.mp.ArcGISProject("CURRENT")
for lyt in aprx.listLayouts():
#Filter down the list to only map frames
for m in lyt.listElements("MAPFRAME_ELEMENT"):
#You want the name of the map in map frame, not the map frame itself
keysList.append(lyt.name)
valuesList.append(m.map.name)
print(keysList)
print(valuesList)
'''Result'''
#['Layout', 'Layout']
#['Extent', 'Grey-scale']
It might be cleaner to make a dictionary:
import arcpy
mapDict= {}
aprx = arcpy.mp.ArcGISProject("CURRENT")
for lyt in aprx.listLayouts():
#Filter down the list to only map frames
for m in lyt.listElements("MAPFRAME_ELEMENT"):
if lyt.name in mapDict:
#You want the name of the map in map frame, not the map frame itself
mapDict[lyt.name].append(m.map.name)
else:
mapDict[lyt.name]=[m.map.name]
print(mapDict)
'''Result'''
#{'Layout': ['Extent', 'Grey-scale']}
Helpful pages:
Hope this helps!
Thank you Alfred. And you are right, I should have used a dictionary instead of two lists. Most appreciated your answer and feedback.
can this code work in 2.8? when I run the code m.map.name is not recognized.
This is so weird.
It should work; I tested it a few times before posting, and I work in 2.9, so there isn't a huge difference.
However, when I try it today, I also have a runtime error.
Specifically, it looks like I can call methods on the map, but I can't call its properties; m.map.listLayers() works, but m.map.mapUnits does not.
I'm going to submit a ticket for this and hopefully we'll get some resolution.
Wait I think I figured it out.
I dug into my testing projects a little more to see where the error was occuring.
Specifically, it always occurred on the same Layout out of the four in the project.
I checked the layout, and guess what? The dataframe isn't referencing any map. Not sure when or how that happened, but here we are.
For some reason, I couldn't get it to tell me straight out if it wasn't referencing anything.
So I ended up just making the whole thing a try/except.
import arcpy
mapDict= {}
errorList = []
aprx = arcpy.mp.ArcGISProject("CURRENT")
for lyt in aprx.listLayouts():
#Filter down the list to only map frames
for m in lyt.listElements("MAPFRAME_ELEMENT"):
try:
if lyt.name in mapDict:
#You want the name of the map in map frame, not the map frame itself
mapDict[lyt.name].append(m.map.name)
else:
mapDict[lyt.name]=[m.map.name]
except:
errorList.append(lyt.name)
print(mapDict, "\n")
print("The following layouts had errors. \n"
"Check to see if their map frames are referencing a map.",
"\n".join(errorList))
'''Results'''
#{'Layout': ['Data Call Wkbk'], 'Layout1': ['Georeferencing'], 'Layout2': ['Map1', 'Tracy_TallgrassMap']}
#The following layouts had errors.
#Check to see if their map frames are referencing a map. Wind Project_2021_Fred
Thank works nicely. Thankyou.
Hi again. I was hoping you could help me understand how to use this code to pull the layers that are in the layout. I am trying to pull this data into a CSV line by line:
layout name, map name, layer name, visible, etc
layout name, map name, layer name, visible, etc
I have some code that basically does this, but for an MDX. I can't figure out how to make it do it for an APRX. Any ideas would be appreciated. thanks, chandler
import arcpy
import csv
import pandas as pd
mapList= []
errorList = []
# Enter in the properties you care about
propList= ['brightness', 'visible', 'isBroken']
outCSV = r'W:\Downloads\testing.csv'
aprx = arcpy.mp.ArcGISProject("CURRENT")
for lyt in aprx.listLayouts():
# Filter down the list to only map frames
for m in lyt.listElements("MAPFRAME_ELEMENT"):
try:
for lay in m.map.listLayers():
# Ignore group and base layers
if lay.isGroupLayer is True:
continue
if lay.isBasemapLayer is True:
continue
tempList= [lyt.name, m.map.name, lay.name]
for prop in propList:
#print(getattr(lay, prop,""))
tempList.extend([getattr(lay, prop,"")])
mapList.append(tempList)
except:
errorList.append(lyt.name)
with open(outCSV, 'w', newline='') as file:
writer = csv.writer(file)
heading = ["Layout", "Map", "Layer"]
heading.extend(propList)
writer.writerow(heading)
writer.writerows(mapList)
#Delete duplicates.
print("Deleting duplicates")
df = pd.read_csv(outCSV)
# This way drops any duplicate maps, leaving only the entries
# for the first layout in which they appear.
df= df.drop_duplicates(subset=heading[1:])
# This one just drops duplicates
#df= df.drop_duplicates()
df.to_csv(outCSV)
print("done")
if len(errorList)>0:
print("The following layouts had errors. \n"
"Check to see if their map frames are referencing a map.",
"\n".join(errorList))
This ought to do it.
It'll print out an excel sheet (file path in Line 9) containing the following columns: Layout, Map name, Layer name, and then whatever properties you want, as entered into propList (Line 8 )(don't worry about the name, that goes in by default).
The end result looks like this.
As presented right now, it also gets rid of duplicate entries for maps. To ignore this and list all maps, regardless of how many times they appear, just comment out Line 43 and un-comment Line 45.
If you're interested in just one layout, you can filter it down in Line 12, e.g. aprx.listLayouts('Layout 1')
Hope this helps!