ArcPy批量制图怎么做?GIS自动化脚本详解(附:源码)

GIS基础理论
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

很多同学在做项目图集、专题图、监测报告附图时,都会遇到同一个问题:ArcPy批量制图怎么做?GIS自动化脚本详解(附:源码)。如果每一张图都手动打开 ArcGIS Pro、修改图层筛选条件、调整标题、导出 PDF 或 PNG,不仅耗时,而且很容易出现漏图、标题错误、比例尺不一致等问题。本文用一个可复用的 ArcPy 批量制图脚本,讲清楚从数据准备、地图模板、参数控制到批量导出的完整流程。

引言:ArcPy批量制图适合解决什么问题

ArcPy批量制图适合处理“版式相同、范围或属性不同、需要重复导出”的地图生产任务。典型场景包括行政区专题图批量出图、地块红线图批量导出、巡查点位分区制图、管线分段图册、遥感解译成果分幅出图等。

本文使用 ArcGIS Pro 的 arcpy.mp 模块进行演示。arcpy.mp 是 ArcGIS Pro 中用于操作工程文件、地图、图层、布局和导出结果的 ArcPy 模块。相比手动制图,它的优势在于:一次配置模板,多次自动替换范围、标题、图层筛选条件和输出文件名。

本文重点不是讲所有 ArcPy API,而是给你一套可以改造的批量制图脚本思路:先做好地图模板,再用字段驱动循环导出。

ArcPy批量制图 ArcPy批量导出地图工作流示意图
ArcPy批量制图的基本流程:模板工程、循环控制、范围定位、版面更新和结果导出。

背景:为什么手工制图容易出错

在 GIS 项目中,批量地图通常具有相同的版式结构:图名、图例、比例尺、指北针、底图、专题图层基本一致,区别只在于某个行政区、某个地块、某个编号或某个时间批次。

如果使用手工方式制作 50 张、100 张甚至更多地图,常见问题包括:

  • 地图范围不一致,有的图过大,有的图裁切不完整。
  • 标题或编号手动修改出错,例如 A 区图名写成 B 区。
  • 图层筛选条件忘记更新,导致导出内容不对应。
  • 导出格式、分辨率、文件命名不统一。
  • 后期数据更新后,需要重新逐张制图,维护成本很高。

ArcPy自动化制图的核心价值就是把这些重复操作脚本化。你只需要维护一个 ArcGIS Pro 工程模板和一张控制字段表,后续批量导出地图即可交给 Python 脚本完成。

原理:ArcPy批量制图的核心逻辑

ArcPy批量制图通常由四个部分组成:工程模板、数据字段、布局元素和导出参数。

1. 工程模板

工程模板一般是一个已经配置好的 ArcGIS Pro 工程文件,也就是 .aprx 文件。模板中需要提前设置好地图、图层样式、布局、图例、比例尺、指北针和文本框。

脚本不建议从零开始创建所有制图元素。更稳定的做法是:人工先完成一张标准图,然后让 ArcPy 只负责批量替换范围、属性过滤、标题和输出路径。

2. 数据字段

批量制图通常需要一个唯一字段作为循环依据。例如行政区名称字段 NAME、地块编号字段 PARCEL_ID、图幅编号字段 SHEET_NO。脚本会逐个读取字段值,并为每个值导出一张地图。

3. 图层定义查询

定义查询是 ArcGIS 中用于筛选图层要素的 SQL 条件。例如:

NAME = '东城区'

在 ArcPy 中,可以通过修改图层的 definitionQuery 属性,让当前地图只显示某个行政区或某个地块。这就是实现 ArcPy按属性批量出图的关键。

4. 地图框范围

布局中的地图框,也就是 Map Frame,决定最终导出的可视范围。脚本可以根据当前要素的几何范围设置地图框 extent,并适当放大一点边界,避免图形贴边。

步骤:ArcPy批量制图完整操作流程

步骤一:准备数据和字段

假设我们有一个行政区面图层,字段如下:

