当前位置: 首页 > news >正文

python在Arcgis Pro中 多边形锐角识别与切割脚本笔记

   

一、脚本定位与应用场景

该脚本基于 ArcGIS Pro Python 3 环境开发,是一款自动化矢量数据处理工具,核心功能是识别多边形要素中的锐角顶点并按规则切割,解决因锐角导致的 GIS 数据拓扑错误、地图可视化失真、空间分析偏差等问题,适用于建筑规划数据标准化、测绘矢量图预处理、土地利用数据优化等场景。

二、脚本结构拆解与代码解读

(一)环境配置与参数定义

1. 基础环境初始化
# -*- coding: utf-8 -*-
import arcpy  # 导入ArcGIS地理数据处理库
import math   # 导入数学计算库(用于角度、距离计算)
import os     # 导入文件路径处理库
import sys    # 导入系统控制库(用于递归限制、程序退出)
from arcpy import env  # 导入ArcGIS环境配置模块# 环境核心设置
env.overwriteOutput = True  # 允许覆盖已存在的输出文件,避免重复运行时文件冲突
sys.setrecursionlimit(1000000)  # 提升递归限制,处理顶点数量多的复杂多边形(如复杂建筑轮廓)

解读:这部分是脚本运行的基础保障,env.overwriteOutput避免手动删除旧文件的麻烦,sys.setrecursionlimit则针对复杂图形的顶点遍历,防止因递归深度不足报错。

2. 关键参数配置
# 输入输出路径(需替换为实际路径,r前缀避免转义字符问题)
input_shp = r"C:\Users\Administrator\Desktop\test\01测试数据\测试数据1\锐角测试.shp"
output_mark = r"C:\Users\Administrator\Desktop\test\内角标示.shp"  # 锐角顶点标记输出
output_cut = r"C:\Users\Administrator\Desktop\test\锐角切割.shp"    # 切割结果输出
output_mid = r"C:\Users\Administrator\Desktop\test\中间点.shp"      # 调试用中间点输出# 核心阈值参数(可根据业务需求调整)
angle_threshold = 30.0  # 锐角判断阈值(单位:度),小于该值的角判定为锐角
max_area = 1.0          # 切割后三角形最大允许面积(单位:平方米),避免切割块过大

解读:路径参数需使用绝对路径,angle_thresholdmax_area是脚本的核心控制参数 —— 前者决定 “什么是锐角”,后者决定 “切割到多大合适”,需根据数据精度要求调整(如高精度测绘数据可将max_area设为 0.5)。

(二)数据校验:避免后续计算出错

# 校验1:输入文件是否存在
if not arcpy.Exists(input_shp):arcpy.AddError(f"输入文件不存在:{input_shp}")  # 向ArcGIS输出错误信息sys.exit(1)  # 终止程序# 校验2:坐标系是否为投影坐标系(确保距离、面积计算单位为米)
desc = arcpy.Describe(input_shp)  # 获取输入文件的描述信息
sr = desc.spatialReference        # 提取坐标系信息
if sr.type != "Projected":arcpy.AddError(f"请使用投影坐标系(如UTM)!当前坐标系:{sr.name}")sys.exit(1)

解读:这是脚本的 “安全检查”—— 若输入文件路径错误,或用经纬度(地理坐标系)计算面积(会出现 “度 ²” 这种无意义单位),脚本会直接报错并退出,避免生成无效结果。

(三)输出图层创建:提前搭建结果存储结构

