一、类定位与核心作用
1. 所属模块
头文件:#include <qgsrasterdataprovider.h>
命名空间:Qgs
依赖模块:QgisCore(栅格数据底层核心类)
2. 核心定义
QgsRasterDataProvider 是所有栅格数据源的底层抽象基类,负责封装栅格文件/服务的底层IO、元数据、像素读写、波段信息、坐标系、金字塔、统计值等能力。
- 上层:
QgsRasterLayer面向用户,做显示、渲染、样式管理; - 底层:
QgsRasterDataProvider面向文件/服务驱动(GDAL、WMS、WMTS、MeshRaster等),只负责原始数据读写,不处理渲染。
3. 继承关系
QObject
└── QgsDataProvider
└── QgsRasterDataProvider
父类 QgsDataProvider 提供通用能力:数据源URI、坐标系、范围、有效性、错误信息、临时缓存等。
二、关键枚举(3.40.13 稳定版本)
1. RasterDataType 像素数据类型(波段存储类型)
cpp
enum DataType
{
UnknownDataType = 0,
Byte = 1, // uint8 0-255
UInt16 = 2,
Int16 = 3,
UInt32 = 4,
Int32 = 5,
Float32 = 6,
Float64 = 7,
CInt16 = 8, // 复数int16
CInt32 = 9,
CFloat32 = 10,
CFloat64 = 11
};
2. RasterIdentifyFormat 识别返回格式
控制 identify() 接口返回像素值结构:
- IdentifyFormatValue:单像素数值
- IdentifyFormatText:文本描述
- IdentifyFormatHtml:HTML弹窗格式
- IdentifyFormatFeature:转为矢量要素
3. Capability 驱动能力掩码(判断支持哪些操作)
cpp
enum Capability
{
NoCapabilities = 0,
Read = 1 << 0, // 可读
Write = 1 << 1, // 可写(可修改栅格)
Create = 1 << 2, // 支持新建栅格
Remove = 1 << 3, // 支持删除文件
BuildPyramids = 1 << 4, // 支持构建金字塔
WriteNoData = 1 << 5, // 可写入Nodata值
CreateMultiBand = 1 << 6,// 多波段创建
// 3.40新增扩展能力
SubLayers = 1 << 7, // 支持子图层(NetCDF、多页TIFF)
Metadata = 1 << 8, // 读写元数据
};
用法:bool canWrite = provider->capabilities() & QgsRasterDataProvider::Write;
三、核心属性接口(元数据、范围、坐标系)
1. 基础数据源信息
cpp
// 数据源路径/连接串(GDAL文件路径、WMS URL)
QString dataSourceUri() const override;
// 驱动名称("gdal", "wms", "wmts", "virtualraster")
virtual QString driverName() const = 0;
// 数据是否有效(打开成功)
bool isValid() const override;
// 获取错误信息
QString error() const override;
2. 空间范围与分辨率
cpp
// 栅格地理边界(原图坐标,未裁剪)
QgsRectangle extent() const override;
// 栅格宽、高(像素行列数)
virtual int xSize() const = 0; // 列数 width
virtual int ySize() const = 0; // 行数 height
// 地理分辨率 dx, dy
virtual double xSizeUnits() const; // 一个像素X方向地理宽度
virtual double ySizeUnits() const; // 一个像素Y方向地理高度
// 像素坐标系转换工具
virtual void blockToXY( int col, int row, double &x, double &y ) const;
virtual void xyToBlock( double x, double y, int &col, int &row ) const;
3. 坐标系与投影
cpp
// 返回栅格原始CRS
QgsCoordinateReferenceSystem crs() const override;
// 地理变换六参数 [xOrigin, dx, rotationX, yOrigin, rotationY, dy]
virtual QgsCoordinateTransformContext transformContext() const;
virtual double transform( int param ) const;
四、波段管理核心接口(最常用)
1. 波段数量
cpp
virtual int bandCount() const = 0;
2. 单波段信息(1-based索引,QGIS栅格波段全部从1开始)
cpp
// 获取波段像素数据类型
virtual DataType dataType( int bandNo ) const = 0;
// 获取波段Nodata值
virtual double noDataValue( int bandNo ) const;
virtual bool sourceHasNoDataValue( int bandNo ) const;
// 波段名称、描述
virtual QString bandName( int bandNo ) const;
virtual QString bandDescription( int bandNo ) const;
// 波段统计信息(最值、均值、标准差)
virtual QgsRasterBandStats bandStatistics(
int bandNo,
int stats = QgsRasterBandStats::All,
const QgsRectangle &extent = QgsRectangle(),
int sampleSize = 0
);
QgsRasterBandStats 包含:min, max, mean, sum, stdDev, count
3. 颜色/渲染相关(原始波段信息,非图层样式)
cpp
// 波段是否有彩色调色板(单波段灰度/索引色)
virtual bool hasColorTable( int bandNo ) const;
virtual QgsRasterColorTable colorTable( int bandNo ) const;
// 直方图
virtual QgsRasterHistogram histogram( int bandNo, ... );
五、像素读写核心:readBlock() 与 QgsRasterBlock
1. readBlock() 函数签名(3.40稳定版)
cpp
virtual QgsRasterBlock *readBlock(
int bandNo,
const QgsRectangle &extent,
int width,
int height,
int feedback = nullptr,
QgsRasterBlock *dest = nullptr
) = 0;
参数说明
- bandNo:波段号(1起始)
- extent:需要读取的地理范围
- width/height:输出像素块宽高(自动重采样)
- feedback:进度条
QgsFeedback* - dest:可选,传入已有块复用内存,减少分配
返回值
QgsRasterBlock* 像素块对象,存储一片区域所有像素值,用完必须手动delete,否则内存泄漏。
2. QgsRasterBlock 常用读取API
cpp
QgsRasterBlock *block = provider->readBlock(1, rect, 512, 512);
if( block && !block->isEmpty() )
{
// 获取行列像素值
double val = block->value( row, col );
// 判断是否Nodata
if( block->isNoData( row, col ) ){}
// 批量原始缓冲区(慎用,类型强依赖)
void *data = block->data();
}
delete block;
3. 写入像素(仅支持 Write 能力的驱动,如GDAL)
cpp
virtual bool writeBlock(
QgsRasterBlock *block,
int bandNo,
int xOffset,
int yOffset
);
xOffset/yOffset:像素起始行列偏移,用于局部覆写栅格。
六、栅格金字塔(Overview)接口
cpp
// 获取金字塔层级数量
virtual int overviewsCount() const;
// 获取指定金字塔分辨率缩放系数
virtual QVector<int> overviewResolutions( int bandNo ) const;
// 构建金字塔
virtual bool buildOverviews(
const QVector<int> &overviewList,
const QString &resamplingMethod,
QgsFeedback *feedback = nullptr
);
重采样支持:"nearest", "bilinear", "cubic", "cubicspline", "lanczos"
七、像素点识别 identify()
用于鼠标点击取像素值,GIS分析高频接口
cpp
virtual QgsRasterIdentifyResult identify(
const QgsPointXY &point,
IdentifyFormat format,
int width = 1,
int height = 1,
int dpi = 96,
const QgsCoordinateTransformContext &transformContext = QgsCoordinateTransformContext()
);
返回 QgsRasterIdentifyResult,包含各波段像素值、坐标、Nodata标记。
八、元数据、子图层、临时操作
1. 元数据
cpp
virtual QString metadata( const QString &domain = "" ) const;
virtual QStringList metadataDomains() const;
GDAL驱动会返回 IMAGE_STRUCTURE、SUBDATASETS 等域元数据。
2. 子图层(NetCDF、多栅格TIFF、HDF)
cpp
virtual QStringList subLayers() const;
virtual QString subLayerUri( int subLayerIndex ) const;
九、创建栅格(静态工厂+创建参数)
1. 工厂创建Provider(标准打开流程)
cpp
#include <qgsrasterproviderregistry.h>
// 1. 获取栅格驱动注册表
QgsRasterProviderRegistry *reg = QgsApplication::rasterProviderRegistry();
// 2. 打开GDAL栅格文件
QString uri = "/data/dem.tif";
QgsRasterDataProvider *provider = reg->createProvider( uri, "gdal" );
if( !provider || !provider->isValid() )
{
qDebug() << "打开失败:" << (provider ? provider->error() : "驱动不存在");
}
2. 新建栅格文件(Create能力)
通过 QgsRasterDataProvider::create() 静态函数,依赖 QgsRasterCreateOptions:
cpp
QgsRasterCreateOptions opts;
opts.setCrs( crs );
opts.setExtent( rect );
opts.setSize( 1024, 1024 );
opts.setBands( 3 );
opts.setDataType( QgsRasterDataProvider::Byte );
opts.setNoData( 0 );
// 创建GeoTIFF
QgsRasterDataProvider *newProvider = QgsRasterDataProvider::create(
"/output/out.tif",
"gdal",
opts
);
十、3.40.13 版本关键变更与注意点
- 内存安全强化
readBlock()返回的QgsRasterBlock不再自动托管,必须手动释放;多线程读取同一provider需加锁,底层GDAL句柄非线程安全。 - 坐标转换重构
废弃旧transform()重载,统一使用QgsCoordinateTransformContext处理动态CRS转换。 - WMS/WMTS Provider拆分
远程栅格驱动独立实现,QgsRasterDataProvider仅保留通用抽象,网络缓存统一走QgsNetworkAccessManager。 - Nodata逻辑修正
sourceHasNoDataValue()区分"文件原生存在Nodata"和"代码临时设置Nodata",3.40之前容易混淆。 - 多波段写入稳定性修复
修复多波段同步writeBlock时缓冲区越界Bug。
十一、完整最小可运行示例(C++ Qt/QGIS 3.40)
cpp
#include <QCoreApplication>
#include <qgsapplication.h>
#include <qgsrasterdataprovider.h>
#include <qgsrasterproviderregistry.h>
#include <qgsrasterblock.h>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
// 初始化QGIS环境
QgsApplication qgisApp(nullptr, true);
qgisApp.setPrefixPath("/usr/local/qgis340", true);
qgisApp.initQgis();
// 1. 打开栅格
QString rasterPath = "/home/test/dem.tif";
QgsRasterProviderRegistry *reg = QgsApplication::rasterProviderRegistry();
QgsRasterDataProvider *prov = reg->createProvider(rasterPath, "gdal");
if (!prov || !prov->isValid())
{
qCritical() << "打开失败:" << (prov ? prov->error() : "无驱动");
qgisApp.exitQgis();
return 1;
}
// 2. 打印基础信息
qDebug() << "驱动:" << prov->driverName();
qDebug() << "宽×高:" << prov->xSize() << prov->ySize();
qDebug() << "波段数:" << prov->bandCount();
qDebug() << "范围:" << prov->extent().toString();
// 3. 读取第一波段512×512像素块
QgsRectangle readRect = prov->extent();
QgsRasterBlock *block = prov->readBlock(1, readRect, 512, 512);
if (block && !block->isEmpty())
{
// 取中心像素值
int midRow = 256;
int midCol = 256;
double val = block->value(midRow, midCol);
qDebug() << "中心像素值:" << val;
if (block->isNoData(midRow, midCol))
qDebug() << "该像素为Nodata";
delete block;
}
// 释放provider
delete prov;
qgisApp.exitQgis();
return 0;
}
十二、常见开发坑点
- 波段索引从1开始,不要传0,会直接崩溃;
QgsRasterBlock*必须手动delete,否则海量内存泄漏;- GDAL驱动不支持多线程并发读写同一个provider实例;
- 判断写入前必须校验
provider->capabilities() & QgsRasterDataProvider::Write; - WMS等远程栅格不支持
writeBlock、buildOverviews; - 浮点型Nodata对比不能直接
==,使用block->isNoData(); - 3.40编译需链接:
qgis_core gdal,CMake需指定QGIS版本3.40。
十三、与上层 QgsRasterLayer 区别总结
| 特性 | QgsRasterDataProvider | QgsRasterLayer |
|---|---|---|
| 职责 | 原始数据IO、元数据、像素读写 | 显示、渲染、样式、符号系统 |
| 可直接读写像素 | ✅ readBlock/writeBlock | ❌ 只能通过dataProvider()调用 |
| 不包含渲染参数 | ✅ 纯原始数据 | ❌ 保存拉伸、色带、透明度 |
| 支持文件创建/写入 | ✅ | ❌ 仅封装展示 |
| 远程WMS/GDAL统一抽象 | ✅ | 统一封装,屏蔽驱动差异 |