第05章 - 矢量数据处理基础
第05章:矢量数据处理基础
5.1 矢量数据概述
5.1.1 什么是矢量数据
矢量数据使用点、线、面等几何对象来表示地理要素,每个几何对象可以关联多个属性信息。与栅格数据相比,矢量数据具有精确的位置表达和清晰的边界定义。
┌─────────────────────────────────────────────────────────────┐
│ 矢量数据结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 点 (Point) 线 (LineString) 面 (Polygon) │
│ • ●───● ●───● │
│ / \ / \ │
│ ● ● ● ● │
│ \ / │
│ ●───● │
│ │
│ 多点 (MultiPoint) 多线 (MultiLineString) 多面(MultiPolygon)│
│ • • • ●──● ●──● ┌─┐ ┌─┐ │
│ • • ●──● └─┘ └─┘ │
│ │
└─────────────────────────────────────────────────────────────┘
5.1.2 矢量数据组成
| 组成部分 | 描述 | 示例 |
|---|---|---|
| 数据源 | 存储矢量数据的容器 | Shapefile、GeoJSON文件、PostGIS数据库 |
| 图层 | 包含同类要素的集合 | 道路图层、建筑物图层 |
| 要素 | 单个地理对象 | 一条河流、一栋建筑 |
| 几何 | 要素的空间形状 | 点坐标、线段、多边形 |
| 属性 | 要素的描述信息 | 名称、面积、类型 |
5.1.3 常见矢量格式
| 格式 | 扩展名 | 特点 | 适用场景 |
|---|---|---|---|
| Shapefile | .shp | 行业标准,兼容性最好 | 通用交换 |
| GeoJSON | .json/.geojson | 文本格式,Web友好 | Web应用 |
| GeoPackage | .gpkg | 现代开放格式,SQLite基础 | 移动/桌面应用 |
| FileGDB | .gdb | Esri格式,功能丰富 | ArcGIS生态 |
| KML/KMZ | .kml/.kmz | Google Earth格式 | 可视化展示 |
| PostGIS | - | 空间数据库 | 企业级应用 |
| GML | .gml | OGC标准XML格式 | 数据交换 |
| FlatGeobuf | .fgb | 高性能二进制格式 | 大数据集 |
5.2 读取矢量数据
5.2.1 打开数据源
from osgeo import ogr
# 启用异常
ogr.UseExceptions()
# 打开数据源(只读)
ds = ogr.Open('data.shp')
# 打开数据源(读写)
ds = ogr.Open('data.shp', 1) # 1 表示可写
# 检查是否成功打开
if ds is None:
print("无法打开数据源")
else:
print(f"驱动: {ds.GetDriver().GetName()}")
print(f"图层数: {ds.GetLayerCount()}")
# 关闭数据源
ds = None
5.2.2 访问图层
from osgeo import ogr
ds = ogr.Open('data.shp')
# 获取图层 - 方法1:按索引
layer = ds.GetLayer(0)
# 获取图层 - 方法2:按名称
layer = ds.GetLayerByName('layer_name')
# 图层基本信息
print(f"图层名称: {layer.GetName()}")
print(f"要素数量: {layer.GetFeatureCount()}")
print(f"几何类型: {ogr.GeometryTypeToName(layer.GetGeomType())}")
# 获取图层范围
extent = layer.GetExtent()
print(f"范围: X({extent[0]:.4f}, {extent[1]:.4f}), Y({extent[2]:.4f}, {extent[3]:.4f})")
# 获取空间参考
srs = layer.GetSpatialRef()
if srs:
print(f"坐标系: {srs.GetName()}")
ds = None
5.2.3 获取图层定义
from osgeo import ogr
ds = ogr.Open('data.shp')
layer = ds.GetLayer()
# 获取图层定义
layer_defn = layer.GetLayerDefn()
# 获取字段信息
field_count = layer_defn.GetFieldCount()
print(f"字段数量: {field_count}")
print("\n字段列表:")
for i in range(field_count):
field_defn = layer_defn.GetFieldDefn(i)
field_name = field_defn.GetName()
field_type = field_defn.GetTypeName()
field_width = field_defn.GetWidth()
field_precision = field_defn.GetPrecision()
print(f" {i+1}. {field_name}: {field_type}({field_width},{field_precision})")
# 获取几何字段信息
geom_field_count = layer_defn.GetGeomFieldCount()
for i in range(geom_field_count):
geom_field_defn = layer_defn.GetGeomFieldDefn(i)
print(f"\n几何字段: {geom_field_defn.GetName()}")
print(f"几何类型: {ogr.GeometryTypeToName(geom_field_defn.GetType())}")
ds = None
5.2.4 遍历要素
from osgeo import ogr
ds = ogr.Open('data.shp')
layer = ds.GetLayer()
# 方法1:使用 GetNextFeature 遍历
layer.ResetReading() # 重置读取位置
feature = layer.GetNextFeature()
while feature:
fid = feature.GetFID()
geom = feature.GetGeometryRef()
print(f"FID: {fid}, 几何类型: {geom.GetGeometryName()}")
feature = layer.GetNextFeature()
# 方法2:使用 for 循环遍历(Python 风格)
layer.ResetReading()
for feature in layer:
print(f"FID: {feature.GetFID()}")
# 方法3:按 FID 获取特定要素
feature = layer.GetFeature(5) # 获取 FID=5 的要素
# 方法4:使用索引获取
layer.SetNextByIndex(10) # 设置下一个要读取的位置
feature = layer.GetNextFeature()
ds = None
5.2.5 读取属性值
from osgeo import ogr
ds = ogr.Open('data.shp')
layer = ds.GetLayer()
for feature in layer:
# 获取 FID
fid = feature.GetFID()
# 方法1:按字段名获取
name = feature.GetField('name')
population = feature.GetField('population')
area = feature.GetField('area')
# 方法2:按字段索引获取
value = feature.GetField(0)
# 方法3:获取特定类型
int_value = feature.GetFieldAsInteger('count')
float_value = feature.GetFieldAsDouble('area')
str_value = feature.GetFieldAsString('name')
# 方法4:获取所有字段为字典
fields = {}
layer_defn = layer.GetLayerDefn()
for i in range(layer_defn.GetFieldCount()):
field_name = layer_defn.GetFieldDefn(i).GetName()
fields[field_name] = feature.GetField(i)
# 检查字段是否为空
if feature.IsFieldNull('name'):
print("name 字段为空")
print(f"FID={fid}: name={name}, population={population}")
ds = None
5.2.6 读取几何
from osgeo import ogr
ds = ogr.Open('data.shp')
layer = ds.GetLayer()
for feature in layer:
geom = feature.GetGeometryRef()
if geom is None:
print("几何为空")
continue
# 几何类型
geom_type = geom.GetGeometryType()
geom_name = geom.GetGeometryName()
print(f"几何类型: {geom_name}")
# 导出为各种格式
wkt = geom.ExportToWkt()
geojson = geom.ExportToJson()
wkb = geom.ExportToWkb()
# 获取坐标
if geom_type == ogr.wkbPoint:
x, y = geom.GetX(), geom.GetY()
print(f"点坐标: ({x}, {y})")
elif geom_type == ogr.wkbLineString:
point_count = geom.GetPointCount()
print(f"线点数: {point_count}")
for i in range(point_count):
x, y = geom.GetPoint(i)[:2]
print(f" 点{i}: ({x}, {y})")
elif geom_type == ogr.wkbPolygon:
ring_count = geom.GetGeometryCount()
print(f"环数量: {ring_count}")
for i in range(ring_count):
ring = geom.GetGeometryRef(i)
point_count = ring.GetPointCount()
print(f" 环{i}有 {point_count} 个点")
# 几何属性
print(f"面积: {geom.GetArea()}")
print(f"长度: {geom.Length()}")
# 获取范围
envelope = geom.GetEnvelope()
print(f"范围: {envelope}")
# 获取质心
centroid = geom.Centroid()
print(f"质心: ({centroid.GetX()}, {centroid.GetY()})")
ds = None
5.3 过滤和查询
5.3.1 属性过滤
from osgeo import ogr
ds = ogr.Open('cities.shp')
layer = ds.GetLayer()
# 设置属性过滤器(SQL WHERE 子句语法)
layer.SetAttributeFilter("population > 1000000")
print(f"人口超过100万的城市数量: {layer.GetFeatureCount()}")
for feature in layer:
name = feature.GetField('name')
pop = feature.GetField('population')
print(f" {name}: {pop}")
# 清除过滤器
layer.SetAttributeFilter(None)
# 更复杂的过滤条件
layer.SetAttributeFilter("""
population > 500000 AND
country = 'China' AND
name LIKE '北%'
""")
# 支持的运算符
# =, <>, <, >, <=, >=
# AND, OR, NOT
# LIKE (通配符 % _)
# IN (值列表)
# BETWEEN
# IS NULL, IS NOT NULL
ds = None
5.3.2 空间过滤
from osgeo import ogr
ds = ogr.Open('data.shp')
layer = ds.GetLayer()
# 方法1:矩形范围过滤
min_x, min_y, max_x, max_y = 116.0, 39.0, 117.0, 40.0
layer.SetSpatialFilterRect(min_x, min_y, max_x, max_y)
print(f"范围内要素数: {layer.GetFeatureCount()}")
# 方法2:几何对象过滤
# 创建过滤几何(圆形缓冲区)
center = ogr.Geometry(ogr.wkbPoint)
center.AddPoint(116.4, 39.9)
buffer = center.Buffer(0.5) # 0.5度缓冲区
layer.SetSpatialFilter(buffer)
print(f"缓冲区内要素数: {layer.GetFeatureCount()}")
# 方法3:使用多边形过滤
filter_polygon = ogr.CreateGeometryFromWkt("""
POLYGON((116 39, 117 39, 117 40, 116 40, 116 39))
""")
layer.SetSpatialFilter(filter_polygon)
# 清除空间过滤器
layer.SetSpatialFilter(None)
ds = None
5.3.3 SQL 查询
from osgeo import ogr
ds = ogr.Open('data.gpkg')
# 执行 SQL 查询
sql = """
SELECT name, population, ST_Area(geom) as area
FROM cities
WHERE population > 1000000
ORDER BY population DESC
LIMIT 10
"""
result_layer = ds.ExecuteSQL(sql)
if result_layer:
print("查询结果:")
for feature in result_layer:
name = feature.GetField('name')
pop = feature.GetField('population')
area = feature.GetField('area')
print(f" {name}: {pop}, {area:.2f}")
# 释放结果图层
ds.ReleaseResultSet(result_layer)
# 空间 SQL 查询(需要支持空间函数的格式,如 GeoPackage、PostGIS)
spatial_sql = """
SELECT a.name, a.population
FROM cities a, provinces b
WHERE ST_Within(a.geom, b.geom) AND b.name = '北京'
"""
# 使用 SQLite 方言
ds.ExecuteSQL(sql, dialect='SQLITE')
# 使用 OGRSQL 方言(默认)
ds.ExecuteSQL(sql, dialect='OGRSQL')
ds = None
5.4 创建矢量数据
5.4.1 创建数据源和图层
from osgeo import ogr, osr
def create_shapefile(filepath, layer_name, geom_type, srs_epsg=4326, fields=None):
"""创建 Shapefile"""
# 获取驱动
driver = ogr.GetDriverByName('ESRI Shapefile')
# 如果文件存在则删除
if driver.DeleteDataSource(filepath) != 0:
pass # 文件不存在,忽略错误
# 创建数据源
ds = driver.CreateDataSource(filepath)
# 创建空间参考
srs = osr.SpatialReference()
srs.ImportFromEPSG(srs_epsg)
# 创建图层
layer = ds.CreateLayer(layer_name, srs, geom_type)
# 添加字段
if fields:
for field_name, field_type, *extra in fields:
field_defn = ogr.FieldDefn(field_name, field_type)
if field_type == ogr.OFTString and extra:
field_defn.SetWidth(extra[0])
elif field_type == ogr.OFTReal and len(extra) >= 2:
field_defn.SetWidth(extra[0])
field_defn.SetPrecision(extra[1])
layer.CreateField(field_defn)
return ds, layer
# 创建点图层
fields = [
('name', ogr.OFTString, 50),
('population', ogr.OFTInteger64),
('area', ogr.OFTReal, 12, 2),
('create_date', ogr.OFTDate),
]
ds, layer = create_shapefile(
'cities.shp',
'cities',
ogr.wkbPoint,
srs_epsg=4326,
fields=fields
)
# 不要忘记关闭
ds = None
5.4.2 创建 GeoJSON
from osgeo import ogr, osr
def create_geojson(filepath, layer_name, geom_type, srs_epsg=4326, fields=None):
"""创建 GeoJSON 文件"""
driver = ogr.GetDriverByName('GeoJSON')
ds = driver.CreateDataSource(filepath)
srs = osr.SpatialReference()
srs.ImportFromEPSG(srs_epsg)
layer = ds.CreateLayer(layer_name, srs, geom_type)
if fields:
for field_name, field_type, *extra in fields:
field_defn = ogr.FieldDefn(field_name, field_type)
layer.CreateField(field_defn)
return ds, layer
# 创建示例
ds, layer = create_geojson('output.geojson', 'points', ogr.wkbPoint)
ds = None
5.4.3 创建 GeoPackage
from osgeo import ogr, osr
def create_geopackage(filepath, layer_name, geom_type, srs_epsg=4326, fields=None):
"""创建 GeoPackage 文件"""
driver = ogr.GetDriverByName('GPKG')
# GeoPackage 可以包含多个图层,所以检查文件是否存在
import os
if os.path.exists(filepath):
ds = ogr.Open(filepath, 1) # 打开现有文件
else:
ds = driver.CreateDataSource(filepath)
srs = osr.SpatialReference()
srs.ImportFromEPSG(srs_epsg)
# 创建选项
options = ['FID=fid', 'GEOMETRY_NAME=geom']
layer = ds.CreateLayer(layer_name, srs, geom_type, options=options)
if fields:
for field_name, field_type, *extra in fields:
field_defn = ogr.FieldDefn(field_name, field_type)
layer.CreateField(field_defn)
return ds, layer
# 创建多图层 GeoPackage
ds, points_layer = create_geopackage('data.gpkg', 'points', ogr.wkbPoint)
ds, lines_layer = create_geopackage('data.gpkg', 'roads', ogr.wkbLineString)
ds, polygons_layer = create_geopackage('data.gpkg', 'buildings', ogr.wkbPolygon)
ds = None
5.4.4 添加要素
from osgeo import ogr, osr
# 创建 Shapefile
driver = ogr.GetDriverByName('ESRI Shapefile')
ds = driver.CreateDataSource('cities.shp')
srs = osr.SpatialReference()
srs.ImportFromEPSG(4326)
layer = ds.CreateLayer('cities', srs, ogr.wkbPoint)
# 添加字段
layer.CreateField(ogr.FieldDefn('name', ogr.OFTString))
layer.CreateField(ogr.FieldDefn('population', ogr.OFTInteger64))
# 获取图层定义
layer_defn = layer.GetLayerDefn()
# 添加要素 - 方法1:完整方式
feature = ogr.Feature(layer_defn)
feature.SetField('name', '北京')
feature.SetField('population', 21540000)
point = ogr.Geometry(ogr.wkbPoint)
point.AddPoint(116.4, 39.9)
feature.SetGeometry(point)
layer.CreateFeature(feature)
feature = None
# 添加要素 - 方法2:使用字典方式设置属性
cities = [
{'name': '上海', 'population': 24280000, 'coords': (121.5, 31.2)},
{'name': '广州', 'population': 18680000, 'coords': (113.3, 23.1)},
{'name': '深圳', 'population': 17560000, 'coords': (114.1, 22.5)},
]
for city in cities:
feature = ogr.Feature(layer_defn)
feature.SetField('name', city['name'])
feature.SetField('population', city['population'])
point = ogr.Geometry(ogr.wkbPoint)
point.AddPoint(*city['coords'])
feature.SetGeometry(point)
layer.CreateFeature(feature)
feature = None
# 保存并关闭
ds = None
5.4.5 创建不同类型的几何
from osgeo import ogr
# 创建点
point = ogr.Geometry(ogr.wkbPoint)
point.AddPoint(116.4, 39.9)
# 创建带高程的点
point_z = ogr.Geometry(ogr.wkbPoint25D)
point_z.AddPoint(116.4, 39.9, 100)
# 创建线
line = ogr.Geometry(ogr.wkbLineString)
line.AddPoint(116.0, 39.0)
line.AddPoint(116.5, 39.5)
line.AddPoint(117.0, 40.0)
# 创建多边形
ring = ogr.Geometry(ogr.wkbLinearRing)
ring.AddPoint(116.0, 39.0)
ring.AddPoint(117.0, 39.0)
ring.AddPoint(117.0, 40.0)
ring.AddPoint(116.0, 40.0)
ring.AddPoint(116.0, 39.0) # 闭合
polygon = ogr.Geometry(ogr.wkbPolygon)
polygon.AddGeometry(ring)
# 创建带孔的多边形
outer_ring = ogr.Geometry(ogr.wkbLinearRing)
outer_ring.AddPoint(0, 0)
outer_ring.AddPoint(10, 0)
outer_ring.AddPoint(10, 10)
outer_ring.AddPoint(0, 10)
outer_ring.AddPoint(0, 0)
inner_ring = ogr.Geometry(ogr.wkbLinearRing)
inner_ring.AddPoint(2, 2)
inner_ring.AddPoint(8, 2)
inner_ring.AddPoint(8, 8)
inner_ring.AddPoint(2, 8)
inner_ring.AddPoint(2, 2)
polygon_with_hole = ogr.Geometry(ogr.wkbPolygon)
polygon_with_hole.AddGeometry(outer_ring)
polygon_with_hole.AddGeometry(inner_ring)
# 创建多点
multipoint = ogr.Geometry(ogr.wkbMultiPoint)
for x, y in [(1, 1), (2, 2), (3, 3)]:
point = ogr.Geometry(ogr.wkbPoint)
point.AddPoint(x, y)
multipoint.AddGeometry(point)
# 创建多线
multiline = ogr.Geometry(ogr.wkbMultiLineString)
for coords in [[(0, 0), (1, 1)], [(2, 2), (3, 3)]]:
line = ogr.Geometry(ogr.wkbLineString)
for x, y in coords:
line.AddPoint(x, y)
multiline.AddGeometry(line)
# 创建多多边形
multipolygon = ogr.Geometry(ogr.wkbMultiPolygon)
multipolygon.AddGeometry(polygon)
# 从 WKT 创建
geom_from_wkt = ogr.CreateGeometryFromWkt(
'POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))'
)
# 从 GeoJSON 创建
geom_from_json = ogr.CreateGeometryFromJson(
'{"type":"Point","coordinates":[116.4,39.9]}'
)
# 从 WKB 创建
# geom_from_wkb = ogr.CreateGeometryFromWkb(wkb_bytes)
5.5 编辑矢量数据
5.5.1 更新要素
from osgeo import ogr
# 打开数据源(可写模式)
ds = ogr.Open('data.shp', 1)
layer = ds.GetLayer()
# 更新单个要素
feature = layer.GetFeature(5) # 获取 FID=5 的要素
if feature:
# 更新属性
feature.SetField('name', '新名称')
feature.SetField('population', 1000000)
# 更新几何
new_geom = ogr.CreateGeometryFromWkt('POINT(116.5 40.0)')
feature.SetGeometry(new_geom)
# 保存更新
layer.SetFeature(feature)
# 批量更新
layer.ResetReading()
for feature in layer:
old_value = feature.GetField('population')
new_value = int(old_value * 1.1) # 增加10%
feature.SetField('population', new_value)
layer.SetFeature(feature)
ds = None
5.5.2 删除要素
from osgeo import ogr
ds = ogr.Open('data.shp', 1)
layer = ds.GetLayer()
# 删除单个要素
layer.DeleteFeature(5) # 删除 FID=5 的要素
# 条件删除
layer.SetAttributeFilter("population < 10000")
for feature in layer:
layer.DeleteFeature(feature.GetFID())
# 清除过滤器
layer.SetAttributeFilter(None)
# 使用 SQL 删除(某些格式支持)
# ds.ExecuteSQL("DELETE FROM layer_name WHERE condition")
ds = None
# 注意:Shapefile 删除要素后会留下空洞
# 需要重新打包来真正删除
driver = ogr.GetDriverByName('ESRI Shapefile')
ds = driver.Open('data.shp', 1)
ds.ExecuteSQL('REPACK data') # 重新打包
ds = None
5.5.3 添加和删除字段
from osgeo import ogr
ds = ogr.Open('data.shp', 1)
layer = ds.GetLayer()
# 添加新字段
new_field = ogr.FieldDefn('new_column', ogr.OFTString)
new_field.SetWidth(50)
layer.CreateField(new_field)
# 删除字段(某些格式支持)
layer_defn = layer.GetLayerDefn()
field_index = layer_defn.GetFieldIndex('old_column')
if field_index >= 0:
layer.DeleteField(field_index)
# 修改字段(某些格式支持)
# layer.AlterFieldDefn(field_index, new_field_defn, ogr.ALTER_NAME_FLAG)
ds = None
5.6 几何操作
5.6.1 空间关系判断
from osgeo import ogr
# 创建两个几何对象
geom1 = ogr.CreateGeometryFromWkt('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))')
geom2 = ogr.CreateGeometryFromWkt('POLYGON((5 5, 15 5, 15 15, 5 15, 5 5))')
point = ogr.CreateGeometryFromWkt('POINT(5 5)')
# 空间关系判断
print(f"geom1 包含 point: {geom1.Contains(point)}")
print(f"point 在 geom1 内: {point.Within(geom1)}")
print(f"geom1 与 geom2 相交: {geom1.Intersects(geom2)}")
print(f"geom1 与 geom2 相离: {geom1.Disjoint(geom2)}")
print(f"geom1 与 geom2 接触: {geom1.Touches(geom2)}")
print(f"geom1 与 geom2 交叉: {geom1.Crosses(geom2)}")
print(f"geom1 与 geom2 重叠: {geom1.Overlaps(geom2)}")
print(f"geom1 等于 geom2: {geom1.Equals(geom2)}")
# 空间关系详情(DE-9IM)
relation = geom1.Relate(geom2)
print(f"空间关系矩阵: {relation}")
5.6.2 空间操作
from osgeo import ogr
geom1 = ogr.CreateGeometryFromWkt('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))')
geom2 = ogr.CreateGeometryFromWkt('POLYGON((5 5, 15 5, 15 15, 5 15, 5 5))')
# 缓冲区
buffer = geom1.Buffer(1.0)
print(f"缓冲区面积: {buffer.GetArea()}")
# 负缓冲(收缩)
negative_buffer = geom1.Buffer(-1.0)
# 交集
intersection = geom1.Intersection(geom2)
print(f"交集: {intersection.ExportToWkt()}")
# 并集
union = geom1.Union(geom2)
print(f"并集面积: {union.GetArea()}")
# 差集
difference = geom1.Difference(geom2)
print(f"差集: {difference.ExportToWkt()}")
# 对称差
sym_difference = geom1.SymDifference(geom2)
print(f"对称差: {sym_difference.ExportToWkt()}")
# 凸包
convex_hull = geom1.ConvexHull()
# 简化
simplified = geom1.Simplify(0.1)
simplified_preserve = geom1.SimplifyPreserveTopology(0.1)
# 质心
centroid = geom1.Centroid()
print(f"质心: {centroid.ExportToWkt()}")
# 外包矩形
envelope = geom1.GetEnvelope()
print(f"外包矩形: MinX={envelope[0]}, MaxX={envelope[1]}, MinY={envelope[2]}, MaxY={envelope[3]}")
5.6.3 几何属性计算
from osgeo import ogr
polygon = ogr.CreateGeometryFromWkt(
'POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))'
)
line = ogr.CreateGeometryFromWkt(
'LINESTRING(0 0, 100 0, 100 100)'
)
# 面积(平面坐标系下的单位面积)
area = polygon.GetArea()
print(f"面积: {area}")
# 长度/周长
perimeter = polygon.Length() # 多边形周长
length = line.Length() # 线长度
print(f"周长: {perimeter}, 线长度: {length}")
# 几何验证
is_valid = polygon.IsValid()
is_simple = polygon.IsSimple()
is_ring = polygon.IsRing()
is_empty = polygon.IsEmpty()
print(f"有效: {is_valid}, 简单: {is_simple}, 闭合: {is_ring}, 空: {is_empty}")
# 点数量
point_count = polygon.GetGeometryRef(0).GetPointCount() # 外环点数
print(f"外环点数: {point_count}")
# 几何维度
dim = polygon.GetDimension() # 0=点, 1=线, 2=面
coord_dim = polygon.GetCoordinateDimension() # 2 或 3
print(f"几何维度: {dim}, 坐标维度: {coord_dim}")
5.6.4 坐标转换
from osgeo import ogr, osr
# 创建几何
geom = ogr.CreateGeometryFromWkt('POINT(116.4 39.9)')
# 创建坐标转换
src_srs = osr.SpatialReference()
src_srs.ImportFromEPSG(4326) # WGS84
dst_srs = osr.SpatialReference()
dst_srs.ImportFromEPSG(3857) # Web Mercator
# 创建转换器
transform = osr.CoordinateTransformation(src_srs, dst_srs)
# 转换几何(就地修改)
geom.Transform(transform)
print(f"转换后: {geom.ExportToWkt()}")
# 克隆并转换
original = ogr.CreateGeometryFromWkt('POINT(116.4 39.9)')
transformed = original.Clone()
transformed.Transform(transform)
print(f"原始: {original.ExportToWkt()}")
print(f"转换后: {transformed.ExportToWkt()}")
5.7 格式转换
5.7.1 使用 ogr2ogr 函数
from osgeo import gdal, ogr
def convert_vector(src_path, dst_path, dst_format='ESRI Shapefile', options=None):
"""转换矢量格式"""
if options is None:
options = []
vector_options = gdal.VectorTranslateOptions(
format=dst_format,
layerCreationOptions=options
)
ds = gdal.VectorTranslate(dst_path, src_path, options=vector_options)
ds = None
# Shapefile 转 GeoJSON
convert_vector('input.shp', 'output.geojson', 'GeoJSON')
# Shapefile 转 GeoPackage
convert_vector('input.shp', 'output.gpkg', 'GPKG')
# GeoJSON 转 Shapefile
convert_vector('input.geojson', 'output.shp', 'ESRI Shapefile')
5.7.2 手动复制转换
from osgeo import ogr, osr
def copy_layer(src_path, dst_path, dst_format='ESRI Shapefile'):
"""复制图层到新格式"""
# 打开源数据
src_ds = ogr.Open(src_path)
src_layer = src_ds.GetLayer()
# 创建目标数据源
driver = ogr.GetDriverByName(dst_format)
# 删除已存在的目标文件
if driver.DeleteDataSource(dst_path) != 0:
pass
dst_ds = driver.CreateDataSource(dst_path)
# 复制图层定义
src_layer_defn = src_layer.GetLayerDefn()
srs = src_layer.GetSpatialRef()
geom_type = src_layer.GetGeomType()
dst_layer = dst_ds.CreateLayer(
src_layer.GetName(),
srs,
geom_type
)
# 复制字段
for i in range(src_layer_defn.GetFieldCount()):
field_defn = src_layer_defn.GetFieldDefn(i)
dst_layer.CreateField(field_defn)
# 复制要素
dst_layer_defn = dst_layer.GetLayerDefn()
for src_feature in src_layer:
dst_feature = ogr.Feature(dst_layer_defn)
# 复制几何
geom = src_feature.GetGeometryRef()
if geom:
dst_feature.SetGeometry(geom.Clone())
# 复制属性
for i in range(dst_layer_defn.GetFieldCount()):
dst_feature.SetField(i, src_feature.GetField(i))
dst_layer.CreateFeature(dst_feature)
dst_feature = None
src_ds = None
dst_ds = None
# 使用示例
copy_layer('input.shp', 'output.geojson', 'GeoJSON')
5.7.3 转换时进行投影
from osgeo import gdal
def convert_with_reprojection(src_path, dst_path, dst_srs='EPSG:3857', dst_format='GeoJSON'):
"""转换格式并重投影"""
options = gdal.VectorTranslateOptions(
format=dst_format,
dstSRS=dst_srs,
reproject=True
)
ds = gdal.VectorTranslate(dst_path, src_path, options=options)
ds = None
# WGS84 转 Web Mercator
convert_with_reprojection('wgs84.shp', 'webmercator.geojson', 'EPSG:3857')
5.7.4 转换时进行过滤
from osgeo import gdal
def convert_with_filter(src_path, dst_path, where_clause=None,
bbox=None, dst_format='GeoJSON'):
"""转换格式并过滤"""
options_dict = {
'format': dst_format,
}
if where_clause:
options_dict['where'] = where_clause
if bbox:
options_dict['spatFilter'] = bbox # [minx, miny, maxx, maxy]
options = gdal.VectorTranslateOptions(**options_dict)
ds = gdal.VectorTranslate(dst_path, src_path, options=options)
ds = None
# 按属性过滤
convert_with_filter(
'cities.shp',
'big_cities.geojson',
where_clause="population > 1000000"
)
# 按范围过滤
convert_with_filter(
'cities.shp',
'beijing_cities.geojson',
bbox=[116.0, 39.0, 117.0, 40.0]
)
5.8 数据库操作
5.8.1 连接 PostGIS
from osgeo import ogr
# 连接 PostGIS 数据库
connection_string = "PG:host=localhost dbname=gisdb user=postgres password=secret"
ds = ogr.Open(connection_string)
if ds:
print(f"连接成功,图层数: {ds.GetLayerCount()}")
# 列出所有图层
for i in range(ds.GetLayerCount()):
layer = ds.GetLayer(i)
print(f" {layer.GetName()}: {layer.GetFeatureCount()} 个要素")
ds = None
else:
print("连接失败")
# 连接时只加载特定图层
connection_string = "PG:host=localhost dbname=gisdb user=postgres password=secret tables=cities,roads"
ds = ogr.Open(connection_string)
5.8.2 查询 PostGIS
from osgeo import ogr
ds = ogr.Open("PG:host=localhost dbname=gisdb user=postgres password=secret")
# 执行 SQL 查询
sql = """
SELECT name, population, ST_AsText(geom) as geom_wkt
FROM cities
WHERE population > 1000000
"""
result = ds.ExecuteSQL(sql)
for feature in result:
print(f"{feature.GetField('name')}: {feature.GetField('population')}")
ds.ReleaseResultSet(result)
# 空间查询
spatial_sql = """
SELECT a.name, a.population
FROM cities a, provinces b
WHERE ST_Within(a.geom, b.geom)
AND b.name = '广东省'
"""
result = ds.ExecuteSQL(spatial_sql)
# ... 处理结果
ds = None
5.8.3 写入 PostGIS
from osgeo import ogr, osr
# 连接数据库(可写)
ds = ogr.Open("PG:host=localhost dbname=gisdb user=postgres password=secret", 1)
# 创建新表
srs = osr.SpatialReference()
srs.ImportFromEPSG(4326)
# 创建图层(表)
layer = ds.CreateLayer(
'new_cities',
srs,
ogr.wkbPoint,
['GEOMETRY_NAME=geom', 'OVERWRITE=YES']
)
# 添加字段
layer.CreateField(ogr.FieldDefn('name', ogr.OFTString))
layer.CreateField(ogr.FieldDefn('population', ogr.OFTInteger64))
# 添加要素
layer_defn = layer.GetLayerDefn()
feature = ogr.Feature(layer_defn)
feature.SetField('name', '测试城市')
feature.SetField('population', 1000000)
point = ogr.Geometry(ogr.wkbPoint)
point.AddPoint(116.4, 39.9)
feature.SetGeometry(point)
layer.CreateFeature(feature)
# 开始事务(提高批量插入性能)
layer.StartTransaction()
for i in range(1000):
feature = ogr.Feature(layer_defn)
feature.SetField('name', f'城市{i}')
feature.SetField('population', i * 1000)
point = ogr.Geometry(ogr.wkbPoint)
point.AddPoint(116.0 + i * 0.01, 39.0 + i * 0.01)
feature.SetGeometry(point)
layer.CreateFeature(feature)
layer.CommitTransaction()
ds = None
5.9 本章小结
本章详细介绍了 OGR 矢量数据处理的基础知识:
- 矢量数据概述:数据结构、组成和常见格式
- 读取矢量数据:
- 打开数据源和访问图层
- 遍历要素
- 读取属性和几何
- 过滤和查询:
- 属性过滤
- 空间过滤
- SQL 查询
- 创建矢量数据:
- 创建不同格式的数据源
- 定义字段和添加要素
- 创建各种几何类型
- 编辑矢量数据:
- 更新和删除要素
- 修改字段定义
- 几何操作:
- 空间关系判断
- 空间运算(缓冲区、交集、并集等)
- 几何属性计算
- 格式转换:
- 使用 VectorTranslate
- 手动复制
- 带投影和过滤的转换
- 数据库操作:
- PostGIS 连接和查询
- 数据写入和事务
5.10 思考与练习
- 解释矢量数据中数据源、图层、要素、几何的层次关系。
- 如何高效遍历一个包含百万要素的 Shapefile?
- 比较 Shapefile 和 GeoPackage 格式的优缺点。
- 编写代码计算两个多边形图层的空间交集。
- 实现一个函数,将 PostGIS 中的数据导出为 GeoJSON。
- 如何在不加载所有数据的情况下获取图层的要素数量?
- 编写代码为多边形图层中的每个要素计算面积并存储到属性字段中。

浙公网安备 33010602011771号