# 清理旧数据(若之前运行过脚本,删除旧输出文件)
for shp in [output_mark, output_cut, output_mid]:if arcpy.Exists(shp):arcpy.Delete_management(shp)# 1. 创建锐角顶点标记图层(点要素)
arcpy.CreateFeatureclass_management(os.path.dirname(output_mark),  # 输出文件夹路径os.path.basename(output_mark), # 输出文件名"POINT",                       # 要素类型(点)spatial_reference=sr           # 与输入文件保持一致的坐标系
)
# 为顶点标记图层添加字段(存储原要素ID和内角值)
arcpy.AddField_management(output_mark, "ORIG_FID", "LONG")    # 关联原多边形的ID
arcpy.AddField_management(output_mark, "INT_ANGLE", "DOUBLE") # 存储锐角的具体角度# 2. 创建切割结果图层(多边形要素)
arcpy.CreateFeatureclass_management(os.path.dirname(output_cut), os.path.basename(output_cut), "POLYGON",  # 要素类型(多边形)spatial_reference=sr
)
# 为切割结果图层添加字段(记录关键信息)
arcpy.AddField_management(output_cut, "ORIG_FID", "LONG")    # 关联原多边形ID
arcpy.AddField_management(output_cut, "VERTEX_ID", "LONG")   # 关联原顶点ID
arcpy.AddField_management(output_cut, "AREA", "DOUBLE")      # 切割后三角形的面积
arcpy.AddField_management(output_cut, "MIN_EDGE", "DOUBLE")  # 切割时用的最短边长度# 3. 创建调试用中间点图层(点要素,用于查看分割点位置)
arcpy.CreateFeatureclass_management(os.path.dirname(output_mid), os.path.basename(output_mid), "POINT", spatial_reference=sr
)
arcpy.AddField_management(output_mid, "TYPE", "TEXT", 10)  # 标记点类型(顶点/分割点)

解读:脚本提前创建 3 个输出图层,并定义专属字段,确保结果能 “带属性存储”—— 比如切割后的三角形,能通过ORIG_FID找到它来自哪个原多边形,通过AREA验证是否符合面积要求,方便后续分析。

(四)核心函数:实现锐角识别与切割的 “大脑”

1. 内角计算函数(calc_angle):判断是否为锐角
def calc_angle(p_prev, p_curr, p_next):"""计算多边形顶点p_curr的内角(核心逻辑)"""try:# 步骤1:计算当前顶点与相邻顶点的向量(v1:前一顶点→当前顶点;v2:后一顶点→当前顶点)v1 = (p_prev.X - p_curr.X, p_prev.Y - p_curr.Y)v2 = (p_next.X - p_curr.X, p_next.Y - p_curr.Y)# 步骤2:计算向量的点积(求夹角余弦值)和叉积(判断凹凸性)dot = v1[0] * v2[0] + v1[1] * v2[1]  # 点积:v1·v2 = |v1||v2|cosθcross = v1[0] * v2[1] - v1[1] * v2[0]# 叉积:v1×v2 = |v1||v2|sinθ(符号判断方向)# 步骤3:计算向量长度(避免除以0)len1, len2 = math.hypot(*v1), math.hypot(*v2)  # hypot计算直角三角形斜边(即向量长度)if len1 < 1e-6 or len2 < 1e-6:  # 若某条边长度接近0(重复顶点),返回0度return 0.0# 步骤4:计算角度(cosθ=点积/(|v1||v2|),再转角度)cos_theta = max(-1.0, min(1.0, dot / (len1 * len2)))  # 限制cosθ在[-1,1](避免计算误差)angle = math.degrees(math.acos(cos_theta))  # 弧度转角度# 步骤5:判断内角方向(凸角/凹角):叉积≥0为凸角(内角=计算值),否则为凹角(内角=360-计算值)return angle if cross >= 0 else 360 - angleexcept Exception as e:arcpy.AddWarning(f"角度计算错误: {str(e)}")  # 捕获异常,输出警告(不终止程序)return 0.0

解读:这是脚本的 “核心算法”—— 通过向量运算精准计算多边形内角:

  • 点积决定 “夹角大小”,叉积决定 “角的方向”(凸多边形的内角都是凸角,凹多边形会有大于 180° 的凹角);
  • 加入1e-6误差控制和异常捕获,避免因数据瑕疵(如重复顶点)导致程序崩溃。
2. 距离计算函数(get_distance):求两点间直线距离
def get_distance(p1, p2):"""计算两个点(p1、p2)之间的直线距离"""try:return math.hypot(p1.X - p2.X, p1.Y - p2.Y)  # 本质是勾股定理:距离=√[(x2-x1)²+(y2-y1)²]except Exception as e:arcpy.AddWarning(f"距离计算错误: {str(e)}")return 0.0

解读:用于计算锐角相邻两边的长度,后续会取 “最短边长度” 作为切割时的 “基准长度”,确保切割出的两条分割边等长。

