断点续跑-
目录
断点续跑
一、核心思路
- 断点记录:用文本文件(
breakpoint.txt
)保存已成功导出的DKBM
值,每完成一个文件就写入记录。 - 断点检测:脚本启动时读取
breakpoint.txt
,获取已完成列表,后续循环中跳过这些DKBM
。 - 容错处理:避免重复写入记录、处理文件读写异常,确保断点数据可靠。
二、完整断点续跑脚本
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)
三、使用说明
首次运行:
- 脚本会自动在输出目录(
outdir
)创建breakpoint.txt
(空文件)。 - 按顺序处理所有要素,每成功导出一个 JPG,就将对应的
DKBM
写入breakpoint.txt
。
- 脚本会自动在输出目录(
中断后续跑:
- 重启脚本时,脚本会先读取
breakpoint.txt
中的已完成DKBM
。 - 循环时跳过这些已完成项,直接处理未记录的
DKBM
,实现 “断点续跑”。
- 重启脚本时,脚本会先读取
重置任务:
- 若需重新处理所有要素,只需删除输出目录下的
breakpoint.txt
,脚本会重新初始化并从头开始。
- 若需重新处理所有要素,只需删除输出目录下的
四、关键优化点
- 避免重复导出:通过
breakpoint.txt
精准记录进度,跳过已完成项。 - 容错性增强:
- 处理 “无匹配要素”“文件读写异常” 等情况,不终止脚本。
- 错误日志(
error_log.txt
)记录异常,便于排查问题。
- 进度可视化:显示总要素数、已完成数、本次处理数,清晰掌握进度。
- 标注修复:确保标注仅显示当前选中要素(通过
lbl_class.SQLQuery
绑定筛选条件)。
五、注意事项
- 路径配置:确保
mxdfile
、shpname
、outdir
的路径与实际文件一致(Windows 路径用r"路径"
避免转义)。 - 字段名匹配:
selectfield
(DKBM
)和label_field
(ZW
)需与要素类的实际字段名一致(区分大小写)。 - ArcGIS 环境:需在安装了 ArcPy 的环境中运行(如 ArcGIS Desktop 的 Python 窗口、ArcGIS Pro 的 Python 环境)。
写 csdn 博客
ArcPy 脚本实现断点续跑:批量导出要素 JPG 的高效解决方案
在 ArcGIS 自动化工作中,批量处理空间数据(如按要素导出 JPG 地图)是常见需求。但实际操作中,若遇到脚本中断(如电脑死机、手动停止),重启后往往需要从头开始,浪费大量时间。本文将分享如何基于 ArcPy 实现断点续跑功能,通过记录进度跳过已完成任务,大幅提升批量处理效率。
一、需求背景与痛点
假设我们有一个矢量要素类(如地块数据),需要按每个要素的DKBM
字段批量导出对应范围的 JPG 地图,并添加ZW
字段作为标注。传统脚本存在以下问题:
- 中断后需重跑:若处理 1000 个要素时在第 500 个中断,重启后需重新处理前 500 个,耗时耗力;
- 无进度记录:无法直观了解已完成多少任务,也无法定位中断原因;
- 容错性差:遇到无匹配要素、文件权限等问题时,脚本直接崩溃,未处理后续要素。
针对这些痛点,我们的核心解决方案是:用文本文件记录已完成任务的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 文档路径正确,且字段名(
DKBM
、ZW
)与脚本配置一致。
2. 脚本调整(关键!)
根据你的数据修改以下参数(脚本中已标注 “需根据实际情况修改”):
参数名 | 说明 | 示例 |
---|---|---|
mxd_path | MXD 地图文档路径 | r"E:\Project\map.mxd" |
out_dir | JPG 导出目录 | 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. 重置任务
若需重新处理所有要素(如修改了导出参数),只需:
- 删除导出目录下的
breakpoint.txt
文件; - (可选)删除已导出的 JPG 文件;
- 重新运行脚本,脚本会从头开始处理。
五、关键优化点与注意事项
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 是否有导出相关错误 |
六、扩展场景
本脚本的断点续跑逻辑