两周前看到一个朋友在后台的留言,
“有个功能能不能开发一下,就是输入一个shp,按其中的某个字段分别导出shp。主要是让不会arcgis的人也能按字段分割shp了。”
我把这个需求可以理解为,按照指定字段的指定记录值,生成新的矢量文件。
这个功能,在arcgis中是常常被使用的。依稀记得是要使用一个属性选择器吗,然后过滤得到我们想保存的矢量。
那如果通过代码的形式,实现这个功能呢?
今天我们就来浅略地说一说,具体操作可以看我开源的代码。
1 介绍
常见的矢量文件格式是shp格式,而shp格式通常由三个不同后缀的文件组合而成,结构如下:
属性信息由字段和各自的记录值组合而成,如下:
字段是唯一的,而记录值可以存在相同的值(可以重复)。
2 后端处理的代码
import os.path
from osgeo import ogr
ogr.UseExceptions() # 启用异常处理
class Shp_split:
def __init__(self, filename):
self.filename = filename
self.driver = ogr.GetDriverByName('ESRI Shapefile')
self.datasource = self.driver.Open(filename, 0) # 0 means read-only
self.get_field_name()
self.layer = self.datasource.GetLayer()
def get_field_name(self):
ds = self.datasource
layer = ds.GetLayer()
layer_defn = layer.GetLayerDefn()
field_contents = []
# 遍历所有的字段
for i in range(layer_defn.GetFieldCount()):
fieldDefn = layer_defn.GetFieldDefn(i)
field_contents.append(fieldDefn.GetName())
self.field = field_contents
# 关闭数据源
ds = None
self.field_contents = field_contents
def get_unique_values(self, field_name):
# 获取字段定义
layerDefn = self.layer.GetLayerDefn()
fieldIndex = layerDefn.GetFieldIndex(field_name)
# 检查字段是否存在
if fieldIndex == -1:
print("Field not found.")
else:
# 获取所有记录
values = []
for feature in self.layer:
value = feature.GetFieldAsString(fieldIndex)
if value is not None:
values.append(value)
return values
def split_by_field(self, field_name, value):
# 打开输入的 Shapefile 数据源
data_source = self.datasource
if data_source is None:
print(f'Could not open {self.filename}')
return
# 获取数据源中的第一层(通常一个 Shapefile 只有一层)
layer = data_source.GetLayer()
if layer is None:
print(f'No layer found in {self.filename}')
return
output_shp_file = os.path.splitext(os.path.basename(self.filename))[0] +'_'+ field_name + '_' +value +'.shp'
outpath = os.path.join(os.path.dirname(os.path.abspath(self.filename)), "out")
os.makedirs(outpath, exist_ok=True)
output_shp = os.path.join(outpath, output_shp_file)
# 创建输出的 Shapefile 数据源
# if ogr.GetDriverByName('ESRI Shapefile').Exists(output_shp):
# ogr.GetDriverByName('ESRI Shapefile').DeleteDataSource(output_shp)
out_data_source = self.driver.CreateDataSource(output_shp)
if out_data_source is None:
print(f'Could not create {output_shp}')
return
# 复制输入层的定义以创建输出层
out_layer = out_data_source.CreateLayer(output_shp.split('.')[0], geom_type=layer.GetGeomType())
for i in range(layer.GetLayerDefn().GetFieldCount()):
field_defn = layer.GetLayerDefn().GetFieldDefn(i)
out_layer.CreateField(field_defn)
# 设置过滤器
layer.SetAttributeFilter(f"{field_name} = '{value}'")
# 遍历并复制匹配的特征
feature = layer.GetNextFeature()
while feature:
out_feature = ogr.Feature(out_layer.GetLayerDefn())
out_feature.SetFrom(feature)
out_layer.CreateFeature(out_feature)
out_feature = None # 释放内存
feature = layer.GetNextFeature()
# 关闭数据源
data_source = None
out_data_source = None
if __name__ == '__main__':
file_path = r'D:\code\China_Provinces_1389.shp'
splitter = Shp_split(file_path)
field_name = 'LABEL_CHN' # Replace with the actual field name you want to use
splitter.split_by_field(field_name)
3 使用
把上面的后端处理部分的代码,整合到rstool中,
最后,我录制了一个简单的使用视频,如下:
输入一个shp文件后,首先在该文件所在的文件夹,生成一个叫做out的文件夹。
接着安装用户选择的字段、选择的记录值,在out文件夹下生成新的shp矢量文件。
目前测试存在两个问题:
1.缺少空间参考文件的问题
2.中文记录值的问题(尽量使用英文就不会有问题)
4 下载
我把功能集成到rstool中,rstool是我写的针对地信、遥感数据处理的工具,其代码开源到github,有需要的朋友自行去github搜索rstool。
本次已将打包后的rstool上传到云盘,有需要的朋友在后台回复rstool获取下载链接。
rstool正在开发中,不定期更新,开源地址是https://github.com/ytkz11/rs-tool
欢迎来白嫖!有哪里不对劲的地方或建议可以在下方留言。
十一假期即将结束,在这七天的晚上,断断续续、修修补补开发了两个小功能。
第一个功能是从照片中提取GPS信息并创建Shapefile:
从照片中提取GPS信息并创建Shapefile(第三版)同时生成一个TXT文本
从照片中提取GPS信息并创建Shapefile(第二版)适用于无人机照片和手机照片
从照片中提取GPS信息并创建Shapefile(第一版)适用于无人机照片和手机照片
第二功能是本篇所介绍的:按照指定字段的指定记录值,生成新的矢量文件。
朋友们在使用这个工具的时候,你无论是运行这个工具成功或者失败,都可以在下方留言。
最后,预估十月份会很忙,不能做到每天都更新公众号。