城乡规划数据批量处理太慢?ArcPy脚本自动化方案(含:蔼若春代码实例)

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

引言

《城乡规划数据批量处理太慢?ArcPy脚本自动化方案(含:蔼若春代码实例)》这篇文章面向经常处理控规、用地、道路、行政区、项目红线等规划数据的 GIS 同学,重点解决一个很常见的问题:同样的裁剪、投影、字段整理、面积统计,如果每个图层都手动点工具,数据一多就非常慢,而且容易出错。

ArcPy 是 ArcGIS 的 Python 自动化接口,适合把 ArcGIS Pro 或 ArcMap 中重复性的地理处理步骤写成脚本。对于城乡规划数据批量处理,ArcPy 的价值不只是“跑得快”,更重要的是让处理流程可复用、可检查、可交付。

本文会用一个贴近规划工作的示例:批量读取多个规划图层,统一坐标系,按研究范围裁剪,修复几何,计算面积字段,并输出到新的地理数据库中。

背景

城乡规划数据通常有几个特点:

  • 图层多:用地现状、规划用地、道路红线、控制线、公共服务设施、行政边界等。
  • 来源杂:CAD 转 GIS、测绘成果、历史 shapefile、第三方矢量数据经常混在一起。
  • 坐标系不统一:有的使用 CGCS2000,有的使用地方坐标,有的甚至没有正确定义投影。
  • 字段不规范:同一个含义可能叫 YDLBDLBM用地代码
  • 处理动作重复:投影、裁剪、修复几何、字段计算、导出成果,每个项目都要做。

如果完全依赖手工操作,常见风险包括:漏处理某个图层、输出路径混乱、面积单位错误、坐标系被误投影、字段计算不一致。ArcPy 脚本自动化的核心目标,就是把这些重复动作变成一套稳定流程。

城乡规划数据批量处理 ArcPy脚本自动化流程示意图
ArcPy 自动化适合把城乡规划数据中的投影、裁剪、修复几何和面积统计流程串联起来。

原理

ArcPy 脚本自动化不是简单地把鼠标操作录下来,而是调用 ArcGIS 的地理处理工具。你在 ArcGIS Pro 中看到的很多工具,例如 Project、Clip、Repair Geometry、Add Field、Calculate Field,都可以在 ArcPy 中用代码执行。

一个典型的城乡规划数据批量处理脚本,一般包含四层逻辑:

  1. 环境设置:设置工作空间、输出路径、是否覆盖已有结果。
  2. 数据遍历:自动找到需要处理的要素类或 shapefile。
  3. 标准处理:对每个图层执行投影、裁剪、修复几何、字段计算等操作。
  4. 日志与检查:记录成功、失败、坐标系、要素数量和输出位置。

这里需要特别注意两个概念:

  • 定义投影:告诉软件这个数据“原本是什么坐标系”,不会改变坐标值。
  • 投影转换:把数据从一个坐标系转换到另一个坐标系,会改变坐标值。

很多规划数据面积不准,根源不是 Calculate Field 写错,而是数据仍在地理坐标系下计算面积,或者原始坐标系定义错误。ArcPy 自动化前,必须先判断数据坐标系是否可信。

步骤

1. 准备输入数据结构

建议先把数据整理成清晰目录,不要直接在原始成果目录中批量运行脚本。推荐结构如下:

project_folder/
  input/
    landuse_existing.shp
    landuse_plan.shp
    road_line.shp
    boundary.shp
  mask/
    study_area.shp
  output/
    planning_result.gdb
  scripts/
    batch_planning_process.py

其中 input 存放待处理图层,mask 存放研究范围,output 存放处理结果。这样做的好处是:原始数据不被覆盖,脚本可以反复运行,结果也更容易交付。

2. 确认目标坐标系

城乡规划项目通常应使用适合本地测绘和面积计算的投影坐标系。不要在经纬度坐标系下直接计算用地面积。

如果项目已有明确的坐标系要求,应以任务书、测绘成果说明或甲方数据标准为准。下面示例中使用一个目标坐标系变量,实际项目中需要替换为你的坐标系。

target_sr = arcpy.SpatialReference(4547)

这里的 WKID 只是示例。正式使用前,请在 ArcGIS Pro 中通过图层属性核对坐标系名称、中央经线、单位和 EPSG/WKID 是否正确。

3. ArcPy 批量处理脚本示例

