[Python] GDAL/OGR操作矢量数据(shp、GeoJSON)
GeoDoer 2019-12-30 12:17:51 2196 收藏 15
分类专栏: # GIS|Python
版权
GDAL项目旨于地理数据抽象模型对地理数据文件进行读写管理;而其项目下有两大类模块:GDAL和OGR
OGR提供操作矢量数据的API,GDAL模块提供栅格数据的API
【相关链接】
1.GDAL/OGR例子合集
2.GDAL官网API
3.网上的学习笔记,教程来自于犹他州立大学
4.中国OSGeo教程
文章目录
OGR中的数据结构
driver解析数据的驱动
datasource管理数据源
layer管理图层
feature管理要素类
从Layer中获得feature
更新要素
删除要素
关闭要素
创建要素
geometry管理要素的几何属性
Geometry的几何属性
从Feature获取Geometry
常用方法
创建Geometry
field管理要素的属性字段
创建字段
更改属性定义
其他
经验
wkt
查询
属性查询
空间查询
SQL查询
其他
设置编码
示例
打开shp
删除数据
遍历所有属性值
创建
创建点状数据集
创建线状数据集
创建多边形数据集
创建属性
创建点Geometry
创建线Geometry
创建多边形Geometry
拷贝
datasource层次的拷贝
layer层次的拷贝
feature层次的拷贝
OGR中的数据结构
driver解析数据的驱动
# 【方法一】从已有数据源中获取驱动变量
ds = ogr.open(r'D:\....\...shp')
driver = ds.GetDriver()
# 【方法二】通过名称获取
json_driver = ogr.GetDrverByName('GeoJSON')
# 获得驱动名的两种方法
# 1. OGR网站上有介绍,通过GDAL/ORG自带的ogrinfo
# 2. 代码中提供的print_drivers函数来获取驱动程序的名字
1
2
3
4
5
6
7
8
datasource管理数据源
(1)创建数据源
有了驱动程序之后,提供数据源名称来使用它创建一个空的数据源。新创建的数据源会自动打开等待写入
# 创建一个功能齐全的SpatialLite数据源,而不是使用SQLite
fn = "filename"
driver = ogr.GetDriverByName("SQLite")
# 创建新的数据源时,不能覆盖现有的数据源。如果你的代码可能会覆盖现有数据,那么在创建新数据之前需要删除旧数据
if os.path.exists(fn):
driver.DeleteDataSource(fn)
ds= driver.CreateDataSource(fn,
options=[ #创建选项:传递一个字符串列表(参数在OGR网站上有文档介绍)
'SPATIALITE=yes'
]
)
if ds is None:
#【注意】如果数据源没有创建成功,那么CreateDataSource函数会返回为空。如果为空对象,之后使用会报AttributeError错误
sys.exit('Could not create {0}'.formaat(json_fn ) )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(2)删除数据源
datasource.Destory() #关闭
del datasource # 删除datasource
""" 删除ds变量强制关闭文件,并将所有的编辑成果写入磁盘中
【注意】删除图层变量并不会触发这个操作,必须关闭数据源才行
【备注】如果想保持数据源打开的话,可以通过图层对象或者数据源对象调用ds.SyncToDisk()
【警告】为了使你的编辑写入到磁盘中,必须关闭文件或者调用SyncToDisk函数。如果没有这么做,并且在交互环境中还打开数据源,那么你会很失望地发现创建了一个空的数据集
"""
1
2
3
4
5
6
7
layer管理图层
layer = datasource.GetLayer(0) #根据下标来获取图层(对于shp而言只有一个图层)
layer.GetGeomType() #图层几何类型
n = layer.GetFeatureCount() #要素数量
extent = layer.GetExtent() #上下左右边界
readedNum = GetFeaturesRead() #已经读取多少条Feature
layer.ResetReading() #重置内部feature指针,指向FID=0的要素
layer.GetSpatialRef() #使用wkt文本表示空间参考系统的实例
layer.schema #获取FieldDefn对象列表(属性名)
1
2
3
4
5
6
7
8
feature管理要素类
从Layer中获得feature
# 1. 【获得图层中的要素】
feature = layer.GetFeature(0)
# 2. 【获取图层中所有要素】
feat = layer.GetNextFeature()
while feat:
feat = layer.GetNextFeature()
layer.ResetReading()
1
2
3
4
5
6
7
更新要素
要素更新操作也大同小异,只不过你操作的是图层中的现有要素,而不是一个空白的对象
# 【例子】为图层的每一个要素添加一个唯一的ID
layer.CreateField( # 为图层添加一个ID字段
ogr.FieldDefn('ID', ogr.OFTInteger)
)
n = 1
for feat in layer:
feat.SetField('ID', n) #设置字段的值
layer.SetFeature(feat) #将数值传递给SetFeature函数,来更新图层中的要素信息(这里不是CreateFeature函数)
n += 1
1
2
3
4
5
6
7
8
9
删除要素
删除要素更容易,需要知道的就是待删除要素的FID
#【例子】删除字段City_Name为"Seattle"的要素
# 找到目标的FID进行删除
for feat in layer:
if feat.GetField('City_Name')=='Seattle':
layer.DeleteFeature(feat.GetFID() ) #删除操作
"""
【但是】
1. 某些特定的数据格式使用此方式并不能完全删除要素。
2. 你可能看不出,有时要素只是被标记为删除了,而不是被完全抛出,它仍然还潜伏着。
3. 正因如此,你不会看到其他要素被分配到刚才删除的FID
4. 这意味着,如果已经删除了很多要素,在文件中可能存在大量不必要的已用空间
【所以】删除这些要素可以回收对应的空间
【如何回收对应空间】如果你有一些关系数据库的经验,应该熟悉这一点。它类似于在微软的Access数据库上运行压缩和修复或在PostgreSQL数据库中使用重组功能(VACUUM)
【具体做法】打开数据源,然后执行一条SQL语句来压缩数据库(不同的数据格式回收的方法不同)
"""
ds.ExecuteSQL('REPACK' + layer.GetName() ) #shapefile格式回收方式
ds.ExecuteSQL('RECOMPUTE EXTENT ON' + layer.GetName() ) #确保空间范围更新
# 当现有要素发生改变或被删除后,它不会更新元数据中的空间范围
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
关闭要素
feature.Destory() #关闭
1
创建要素
往现有图层中添加新要素和往全新的图层中添加要素的操作一样,具体请看示例
【步骤】
1.基于图层字段创建一个空要素,填充它
2.然后把它插入到该图层
geometry管理要素的几何属性
Geometry的几何属性
【org.Geometry中的几何类型】ogr.wkbPoint、org.wkbLineString、org.wkbPolygon等,请看wkt
# 获得Geometry的几何属性
layer.GetGeomType() == ogr.wkbPolygon
layer.GetGeometryName()
1
2
3
从Feature获取Geometry
# 两种方法
geometry = feature.geometry()
geometry = feat.GetGeometryRef()
1
2
3
常用方法
geom.GetGeometryName() #要素类型
geom.GetPointCount() #要素点个数
geom.GetX(0);geom.GetY(0);geom.GetZ(0) #获得第一个坐标点的X、Y、Z
print(geom) #打印出所有点
geom.ExportToWkt() #导出WKT
1
2
3
4
5
创建Geometry
请看示例
field管理要素的属性字段
【字段类型常量】
有一些不同的属性字段类型,但并不是每一种字段类型都支持所有的数据格式。这时候用于描述各种数据格式的在线文档就派上用场了。
字段数据类型 OGR常量
Integer OFTInteger
List of integers OFTIntegerList
Floating point number OFTReal
List of floating point numbers OFTRealList
String OFTString
List of strings OFTStringList
Date OFTDate
Time of day OFTTime
Date and time OFTDateTime
创建字段
在Layer中新建属性字段
# 创建并添加第一个属性字段
coord_fld = ogr.FieldDefn('X', ogr.OFTReal)
# 设置限制
coord_fld.SetWidth(8)
coord_fld.SetPrecision(3)
out_lyr.CreateField(coord_fld)
# 重用FieldDefn对象来创建第二个字段
coor_fld.SetName('Y')
out_lyr.CreateField(coord_fld)
1
2
3
4
5
6
7
8
9
更改属性定义
【函数】AlterFieldDefn(iField, field_def, nFlags)
【作用】用新字段的规则替换现有的字段
【参数说明】
iField是想改变的属性字段对应的索引值
field_def是新属性字段的定义对象
nFlags是一个整数,是下表中的一个或多个常数的总和
【用于指明字段定义的哪个属性可以更改的标记,如果想使用多个,一起添加即可】
需要更改的字段属性 OGR常量
Field name only ALTER_NAME_FLAG
Field type only ALTER_TYPE_FLAG
Field width and/or precision only ALTER_WIDTH_PRECISION_FLAG
All of the above ALTER_ALL_FLAG
# 【例子一】 更改原字段的名字
index = layer.GetLayerDefn().GetFieldIndex('NAME') #获取字段的索引值
fld_defn = ogr.FieldDefn('City_Name', ogr.OFTString) #创建新属性的字段定义
layer.AlterFieldDefn(index, fld_defn, ogr.ALTER_NAME_FLAG)
# 【例子二】 更改字段的多个属性,例如名称和精度
lyr_defn = layer.GetLayerDefn()
index = lyr_defn.GetFieldIndex('X')
width = lyr_defn.GetFieldDefn(index).GetWidth()
fld_defn = ogr.FieldDefn('X_coord', ogr.OFTReal)
fld_defn.SetWidth(width)
fld_defn.SetPrecision(4)
flag = ogr.ALTER_NAME_FLAG + ogr.ALTER_WIDTH_PRECISION_FLAG
layer.AlterFieldDefn(index, fld_defn, flag)
"""【注意】在此例子中创建新的字段定义时使用的是原始字段宽度
如果没有为原始数据设置足够大的字段宽度,结果将不正确
为了解决这个问题,需要使用与原始数据一样的字段宽度
为了使更改的精度生效,所有记录必须重写
为数据设置一个比它自身更高的精度并不会提高精度,因为数据不能凭空产生,但如果设置的精度不够高,精度就会降低
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
其他
feature.GetFieldCount() # 属性总数
feature.keys() # 属性名
feat.GetField('AREA') # 获取字段
feature.GetFieldAsString("FID") # 获取字段成string
1
2
3
4
经验
1.没能为GeoJSON文件设置一个精度
2.想在shapefile中设置字段精度,必须设置字段宽度
3.设置字段的默认值:FieldDefn.SetDefault("我是默认值")
wkt
WKB(二进制Well-KnownBinary),用于不同软件程序间进行几何要素类型转换的一种二进制表示标准。
因为它是二进制格式,所以人们无法直接阅读获取其表示的内容,但是熟知文本格式(Well-Known Text,WKT)可以阅读。
几何要素类型 对应OGR常量
Point wkbPoint
Multipoint wkbMultiPoint
Line wkbLineString
Multiline wkbMultiLineString
Polygon wkbPolygon
Multipolygon wkbMultiPolygon
Unknown geometry type wkbUnknown(图层如果有多种几何类型就返回wkbUnknown)
No geometry wkbNone
查询
属性查询
from osgeo import ogr
import os
shpfile = r'C:\tmp\data.shp'
ds = ogr.Open(shpfile)
layer = ds.GetLayer(0) #得到图层
lyr_count = layer.GetFeatureCount()
print(lyr_count) #原先的要素总数
layer.SetAttributeFilter("AREA > 800000") #属性查询条件
lyr_count = layer.GetFeatureCount() #查询过后,layer被更新
print(lyr_count) #满足条件的layer
# 将该layer保存成shp
driver = ogr.GetDriverByName("ESRI Shapefile")
extfile = r'C:\tmp\data.shp'
if os.access( extfile, os.F_OK ):
driver.DeleteDataSource( extfile )
newds = driver.CreateDataSource(extfile)
layernew = newds.CreateLayer('rect',None,ogr.wkbPolygon)
# 遍历,复制
feat = layer.GetNextFeature()
while feat is not None:
layernew.CreateFeature(feat)
feat = layer.GetNextFeature()
newds.Destroy()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
空间查询
SQL查询
from osgeo import ogr
driver = ogr.GetDriverByName("ESRI Shapefile")
world_shp = r'C:\tmp\test.shp'
world_ds = ogr.Open(world_shp)
world_layer = world_ds.GetLayer()
world_layer_name = world_layer.GetName()
result = world_ds.ExecuteSQL("select * from %s where prov_id = '22' order by BNDRY_ID desc" % (world_layer_name)) # ) # ExecuteSQL是基于数据集进行的,而不是图层
resultFeat = result.GetNextFeature ()
out_shp = r'C:\tmp\test\test_sql_result.shp'
create_shp_by_layer(out_shp, result) #保存结果
# 对查询结果进行遍历
while resultFeat :
print resultFeat.GetField('BNDRY_ID')
resultFeat = result.GetNextFeature ()
#执行下一条SQL语句之前一定要先释放
world_ds.ReleaseResultSet(result)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
其他
设置编码
# 全局设置
gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES") #支持文件名称及路径内的中文
gdal.SetConfigOption("SHAPE_ENCODING", "GBK") #支持属性字段中的中文
# 为图层指定编码
out_layer = ds.CreateLayer( "CommercialHousing",
srs = in_layer.GetSpatialRef(),
geom_type= ogr.wkbPoint,
options = [
"ENCODING=UTF-8"
]
)
1
2
3
4
5
6
7
8
9
10
11
示例
打开shp
from osgeo import ogr
inshp_path = 'D:\mycode\GISandPython\0data\park_point_shp\xiamen_20181128_park.shp'
driver = ogr.GetDriverByName('ESRI Shapefile') #查找一个特定的驱动程序
datasource = driver.Open(inshp_path, 0) #0只读,1可写
dir( datasource) #使用Python的内省函数dir()查看所有方法
1
2
3
4
5
删除数据
# 删除矢量数据文件
if os.path.exists(out_shp)
driver.DeleteDataSource(out_shp)
ds2 = driver.CreateDataSource(out_shp)
1
2
3
4
遍历所有属性值
#【遍历所有属性值】
for i in range(feature.GetFieldCount() ):
print( feature.GetField(i) )
### 【查看表的结构,各个字段的名称等信息】在layer附加信息中看
layerdef = layer.GetLayerDefn()
for i in range(layerdef.GetFieldCount() ):
defn = layerdef.GetFieldDefn(i)
print(defn.GetName(), defn.GetWidth(), defn.GetType(), defn.GetPrecision() )
1
2
3
4
5
6
7
8
创建
创建点状数据集
from osgeo import ogr
import os,math
driver = ogr.GetDriverByName("ESRI Shapefile")
extfile = 'point_demo.shp'
point_coors = [[300,450], [750, 700], [1200, 450], [750, 200], [750, 450]]
print point_coors
driver = ogr.GetDriverByName("ESRI Shapefile")
if os.access( extfile, os.F_OK ):
driver.DeleteDataSource( extfile )
newds = driver.CreateDataSource(extfile)
layernew = newds.CreateLayer('point',None,ogr.wkbPoint)
for aa in point_coors:
wkt = 'POINT (' + str(aa[0]) + ' ' + str(aa[1]) + ')'
geom = ogr.CreateGeometryFromWkt(wkt)
feat = ogr.Feature(layernew.GetLayerDefn())
feat.SetGeometry(geom)
layernew.CreateFeature(feat)
newds.Destroy()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
创建线状数据集
from osgeo import ogr
import os,math
driver = ogr.GetDriverByName("ESRI Shapefile")
extfile = 'line_demo.shp'
point_coors = [300,450, 750, 700, 1200, 450, 750, 200]
print point_coors
driver = ogr.GetDriverByName("ESRI Shapefile")
if os.access( extfile, os.F_OK ):
driver.DeleteDataSource( extfile )
newds = driver.CreateDataSource(extfile)
layernew = newds.CreateLayer('point',None,ogr.wkbLineString)
wkt = 'LINESTRING (%f %f,%f %f,%f %f,%f %f,%f %f)' % (point_coors[0],point_coors[1],
point_coors[2],point_coors[3], point_coors[4],point_coors[5],
point_coors[6],point_coors[7], point_coors[0],point_coors[1])
print(wkt)
geom = ogr.CreateGeometryFromWkt(wkt)
feat = ogr.Feature(layernew.GetLayerDefn())
feat.SetGeometry(geom)
layernew.CreateFeature(feat)
newds.Destroy()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
创建多边形数据集
from osgeo import ogr
import os,math
driver = ogr.GetDriverByName("ESRI Shapefile")
extfile = 'rect_demo.shp'
if os.access( extfile, os.F_OK ):
driver.DeleteDataSource( extfile )
extent = [400, 1100, 300, 600]
newds = driver.CreateDataSource(extfile)
layernew = newds.CreateLayer('rect',None,ogr.wkbPolygon)
width = math.fabs(extent[1]-extent[0])
height = math.fabs(extent[3]-extent[2])
tw = width/2
th = width/2
extnew = extent[0]+tw
wkt = 'POLYGON ((%f %f,%f %f,%f %f,%f %f,%f %f))' % (extent[0],extent[3],
extent[1],extent[3], extent[1],extent[2],
extent[0],extent[2], extent[0],extent[3])
geom = ogr.CreateGeometryFromWkt(wkt)
feat = ogr.Feature(layernew.GetLayerDefn())
feat.SetGeometry(geom)
layernew.CreateFeature(feat)
newds.Destroy()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
创建属性
from osgeo import ogr
import os,math
driver = ogr.GetDriverByName("ESRI Shapefile") #shp驱动器
extfile = 'rect_field_demo.shp' #输出的shp路径
if os.access( extfile, os.F_OK ): #文件是否已存在
driver.DeleteDataSource( extfile )
extent = [400, 1100, 300, 600] #范围
newds = driver.CreateDataSource(extfile) #按照驱动器创建数据源
layernew = newds.CreateLayer('rect',None,ogr.wkbPolygon) #创建图层
# 创建字段
fieldcnstr = ogr.FieldDefn("fd",ogr.OFTString) #创建字段(字段名,类型)
fieldcnstr.SetWidth(32) #设置宽度
layernew.CreateField(fieldcnstr) #将字段设置到layer
fieldf = ogr.FieldDefn("f",ogr.OFTReal)
layernew.CreateField(fieldf)
# wkt格式的polygon
wkt = 'POLYGON ((%f %f,%f %f,%f %f,%f %f,%f %f))' % (extent[0],extent[3],
extent[1],extent[3], extent[1],extent[2],
extent[0],extent[2], extent[0],extent[3])
geom = ogr.CreateGeometryFromWkt(wkt) #根据wkt创建geometry
feat = ogr.Feature( layernew.GetLayerDefn() ) #从layer中得到feature的要求,创建一个Feature对象
feat.SetField('fd',"这里是字段的值") #设置字段值
feat.SetGeometry(geom) #设置要素的geometry属性
layernew.CreateFeature(feat) #在图层中创建该feature
newds.Destroy() #删除(包括保存)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
创建点Geometry
from osgeo import ogr
point = ogr.Geometry(ogr.wkbPoint)
point.AddPoint(10,20) #向Point添加多个点,不会报错,但最终只会用最后添加的点
1
2
3
创建线Geometry
from osgeo import ogr
line = ogr.Geometry(ogr.wkbLineString) # 创建
line.AddPoint(10, 20) # 添加点
line.SetPoint(1, 50, 50) # 修改点坐标
line.GetPointCount() # 得到所有点数目
line.GetX(0); line.GetY(0) # 读取0号点的坐标
1
2
3
4
5
6
创建多边形Geometry
from osgeo ipmort ogr
# 创建环
ring = ogr.Geometry(ogr.wkbLinearRing) #创建
ring.AddPoint(10,10) #添加点
ring.CloseRings() #闭合 或 添加的最后一个点和第一个相同
# 将环添加到多边形中
polygon = ogr.Geometry(ogr.wkbPolygon)
polygon.AddGeometry(outring)
polygon.AddGeometry(inring)
ring = polygon.GetGeometryRef(0) #得到多边形内的几何对象
1
2
3
4
5
6
7
8
9
10
拷贝
datasource层次的拷贝
from osgeo import ogr
import os,math
inshp = "C:\tmp\test.shp"
ds = ogr.Open(inshp)
driver = ogr.GetDriverByName("ESRI Shapefile")
outputfile = 'C:\tmp\test_copy.shp'
if os.access( outputfile, os.F_OK) : #已经有该文件
driver.DeleteDataSource(outputfile) #删除
pt_cp = driver.CopyDataSource(ds, outputfile) #根据数据源ds创建数据,返回指针
pt_cp.Release() #释放指针,将数据写入到磁盘
1
2
3
4
5
6
7
8
9
10
layer层次的拷贝
from osgeo import ogr
import os,math
inshp = "C:\tmp\test.shp"
ds = ogr.Open(inshp)
driver = ogr.GetDriverByName("ESRI Shapefile")
outputfile = "C:\tmp\test_copy2.shp"
if os.access( outputfile, os.F_OK):
driver.DeleteDataSourse( outputfile )
layer = ds.GetLayer()
newds = driver.CreateDataSource(outputfile) #首先得有数据源
pt_layer = newds.CopyLayer(layer, 'abcd') #复制图层,返回指针
newds.Destroy() #对newds进行Destroy()操作,才能将数据写入磁盘
1
2
3
4
5
6
7
8
9
10
11
12
feature层次的拷贝
from osgeo import ogr
import os,math
inshp = 'C:\tmp\test.shp'
ds = ogr.Open(inshp)
driver = ogr.GetDriverByName("ESRI Shapefile")
outputfile = 'c:\tmp\test.shp'
if os.access( outputfile, os.F_OK ):
driver.DeleteDataSource( outputfile )
newds = driver.CreateDataSource(outputfile) #按outputfile的数据源创建一个newds
layernew = newds.CreateLayer('worldcopy',None,ogr.wkbLineString) #创建一个图层
layer = ds.GetLayer() #得到原数据源的图层
extent = layer.GetExtent() #图层范围
# 遍历旧图层的所有feature,进行复制
feature = layer.GetNextFeature()
while feature is not None:
layernew.CreateFeature(feature)
feature = layer.GetNextFeature()
newds.Destroy() # 包括了将数据flush到磁盘
————————————————
版权声明:本文为CSDN博主「GeoDoer」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/summer_dew/article/details/87930241