目录

断点续跑-

断点续跑

一、核心思路

  1. 断点记录:用文本文件(breakpoint.txt)保存已成功导出的DKBM值,每完成一个文件就写入记录。
  2. 断点检测:脚本启动时读取breakpoint.txt,获取已完成列表,后续循环中跳过这些DKBM
  3. 容错处理:避免重复写入记录、处理文件读写异常,确保断点数据可靠。

二、完整断点续跑脚本

python

运行


#!/usr/bin/python
# -*- coding: UTF-8 -*-
import arcpy
import sys
import os  # 新增:用于文件路径操作和断点记录
import datetime
from arcpy import env
import math

# -------------------------- 1. 断点工具函数(新增)--------------------------
def init_breakpoint_file(breakpoint_path):
    """初始化断点文件:若不存在则创建空文件"""
    if not os.path.exists(breakpoint_path):
        with open(breakpoint_path, 'w', encoding='utf-8') as f:
            f.write("")  # 创建空文件
        print(f"初始化断点文件:{breakpoint_path}")

def get_completed_dkbms(breakpoint_path):
    """读取断点文件,返回已完成的DKBM列表"""
    completed = []
    if os.path.exists(breakpoint_path):
        with open(breakpoint_path, 'r', encoding='utf-8') as f:
            lines = f.readlines()
            completed = [line.strip() for line in lines if line.strip()]  # 去空行和空格
    print(f"已完成导出的DKBM数量:{len(completed)}")
    return completed

def add_completed_dkbm(breakpoint_path, dkbm):
    """将已完成的DKBM写入断点文件(避免重复)"""
    completed = get_completed_dkbms(breakpoint_path)
    if dkbm not in completed:
        with open(breakpoint_path, 'a', encoding='utf-8') as f:
            f.write(f"{dkbm}\n")  # 每行存一个DKBM,便于后续读取
        print(f"记录完成:DKBM={dkbm}")

# -------------------------- 2. 原功能函数保留(修复部分问题)--------------------------
def mkdir(path):             
    path = path.strip().rstrip("\\")
    if not os.path.exists(path):
        os.makedirs(path) 
        print(f"{path} 创建成功")
        return True
    else:
        print(f"{path} 已存在")
        return False