下面是一个可改造的蔼若春代码实例,用于批量处理城乡规划数据。它会遍历输入目录中的 shapefile,投影到目标坐标系,按研究范围裁剪,修复几何,添加面积字段,并输出到文件地理数据库。

import arcpy
import os
import traceback

arcpy.env.overwriteOutput = True

input_folder = r"D:planning_projectinput"
mask_fc = r"D:planning_projectmaskstudy_area.shp"
output_folder = r"D:planning_projectoutput"
output_gdb = os.path.join(output_folder, "planning_result.gdb")

target_sr = arcpy.SpatialReference(4547)

if not os.path.exists(output_folder):
    os.makedirs(output_folder)

if not arcpy.Exists(output_gdb):
    arcpy.management.CreateFileGDB(output_folder, "planning_result.gdb")

arcpy.env.workspace = input_folder
feature_classes = arcpy.ListFeatureClasses("*.shp")

if not feature_classes:
    raise RuntimeError("input 目录下没有找到 shapefile 数据。")

for fc in feature_classes:
    try:
        name = os.path.splitext(fc)[0]
        input_fc = os.path.join(input_folder, fc)

        print("正在处理:{}".format(fc))

        desc = arcpy.Describe(input_fc)
        source_sr = desc.spatialReference

        if source_sr is None or source_sr.name == "Unknown":
            print("跳过:{} 未定义坐标系,请先人工核对。".format(fc))
            continue

        projected_fc = os.path.join("in_memory", name + "_prj")
        clipped_fc = os.path.join(output_gdb, name + "_clip")

        arcpy.management.Project(
            in_dataset=input_fc,
            out_dataset=projected_fc,
            out_coor_system=target_sr
        )

        arcpy.analysis.Clip(
            in_features=projected_fc,
            clip_features=mask_fc,
            out_feature_class=clipped_fc
        )

        arcpy.management.RepairGeometry(clipped_fc)

        fields = [f.name for f in arcpy.ListFields(clipped_fc)]
        if "AREA_M2" not in fields:
            arcpy.management.AddField(clipped_fc, "AREA_M2", "DOUBLE")

        arcpy.management.CalculateGeometryAttributes(
            in_features=clipped_fc,
            geometry_property=[["AREA_M2", "AREA"]],
            area_unit="SQUARE_METERS"
        )

        count = arcpy.management.GetCount(clipped_fc)[0]
        print("完成:{},输出要素数:{}".format(clipped_fc, count))

    except Exception:
        print("处理失败:{}".format(fc))
        print(traceback.format_exc())

print("全部处理完成。")

4. 检查输出结果

脚本运行完成后,不要直接把输出数据交付。至少检查以下内容:

  • 输出地理数据库中是否每个输入图层都有对应结果。
  • 各图层是否都被裁剪到研究范围内。
  • 图层坐标系是否为目标投影坐标系。
  • AREA_M2 字段是否存在,面积值是否明显异常。
  • 要素数量是否为 0,若为 0,需要检查裁剪范围和坐标系是否匹配。

5. 将脚本改造成自己的项目模板

实际项目中,可以继续扩展这段 ArcPy 脚本自动化方案:

  • 增加字段标准化,例如把不同来源的用地代码统一到 LAND_CODE 字段。
  • 增加按用地类型汇总面积的统计表。
  • 增加日志文件,记录每个图层处理时间、坐标系、要素数量。
  • 增加异常图层清单,方便后续人工处理。
  • 增加图层白名单,只处理指定类型的数据。

常见坑

坑 1:把未定义投影的数据直接 Project

如果数据本身没有定义坐标系,ArcPy 无法判断原坐标值的真实含义。此时直接执行投影转换,很容易得到位置错误的数据。正确做法是先确认原始坐标系,再使用 Define Projection 定义投影,最后再执行 Project。

坑 2:在经纬度坐标系下计算面积

规划项目中的用地面积通常需要平方米或公顷。如果图层是经纬度坐标系,直接计算面积会导致结果不可用。应先转换到合适的投影坐标系,再计算面积。

坑 3:输出 shapefile 导致字段名被截断

shapefile 字段名长度有限,中文字段和长字段名也容易出现兼容问题。批量处理城乡规划数据时,建议输出到文件地理数据库,减少字段截断、编码和几何类型限制带来的问题。

坑 4:研究范围与输入图层坐标系不一致

Clip 工具看起来能运行,不代表结果一定正确。如果研究范围和输入图层坐标系不同,或者其中一个坐标系定义错误,可能出现裁剪结果为空、偏移或范围异常。

