土地报备坐标txt文件转shp遇到的坑以及该功能的 Python(Arcpy) 实现

it2025-02-07  28

文章目录

土地报备坐标txt文件转shp遇到的坑以及该功能的 Python(Arcpy) 实现一. 使用 Python(ArcPy) 绘制shp什么是ArcPy如何构造shp(面)主要涉及方法构造面 二. 土地报备坐标txt文件(坐标交换数据)数据主要依据标准《勘测定界界址点坐标交换格式》:books:转换过程中的坑解决思路 :thinking:实现代码 参考结尾

土地报备坐标txt文件转shp遇到的坑以及该功能的 Python(Arcpy) 实现

一. 使用 Python(ArcPy) 绘制shp

什么是ArcPy

ArcPy 是一个安装 ArcGIS 会附带的站点包,通过 Python 实现。简言之,能通过 Python 直接调用 arcpy 执行地理数据分析、数据转换、数据管理和地图自动化以及多种客制化的需求。

ArcGIS 帮助链接

[https://desktop.arcgis.com/zh-cn/arcmap/10.3/analyze/arcpy/what-is-arcpy-.htm](ArcGIS help)

需要注意的是 ArcPy 是通过 Python2 实现的,不兼容 Python3!

需要注意的是 ArcPy 是通过 Python2 实现的,不兼容 Python3!

需要注意的是 ArcPy 是通过 Python2 实现的,不兼容 Python3!

如何构造shp(面)

主要涉及方法

arcpy.Array()

https://desktop.arcgis.com/zh-cn/arcmap/10.3/analyze/arcpy-classes/array.htm

arcpy.Polygon()

https://desktop.arcgis.com/zh-cn/arcmap/10.3/analyze/arcpy-classes/polygon.htm

构造面

点连成线,线组成了一个面。所有只需在 arcpy.Polygon() 方法中输入一个点集就能形成一个面,点集(数组)通过 arcpy.Array() 构造传入(最后一个点不必与首个点相同,就算没有也会自动闭合)。

[[20.0, 20.0],[30.0, 20.0], [30.0, 10.0],[20.0, 10.0]]

多个面就是多个点集。两个点集就是两个图形。

[ [[5.0, 3.0],[3.0, 3.0], [3.0, 5.0],[5.0, 3.0]], [[7.0, 5.0],[5.0, 5.0], [5.0, 7.0],[7.0, 5.0]] ]

那么如果是环岛、孔洞等图形呢?也是多个点集,当某个点集在另一个点集范围内时,arcpy.Polygon() 方法可以自动排除重叠部分,实现环岛、孔洞等,真正傻瓜一键化绘制shp图形。

[ [[40.0, 40.0], [50.0, 40.0], [50.0, 30.0], [40.0, 30.0]], [[45.0, 35.0], [48.0, 35.0], [48.0, 36.0], [45.0, 36.0]] ]

以下是 使用 arcpy.Array() 和 arcpy.Polygon() 实现简单的点集转 shp 代码示例:

-*- coding:utf-8 -*- # --------------------------------------------------------------------------- # Author: Lcc hygnic # Created on: 2020/10/16 15:55 # Reference: """ Description:Python2.7 Usage: """ # --------------------------------------------------------------------------- import arcpy import os def draw_poly(coord_list, sr, y, x): """ 创建多边形 coord_list(List):多个点组成的坐标 sr: 投影系 y(Int): y坐标列 x(Int): x坐标列 """ parts = arcpy.Array() yuans = arcpy.Array() yuan = arcpy.Array() for part in coord_list: for pnt in part: if pnt: yuan.add(arcpy.Point(pnt[y], pnt[x])) else: # null point - we are at the start of a new ring yuans.add(yuan) yuan.removeAll() # we have our last ring, add it yuans.add(yuan) yuan.removeAll() # if we only have one ring: remove nesting if len(yuans) == 1: yuans = yuans.getObject(0) parts.add(yuans) yuans.removeAll() # 只有一个,单个图形 if len(parts) == 1: parts = parts.getObject(0) return arcpy.Polygon(parts, sr) if __name__ == '__main__': # 点集,将之前的示例的三个图形放到一起 poly = [ [[20.0, 20.0], [30.0, 20.0], [30.0, 10.0], [20.0, 10.0]], [[5.0, 3.0], [3.0, 3.0], [3.0, 5.0], [5.0, 3.0]], [[7.0, 5.0], [5.0, 5.0], [5.0, 7.0], [7.0, 5.0]], [[40.0, 40.0], [50.0, 40.0], [50.0, 30.0], [40.0, 30.0]], [[45.0, 35.0], [48.0, 35.0], [48.0, 36.0], [45.0, 36.0]] ] arcpy.env.overwriteOutput = True # 输出的shp文件可以覆盖同名文件 output_folder = os.getcwd() # 创建空白 shp name = "NewSHP" blank_shp = arcpy.CreateFeatureclass_management(output_folder, name,"Polygon") # 创建、写入面 Rows = arcpy.da.InsertCursor(blank_shp, "SHAPE@") f = poly p = draw_poly(f, sr=None, y=0, x=1) Rows.insertRow([p]) write_sth = "Write done: " + os.path.join(output_folder, name) print write_sth del Rows

二. 土地报备坐标txt文件(坐标交换数据)

数据主要依据标准《勘测定界界址点坐标交换格式》📚

我们拿到的TXT文件中的数据基本按照以下标准:

附录一:《勘测定界界址点坐标交换格式》

... 坐标交换格式具有两种格式,分别如下: 文本格式 [属性描述] 格式版本号= 数据产生单位= 数据产生日期= 坐标系= 几度分带= 投影类型= 计量单位= 带号= 精度= 转换参数=X平移,Y平移,Z平移,X旋转,Y旋转,Z旋转,尺度参数 [地块坐标] 界址点数,地块面积,地块编号,地块名称,记录图形属性(点、线、面),图幅号,地块用途,地类编码,@ {点号,地块圈号,X坐标,Y坐标 ... ... 点号,地块圈号,X坐标,Y坐标} 界址点数,地块面积,地块编号,地块名称,记录图形属性(点、线、面),图幅号,地块用途,地类编码,@ {点号,地块圈号,X坐标,Y坐标 ... ... 点号,地块圈号,X坐标,Y坐标} 注意: 所有的逗号分隔符都必须是英文输入法状态下的逗号;地块圈号不能小于零;数据产生日期的格式为:2000-12-12;坐标系为54北京坐标系或80国家大地坐标系;投影类型为高斯克吕格或等角多圆锥;几度分带为3或6;带号、精度、转换参数、界址点数、地块面积、地块圈号,X坐标,Y坐标必须为数字型;且不能用该(9999,000,000)方式表示;地块编号、地块名称、记录图形属性(点、线、面)、图幅号、地块用途、地类编码、点号的每项里不能含有“,” 、“@”符号。 点号中不能有字母,如J01等,建议为1。(预审报盘坐标格式要求) ... 例子:(该例子中数据经过处理,如果使用该数据生成shp,结果可能不符合预期) [属性描述] 格式版本号= 数据产生单位=国土资源部 数据产生日期=2020-9-10 坐标系=2000国家大地坐标系 几度分带=6 投影类型=高斯克吕格 计量单位=米 带号=35 精度=0.001 转换参数=0,0,0,0,0,0,0 [地块坐标] 8137,559.4952,1,火星乡地球村改造项目,面,,,,@ J1,1,3433398.731421,35572460.011417 J2,1,3433413.686436,35572361.307118 J3,1,3433422.659303,35572214.746216 J4,1,3433425.650143,35572095.104671 J5,1,3433431.631890,35571897.696121 J6,1,3433446.586915,35571804.9739 J7,1,3433437.613643,35571673.368316 J8,1,3433377.792817,35571493.906440 J1,1,3433398.731421,35572460.011417 18400,998.7312,1,火星乡地球村改造项目,面,,,,@ J1,1,3437490.7824,35577811.1468 J2,1,3437488.2225,35577838.46 J3,1,3437488.2225,35577864.9191 J4,1,3437503.5857,35577884.5502 J5,1,3437514.6817,35577899.9133 J6,1,3437514.6818,35577928.0804 J7,1,3437506.9998,35577940.0295 J8,1,3437506.5118,35577944.1285 J9,1,3437503.9028,35577960.8496 J1,1,3437490.7824,35577811.1468

数据结构说明:

转换过程中的坑

那么把土地报备坐标txt文件转换成shp,主要做的就是 把TXT文件中的点正确的分成一个个点集(数组),然后传入 arcpy.Polygon() 方法就行了,然而在实践中,就是在这个过程本人遇到过很多坑。导致出现下面这些情况:

这种情况出现的原因通常只有一个,就是没有正确的把一个个点集分开,导致本应该闭合的一个图形没有结束,然后连到下一个图形,导致出现这种线“乱飞乱连”的错误情况。

而为什么没有正确把点集分开,是因为对数据的理解不全面(或者说数据有几种样式,txt文本亲自从权威官网下载,不存在其他人私自修改的可能),上文所说的标准是我在网上找到的,感觉说的不甚清楚且不全。


实际上在实践中,出现了以下几种样式(情况):

无地块描述,如 18400,998.7312,1,火星乡地球村改造项目,面,@ 这种@结尾的段落,本可以作为两个地块的标志物,但是实际效果不理想

以下可以看到是多部件,部件 从1变成2,序号 依然是连续的(结尾的 J1 可有可无)。

也是多部件,部件 从1变成2,但是和上一张图相反, 序号 却重新开始了。

可以发现,多部件和地块的区分容易混淆,更糟糕的是,我还遇到过以下几种情况(分别是序号开头不为1、部件开头不为1和部件计数跳跃):

解决思路 🤔

所以我的思路是不要去纠结 部件 和地块 序号,全部当成不同的地块处理(相当于有多部件的地块被打散了,这完全不影响出图,反正在一张shp文件上)。

到了这里,我已经逐渐理解一切,关键在于 序号 列和 部件 列,一旦 序号 列从1重新开始或者 部件 号发生改变,数据进行分割。

分好的数据(一个地块)是一个列表,然后一个txt文件中的所有数据组成一个大列表进行处理。

实现代码

完整实现代码(脚本形式): ✌️好耶

#!/usr/bin/env python # -*- coding:utf-8 -*- # --------------------------------------------------------------------------- # arcgis 10.3 # Author: hygnic lcc # Created on: 2020/4/8 16:33 # Reference: """ Description: 将国土土地报备坐标txt文本处理生成shp文件 原始数据如下: [属性描述] 格式版本号= 数据产生单位= 数据产生日期=2077 坐标系=2000ff 几度分带= 投影类型=高斯克吕格 计量单位=米 带号=ffff 精度=ffff 转换参数=fffffff [地块坐标] 41,0,1,0,面,0,0,,@ - J1,1,3.0, 8.0 J2,1,1.0, 8.0 J3,1,2.0, 10.0 J4,1,3.0, 8.0 555,0,1,0,面,0,0,,@ J1,1,9.0, 11.0 J2,1,9.0, 8.0 J3,1,6.0, 8.0 J4,1,6.0, 11.0 J1,2,7.0, 10.0 - J2,2,7.0, 9.0 - J3,2,8.0, 9.0 - J4,2,8.0, 10.0 处理为如下列表: [ [ [3.0, 8.0], [1.0, 8.0], [2.0, 10.0], [3.0, 8.0] ] , [ [9.0, 11.0], [9.0, 8.0], [6.0, 8.0], [6.0, 11.0], [9.0, 11.0] ] , [ [7.0, 10.0], [7.0, 9.0], [8.0, 9.0], [8.0, 10.0], [7.0, 10.0] ] ] 每一个会闭合的线都组成一个单独的列表,防止因为首尾不接导致闭合面错误 Usage: """ # --------------------------------------------------------------------------- import re import arcpy import os def points_genarator(txt_file): """ points_genarator(txt_file) return list txt_file:文本文件地址 将txt转换为可以使用的点集列表 """ fffs = open(txt_file, "r") input_data = fffs.readlines() # input_data.remove("\n") # 去除换行符 # input_data = [x.strip() for x in input_data] # input_data = input_data[2:] # 去除前13行 input_data = [x.strip() for x in input_data if "@" not in x] # 去除带@的行 # 去除非J行 while True: # 去除前13行 # 如果首行字符不在下列元组中 first = ("J", "1", "0", "2", "3", "4", "5", "6", "7", "8", "9") if input_data[0][0] not in first: del input_data[0] # elif : # del input_data[0] else: break # input_data = input_data[12:] # 去除前13行 fffs.close() # 每一根闭合线单独组成一个列表 line_closed = [] # 多根线条组成面 polygon_list = [] while input_data: row = input_data.pop(0) row1 = row.split(",") # ['J1', '1', '3405133.8969', '35353662.0113'] # 去除字母和数字组合中的字母 如 "J1" 只保留 "1" row_num = re.findall(r'[0-9]+|[a-z]+', row1[0]) # ['1'] row_num2 = row1[1] # '1' if not line_closed: # 为空时 line_closed.append(row1) flag1 = int(row_num[0]) # 1 第一列数字 # 第二列数字 flag2 = row1[1] # '1' elif int(row_num[0]) > flag1: if row_num2 != flag2: # 第二列出现不一样的,新一轮开始 flag2 = row_num2 polygon_list.append(line_closed) line_closed = [] # 未开始新一轮就接着上一个,如果开始新一轮那么这里就是第一个起始点 line_closed.append(row1) elif int(row_num[0]) == flag1: # 新一轮开始 polygon_list.append(line_closed) line_closed = [] line_closed.append(row1) if line_closed: polygon_list.append(line_closed) return polygon_list def draw_poly(coord_list, sr, y, x): """ 创建多边形 coord_list(List):多个点组成的坐标 sr: 投影系 y(Int): y坐标列 x(Int): x坐标列 """ parts = arcpy.Array() yuans = arcpy.Array() yuan = arcpy.Array() for part in coord_list: for pnt in part: if pnt: yuan.add(arcpy.Point(pnt[y], pnt[x])) else: # null point - we are at the start of a new ring yuans.add(yuan) yuan.removeAll() # we have our last ring, add it yuans.add(yuan) yuan.removeAll() # if we only have one ring: remove nesting if len(yuans) == 1: yuans = yuans.getObject(0) parts.add(yuans) yuans.removeAll() # 只有一个,单个图形 if len(parts) == 1: parts = parts.getObject(0) return arcpy.Polygon(parts, sr) def main(info, txt_folder, output_folder): """ 功能实现的主函数 info(List): 存放信息的列表,作为独立脚本使用时没啥用 txt_folder(Unicode/String): 包含txt文件的文件夹 output_folder(Unicode/String): 导出文件夹 """ arcpy.env.overwriteOutput = True txts = os.listdir(txt_folder) for txt in txts: if txt[-3:].lower() == "txt": txt_p = os.path.join(txt_folder, txt) f = points_genarator(txt_p) name = os.path.splitext(os.path.basename(txt_p))[0] # 获取名称不带后缀 # 创建空白shp blank_shp = arcpy.CreateFeatureclass_management(output_folder, name, "Polygon", spatial_reference=None) # create the polygons and write them Rows = arcpy.da.InsertCursor(blank_shp, "SHAPE@") p = draw_poly(f, sr=None, y=3, x=2) Rows.insertRow([p]) del Rows write_sth = "Write done: " + os.path.join(output_folder, name) print write_sth info.append(write_sth) # 给新建的shp文件添加 MC 字段和值 shp_name = name + ".shp" newfield_name = "MC" fresh_layer = arcpy.mapping.Layer( os.path.join(output_folder, shp_name)) arcpy.AddField_management(fresh_layer, newfield_name, "TEXT", field_length=100) cursor2 = arcpy.da.UpdateCursor(fresh_layer, newfield_name) for roww in cursor2: roww[0] = name cursor2.updateRow(roww) del cursor2 if __name__ == '__main__': """脚本单独使用""" """---------------------------------------------------------------""" """------------------------PARA-----------------------------------""" arcpy.env.overwriteOutput = True dir_p = ur"G:\sc\txt\新建文件夹" # 包含txt的文件夹 output_dir = ur"G:\1" # 输出文件夹 """---------------------------------------------------------------""" """---------------------------------------------------------------""" infos = [] main(infos, dir_p, output_dir)

正确分割数据得到的正确shp文件与错误shp文件的对比:

参考

关于构造面:

​ https://gis.stackexchange.com/questions/14952/arcgis-10-0-arcpy-how-to-create-a-polygon-geometry-from-inner-and-outer-ring-po

结尾


有疑问欢迎留言询问 原创不易,欢迎转发和分享
了解更多文章,关注我的微信

😄😃😙😸😂😁😗😝😌😄😃😙😸😂😁😗😝😌😄😃😙😸😂😁😗😝😌😄😃😙😸😂😁😗😝😌 关联账号:

知乎

最新回复(0)