【ArcGIS技巧】根据地块、界址点图层生成界址线

"农经权二轮延包我已经写的差不多了,需要的一些生成四至、分割地块的功能也分享了替代的插件。前面刚分享完界址点的生成,今天分享界址线的生成,有需要的自取,至此,基本可以用这些功能完成出成果工作。"

1、工具使用

在使用前,先确保有地块(DK.shp)要素图层,且包含字段"DKBM"、"ZJRXM";界址点(JZD.shp)要素图层(含字段"DKBM", "JZDH")。

插件因为生成界址线比较复杂,而且生成JZX.shp图层包含了:"DKBM"、"QZJDH"、"ZJZDH"、"JZXSM",特别是"JZXSM"字段比较复杂,所以生成的时间稍微长些,请注意等待。

下载后记得加载py文件,不会的参考:【ArcGIS技巧】分享个判断是否基本农田的工具

2、脚本逻辑

脚本的生成逻辑大致分为三步:1、先利用地块要素图层生成只要两个点的直线,生成DKBM、PLDWQLR字段;2、根据生成DKBM、PLDWQLR字段合并直线,因为前面界址点生成简化了面,所以要确保每个界址线的起止点都在界址线图层中,再根据界址点去截断界址线;3、根据界址点的JZDH以及界址线的长度与方位等,生成"QZJDH"、"ZJZDH"、"JZXSM"字段内容。

值得注意的是:工具的py文件,在pycharm等解析器中写完后,需要用文本文件编辑器打开,另存文件覆盖原来的.py文件,编码选ANSI格式,只有这样,其中一些中文什么的才会执行时不报错。

3、具体代码

工具的具体代码如下:

python 复制代码
# -*- coding: utf-8 -*-
import arcpy
import os
from collections import defaultdict
import tempfile
import math
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
arcpy.env.workspace = u"C:\\data"
arcpy.env.overwriteOutput = True
arcpy.env.qualifiedFieldNames = False

dk_shp = u"C:\\data\\DK.shp"
jzd_path = u"C:\\data\\JZD.shp"
output_folder = u"C:\\data\\new"

# 中间文件路径
temp_folder = tempfile.mkdtemp()
jzx1_path = os.path.join(temp_folder, u"JZX1.shp")
dissolve_path = os.path.join(temp_folder, u"dissolved.shp")
rounded_points = os.path.join(temp_folder, u"rounded_points.shp")

def create_jzx():
    # 验证必须字段存在
    required_fields = ["DKBM", "ZJRXM"]
    actual_fields = [f.name for f in arcpy.ListFields(dk_shp)]
    missing = [f for f in required_fields if f not in actual_fields]
    if missing:
        raise arcpy.ExecuteError(u"缺失必要字段:{}".format("/".join(missing)))

    # ====================== 创建输出要素 ======================
    sr = arcpy.Describe(dk_shp).spatialReference
    arcpy.CreateFeatureclass_management(
        out_path=temp_folder,
        out_name=os.path.basename(jzx1_path),
        geometry_type="POLYLINE",
        spatial_reference=sr
    )

    # 添加字段
    arcpy.AddField_management(jzx1_path, "DKBM", "TEXT", field_length=100)
    arcpy.AddField_management(jzx1_path, "PLDWZJR", "TEXT", field_length=255)

    # ====================== 构建DKBM-ZJRXM映射 ======================
    dk_zjr_map = {}
    with arcpy.da.SearchCursor(dk_shp, ["DKBM", "ZJRXM"]) as cursor:
        for row in cursor:
            dk_zjr_map[row[0]] = row[1]

    # ====================== 主处理逻辑 ======================
    line_dict = defaultdict(set)
    arcpy.AddMessage(u"正在采集边界线段...")

    # 第一次遍历:收集所有边界线段
    with arcpy.da.SearchCursor(dk_shp, ["OID@", "SHAPE@", "DKBM"]) as cursor:
        for row in cursor:
            geom = row[1]
            dkbm = row[2]

            # 分解多边形边界线
            for part in geom.getPart():
                for i in range(len(part) - 1):
                    pt1 = part[i]
                    pt2 = part[i + 1]

                    # 标准化线段方向(避免反向重复)
                    if (pt1.X, pt1.Y) > (pt2.X, pt2.Y):
                        pt1, pt2 = pt2, pt1

                    # 生成唯一标识(精确到毫米)
                    line_key = (
                        round(pt1.X, 3), round(pt1.Y, 3),
                        round(pt2.X, 3), round(pt2.Y, 3)
                    )
                    line_dict[line_key].add(dkbm)

    # ====================== 写入结果 ======================
    arcpy.AddMessage(u"正在生成界址线...")
    with arcpy.da.InsertCursor(jzx1_path, ["SHAPE@", "DKBM", "PLDWZJR"]) as i_cursor:
        # 第二次遍历:处理相邻地块逻辑
        for line_key, dkbms in line_dict.iteritems():
            pt1 = arcpy.Point(line_key[0], line_key[1])
            pt2 = arcpy.Point(line_key[2], line_key[3])
            line = arcpy.Polyline(arcpy.Array([pt1, pt2]), sr)

            # 处理DKBM字段(按字母排序)
            sorted_dkbms = sorted(dkbms, key=lambda x: str(x))
            dkbm_str = "/".join(sorted_dkbms)

            # 处理PLDWZJR字段
            if len(sorted_dkbms) == 1:
                zjrxm = u"无"
            else:
                # 取"/"后面所有地块的ZJRXM
                zjrxm_list = []
                for dk in sorted_dkbms[1:]:  # 从第二个开始
                    if dk in dk_zjr_map:
                        zjrxm_list.append(dk_zjr_map[dk])
                zjrxm = "/".join(zjrxm_list) if zjrxm_list else u"无"

            i_cursor.insertRow([line, dkbm_str, zjrxm])

    arcpy.AddMessage(u"处理完成!输出路径:{}".format(jzx1_path))