字段名 含义 示例
NAME 行政区名称,用于标题和筛选 东城区
CODE 行政区代码,用于文件命名 110101
TYPE 区域类型,可选 街道

建议在正式写脚本前,先检查以下内容:

  • 字段值是否唯一,避免一个名称对应多个区域却只想导出一张图。
  • 字段中是否有空值,空值会影响标题和文件名生成。
  • 字段值是否包含斜杠、冒号、星号等不能用于文件名的字符。
  • 数据坐标系是否正确,避免地图范围定位异常。

步骤二:制作 ArcGIS Pro 地图模板

打开 ArcGIS Pro,新建工程并完成一张标准地图。建议按以下方式整理:

  • 地图名称设置为 Main Map
  • 布局名称设置为 Batch Layout
  • 地图框名称设置为 Main Map Frame
  • 需要批量筛选的图层名称设置为 行政区边界
  • 标题文本框名称设置为 Title Text

这些名称非常重要,因为脚本会根据名称查找对象。如果名称写错,脚本通常不会直接制图失败,但会出现找不到图层、找不到布局元素的问题。

步骤三:设置输出目录

建议单独建立一个输出文件夹,例如:

D:GISProjectbatch_mapsoutput

不要把输出文件直接放在工程根目录或数据目录中。批量导出时文件数量较多,单独目录更方便检查和归档。

步骤四:编写 ArcPy 批量导出地图源码

下面是一份可改造的 ArcPy 批量制图脚本。它会读取行政区边界图层的字段值,按每个行政区设置定义查询,自动缩放到当前区域,更新标题,并导出 PNG 图片。

import arcpy
import os
import re

# 1. 参数配置
aprx_path = r"D:GISProjectbatch_mapstemplate.aprx"
output_folder = r"D:GISProjectbatch_mapsoutput"

map_name = "Main Map"
layout_name = "Batch Layout"
map_frame_name = "Main Map Frame"
target_layer_name = "行政区边界"
title_element_name = "Title Text"

name_field = "NAME"
code_field = "CODE"

export_dpi = 300
buffer_ratio = 1.15

# 2. 文件名清洗函数
def safe_filename(text):
    text = str(text)
    text = re.sub(r'[/:*?"<>|]', "_", text)
    text = text.strip()
    return text if text else "unknown"

# 3. 打开 ArcGIS Pro 工程
aprx = arcpy.mp.ArcGISProject(aprx_path)

maps = aprx.listMaps(map_name)
if not maps:
    raise RuntimeError(f"找不到地图:{map_name}")
m = maps[0]

layouts = aprx.listLayouts(layout_name)
if not layouts:
    raise RuntimeError(f"找不到布局:{layout_name}")
layout = layouts[0]

map_frames = layout.listElements("MAPFRAME_ELEMENT", map_frame_name)
if not map_frames:
    raise RuntimeError(f"找不到地图框:{map_frame_name}")
mf = map_frames[0]

title_elements = layout.listElements("TEXT_ELEMENT", title_element_name)
if not title_elements:
    raise RuntimeError(f"找不到标题文本框:{title_element_name}")
title_element = title_elements[0]

target_layers = m.listLayers(target_layer_name)
if not target_layers:
    raise RuntimeError(f"找不到图层:{target_layer_name}")
target_layer = target_layers[0]

# 4. 确保输出目录存在
os.makedirs(output_folder, exist_ok=True)

# 5. 读取唯一值并循环出图
fields = [name_field, code_field]

