I'm on ArcGIS Pro (self-taught) and I am trying to make a batch of layouts for different species occurrence records over the same extent. I have all the XY to Point data as a layer and have been using the display filter to limit which species is shown, then manually exporting the layout, and changing the species again. All other factors are the same, save the species. I want to automate this since I have 80 or so to do (and do this often). Using Map series hasn't worked, since the extent always changes, so I thought I would attempt with arcpy since I know a little bit about python...
Ideally, I want to create a layout for each species, with the same extent, layers, etc, but with a different species selected from the XY to Point layer. I then want to export the layout with the name of the species selected.
This is the code I have so far, and it all seems to work (i.e. it exports a pdf for each, with the layers I can see looking good, and runs without errors) but it has an odd extent, off to the right of the continental U.S., and a North arrow that is not in the layout I am calling. I got the extent values from the extent on the formatted layout/mapframe by looking in properties and copying the values there. Is there a way to edit the extent of the layout?
import arcpy, os #Project path aprx = arcpy.mp.ArcGISProject( r"pathtoproject.aprx") # Set the name of the XY to Point table table_name = "ScioSpecies" # Set the name of the field that contains the class names class_field = "Species" #Set a reference to the map view in my project map_view = aprx.listMaps()[0] # Get a reference to the layout layout = aprx.listLayouts()[0] #Setting up layout/mapframe map_frame = layout.listElements("MAPFRAME_ELEMENT")[0] extent = arcpy.Extent(5331425.860609497, -12839586.820429197, -12017230.660916878, 4267200.242417085) map_frame.camera.setExtent(extent) class_list = [] with arcpy.da.SearchCursor(table_name, class_field) as cursor: for row in cursor: if row[0] not in class_list: class_list.append(row[0]) #Verify all species are present print(class_list) for species in class_list: arcpy.SelectLayerByAttribute_management(table_name, "NEW_SELECTION", "Species = '{}'".format(species)) layout.exportToJPEG( r"pathtosavefile{}.jpg".format(species), resolution=800)
Solved! Go to Solution.
Thank you! This led me to the solution, and although I don't need the text box now, thank you for showing me how to do it!
Here's what I ended up doing.
#Project path aprx = arcpy.mp.ArcGISProject( r"projectpath.aprx") # Set the name of the field that contains the class names class_field = "Species" # Set Map View and Layout and Map Frame map_view = aprx.listMaps("Map")[0] layout = aprx.listLayouts("Layout2")[0] map_frame = layout.listElements("MAPFRAME_ELEMENT")[0] # Set species layer species_lyr = map_view.listLayers("ScioSpecies")[0] # Set shapefile polygon to custom extent customextent = map_view.listLayers("customextent")[0] # Set output output_folder = output path #clear out all the existing definitionqueries species_lyr.updateDefinitionQueries([]) # Grab each species, and make sure all are present class_list = [] with arcpy.da.SearchCursor(species_lyr, class_field) as cursor: for row in cursor: if row[0] not in class_list: class_list.append(row[0]) print(class_list) #set the extent based on the species layer map_frame.camera.setExtent(map_frame.getLayerExtent(customextent, False, True)) #Loop for each species for species in class_list: species_lyr.definitionQuery = "{0} = '{1}'".format(class_field,species) outfile = "{0}\\{1}.pdf".format(output_folder,species) layout.exportToJPEG( exportfolder".format(species), resolution=900) print("Successful export of ", species)
I'm sure it's not the prettiest code, but it got the output I wanted, having the custom rectangle to define extent really helped to standardize all of them.
Since your headline asks about definition queries I'll add to your code for that. If you aren't getting the layout you expect, you might want to use the wildcard feature in the listLayouts function to find the name of the layout. If the extent doesn't change on the layout between exports, then you probably don't need to change it. Or, you can get the layer's extent and set the camera to that.
map_view = aprx.listMaps("SpeciesMapName")[0]
layout = aprx.listLayouts("SpeciesLayoutName")[0]
map_frame = layout.listElements("MAPFRAME_ELEMENT")[0]
species_lyr = map_view.listLayers("ScioSpecies")[0]
#you can have a text box in your layout with a name, and change it to #species name
species_txt = None
for txt in layout.listElements("TEXT_ELEMENT"):
if txt.name == "SpeciesName":
species_txt = txt
break
class_list = [row[0] for row in arcpy.da.SearchCursor(species_lyr,class_field)]
class_list = set(class_list)
#clear out all the existing definitionqueries
species_lyr.updateDefinitionQueries([])
for species in class_list:
if species_txt is not None:
species_txt.text = species
species_lyr.definitionQuery = "{0} = '{1}'".format(class_field,species)
#set the extent based on the species layer
map_frame.camera.setExtent(map_frame.getLayerExtent(species_lyr, False, True))
#map_frame.camera.scale = scale_value
outfile = "{0}\\{1}.pdf".format(output_folder,species)
layout.exportToPDF(outfile, 300, 'BEST',output_as_image=True)
time.sleep(1)
Thank you! This led me to the solution, and although I don't need the text box now, thank you for showing me how to do it!
Here's what I ended up doing.
#Project path aprx = arcpy.mp.ArcGISProject( r"projectpath.aprx") # Set the name of the field that contains the class names class_field = "Species" # Set Map View and Layout and Map Frame map_view = aprx.listMaps("Map")[0] layout = aprx.listLayouts("Layout2")[0] map_frame = layout.listElements("MAPFRAME_ELEMENT")[0] # Set species layer species_lyr = map_view.listLayers("ScioSpecies")[0] # Set shapefile polygon to custom extent customextent = map_view.listLayers("customextent")[0] # Set output output_folder = output path #clear out all the existing definitionqueries species_lyr.updateDefinitionQueries([]) # Grab each species, and make sure all are present class_list = [] with arcpy.da.SearchCursor(species_lyr, class_field) as cursor: for row in cursor: if row[0] not in class_list: class_list.append(row[0]) print(class_list) #set the extent based on the species layer map_frame.camera.setExtent(map_frame.getLayerExtent(customextent, False, True)) #Loop for each species for species in class_list: species_lyr.definitionQuery = "{0} = '{1}'".format(class_field,species) outfile = "{0}\\{1}.pdf".format(output_folder,species) layout.exportToJPEG( exportfolder".format(species), resolution=900) print("Successful export of ", species)
I'm sure it's not the prettiest code, but it got the output I wanted, having the custom rectangle to define extent really helped to standardize all of them.