# -------------------------- 3. 主逻辑(新增断点续跑)--------------------------
if __name__ == "__main__":
    # -------------------------- 基础参数配置 --------------------------
    resolution = 10
    scale = 96 * 1000 * resolution / 25.4
    imgWidth = 300    # 导出图片宽度
    imgHeight = 200   # 导出图片高度
    selectfield = "DKBM"  # 用于筛选和命名的字段
    label_field = "ZW"    # 标注字段

    # 文件路径配置(根据实际情况修改)
    mxdfile = r"E:\gzct\gzct.mxd"       # MXD文件路径
    outdir = r"E:\gzct"                 # 导出JPG的输出目录
    shpname = r"E:\gzct\dk6209222021_241106_2.shp"  # 要素类路径
    breakpoint_file = os.path.join(outdir, "breakpoint.txt")  # 断点记录文件(放在输出目录下)

    # -------------------------- 初始化:创建输出目录和断点文件 --------------------------
    mkdir(outdir)  # 确保输出目录存在
    init_breakpoint_file(breakpoint_file)  # 初始化断点文件
    completed_dkbms = get_completed_dkbms(breakpoint_file)  # 读取已完成的DKBM

    # -------------------------- 加载MXD和图层 --------------------------
    try:
        mxd = arcpy.mapping.MapDocument(mxdfile)
        df = arcpy.mapping.ListDataFrames(mxd)[0]  # 获取第一个数据框
        input_layer = arcpy.mapping.ListLayers(mxd, "", df)[0]  # 获取目标图层
        target_layer = input_layer  # 标注用图层(与目标图层一致)
        print(f"成功加载MXD:{mxdfile}")
        print(f"目标图层:{input_layer.name}")
    except Exception as e:
        print(f"加载MXD/图层失败:{str(e)}")
        sys.exit(1)  # 加载失败则退出

    # -------------------------- 断点续跑:循环处理要素 --------------------------
    total_count = 0  # 总要素数
    processed_count = 0  # 本次处理的要素数
    skipped_count = 0  # 跳过的已完成要素数

    # 先统计总要素数(可选,用于进度显示)
    with arcpy.da.SearchCursor(shpname, [selectfield]) as count_cursor:
        total_count = sum(1 for _ in count_cursor)
    print(f"要素类总要素数:{total_count}")

    # 核心:循环处理每个要素(跳过已完成项)
    with arcpy.da.SearchCursor(shpname, [selectfield, label_field]) as cursor:
        for row in cursor:
            current_dkbm = str(row[0]).strip()  # 当前要素的DKBM
            current_zw = str(row[1]).strip()     # 当前要素的标注文本

            # 1. 断点判断:若已完成,直接跳过
            if current_dkbm in completed_dkbms:
                skipped_count += 1
                print(f"跳过已完成:DKBM={current_dkbm}(累计跳过:{skipped_count})")
                continue

            # 2. 处理未完成的要素:筛选、标注、导出
            try:
                # 2.1 按DKBM筛选要素
                where_clause = f'"{selectfield}" = \'{current_dkbm}\''  # SQL筛选条件
                arcpy.SelectLayerByAttribute_management(
                    in_layer_or_view=input_layer,
                    selection_type="NEW_SELECTION",
                    where_clause=where_clause
                )

                # 2.2 检查是否有选中要素
                selected_count = int(arcpy.GetCount_management(input_layer).getOutput(0))
                if selected_count == 0:
                    print(f"警告:DKBM={current_dkbm} 无匹配要素,跳过")
                    continue

                # 2.3 缩放至选中要素(可调整缩放比例)
                df.zoomToSelectedFeatures()
                df.scale = df.scale * 1.1  # 扩大10%视野(避免要素贴边)
                arcpy.RefreshActiveView()  # 刷新视图

                # 2.4 设置标注(仅显示当前选中要素的标注)
                if target_layer.supports("LABELCLASSES"):
                    lbl_class = target_layer.labelClasses[0]
                    lbl_class.expression = f"[{label_field}]"  # 标注字段
                    lbl_class.SQLQuery = where_clause  # 仅标注当前选中要素
                    target_layer.showLabels = True
                    arcpy.RefreshActiveView()  # 刷新标注

                # 2.5 导出JPG
                out_file = os.path.join(outdir, f"{current_dkbm}.jpg")
                arcpy.mapping.ExportToJPEG(
                    map_document=mxd,
                    out_jpeg=out_file,
                    data_frame=df,
                    width=imgWidth,
                    height=imgHeight,
                    resolution=90  # 图片分辨率
                )
                print(f"导出成功:{out_file}")

                # 2.6 记录断点:只有导出成功才写入记录
                add_completed_dkbm(breakpoint_file, current_dkbm)
                processed_count += 1

            except Exception as e:
                # 异常处理:记录错误但不终止脚本(继续处理下一个)
                error_msg = f"处理DKBM={current_dkbm}时出错:{str(e)}"
                print(error_msg)
                # 可选:将错误记录到日志文件
                with open(os.path.join(outdir, "error_log.txt"), 'a', encoding='utf-8') as err_f:
                    err_f.write(f"{datetime.datetime.now()} - {error_msg}\n")
                continue

    # -------------------------- 结束:清理资源和显示统计 --------------------------
    # 清理ArcGIS资源
    arcpy.SelectLayerByAttribute_management(input_layer, "CLEAR_SELECTION")  # 清除选择
    del mxd, df, input_layer, target_layer
    arcpy.RefreshActiveView()
    arcpy.RefreshTOC()

    # 显示最终统计
    print("\n" + "="*50)
    print("断点续跑任务完成!")
    print(f"总要素数:{total_count}")
    print(f"已完成总数(含历史):{len(get_completed_dkbms(breakpoint_file))}")
    print(f"本次处理数:{processed_count}")
    print(f"本次跳过数:{skipped_count}")
    print(f"断点文件路径:{breakpoint_file}")
    print("="*50)

