This section highlights some important guidelines for working with the arcpy.mapping module. It also provides some insight on the design of the arcpy.mapping API and suggests some strategies for different scenarios.
Must work with existing map documents or layer files
The arcpy.mapping module was designed so that it can be used to modify existing elements within already existing map documents (.mxd) or layer files (.lyr). In other words, it helps with the automation of existing features but it can't be used to author new objects. It was not designed to be a complete replacement for ArcObjects or an attempt at creating a function, method, or property for every conceivable button, dialog box, menu choice, or context item in the ArcMap interface (that is what ArcObjects provides). You must carefully author a map document or layer file using ArcMap ahead of time with all the appropriate elements and then use arcpy.mapping to manipulate its contents.
The following are only a few simple examples in how arcpy.mapping can be used:
- Replace a layer's data source.
- Iterate through a series of data frame extents, find and replace text values, and export the page layout to PDF.
- Build a complete map book by appending PDF documents into a final product.
Note:
It is possible to make changes to existing map documents or layer files and then save the changes out to a new file using the saveACopy method on the MapDocument or Layer objects.
Reference a map document on disk or use the CURRENT keyword within ArcMap
There are two different ways that a MapDocument object can be created using the MapDocument function. The first, and most recommended, method is to provide a system path to the location of the map document (.mxd) on disk. This technique is most versatile because a script can then be run outside an ArcGIS application. Referencing a specific map document on disk provides more control in terms of how the script will execute because a given script may not work on all map documents.
The second technique is to use the CURRENT keyword as an input parameter to the MapDocument function. This technique only works from within an ArcMap application because the MapDocument object references the map document that is currently loaded into the ArcMap application. Using CURRENT is very helpful when quickly testing and learning the scripting capabilities and command syntax within the Python window. You may start off learning the syntax in the Python window, then start pasting those lines of code into a more permanent script saved to disk.
Script tools that use the CURRENT keyword must be run from within ArcMap (either from a custom menu or the Catalogwindow). Script tools using CURRENT will not execute properly if run from within the ArcCatalog application. For this same reason, if a script tool has a validation script that includes a reference to CURRENT, an error may occur if you try to edit the validation script from ArcCatalog. Be sure to edit the script tool's validation code from within the ArcMap Catalog window.
To use the CURRENT keyword within a script tool, background processing must be disabled. Background processing runs all scripts as though they were being run as stand-alone scripts outside an ArcGIS application, and for this reason, CURRENT will not work with background processing enabled. There is a new script tool option called Always run in foreground that ensures a script tool will run in the foreground, even if background processing is enabled.
Adding layers and working with template map documents
As mentioned in the section above, arcpy.mapping does not allow you to completely author new map documents. By design, it also does not provide the ability to change an existing map document's page size or orientation. The solution is easy. Author template map documents with all the appropriate elements, page sizes, orientations, and so on, ahead of time, then use arcpy.mapping to manipulate the contents.
A common scenario is to author a template map document with no layers, then use the arcpy.mapping AddLayer, AddLayerToGroup, or InsertLayer functions to add layers to the map document. If a legend element is pre-authored to automatically add new items to the legend, the legend items will appear automatically.
Another common scenario involves map books with facing pages. The left and right pages include different offsets to make space for the binding. This scenario includes having two map documents: one for left pages and another for right pages. Arcpy.mapping scripting logic is used to pull together all the individual pages into a final output multipage PDF. See Creating a map book with facing pages which covers this scenario in detail and includes sample arcpy.mapping code.
Author all objects with a unique name
In order to easily reference a map element, for example, a data frame, layer, layout element, or table so it can be accessed and modified, the map element must have a unique name. Many of the arcpy.mapping list functions—for example, ListDataFrames, ListLayers, ListLayoutElements, and ListTableViews—have a wild card parameter that allows you to filter on the name property. These list functions always return a Python list object. In order to reference a map element from the Python list you can either iterate through the list using a loop or you can append an index number at the end of the function. If you use the wild card and specify a unique name, the resulting Python list will always have one item, and it can be referenced using an index value of 0.
mxd = arcpy.mapping.MapDocument("CURRENT")
riverLyr = arcpy.mapping.ListLayers(mxd, "Rivers")[0]
titleTxt = arcpy.mapping.ListLayoutElements(mxd, "TEXT_ELEMENT", "title")[0]
Page layout elements have a separate property called Element Name. This can set in the Size and Position tab within the element's Properties dialog box.
Data frames, layers, and tables do not have a separate name property as with page layout elements. Their name is based on the label seen in the table of contents. Ideally, all data frames, layers, and tables within a single map document would be given a unique name. If there are cases where you need to have duplicate names but need to be able to isolate one item from another, you will need to author the map document so that they can be distinguished using other properties.
The code below shows an example of how layers with the same name 'Streets' in the same data frame 'County Maps' can be further isolated using the description property of the Layer object.
mxd = arcpy.mapping.MapDocument("C:/Project/Project.mxd")
df = arcpy.mapping.ListDataFrames(mxd, "County Maps")[0]
for lyr in arcpy.mapping.ListLayers(mxd):
if lyr.name == "Streets":
if lyr.description == "1:10000":
lyr.visible = True
if lyr.description == "1:100000":
lyr.visible = False
Author extra layout elements and move them on and off the page layout when needed
There may be a situation where you are creating a map series and there are some pages that have additional map elements, for example, an extra data frame, additional picture, or text elements, and so on. Rather than authoring separate map documents just for those scenarios, you can author a single map document with all possible layout elements, then use arcpy.mapping scripting logic to move the elements on and off the page as needed. If an element is moved outside the page layout boundary, it won't get printed or exported.
In the example below, the resulting layout will display a different style scale bar based on the scale of the current data frame. If the scale is larger than 1:25,000 the scale bar units will be in meters. If the scale is smaller than or equal to 1:25,000 the scale bar units will be in kilometers.
mxd = arcpy.mapping.MapDocument("C:/Project/Project.mxd")
m_scale = arcpy.mapping.ListLayoutElements(mxd, "MAPSURROUND_ELEMENT", "m scale bar")[0]
km_scale = arcpy.mapping.ListLayoutElements(mxd, "MAPSURROUND_ELEMENT", "km scale bar")[0]
df = arcpy.mapping.ListDataFrames(mxd, "Main DF")[0]
if df.scale < 25000:
m_scale.elementPositionX = 5 #on the page
km_scale.elementPostitionX = 15 #off the page
else:
m_scale.elementPositionX = 15 #off the page
km_scale.elementPostitionX = 5 #on the page