3. 分割点计算函数(get_split_point):确定切割位置
def get_split_point(p_start, p_end, length):"""在p_start到p_end的边上,按指定长度length计算分割点(从p_start向p_end截取)"""try:dx = p_end.X - p_start.X  # 边在X轴方向的增量dy = p_end.Y - p_start.Y  # 边在Y轴方向的增量total_len = math.hypot(dx, dy)  # 边的总长度if total_len < 1e-6:  # 若边长度接近0,直接返回终点(避免错误)return p_end# 计算截取比例(若length超过总长度,取1.0,避免分割点超出边范围)ratio = min(length / total_len, 1.0)# 计算分割点坐标:p_start + 比例*增量return arcpy.Point(p_start.X + dx * ratio,p_start.Y + dy * ratio)except Exception as e:arcpy.AddWarning(f"分割点计算错误: {str(e)}")return p_end

解读:比如锐角相邻两边中,最短边长度为 2 米,就会在两条边上各从顶点截取 2 米,得到两个分割点 —— 这两个点和原顶点会构成一个等腰三角形,确保切割形状规则。

(五)主处理逻辑:串联所有功能,实现自动化处理

try:# 打开3个输出图层的插入游标(用于写入结果,Python 3上下文管理器自动释放资源)with arcpy.da.InsertCursor(output_mark, ["SHAPE@", "ORIG_FID", "INT_ANGLE"]) as mark_cursor, \arcpy.da.InsertCursor(output_cut, ["SHAPE@", "ORIG_FID", "VERTEX_ID", "AREA", "MIN_EDGE"]) as cut_cursor, \arcpy.da.InsertCursor(output_mid, ["SHAPE@", "TYPE"]) as mid_cursor:# 遍历输入多边形要素(读取每个多边形的ID和形状)with arcpy.da.SearchCursor(input_shp, ["OID@", "SHAPE@"]) as search_cursor:for oid, shape in search_cursor:arcpy.AddMessage(f"\n处理要素 ID: {oid}")  # 向ArcGIS输出处理进度# 处理多边形的每个“环”(复杂多边形可能有外环、内环,如带孔洞的地块)for part in shape:points = list(part)  # 提取环的所有顶点num_points = len(points)if num_points < 3:  # 少于3个顶点无法构成多边形,跳过arcpy.AddWarning(f"要素 {oid} 的部分顶点数不足3个,跳过")continue# 处理闭合点(ArcGIS中多边形会自动在末尾添加与第一个点相同的闭合点,需删除)if num_points > 1 and points[0].equals(points[-1]):valid_points = points[:-1]  # 去除重复的最后一个点else:valid_points = pointsnum_valid = len(valid_points)if num_valid < 3:arcpy.AddWarning(f"要素 {oid} 的有效顶点数不足3个,跳过")continue# 遍历每个有效顶点,判断是否为锐角并切割for i in range(num_valid):# 获取当前顶点的前一个、当前、后一个顶点(首尾相连,如第0个顶点的前一个是最后一个)p_prev = valid_points[i-1] if i > 0 else valid_points[-1]p_curr = valid_points[i]p_next = valid_points[(i+1) % num_valid]# 步骤1:计算当前顶点的内角angle = calc_angle(p_prev, p_curr, p_next)arcpy.AddMessage(f"顶点 {i} 内角:{angle:.2f}°")# 步骤2:若为锐角(0<角度<阈值),执行标记和切割if 0 < angle < angle_threshold:# 标记锐角顶点(写入顶点标记图层)mark_cursor.insertRow([p_curr, oid, round(angle, 2)])# 标记顶点到调试图层mid_cursor.insertRow([p_curr, "顶点"])# 步骤3:计算相邻两边的长度,取最短边作为切割基准len_prev = get_distance(p_curr, p_prev)  # 当前顶点到前一顶点的长度len_next = get_distance(p_curr, p_next)  # 当前顶点到后一顶点的长度min_len = min(len_prev, len_next)        # 最短边长度if min_len < 1e-6:  # 若最短边接近0(重复顶点),跳过切割arcpy.AddWarning(f"顶点 {i} 存在零长度边,跳过切割")continue# 步骤4:计算两条边上的分割点(从当前顶点截取最短边长度)s_prev = get_split_point(p_curr, p_prev, min_len)  # 前一条边上的分割点s_next = get_split_point(p_curr, p_next, min_len)  # 后一条边上的分割点# 标记分割点到调试图层mid_cursor.insertRow([s_prev, "分割点"])mid_cursor.insertRow([s_next, "分割点"])# 步骤5:创建切割后的三角形,并处理面积超限问题try:# 构建三角形顶点(需闭合:起点=终点)triangle_points = [p_curr, s_prev, s_next, p_curr]# 创建三角形多边形(指定坐标系)triangle = arcpy.Polygon(arcpy.Array(triangle_points), sr)area = triangle.area  # 计算三角形面积# 若面积超过max_area,按比例缩小分割点(确保面积合规)if area > max_area:scale = math.sqrt(max_area / area)  # 缩放比例(面积比开平方=长度比)# 计算缩放后的分割点(以当前顶点为原点缩放)s_prev_scaled = arcpy.Point(p_curr.X + (s_prev.X - p_curr.X) * scale,p_curr.Y + (s_prev.Y - p_curr.Y) * scale)s_next_scaled = arcpy.Point(p_curr.X + (s_next.X - p_curr.X) * scale,p_curr.Y + (s_next.Y - p_curr.Y) * scale)# 重新创建缩小后的三角形triangle_points = [p_curr, s_prev_scaled, s_next_scaled, p_curr]triangle = arcpy.Polygon(arcpy.Array(triangle_points), sr)area = triangle.areaarcpy.AddMessage(f"顶点 {i} 面积超限,已缩放至 {area:.2f}㎡")# 步骤6:将切割结果写入切割图层cut_cursor.insertRow([triangle, oid, i, round(area, 4), round(min_len, 4)])arcpy.AddMessage(f"顶点 {i} 已切割:面积={area:.2f}㎡,最短边={min_len:.2f}m")except Exception as e:arcpy.AddWarning(f"顶点 {i} 创建三角形失败:{str(e)}")# 处理完成,输出结果路径arc