三、使用说明

  1. 首次运行

    • 脚本会自动在输出目录(outdir)创建breakpoint.txt(空文件)。
    • 按顺序处理所有要素,每成功导出一个 JPG,就将对应的DKBM写入breakpoint.txt
  2. 中断后续跑

    • 重启脚本时,脚本会先读取breakpoint.txt中的已完成DKBM
    • 循环时跳过这些已完成项,直接处理未记录的DKBM,实现 “断点续跑”。
  3. 重置任务

    • 若需重新处理所有要素,只需删除输出目录下的breakpoint.txt,脚本会重新初始化并从头开始。

四、关键优化点

  1. 避免重复导出:通过breakpoint.txt精准记录进度,跳过已完成项。
  2. 容错性增强
    • 处理 “无匹配要素”“文件读写异常” 等情况,不终止脚本。
    • 错误日志(error_log.txt)记录异常,便于排查问题。
  3. 进度可视化:显示总要素数、已完成数、本次处理数,清晰掌握进度。
  4. 标注修复:确保标注仅显示当前选中要素(通过lbl_class.SQLQuery绑定筛选条件)。

五、注意事项

  1. 路径配置:确保mxdfileshpnameoutdir的路径与实际文件一致(Windows 路径用r"路径"避免转义)。
  2. 字段名匹配selectfieldDKBM)和label_fieldZW)需与要素类的实际字段名一致(区分大小写)。
  3. ArcGIS 环境:需在安装了 ArcPy 的环境中运行(如 ArcGIS Desktop 的 Python 窗口、ArcGIS Pro 的 Python 环境)。

写 csdn 博客

ArcPy 脚本实现断点续跑:批量导出要素 JPG 的高效解决方案

在 ArcGIS 自动化工作中,批量处理空间数据(如按要素导出 JPG 地图)是常见需求。但实际操作中,若遇到脚本中断(如电脑死机、手动停止),重启后往往需要从头开始,浪费大量时间。本文将分享如何基于 ArcPy 实现断点续跑功能,通过记录进度跳过已完成任务,大幅提升批量处理效率。

一、需求背景与痛点

假设我们有一个矢量要素类(如地块数据),需要按每个要素的DKBM字段批量导出对应范围的 JPG 地图,并添加ZW字段作为标注。传统脚本存在以下问题:

  1. 中断后需重跑:若处理 1000 个要素时在第 500 个中断,重启后需重新处理前 500 个,耗时耗力;
  2. 无进度记录:无法直观了解已完成多少任务,也无法定位中断原因;
  3. 容错性差:遇到无匹配要素、文件权限等问题时,脚本直接崩溃,未处理后续要素。

针对这些痛点,我们的核心解决方案是:用文本文件记录已完成任务的DKBM,重启时跳过已记录项,仅处理未完成要素

二、核心原理

断点续跑的实现依赖 3 个关键步骤,流程如下:

初始化

创建断点文件 breakpoint.txt

读取已完成的DKBM列表

循环处理要素

当前DKBM是否在已完成列表?

跳过该要素

筛选要素→设置标注→导出JPG

将当前DKBM写入断点文件

所有要素处理完成,输出统计结果

  • 断点文件:用breakpoint.txt存储已成功导出的DKBM(每行一个),作为进度记录的 “凭证”;
  • 断点检测:脚本启动时读取断点文件,生成已完成列表,循环中通过判断DKBM是否在列表中决定是否跳过;
  • 容错处理:新增错误日志记录异常,确保单个要素处理失败不影响整体流程。

三、完整代码实现

以下是包含断点续跑、标注配置、错误日志的完整 ArcPy 脚本,可直接复制使用(需根据实际路径和字段名调整):

python

运行


#!/usr/bin/python
# -*- coding: UTF-8 -*-
import arcpy
import sys
import os
import datetime
from arcpy import env
import math

# -------------------------- 1. 断点工具函数(核心)--------------------------
def init_breakpoint_file(breakpoint_path):
    """初始化断点文件:若不存在则创建空文件,避免后续读取报错"""
    if not os.path.exists(breakpoint_path):
        with open(breakpoint_path, 'w', encoding='utf-8') as f:
            f.write("")
        print(f"✅ 初始化断点文件:{breakpoint_path}")

