QgsRasterDataProvider 完整详解(QGIS 3.40.13 C++)

一、类定位与核心作用

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_STRUCTURESUBDATASETS 等域元数据。

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 版本关键变更与注意点

  1. 内存安全强化
    readBlock() 返回的 QgsRasterBlock 不再自动托管,必须手动释放;多线程读取同一provider需加锁,底层GDAL句柄非线程安全。
  2. 坐标转换重构
    废弃旧 transform() 重载,统一使用 QgsCoordinateTransformContext 处理动态CRS转换。
  3. WMS/WMTS Provider拆分
    远程栅格驱动独立实现,QgsRasterDataProvider 仅保留通用抽象,网络缓存统一走 QgsNetworkAccessManager
  4. Nodata逻辑修正
    sourceHasNoDataValue() 区分"文件原生存在Nodata"和"代码临时设置Nodata",3.40之前容易混淆。
  5. 多波段写入稳定性修复
    修复多波段同步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. 波段索引从1开始,不要传0,会直接崩溃;
  2. QgsRasterBlock* 必须手动delete,否则海量内存泄漏;
  3. GDAL驱动不支持多线程并发读写同一个provider实例;
  4. 判断写入前必须校验 provider->capabilities() & QgsRasterDataProvider::Write
  5. WMS等远程栅格不支持 writeBlockbuildOverviews
  6. 浮点型Nodata对比不能直接 ==,使用 block->isNoData()
  7. 3.40编译需链接:qgis_core gdal,CMake需指定QGIS版本3.40。

十三、与上层 QgsRasterLayer 区别总结

特性 QgsRasterDataProvider QgsRasterLayer
职责 原始数据IO、元数据、像素读写 显示、渲染、样式、符号系统
可直接读写像素 ✅ readBlock/writeBlock ❌ 只能通过dataProvider()调用
不包含渲染参数 ✅ 纯原始数据 ❌ 保存拉伸、色带、透明度
支持文件创建/写入 ❌ 仅封装展示
远程WMS/GDAL统一抽象 统一封装,屏蔽驱动差异
相关推荐
yaoxin5211232 小时前
462. Java 反射 - 获取声明类与封闭类
java·开发语言·python
阿里嘎多学长2 小时前
2026-07-14 GitHub 热点项目精选
开发语言·程序员·github·代码托管
charlie1145141913 小时前
Cinux: 为大内核铺路
开发语言·c++·操作系统·现代c++
2501_914245933 小时前
C语言设计模式详解:从理论到实践的完整指南
c语言·开发语言·设计模式
Hazenix4 小时前
Go 指南:一篇文章速通 Golang
开发语言·后端·golang
灯澜忆梦4 小时前
GO_复合类型---指针
开发语言·后端·golang
Ulyanov4 小时前
雷达导引头Python仿真框架:GPU加速、6-DOF模型与半实物仿真接口
开发语言·python·雷达信号处理·雷达导引头
名字还没想好☜5 小时前
Go slice 的 append 陷阱:共享底层数组导致的数据串改
开发语言·后端·golang·go·slice
bitbrowser5 小时前
Claude 账号频繁被封?转向国内可直接使用的 AI 工具
开发语言·php