create_jzx()

try:
    # ===== 阶段1:合并界址线 =====
    arcpy.Dissolve_management(
        in_features=jzx1_path,
        out_feature_class=dissolve_path,
        dissolve_field=["DKBM", "PLDWZJR"],
        multi_part="SINGLE_PART"
    )
    # ===== 阶段2:创建精确坐标点 =====
    arcpy.CreateFeatureclass_management(
        temp_folder,
        os.path.basename(rounded_points),
        "POINT",
        spatial_reference=arcpy.Describe(jzd_path).spatialReference
    )

    # 插入舍入坐标点
    with arcpy.da.SearchCursor(jzd_path, ["SHAPE@XY"]) as sc, \
            arcpy.da.InsertCursor(rounded_points, ["SHAPE@XY"]) as ic:
        for row in sc:
            x, y = row[0]
            ic.insertRow([(round(x, 3), round(y, 3))])

    # ===== 阶段3:批量拆分线段 =====
    # 创建内存图层
    arcpy.MakeFeatureLayer_management(dissolve_path, "lines_lyr")
    arcpy.MakeFeatureLayer_management(rounded_points, "points_lyr")

    # 执行拆分操作
    split_result = arcpy.management.SplitLineAtPoint(
        in_features="lines_lyr",
        point_features="points_lyr",
        out_feature_class=os.path.join(output_folder, "JZX"),
        search_radius="0.001 Meters"
    )
finally:
    # 清理临时数据
    for item in [dissolve_path, rounded_points]:
        if arcpy.Exists(item):
            arcpy.Delete_management(item)

def calculate_direction(angle):
    """根据方位角计算方向字典"""
    if 337.5 <= angle or angle < 22.5:
        return "东"
    elif 22.5 <= angle < 67.5:
        return "东南"
    elif 67.5 <= angle < 112.5:
        return "南"
    elif 112.5 <= angle < 157.5:
        return "西南"
    elif 157.5 <= angle < 202.5:
        return "西"
    elif 202.5 <= angle < 247.5:
        return "西北"
    elif 247.5 <= angle < 292.5:
        return "北"
    elif 292.5 <= angle < 337.5:
        return "东北"

def get_angle(start_point, end_point):
    """计算两点之间的方位角"""
    dx = end_point.X - start_point.X
    dy = end_point.Y - start_point.Y
    angle = math.degrees(math.atan2(dy, dx))
    return (angle + 360) % 360

# 创建字段
split_lines = os.path.join(output_folder, "JZX.shp")
for field in ["QZJDH", "ZJZDH", "JZXSM"]:
    if not arcpy.ListFields(split_lines, field):
        arcpy.AddField_management(split_lines, field, "TEXT", field_length=100)

# 构建界址点索引
jzd_dict = defaultdict(dict)
with arcpy.da.SearchCursor(jzd_path, ["DKBM", "JZDH", "SHAPE@"]) as cursor:
    for row in cursor:
        key = (row[0], row[1])
        jzd_dict[key] = row[2].centroid # 获取质心坐标