def get_completed_dkbms(breakpoint_path):
    """读取断点文件,返回已完成的DKBM列表(去重、去空)"""
    completed = []
    if os.path.exists(breakpoint_path):
        with open(breakpoint_path, 'r', encoding='utf-8') as f:
            lines = f.readlines()
            completed = [line.strip() for line in lines if line.strip()]
    print(f"📊 已完成导出的DKBM数量:{len(completed)}")
    return completed

def add_completed_dkbm(breakpoint_path, dkbm):
    """将已完成的DKBM写入断点文件(避免重复写入)"""
    completed = get_completed_dkbms(breakpoint_path)
    if dkbm not in completed:
        with open(breakpoint_path, 'a', encoding='utf-8') as f:
            f.write(f"{dkbm}\n")
        print(f"📝 记录完成:DKBM={dkbm}")

# -------------------------- 2. 辅助工具函数 --------------------------
def mkdir(path):
    """创建目录(若不存在)"""
    path = path.strip().rstrip("\\")
    if not os.path.exists(path):
        os.makedirs(path)
        print(f"📁 目录创建成功:{path}")
        return True
    else:
        print(f"ℹ️  目录已存在:{path}")
        return False

# -------------------------- 3. 主逻辑(断点续跑核心)--------------------------
if __name__ == "__main__":
    # ====================== 1. 基础参数配置(需根据实际情况修改)======================
    img_width = 300       # 导出JPG宽度(像素)
    img_height = 200      # 导出JPG高度(像素)
    img_resolution = 90   # 导出JPG分辨率(DPI)
    scale_expand = 1.1    # 缩放系数(缩放至要素后扩大10%,避免要素贴边)
    select_field = "DKBM" # 用于筛选要素和命名文件的字段
    label_field = "ZW"    # 用于地图标注的字段

    # 文件路径配置(Windows路径需加r转义,避免反斜杠问题)
    mxd_path = r"E:\gzct\gzct.mxd"                 # MXD地图文档路径
    out_dir = r"E:\gzct\export_jpg"                # JPG导出目录
    shp_path = r"E:\gzct\dk6209222021_241106_2.shp"# 矢量要素类路径
    breakpoint_path = os.path.join(out_dir, "breakpoint.txt") # 断点文件路径
    error_log_path = os.path.join(out_dir, "error_log.txt")   # 错误日志路径

    # ====================== 2. 初始化:目录与断点文件 ======================
    mkdir(out_dir)                # 确保导出目录存在
    init_breakpoint_file(breakpoint_path)  # 初始化断点文件
    completed_dkbms = get_completed_dkbms(breakpoint_path)  # 读取已完成列表

    # ====================== 3. 加载ArcGIS资源(MXD、图层、数据框)======================
    try:
        # 加载MXD文档
        mxd = arcpy.mapping.MapDocument(mxd_path)
        # 获取第一个数据框(若有多个,需指定名称,如ListDataFrames(mxd, "数据框1"))
        df = arcpy.mapping.ListDataFrames(mxd)[0]
        # 获取目标图层(若有多个,需指定名称,如ListLayers(mxd, "地块图层", df)[0])
        target_layer = arcpy.mapping.ListLayers(mxd, "", df)[0]
        
        print(f"✅ 成功加载资源:")
        print(f"   - MXD文档:{mxd_path}")
        print(f"   - 数据框:{df.name}")
        print(f"   - 目标图层:{target_layer.name}")
    except Exception as e:
        print(f"❌ 加载ArcGIS资源失败:{str(e)}")
        sys.exit(1)  # 资源加载失败,直接退出脚本

    # ====================== 4. 统计总要素数(可选,用于进度展示)======================
    try:
        with arcpy.da.SearchCursor(shp_path, [select_field]) as count_cursor:
            total_features = sum(1 for _ in count_cursor)  # 统计总要素数
        print(f"📊 要素类统计:总要素数={total_features},已完成数={len(completed_dkbms)}")
    except Exception as e:
        print(f"⚠️  统计总要素数失败:{str(e)},将跳过统计")
        total_features = "未知"

    # ====================== 5. 断点续跑:批量处理要素 ======================
    processed_count = 0  # 本次处理的未完成要素数
    skipped_count = 0    # 本次跳过的已完成要素数

    # 使用da.SearchCursor高效遍历要素(比旧版Cursor更快、更稳定)
    with arcpy.da.SearchCursor(shp_path, [select_field, label_field]) as cursor:
        for row in cursor:
            current_dkbm = str(row[0]).strip()  # 当前要素的DKBM(去空格,避免匹配问题)
            current_zw = str(row[1]).strip()     # 当前要素的标注文本

            # -------------------------- 5.1 断点判断:跳过已完成要素 --------------------------
            if current_dkbm in completed_dkbms:
                skipped_count += 1
                print(f"ℹ️  跳过已完成:DKBM={current_dkbm}(累计跳过:{skipped_count})")
                continue

            # -------------------------- 5.2 处理未完成要素:筛选→标注→导出 --------------------------
            try:
                # 5.2.1 按DKBM筛选要素(构建SQL条件,注意字段名加双引号,值加单引号)
                where_clause = f'"{select_field}" = \'{current_dkbm}\''
                arcpy.SelectLayerByAttribute_management(
                    in_layer_or_view=target_layer,
                    selection_type="NEW_SELECTION",  # 新建选择集
                    where_clause=where_clause
                )

                # 5.2.2 检查是否有匹配的选中要素
                selected_count = int(arcpy.GetCount_management(target_layer).getOutput(0))
                if selected_count == 0:
                    print(f"⚠️  无匹配要素:DKBM={current_dkbm},跳过")
                    continue

                # 5.2.3 缩放至选中要素(并扩大视野,避免要素贴边)
                df.zoomToSelectedFeatures()  # 缩放至选中要素范围
                df.scale = df.scale * scale_expand  # 扩大视野(可根据需求调整系数)
                arcpy.RefreshActiveView()  # 刷新视图,确保缩放生效

                # 5.2.4 配置标注(仅显示当前选中要素的标注,避免干扰)
                if target_layer.supports("LABELCLASSES"):  # 检查图层是否支持标注类
                    lbl_class = target_layer.labelClasses[0]  # 获取第一个标注类
                    lbl_class.expression = f"[{label_field}]"  # 设置标注字段(如[ZW])
                    lbl_class.SQLQuery = where_clause  # 仅标注当前选中要素(关键!)
                    target_layer.showLabels = True  # 启用标注
                    arcpy.RefreshActiveView()  # 刷新标注,确保生效
                else:
                    print(f"⚠️  图层{target_layer.name}不支持标注类,跳过标注设置")

                # 5.2.5 导出JPG地图
                jpg_name = f"{current_dkbm}.jpg"  # 以DKBM命名JPG文件
                jpg_path = os.path.join(out_dir, jpg_name)  # 完整JPG路径

                # 调用ExportToJPEG函数导出
                arcpy.mapping.ExportToJPEG(
                    map_document=mxd,
                    out_jpeg=jpg_path,
                    data_frame=df,          # 导出的数据框
                    width=img_width,        # 图片宽度
                    height=img_height,      # 图片高度
                    resolution=img_resolution,  # 图片分辨率
                    world_file=False        # 是否生成世界文件(.jgw),按需开启
                )
                print(f"✅ 导出成功:{jpg_path}(本次处理:{processed_count + 1})")

                # 5.2.6 记录断点:仅当导出成功时,才将DKBM写入断点文件
                add_completed_dkbm(breakpoint_path, current_dkbm)
                processed_count += 1

            # -------------------------- 5.3 异常处理:单个要素失败不影响整体 --------------------------
            except Exception as e:
                error_msg = f"处理DKBM={current_dkbm}时出错:{str(e)}"
                print(f"❌ {error_msg}")
                # 将错误信息写入日志文件,便于后续排查
                with open(error_log_path, 'a', encoding='utf-8') as err_f:
                    err_f.write(f"[{datetime.datetime.now()}] {error_msg}\n")
                continue  # 跳过当前错误要素,继续处理下一个

    # ====================== 6. 清理资源与输出最终统计 ======================
    # 清理ArcGIS资源(避免内存泄漏)
    arcpy.SelectLayerByAttribute_management(target_layer, "CLEAR_SELECTION")  # 清除选择集
    del mxd, df, target_layer  # 删除MXD、数据框、图层对象
    arcpy.RefreshActiveView()  # 刷新视图
    arcpy.RefreshTOC()         # 刷新目录表

    # 输出最终统计结果
    print("\n" + "="*60)
    print("🎯 断点续跑任务完成!")
    print(f"📊 统计汇总:")
    print(f"   - 总要素数:{total_features}")
    print(f"   - 已完成总数(含历史):{len(get_completed_dkbms(breakpoint_path))}")
    print(f"   - 本次处理数:{processed_count}")
    print(f"   - 本次跳过数:{skipped_count}")
    print(f"📁 关键文件路径:")
    print(f"   - 断点文件:{breakpoint_path}")
    print(f"   - 错误日志:{error_log_path}")
    print("="*60)

