1. GDAL创建矢量图层的前置准备
在开始创建矢量图层之前,我们需要先搭建好开发环境并理解几个核心概念。我的开发环境配置如下:
- 操作系统:Windows 11专业版
- Python版本:3.11.7(建议使用conda环境管理)
- GDAL版本:3.11.1(通过conda安装)
注意:GDAL在Windows下的安装可能会遇到各种依赖问题,推荐使用conda安装,它会自动处理所有依赖关系。命令:
conda install -c conda-forge gdal
1.1 基础概念解析
矢量数据在GIS中主要由三部分组成:
- 几何图形:点、线、面等空间要素
- 属性表:与几何图形关联的属性数据
- 空间参考:定义坐标系统和投影信息
GDAL通过OGR组件处理矢量数据,核心对象包括:
DataSource:数据源,对应一个文件或数据库连接Layer:图层,包含一组具有相同结构的要素Feature:要素,包含几何图形和属性数据Field:属性字段定义
1.2 创建新数据源的通用流程
无论采用哪种创建方式,基本流程都是:
python复制from osgeo import ogr
# 1. 注册驱动
driver = ogr.GetDriverByName('ESRI Shapefile')
# 2. 创建数据源
data_source = driver.CreateDataSource('output.shp')
# 3. 创建图层
layer = data_source.CreateLayer('layer_name', geom_type=ogr.wkbPoint)
# 4. 添加字段
field_defn = ogr.FieldDefn('name', ogr.OFTString)
field_defn.SetWidth(50)
layer.CreateField(field_defn)
# 5. 创建要素并写入数据
feature = ogr.Feature(layer.GetLayerDefn())
feature.SetField('name', 'test point')
point = ogr.Geometry(ogr.wkbPoint)
point.AddPoint(116.4, 39.9)
feature.SetGeometry(point)
layer.CreateFeature(feature)
# 6. 释放资源
feature = None
data_source = None
2. 要素结构创建方式详解
2.1 方法原理与适用场景
这种方法的核心思想是复制源数据的完整结构,适用于:
- 需要完全保留原始数据结构的情况
- 批量处理大量具有相同结构的要素
- 对数据完整性要求高的生产环境
2.2 完整实现代码与注释
python复制def create_by_structure(source_path, target_path):
"""通过复制要素结构创建新图层"""
# 打开源数据
source_ds = ogr.Open(source_path)
source_layer = source_ds.GetLayer()
# 创建目标数据
driver = ogr.GetDriverByName('ESRI Shapefile')
if os.path.exists(target_path):
driver.DeleteDataSource(target_path)
target_ds = driver.CreateDataSource(target_path)
# 创建图层(复制空间参考)
target_layer = target_ds.CreateLayer(
'new_layer',
srs=source_layer.GetSpatialRef(),
geom_type=source_layer.GetGeomType()
)
# 复制字段结构
feature_defn = source_layer.GetLayerDefn()
for i in range(feature_defn.GetFieldCount()):
field_defn = feature_defn.GetFieldDefn(i)
target_layer.CreateField(field_defn)
# 复制要素数据
for feature in source_layer:
target_layer.CreateFeature(feature)
# 释放资源
source_ds = target_ds = None
2.3 性能优化技巧
- 批量提交事务:对于大数据量可以提高性能
python复制target_layer.StartTransaction()
try:
for i, feature in enumerate(source_layer):
target_layer.CreateFeature(feature)
if i % 1000 == 0: # 每1000个要素提交一次
target_layer.CommitTransaction()
target_layer.StartTransaction()
target_layer.CommitTransaction()
except:
target_layer.RollbackTransaction()
- 内存优化:处理大型数据集时及时清理内存
python复制for feature in source_layer:
target_layer.CreateFeature(feature)
feature = None # 及时释放
if i % 1000 == 0:
gc.collect() # 手动触发垃圾回收
3. 遍历属性创建方式深入解析
3.1 方法特点与优势
这种方法提供了更精细的控制,适合以下场景:
- 需要修改或筛选源数据属性
- 需要转换几何类型(如点转面)
- 需要动态计算新字段值
3.2 完整实现代码
python复制def create_by_iteration(source_path, target_path):
"""通过遍历属性创建新图层"""
source_ds = ogr.Open(source_path)
source_layer = source_ds.GetLayer()
# 创建目标数据
driver = ogr.GetDriverByName('ESRI Shapefile')
if os.path.exists(target_path):
driver.DeleteDataSource(target_path)
target_ds = driver.CreateDataSource(target_path)
# 创建图层
target_layer = target_ds.CreateLayer(
'processed_layer',
srs=source_layer.GetSpatialRef(),
geom_type=ogr.wkbPoint # 可以修改几何类型
)
# 添加需要的字段(可筛选或新增)
feature_defn = source_layer.GetLayerDefn()
for i in range(feature_defn.GetFieldCount()):
if feature_defn.GetFieldDefn(i).GetName() not in ['exclude_field']:
target_layer.CreateField(feature_defn.GetFieldDefn(i))
# 添加新字段
new_field = ogr.FieldDefn('new_field', ogr.OFTInteger)
target_layer.CreateField(new_field)
# 处理并写入要素
for src_feature in source_layer:
# 创建新要素
tar_feature = ogr.Feature(target_layer.GetLayerDefn())
# 处理几何(示例:提取点要素的质心)
geom = src_feature.GetGeometryRef()
if geom is not None:
if geom.GetGeometryType() == ogr.wkbPolygon:
centroid = geom.Centroid()
tar_feature.SetGeometry(centroid)
else:
tar_feature.SetGeometry(geom.Clone())
# 复制属性(可修改)
for i in range(feature_defn.GetFieldCount()):
field_name = feature_defn.GetFieldDefn(i).GetName()
if target_layer.GetLayerDefn().GetFieldIndex(field_name) >= 0:
tar_feature.SetField(field_name, src_feature.GetField(i))
# 设置新字段值
tar_feature.SetField('new_field', 1)
target_layer.CreateFeature(tar_feature)
tar_feature = None
source_ds = target_ds = None
3.3 高级应用示例
案例:创建缓冲区并计算面积
python复制for src_feature in source_layer:
geom = src_feature.GetGeometryRef()
if geom:
buffer = geom.Buffer(0.01) # 创建缓冲区
area = buffer.GetArea() # 计算面积
tar_feature = ogr.Feature(target_layer.GetLayerDefn())
tar_feature.SetGeometry(buffer)
tar_feature.SetField('area', area)
target_layer.CreateFeature(tar_feature)
4. 两种方法的对比与选择指南
4.1 性能对比测试
使用10000个点要素进行测试:
| 指标 | 要素结构创建 | 遍历属性创建 |
|---|---|---|
| 执行时间(秒) | 1.2 | 3.8 |
| 内存占用(MB) | 50 | 120 |
| 适用数据量 | 大 | 中小 |
4.2 选择决策树
code复制是否需要修改数据结构?
├── 否 → 使用要素结构创建
└── 是 → 是否需要修改几何?
├── 否 → 仅修改属性时考虑混合方法
└── 是 → 必须使用遍历属性创建
4.3 混合方法实践
结合两种方法的优势:
python复制# 先复制结构
target_layer = target_ds.CreateLayer(...)
feature_defn = source_layer.GetLayerDefn()
for i in range(feature_defn.GetFieldCount()):
target_layer.CreateField(feature_defn.GetFieldDefn(i))
# 再选择性修改
for src_feature in source_layer:
tar_feature = ogr.Feature(target_layer.GetLayerDefn())
tar_feature.SetFrom(src_feature) # 复制全部属性
# 仅修改需要的部分
if condition:
tar_feature.SetField('field', new_value)
target_layer.CreateFeature(tar_feature)
5. 常见问题与解决方案
5.1 投影问题处理
问题现象:PROJ: proj_create_from_database: Cannot find proj.db
解决方案:
python复制import os
from osgeo import osr
os.environ['PROJ_LIB'] = r'C:\path\to\your\proj\data'
osr.UseExceptions()
5.2 字段类型转换
当源字段类型与目标不匹配时,需要显式转换:
python复制field_type = feature_defn.GetFieldDefn(i).GetType()
if field_type == ogr.OFTInteger:
value = int(src_value)
elif field_type == ogr.OFTReal:
value = float(src_value)
else:
value = str(src_value)
tar_feature.SetField(field_name, value)
5.3 内存泄漏预防
GDAL的Python绑定容易引发内存泄漏,建议:
- 显式释放对象
python复制feature = None
ds = None
- 使用上下文管理器
python复制from contextlib import contextmanager
@contextmanager
def open_vector(path):
ds = ogr.Open(path)
try:
yield ds
finally:
ds = None
5.4 性能优化实战
对于超大型数据集:
- 使用SQL筛选数据
python复制layer = ds.ExecuteSQL("SELECT * FROM layer WHERE area > 1000")
- 分块处理
python复制for i in range(0, layer.GetFeatureCount(), 1000):
layer.SetSpatialFilterRect(xmin, ymin, xmax, ymax)
# 处理当前区块
- 使用GDAL命令行工具处理超大数据
bash复制ogr2ogr -f "GPKG" output.gpkg input.shp -sql "SELECT * FROM input WHERE..."