# ========== 处理界址线 ==========
with arcpy.da.UpdateCursor(split_lines, ["DKBM", "PLDWZJR", "SHAPE@", "QZJDH", "ZJZDH", "JZXSM"]) as cursor:
    for row in cursor:
        dkbm_line = row[0]
        pldwzjr = row[1] if row[1] else "无"
        geometry = row[2]

        # ===== 步骤1:调整线段方向 =====
        # 获取首尾点坐标
        first_point = geometry.firstPoint
        last_point = geometry.lastPoint

        # 查找对应的界址点号
        found_start = None
        found_end = None
        # 建立一个DKBM与ZJDH的键值对
        found_sartdkbm = None
        found_enddkbm = None
        for (dkbm_part, jzdh), point in jzd_dict.items():
            set1 = set(dkbm_line.split("/"))
            set2 = set(dkbm_part.split("/"))
            if not set1.isdisjoint(set2):
                if (abs(point.X - first_point.X) < 0.001 and
                            abs(point.Y - first_point.Y) < 0.001):
                    found_start = jzdh
                    found_sartdkbm=dkbm_part
                if (abs(point.X - last_point.X) < 0.001 and
                            abs(point.Y - last_point.Y) < 0.001):
                    found_end = jzdh
                    found_enddkbm=dkbm_part
        line_dkbm=dkbm_line.split("/")
        list_qjzdh=[]
        list_zjzdh = []
        for i in line_dkbm:
            list_jzdh1=found_start.split("/")
            list_dkbm1=found_sartdkbm.split("/")
            for j in range(len(list_dkbm1)):
                if i==list_dkbm1[j]:
                    list_qjzdh.append(int(list_jzdh1[j]))
            list_jzdh2 = found_end.split("/")
            list_dkbm2 = found_enddkbm.split("/")
            for j in range(len(list_dkbm2)):
                if i == list_dkbm2[j]:
                    list_zjzdh.append(int(list_jzdh2[j]))
        def reverse_polyline(geom):
            array = arcpy.Array()
            part = geom.getPart(0)  # 获取线段的点集合
            reversed_part = [pnt for pnt in part][::-1]  # 反转坐标点顺序
            for pnt in reversed_part:
                array.add(pnt)
            return arcpy.Polyline(array)  # 重建反转后的线段
        # 判断是否需要反转
        if found_start and found_end and sum(list_qjzdh) > sum(list_zjzdh):
            geometry = reverse_polyline(geometry)

        # ===== 步骤2:生成起止点号 =====
        # 生成字段值
        str_qjzdh=''
        if len(list_qjzdh)==1:
            str_qjzdh =str_qjzdh + 'J'+str(list_qjzdh[0])
        else:
            str_qjzdh = str_qjzdh + 'J' + str(list_qjzdh[0])+u'/J'+str(list_qjzdh[1])
        str_zjzdh=''
        if len(list_zjzdh)==1:
            str_zjzdh =str_zjzdh + 'J'+str(list_zjzdh[0])
        else:
            str_zjzdh = str_zjzdh + 'J' + str(list_zjzdh[0])+u'/J'+str(list_zjzdh[1])

        row[3] =str_qjzdh
        row[4] = str_zjzdh

        # ===== 步骤3:生成描述字段 =====
        # 计算长度
        length = round(geometry.length, 2)

        # 计算方向
        start_pt = geometry.firstPoint
        end_pt = geometry.lastPoint
        angle = get_angle(start_pt, end_pt)
        direction = calculate_direction(angle)

        # 构建描述
        desc = "{}-{}:由{}界址点沿{}宗地分界线往{}方向长度{}m至{};".format(
            row[3].split("/")[0], row[4].split("/")[0], row[3].split("/")[0],
            pldwzjr if pldwzjr != "无" else "",
            direction,
            length,
            row[4].split("/")[0]
        )
        row[5] = desc

        cursor.updateRow(row)

print(u"处理完成!")
相关推荐
你是一个铁憨憨11 小时前
使用深度学习预训练模型检测物体
人工智能·深度学习·arcgis·影像
嘘嘘出差1 天前
ArcGIS切片方案记录bundle文件
arcgis
摆烂老大2 天前
SWAT| 水文 | SWAT模型(三):土壤数据库制备
arcgis·水文·swat模型
鸿业远图科技2 天前
分式注记种表达方式arcgis
python·arcgis
城市数据匠3 天前
31【干货】Arcgis属性表常用查询表达式实战大全
arcgis·gis·cad·国土空间规划·自然资源局
新中地GIS开发老师3 天前
【Cesium入门教程】第七课:Primitive图元
arcgis·信息可视化·gis开发·webgis·地理信息系统·地理信息科学
王孝点4 天前
geoserver发布arcgis瓦片地图服务(最新版本)
arcgis
没有梦想的咸鱼185-1037-16636 天前
【大语言模型ChatGPT4/4o 】“AI大模型+”多技术融合:赋能自然科学暨ChatGPT在地学、GIS、气象、农业、生态与环境领域中的应用
人工智能·python·机器学习·arcgis·语言模型·chatgpt·数据分析
新中地GIS开发老师6 天前
【Cesium入门教程】第五课:数据源
arcgis·遥感·gis开发·webgis·地理信息科学