四、使用教程

1. 环境准备

  • 安装 ArcGIS Desktop(10.2+)或 ArcGIS Pro,确保arcpy模块可用;
  • 确保矢量要素类(.shp)、MXD 文档路径正确,且字段名(DKBMZW)与脚本配置一致。

2. 脚本调整(关键!)

根据你的数据修改以下参数(脚本中已标注 “需根据实际情况修改”):

参数名说明示例
mxd_pathMXD 地图文档路径r"E:\Project\map.mxd"
out_dirJPG 导出目录r"E:\Project\export"
shp_path矢量要素类路径r"E:\Project\data\parcels.shp"
select_field筛选 / 命名字段"DKBM"(你的要素类中用于唯一标识的字段)
label_field标注字段"ZW"(你的要素类中用于标注的字段)
img_width/img_height导出图片尺寸500/400(根据需求调整)

3. 运行脚本

  • 打开 ArcGIS 的 Python 窗口(ArcMap→地理处理→Python);
  • 将脚本复制到 Python 窗口,按 Enter 运行;
  • 运行过程中,脚本会实时打印进度(如 “跳过已完成”“导出成功”)。

4. 中断后续跑

若脚本中断(如关闭窗口、电脑重启),只需重新运行脚本:

  • 脚本会自动读取breakpoint.txt中的已完成DKBM
  • 跳过所有已导出的要素,直接处理未完成项。

