# 自动化制图

## 前言

关于制图，是一项非常庞大的部分，这里只简单的介绍如何读取文档，设置图例、标题以及出图。

## `arcpy.mapping`

`arcpy.mapping` 是ArcPy中用于操作地图文档 (**.mxd**) 和图层文件 (**.lyr**) 的的模块，可以使用`arcpy.mapping` 很方便的创建专题地图册等。

## 读取mxd文档

`arcpy.mapping`读取地图文档，使用的是`arcpy.mapping.MapDocument`方法，其定义非常简单：

```
MapDocument(mxd_path)
```

mxd\_path就是mxd文档的路径。`MapDocument`打开一个地图文档后，将会返回一个**MapDocument**对象。

示例：

```
 mxd_file = arcpy.mapping.MapDocument(mxdFilePath)
 # do something
​
del mxd_file
```

**注1：**&#x6709;一点需要指出，arcpy中一些方法在官网上都没有看到返回值的说明，但是其会一个返回值。一般对于操作而言，返回值的就是这个操作的结果。

**注2：**&#x4F7F;用完MapDocument后记得释放资源

## 获取地图文档中数据框

ArcGIS中允许定义多个数据框，数据框的获取，是通过`arcpy.mapping.ListDataFrames`方法来实现的。`arcpy.mapping.ListDataFrames`方法可以获取到指定地图文档中的所有数据框列表，其语法如下：

```
ListDataFrames(map_document, {wildcard})
```

其参数的意义如下：

**map\_document**: 用于查看数据框的MapDocument对象

**wildcard**: 数据库名通配符，与之前讲到的类似。

`ListDataFrames`返回的是所有的数据框的列表，可以通过索引来获取指定的数据框，如果要获取map\_document中激活的数据框，则可以通过`map_document.activeDataFrame`来获取

## 获取数据框中的图层

获取图层是通过`arcpy.mapping.ListLayers`来实现的。`ListLayers`可以获取到当前地图文档的所有图层，要获取指定的数据框中的图层，需要将该数据框作为一个参数传递到方法里面。`ListLayers`的语法如下：

```
ListLayers(map_document_or_layer, {wildcard}, {data_frame})
```

其参数定义如下：

**map\_document\_or\_layer**：地图文档或则是lyr图层（注：凡是未指定为lyr图层的**图层**均为普通图层）

**wildcard**：图层名通配符

**data\_frame**：数据框

例如，获取某个地图文档中的第一个数据框中第一个图层：

```
import arcpy
mxd = arcpy.mapping.MapDocument(r"xxx\xxx\x.mxd")
df = arcpy.mapping.ListDataFrames(mxd)[0]
print arcpy.mapping.ListLayers(mxd, "", df)[0].name
del mxd
```

注：此段代码来自ArcGIS官网

## 制图

说实话，直接通过ArcPy来对地图进行符号化等设置是比较麻烦的，还好ArcGIS提供lyr图层文件。与普通的shapefile文件和要素类等相比，lyr图层文件可以将数据及符号化的结果都保存在一起（注意，lyr图层对数据的保存仅仅是保存的是**数据的路径**，其主要保存的内容为符号系统的设置）。因此我们可以先使用ArcGIS定义好符号系统，在通过ArcPy来进行操作，因此这里将不在讲讲解。

制图有三大不可缺少的东西：方向、比例尺、图例，合称为地图三要素。

制作地图册时，如果直接通过ArcPy来创建三要素，对于要素的摆放位置是难以可视化调整的，我们可以通过预先摆放好三要素，然后在通过ArcPy动态的更新其内容显示的地图图层内容即可，尤其是方向、比例尺，我们基本可以不用改变，仅仅来控制图例以及地图标题即可。

对于三要素以及标题等这些，在**ArcGIS**中统称为布局要素。获取布局要素，使用的是`arcpy.mapping.ListLayoutElements`方法，其语法定义如下：

```
ListLayoutElements (map_document, {element_type}, {wildcard})
```

其中参数的意义如下：

**map\_document**：用于操作的MapDocument对象

**element\_type**：筛选返回的布局元素列表的元素类型的字符串。其可选择如下：`DATAFRAME_ELEMENT`、`GRAPHIC_ELEMENT`、`LEGEND_ELEMENT`、`MAPSURROUND_ELEMENT`、`PICTURE_ELEMENT`、`TEXT_ELEMENT`。

**wildcard**：用于筛选返回结果的通配符

我们的图中往往有标题，但是我们并没在**element\_type**发现标题布局元素，这因为标题也是属于文本元素`TEXT_ELEMENT`。假如我们地图中只有一个文本类型的元素——标题，那我们可以使用如下方法更改地图的标题：

```
def adjust_the_title(mxd_file, titleText):
    # 获取布局元素标题
    titleEle = arcpy.mapping.ListLayoutElements(mxd_file, "TEXT_ELEMENT")[0]
    # 更改标题内容
    titleEle.text = titleText
```