(六)输出图层设计

脚本自动创建 3 个输出图层,满足不同分析需求:

图层名称要素类型核心字段用途
顶点标记图层ORIG_FID(原要素 ID)、INT_ANGLE(内角值)标记锐角顶点位置及角度,用于后续核查
切割结果图层多边形ORIG_FID、VERTEX_ID(顶点 ID)、AREA(面积)、MIN_EDGE(最短边长度)存储切割生成的三角形,记录关键属性
中间点图层TYPE(类型:顶点 / 分割点)

调试用图层,查看分割点位置是否合理

三、完整代码

# -*- coding: utf-8 -*-
import arcpy
import os
import sys
from arcpy import env# 环境设置
env.overwriteOutput = True
env.XYResolution = "0.001 Meters"
env.XYTolerance = "0.001 Meters"# 临时文件路径
temp_workspace = r"C:\Users\Administrator\Desktop\test\临时文件"
overlap_temp = os.path.join(temp_workspace, "临时重叠区.shp")
remain_temp = os.path.join(temp_workspace, "临时剩余区.shp")
env.scratchWorkspace = temp_workspace# 自动创建临时文件夹
if not os.path.exists(temp_workspace):try:os.makedirs(temp_workspace)arcpy.AddMessage(f"自动创建临时文件夹:{temp_workspace}")except Exception as e:arcpy.AddError(f"创建临时文件夹失败:{str(e)}")sys.exit(1)# 输入输出参数
target_shp = r"C:\Users\Administrator\Desktop\test\01测试数据\测试数据1\锐角测试.shp"
tool_shp = r"C:\Users\Administrator\Desktop\test\锐角切割.shp"
output_split = r"C:\Users\Administrator\Desktop\test\最终分割结果.shp"# 基础检查
check_list = [(arcpy.Exists(target_shp), f"目标图层不存在:{target_shp}"),(arcpy.Exists(tool_shp), f"工具图层不存在:{tool_shp}"),(arcpy.Describe(target_shp).spatialReference.type == "Projected", f"目标图层不是投影坐标系:{arcpy.Describe(target_shp).spatialReference.name}"),(arcpy.Describe(tool_shp).spatialReference.name == arcpy.Describe(target_shp).spatialReference.name, f"坐标系不一致:目标={arcpy.Describe(target_shp).spatialReference.name},工具={arcpy.Describe(tool_shp).spatialReference.name}")
]for is_valid, error_msg in check_list:if not is_valid:print(f"检查失败:{error_msg}")arcpy.AddError(error_msg)sys.exit(1)# 核心分割操作
try:# 清理旧临时文件for temp in [overlap_temp, remain_temp]:if arcpy.Exists(temp):arcpy.Delete_management(temp)# 步骤1:提取重叠区域arcpy.AddMessage("提取重叠区域...")arcpy.Intersect_analysis(in_features=[target_shp, tool_shp],out_feature_class=overlap_temp,join_attributes="ALL")arcpy.AddField_management(overlap_temp, "SPLIT_TYPE", "TEXT", 20)with arcpy.da.UpdateCursor(overlap_temp, ["SPLIT_TYPE"]) as cur:for row in cur:row[0] = "重叠切割部分"cur.updateRow(row)# 步骤2:提取剩余区域arcpy.AddMessage("提取剩余区域...")arcpy.Erase_analysis(in_features=target_shp,erase_features=tool_shp,out_feature_class=remain_temp,cluster_tolerance="0.001 Meters")arcpy.AddField_management(remain_temp, "SPLIT_TYPE", "TEXT", 20)with arcpy.da.UpdateCursor(remain_temp, ["SPLIT_TYPE"]) as cur:for row in cur:row[0] = "非重叠剩余部分"cur.updateRow(row)# 步骤3:合并结果(修正工具名称为Merge_management)arcpy.AddMessage("合并分割结果...")arcpy.Merge_management(  # 此处为修正点inputs=[overlap_temp, remain_temp],output=output_split)# 清理临时文件for temp in [overlap_temp, remain_temp]:if arcpy.Exists(temp):arcpy.Delete_management(temp)arcpy.AddMessage(f"\n分割完成!结果路径:{output_split}")arcpy.AddMessage("可通过 'SPLIT_TYPE' 字段区分两部分")except Exception as e:print(f"分割过程出错:{str(e)}")arcpy.AddError(f"分割出错:{str(e)}")for temp in [overlap_temp, remain_temp]:if arcpy.Exists(temp):arcpy.Delete_management(temp)sys.exit(1)

