最近遇到一个问题,就是对shapefile文件(矢量面文件)进行平滑的时候,由于数据比较大(我的shp文件大约30多兆),导致调用arcpy平滑过程非常非常慢,大概40-50分钟,但是在arcgis里面处理就很快,只需要几分钟。为了降低平滑时间,我在网上进行了各种搜索,也没有看到有人遇到类似的问题。后来搜到了用gdal对shp文件进行平滑的代码,想用gdal进行shp文件的平滑,但是gdal平滑的结果有问题。下面就先将gdal和arcpy进行平滑的做个对比。
1. gdal对矢量面文件进行平滑
代码如下:
from osgeo import ogr
from pathlib import Path
import os
def shapefile_edge_smooth(_input_shapefile, _smoothed_shapefile_output_path, _buffer_distance=0.0005):
'''平滑矢量文件边界
_input_shapefile:输入的矢量文件路径
_smoothed_shapefile_output_path:平滑后输出的矢量文件路径
_buffer_distance:平滑缓冲区距离,单位是米,可以根据影像分辨率设定,不能低于影像分辨率'''
in_ds = ogr.Open(_input_shapefile)
in_lyr = in_ds.GetLayer()
# feature_number = in_lyr.GetFeatureCount()
driver = ogr.GetDriverByName('ESRI Shapefile')
if Path(_smoothed_shapefile_output_path).exists():
# 如果输出的文件存在,则删除这个文件
driver.DeleteDataSource(_smoothed_shapefile_output_path)
out_ds = driver.CreateDataSource(_smoothed_shapefile_output_path)
out_lyr = out_ds.CreateLayer(_smoothed_shapefile_output_path, in_lyr.GetSpatialRef(), ogr.wkbPolygon)
def_feature = out_lyr.GetLayerDefn()
for feature in in_lyr:
geometry = feature.GetGeometryRef()
_buffer = geometry.Buffer(_buffer_distance).Buffer(-1 * _buffer_distance)
out_feature = ogr.Feature(def_feature)
out_feature.SetGeometry(_buffer)
out_lyr.CreateFeature(out_feature)
out_feature = None
out_ds.FlushCache()
in_ds.Destroy()
out_ds.Destroy()
if __name__ == '__main__':
input_shp = r'D:\data\test_data\HZ\shp\HZ.shp'
output_shp = r'D:\data\test_data\HZ\smooth_result\gdal_smoothed2.shp'
shapefile_edge_smooth(input_shp, output_shp)
2. arcpy对矢量面文件进行平滑
代码如下:
# encoding=utf8
# Import system modules
import os
import arcpy
import arcpy.cartography as CA
import arcpy.management as DM
import time
arcpy.env.overwriteOutput = True
def smooth_shp(workspace_path, inshp_path, outshp_path):
# 设置workspace路径
tempworkspace = os.path.join(workspace_path, 'tempworkspace.gdb')
arcpy.env.workspace = tempworkspace
infeature_layer = os.path.join(tempworkspace, 'infeatures')
arcpy.MakeFeatureLayer_management(inshp_path, infeature_layer) #将shp文件转换为feature layer
CA.SmoothPolygon(infeature_layer, outshp_path, "PAEK", 0.0005, "", "FLAG_ERRORS") #矢量面平滑
if __name__ == '__main__':
smoothed_result = r'D:\data\test_data\HZ\smoothed_result.shp'
shp_path = r'D:\data\test_data\HZ\shp\infeature.shp'
workspace_path = r'D:\data\test_data\HZ\shp'
smooth_shp(workspace_path, inshp_path=shp_path, outshp_path=smoothed_result)
3. gdal和arcpy平滑结果对比
下图的黑线是原始数据的图斑边界,红线是gdal平滑后的图斑边界,蓝线是arcpy平滑后的图斑边界。
可以看出,gdal和arcpy的平滑都相较原始数据的边界都少了锯齿和尖角,可见平滑都是有效果的。二者的结果边界不太一样,可能是内置的算法的问题,这样看起来gdal和arcpy平滑效果都还不错。
但是,真的如此吗?
再看一些结果对比
可以看出,gdal的平滑结果在图斑内部生成了很多⚪,而且gdal平滑的边界不准。两种平滑方法我的平滑的单位都是设置的0.0005,完全一样的参数。 但是gdal的平滑外边界明显比arcpy平滑的边界大很多,也会在图形内部生成不必要的边界线(这可能跟gdal平滑方法是建立在buffer上有关)。所以gdal平滑不太适用于我这个数据量大,图形复杂的数据上,但是有些图形相对简单的数据gdal的结果还是可以的。
最终还是得用arcpy进行数据平滑。
但是调用arcpy进行smooth的时候,太慢了,这又是怎么回事呢?
4. 调用arcpy 进行smooth polygon 时间过长
经过我无数次的实验和调参数,加上查阅官方帮助文档,发现,arcpy 用来平滑的函数SmoothPolygon,需要输入以下参数SmoothPolygon(infeature_layer, outshp_path, “PAEK”, 0.0005, “”, “FLAG_ERRORS”)
下面一个一个讲解这几个参数含义:
infeature_layer:输入数据集要素
outshp_path:输出文件路径
“PAEK”:平滑所采用的算法,可以是 “PAEK"或者"BEZIER_INTERPOLATION”
0.0005:容差,一般以米为单位的话是15左右比较合适(因数据而异),这里我的数据是经纬度,所以我用了一个很小的值。
“FLAG_ERRORS”:这个参数是对于数据中的拓扑错误是否检查,arcgis10.8有三种选项:“NO_CHECK”,"FLAG_ERRORS"和"RESOLVE_ERRORS"分别对应不检擦拓扑错误,检查并标记拓扑错误和解决拓扑错误,arcgis10.2只有前两种选项。
在arcgis中,进行平滑默认是"NO_CHECK"即不检查拓扑错误,所以运行起来很快。但是arcpy调用的时候,官方文档中推荐的是"FLAG_ERRORS",所以我也写的"FLAG_ERRORS",这就导致遇到特别大的数据时运行特别特别慢,当改成"NO_CHECK"后就快了。就是这个小小的问题,困扰了我好久啊,在这里记录一下,希望能够帮助到大家。