坑 5:忽略拓扑和几何错误

CAD 转 GIS 或历史数据中经常存在自相交、多部件异常、空几何等问题。Repair Geometry 可以处理一部分几何错误,但不能替代完整的数据质检。重要成果仍应结合拓扑规则检查。

方法比较

方法 适用场景 优点 限制
ArcGIS Pro 手工工具 少量图层、一次性处理 直观,适合初学者理解流程 重复操作多,容易漏步骤
ModelBuilder 流程固定、希望可视化配置 不需要写太多代码,便于展示流程 复杂判断、批量日志和异常处理不如脚本灵活
ArcPy 脚本 多图层、多项目、标准化处理 可复用、可批量、可记录日志,适合城乡规划数据批量处理 需要 Python 基础,坐标系和路径必须配置准确
GeoPandas 脚本 开源环境、轻量矢量处理 适合表格化分析和开源流程 与 ArcGIS 地理数据库、部分高级地理处理工具衔接不如 ArcPy 直接

如果团队主要使用 ArcGIS Pro,并且成果数据需要在 ArcGIS 环境中交付,ArcPy 脚本通常是更稳妥的自动化方案。如果只是做开源数据清洗或 WebGIS 前处理,也可以考虑 GeoPandas、GDAL 或 PostGIS。

检查清单

在运行城乡规划数据批量处理脚本前,建议按下面清单逐项确认:

  • 输入数据是否已经备份,脚本是否不会覆盖原始数据。
  • 每个输入图层是否有正确坐标系。
  • 目标坐标系是否符合项目要求。
  • 研究范围图层是否与项目范围一致。
  • 输出路径是否使用文件地理数据库,而不是混乱的临时目录。
  • 字段名是否符合交付标准,是否避免过长字段名和重复字段。
  • 面积字段单位是否明确,例如平方米或公顷。
  • 脚本是否记录处理失败的数据名称。
  • 运行完成后是否抽查地图位置、要素数量和面积值。

如果你的项目数据量较大,还可以增加分批处理机制。例如按图层类型、行政区或项目阶段分批运行,避免一次性处理过多数据导致定位问题困难。

FAQ

ArcPy 脚本自动化适合所有城乡规划数据吗?

不适合所有情况。ArcPy 适合规则明确、步骤重复、输入结构相对稳定的数据处理。如果原始数据质量很差、坐标系未知、字段混乱严重,仍需要先人工判断和整理。

城乡规划数据批量处理时,为什么裁剪结果为空?

最常见原因是输入图层和研究范围坐标系不一致,或者其中一个图层的坐标系定义错误。也可能是两者空间范围本来没有相交。建议先在 ArcGIS Pro 中叠加查看,再检查图层属性中的坐标系和范围值。

ArcPy 计算面积和字段计算器计算面积有什么区别?

底层思路类似,关键区别在于 ArcPy 可以批量执行,并且可以在计算前自动完成投影、裁剪和几何修复。对于多个规划图层,ArcPy 更容易保证面积字段计算规则一致。

是否必须使用文件地理数据库输出?

不是必须,但强烈建议使用。文件地理数据库对字段名、数据类型、空间索引和多图层管理更友好。shapefile 在字段长度、编码和数据结构上限制较多,不适合复杂规划成果的中间处理。

ArcPy 脚本运行很慢怎么办?

可以先检查数据量、几何复杂度、网络磁盘路径和是否频繁写入 shapefile。建议使用本地磁盘、文件地理数据库、必要时使用 in_memory 临时数据,并避免在循环中重复执行不必要的投影和字段创建。

没有 Python 基础能不能使用这套方案?

可以从固定模板开始。先只修改输入路径、输出路径、研究范围和目标坐标系。等流程稳定后,再逐步增加字段整理、面积汇总、日志输出等功能。

结论

城乡规划数据批量处理慢,通常不是单个工具的问题,而是重复步骤太多、数据标准不统一、人工操作不可复用。ArcPy 脚本自动化可以把投影、裁剪、修复几何、面积计算和成果输出串成一套稳定流程。

真正可靠的 ArcPy 自动化方案,应同时关注三件事:坐标系是否正确、处理过程是否可追踪、输出结果是否可检查。只要这三点做好,脚本就不只是“省时间”,而是能显著降低规划数据处理中的人为错误。

建议你先用本文的代码实例处理一个小项目,确认坐标系、裁剪范围和面积字段都正确后,再扩展到完整的城乡规划数据批量处理流程。