with arcpy.da.SearchCursor(target_layer, fields) as cursor:
    for row in cursor:
        name_value = row[0]
        code_value = row[1]

        if name_value is None:
            print("跳过空名称记录")
            continue

        # 注意:字符串字段需要加单引号
        where_clause = f"{arcpy.AddFieldDelimiters(target_layer, name_field)} = '{name_value}'"
        target_layer.definitionQuery = where_clause

        # 根据筛选后的图层获取范围
        extent = mf.getLayerExtent(target_layer, False, True)

        if extent is None:
            print(f"未获取到范围,跳过:{name_value}")
            continue

        # 适当放大范围,避免图形贴边
        width = extent.XMax - extent.XMin
        height = extent.YMax - extent.YMin
        x_center = (extent.XMin + extent.XMax) / 2
        y_center = (extent.YMin + extent.YMax) / 2

        new_width = width * buffer_ratio
        new_height = height * buffer_ratio

        new_extent = arcpy.Extent(
            x_center - new_width / 2,
            y_center - new_height / 2,
            x_center + new_width / 2,
            y_center + new_height / 2
        )

        mf.camera.setExtent(new_extent)

        # 更新标题
        title_element.text = f"{name_value}专题图"

        # 输出文件名
        output_name = f"{safe_filename(code_value)}_{safe_filename(name_value)}_专题图.png"
        output_path = os.path.join(output_folder, output_name)

        # 导出PNG
        layout.exportToPNG(output_path, resolution=export_dpi)

        print(f"已导出:{output_path}")

# 6. 清除定义查询
target_layer.definitionQuery = ""

# 7. 可选:保存工程副本,保留最后一次制图状态
# aprx.saveACopy(r"D:GISProjectbatch_mapstemplate_result.aprx")

print("批量制图完成。")

步骤五:运行脚本

运行脚本前,建议使用 ArcGIS Pro 自带的 Python 环境。常见方式有三种:

  • 在 ArcGIS Pro 的 Python 窗口中测试核心代码。
  • 使用 ArcGIS Pro 安装目录中的 Python 命令行运行 .py 文件。
  • 在 PyCharm 或 VS Code 中配置 ArcGIS Pro 的 Python 解释器后运行。

如果你是初学者,建议先在 ArcGIS Pro 中打开模板工程,确认图层、布局和地图框名称无误,再运行脚本。这样更容易定位错误。

步骤六:检查导出结果

ArcPy批量导出地图完成后,不要只看文件数量,还要抽查地图内容。至少检查以下项目:

  • 输出文件数量是否等于预期行政区数量。
  • 每张图的标题是否和地图内容一致。
  • 范围是否完整,边界是否被裁切。
  • 图例、比例尺、指北针是否正常显示。
  • 中文文件名是否正常,是否出现乱码或非法字符。

常见坑:ArcPy自动化制图容易失败的地方

1. 图层名称和脚本名称不一致

ArcPy 查找图层通常依赖名称。如果 ArcGIS Pro 中图层叫“行政区”,脚本中写的是“行政区边界”,就会报错或找不到对象。建议在模板工程中统一命名,并避免使用多个同名图层。

2. 字符串字段没有正确加引号

定义查询中,文本字段需要使用单引号,例如:

NAME = '东城区'

数值字段则不需要单引号,例如:

OBJECTID = 1

如果字段类型判断错误,ArcPy按属性批量出图时会出现查询为空、范围获取失败等问题。

3. 字段值中包含单引号

如果行政区名称或项目名称中包含英文单引号,SQL 条件可能出错。实际项目中应尽量避免在关键字段中使用特殊符号,或者在脚本中增加转义处理。

4. 地图范围过小或贴边

直接使用要素范围时,图形可能紧贴图框边缘。本文脚本使用 buffer_ratio 参数对范围进行放大。一般可以从 1.10 到 1.30 之间调整,具体取决于版式和图面留白要求。

5. 输出文件被占用

如果导出的 PNG 或 PDF 正在被图片查看器、PDF 阅读器或其他程序打开,脚本再次导出同名文件时可能失败。批量运行前建议关闭输出目录中的旧文件。

6. 布局元素没有命名

很多人只修改了文本框内容,却没有修改元素名称。ArcPy 的 listElements 查找的是元素名称,不一定是你看到的文字内容。请在 ArcGIS Pro 的布局元素属性中确认名称。

方法比较:ArcPy批量制图、地图系列和手工导出怎么选