5. 重置任务

若需重新处理所有要素(如修改了导出参数),只需:

  1. 删除导出目录下的breakpoint.txt文件;
  2. (可选)删除已导出的 JPG 文件;
  3. 重新运行脚本,脚本会从头开始处理。

五、关键优化点与注意事项

1. 性能优化

  • 使用arcpy.da.SearchCursor:比旧版arcpy.SearchCursor更快,支持批量字段读取,减少内存占用;
  • 避免重复加载资源:MXD、图层等资源仅加载一次,而非循环中重复加载;
  • 进度统计:可选统计总要素数,便于预估剩余时间。

2. 容错处理

  • 资源加载失败:直接退出脚本,避免后续报错;
  • 无匹配要素:跳过该要素,不终止脚本;
  • 异常日志:将错误信息写入error_log.txt,便于排查(如 “字段不存在”“权限不足”)。

3. 常见问题与解决方案

问题现象可能原因解决方案
脚本提示 “加载 MXD 失败”MXD 路径错误 / 文件损坏检查mxd_path是否正确,尝试手动打开 MXD 确认是否正常
无要素被筛选select_field字段名错误 / SQL 条件格式问题1. 确认字段名与要素类一致(区分大小写);2. 检查 SQL 条件格式(如字段加双引号,值加单引号)
标注不显示图层不支持标注类 / 标注字段错误1. 在 ArcMap 中手动测试标注是否正常;2. 确认label_field字段名正确
断点文件不更新导出 JPG 失败 / 权限不足1. 检查导出目录是否有写入权限;2. 查看error_log.txt是否有导出相关错误

六、扩展场景

本脚本的断点续跑逻辑