再来看个动态修改图例的例子：

```
def adjust_the_legend(mxd_file, data_frame, layer, colCount):
    # 获取布局元素图例
    legendEle = arcpy.mapping.ListLayoutElements(mxd_file, "LEGEND_ELEMENT")[0]
    # 向数据框中添加图层
    arcpy.mapping.AddLayer(data_frame, layer, "TOP")
    # 调整图例中列数
    if colCount > 1:
        legendEle.adjustColumnCount(colCount)
```

在ArcGIS中，如果向数据框中添加图层，图例中会自动显示该图层；如果从数据框移出图层，图例中会自动移除该图层。因此，我们不用管图例中图层的显示问题。因此，我们只需要在需要的时候对图例的列数进行调整即可。

## 导出地图

`arcpy.mapping`下有一系列的`ExportTo*`方法用于导出地图，例如：`ExportToPDF`，`ExportToPng`，`ExportToJPEG`等，分别用来将地图导出为pdf、png、和jpg文件。这些方法虽然参数比较多，但是其最简单的使用如下：

```
# 导出为pdf
arcpy.mapping.ExportToPDF(mxd_file_object, r"C:\xx\xxx\xxxx.pdf")
# 导出为png
arcpy.mapping.ExportToPNG(mxd, rr"C:\xx\xxx\xxxx.png")
# 导出为jpg
arcpy.mapping.ExportToJPEG(mxd, rr"C:\xx\xxx\xxxx.jpg")
```

各个方法的具体语法请见ArcGIS的官网

“<https://desktop.arcgis.com/zh-cn/arcmap/10.3/analyze/arcpy-mapping/exportXXX.htm>”

## 批量制图

在制作系列专题地图时，我们可能会要求图例、比例尺不变，仅仅是数据的数据源发生变化，比如对某一地区的各类POI数据进行制图，并使得各类POI各出图一张。在ArcPy中操作符号系统是一件比较困难的事情，这时我们可以使用`arcpy.mapping.UpdateLayer`函数，其语法如下：

```
UpdateLayer (data_frame, update_layer, source_layer, {symbology_only})
```

其参数定义如下：

**data\_frame**：一个数据框`DataFrame`对象

**update\_layer**：要被进行的图层

**source\_layer**：用于更新 update\_layer 图层的图层

**symbology\_only**：是否只更新 update\_layer 图层的符号系统，默认是True

例如，用已经做好的符号系统的lyr图层文件去动态生成多个地图并导出，其示例代码如下：

```
source_layer = arcpy.mapping.Layer("base.lyr")
···
# shp_list 为一系列shapefile文件路径
for shp_file in shp_list:
    # 创建一个图层，并将其加入到数据框中
    add_layer = arcpy.mapping.Layer(shp_file)
    add_layer.visible = True
    arcpy.mapping.AddLayer(data_frames, add_layer)
​
    # 更新图层的符号系统
    update_layer = arcpy.mapping.ListLayers(map_document, "*", data_frames)[0]
    arcpy.mapping.UpdateLayer(data_frames, update_layer, source_layer, True)
​
    # 获取shapefile的文件名（不带扩展名）
    file_name = get_file_name(shp_file)
    # 导出为png
    out_file = arcpy.env.workspace + "/png/" + get_parent_dir(shp_file) + '/' + file_name + ".png"
    dir_path = os.path.dirname(out_file)
    if not os.path.exists(dir_path):
        os.makedirs(dir_path)
​
    arcpy.mapping.ExportToPNG(map_document, out_file)
    
    # 将刚刚加入到数据框中的图层移除
    # 注意，不能直接移除add_layer，因为添加到数据框后 add_layer 与 data_frames)[0] 是两个对象
    arcpy.mapping.RemoveLayer(data_frames, arcpy.mapping.ListLayers(map_document, "*", data_frames)[0])
​
```

该示例的完整代码与数据可见示例代码下 **5/batchMapping** 文件夹。

## 制作地图册

创建一个pdf的地图册可以使用`PDFDocumentCreate`方法，将多个pdf地图文件合并为一个地图册：

```
pdf_atlas = arcpy.mapping.PDFDocumentCreate(r"C:\xx\xxx.pdf")
​
pdf_atlas.appendPages(r"C:\xx\a.pdf")
pdf_atlas.appendPages(r"C:\xx\b.pdf")
​
pdf_atlas.saveAndClose()
del pdf_atlas
```

**注：**&#x4F7F;用完`PDFDocumentCreate`后记得释放资源

将数据驱动页面和`PDFDocumentCreate`结合，可以很方便的创建一系列专题地图册。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://zxyao.gitbook.io/arcpy/5.-zhi-tu/chuang-jian-di-tu-ce.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