方法 适合场景 优点 限制
手工导出 少量地图、一次性任务 上手简单,所见即所得 效率低,容易出错,不适合大量重复制图
ArcGIS Pro 地图系列 按索引图层批量出图 无需大量代码,适合标准图册 复杂动态文本、复杂筛选和多数据源联动时不够灵活
ArcPy批量制图 需要脚本控制范围、标题、图层、输出格式和文件名 灵活、可复用、适合自动化生产 需要理解 ArcPy、模板工程和字段规则

如果你的任务只是按一个索引图层导出标准图册,可以优先考虑 ArcGIS Pro 的地图系列功能。如果你需要同时控制多个图层筛选条件、标题字段、输出命名、分辨率、格式,或者要和数据处理流程连在一起,ArcPy批量制图更合适。

检查清单:运行ArcPy批量制图前请逐项确认

  • ArcGIS Pro 工程文件 .aprx 可以正常打开。
  • 地图名称、布局名称、地图框名称和脚本中完全一致。
  • 目标图层名称唯一,没有多个同名图层。
  • 用于循环的字段存在,且字段值不为空。
  • 用于文件命名的字段不包含非法字符,或脚本已做清洗。
  • 图层坐标系正确,范围可以正常缩放。
  • 输出目录存在,且当前用户有写入权限。
  • 导出格式、DPI 和页面尺寸符合项目要求。
  • 先用 2 到 3 条记录测试,再全量批量运行。
  • 批量完成后抽查标题、范围、图例和输出文件数量。

FAQ:ArcPy批量制图常见问题

Q1:ArcPy批量制图必须使用 ArcGIS Pro 吗?

本文脚本使用的是 ArcGIS Pro 的 arcpy.mp 模块,因此需要 ArcGIS Pro 环境。ArcMap 时代也有 arcpy.mapping,但接口和工程文件体系不同,不建议直接混用。

Q2:可以把 PNG 改成 PDF 吗?

可以。把导出语句替换为 PDF 导出即可。例如:

layout.exportToPDF(output_path, resolution=300)

同时把输出文件名后缀从 .png 改为 .pdf。对于正式报告或图册,PDF 通常更适合;对于网页展示或快速预览,PNG 更方便。

Q3:如何一次导出多个图层组合?

可以在循环中同时修改多个图层的 definitionQuery,或者控制图层 visible 属性。例如地块图层按 PARCEL_ID 筛选,影像解译图层按 BATCH_ID 筛选。关键是保证字段之间有明确对应关系。

Q4:为什么脚本能运行,但导出的图是空白的?

常见原因有三个:定义查询没有匹配到要素、地图框范围没有定位到目标图层、目标图层不可见。建议先打印 where_clause,再在 ArcGIS Pro 中手动测试该查询条件是否有结果。

Q5:ArcPy批量导出地图时可以自动修改比例尺吗?

可以。除了使用 extent 控制范围,也可以直接设置地图框相机比例尺。例如:

mf.camera.scale = 50000

如果项目要求所有地图使用统一比例尺,可以采用固定 scale;如果区域大小差异很大,使用 extent 自动适配通常更合理。

Q6:批量制图脚本需要每次都打开 ArcGIS Pro 界面吗?

不一定。只要 Python 环境正确、许可可用、路径配置正确,可以直接运行 .py 文件。但调试模板元素、检查布局和验证符号化时,仍建议在 ArcGIS Pro 界面中先做好模板。

结论:用模板加脚本,让GIS制图从重复劳动变成自动流程

ArcPy批量制图的关键不是把所有制图工作都写成代码,而是把“固定的版式”交给 ArcGIS Pro 模板,把“重复变化的部分”交给 ArcPy 脚本。这样既能保留人工制图的版面质量,又能提高批量导出的效率和一致性。

实际项目中,你可以从本文源码开始改造:先替换 .aprx 路径、图层名称、字段名称和输出目录,再用少量记录测试,确认范围、标题和文件名都正确后,再进行全量导出。只要模板和字段规则稳定,ArcPy自动化制图就可以成为 GIS 项目中非常可靠的生产工具。