http://www.dtcms.com/a/525550.html

相关文章:

  • 一种使用 PowerToys 的键盘管理器工具编辑惠普暗影精灵11 的 OMEN 自定义按键的方法
  • 锂电池充放电管理学习
  • 复数等式:为何对所有整数都成立?
  • CLIP模型全解析:从对比学习到零样本识别的革命
  • 广州网站优化步骤用公司网站后缀做邮箱
  • 202.快乐数
  • 黑色asp企业网站源码办公空间设计说明300字
  • 站群 网站如何做网站页面设计网页说明
  • 昆山的网站建设网站建设改手机号
  • HTML:Video视频切换时不重新加载
  • CSP-S模拟赛六总结
  • 网站开发工具中的三剑客湖南建设人力资源网证书查询
  • 朝阳网站推广网站开发费用说明
  • seo做的好的网站有哪些东莞最新招聘
  • seo网站优化多少钱全球咨询公司排名
  • ElementPlus 如何支持移动端
  • 家庭服务器分享
  • xv6 第二章_操作系统架构
  • 创建网站要多长时间网站链接只显示到文件夹怎么做的
  • 写出网站开发的基本流程wordpress nova 汉化
  • 1024~
  • 选图片的网站网站引流.
  • Java的内部类介绍
  • 企业自建服务器网站建设流程建设网站需要注意事项
  • 描述性统计 vs 推断性统计:观察与判断的区别
  • Power BI 10 月功能更新全解析
  • C语言解决轮转数组
  • 网站备案 上线app网站开发住房公积金
  • 网站建设后期需要做什么提供营销网站建设公司
  • 开发运维警示录-《论日志和监控的重要性》_20251024