前言
QGis平台集成了多种类型的算法,主要包括基于原生C++开发的QGIS算法、使用Python编写的QGIS脚本算法,以及第三方提供的GDAL相关工具。本文重点介绍如何调用QGIS原生C++实现的算法模块。
所有示例代码均来源于开源项目 qgis_cpp_api_apps,仅供学习与参考。
常用算法应用示例
本文选取三个典型算法进行说明:在多边形内生成随机点(Random points in polygons)、裁剪(Clip)以及缓冲区分析(Buffer),以展示QGIS中核心算法的实际调用方式。
随机点生成:Random Points in Polygons
该功能常用于地理空间数据的采样设计、监督分类中的训练样本选取,以及精度评估过程中的验证点布设。例如,在采用最小距离法或最大似然法对区域土地利用类型进行分类时,通常需要先获取具有代表性的训练样本,这些样本可通过随机点方式生成。同样地,在进行模型验证和混淆矩阵构建时,也需要大量独立验证点,其位置亦可借助此类方法自动产生。
以文件 jilin_dist.shp 为例,首先加载该矢量图层。
随后进入“工具箱” → “矢量创建(Vector creation)” → 选择“Random points in polygons”工具。
设置参数如下:指定生成点的数量为10个。
执行后生成结果如下图所示:
代码实现机制解析
所有QGIS处理算法均继承自一个统一的基类:
QgsProcessingAlgorithm
该基类定义了算法的基本结构与行为规范,详细信息可查阅官方文档。其类关系图如下:
其中关键成员函数如下:
QgsProcessingAlgorithm *create( const QVariantMap &configuration = QVariantMap() )
该函数负责实例化具体的算法对象。
算法的集中管理由以下类完成:
QgsProcessingRegistry
此管理器负责注册、查找和创建各类算法,具体说明见相关文档。
在使用任何算法前,必须完成初始化操作,初始化代码如下:
QgsApplication::processingRegistry()->addProvider( new QgsNativeAlgorithms( QgsApplication::processingRegistry() ) );
通过以下类的方法可以创建指定ID的算法实例:
QgsProcessingRegistry
其内部函数
QgsProcessingAlgorithm *createAlgorithmById( const QString &id, const QVariantMap &configuration = QVariantMap() )
会调用对应算法类的create方法来完成实例化。
针对“在多边形内生成随机点”的功能,其实现类为:
QgsRandomPointsInPolygonsAlgorithm
该类的关键成员函数之一是:
name()
用于返回算法的名称,该名称将在createAlgorithmById中被用来定位并创建实例。
示例代码如下:
QString QgsRandomPointsInPolygonsAlgorithm::name() const
{
return QStringLiteral( "randompointsinpolygons" );
}
需要注意的是,算法的完整ID由前缀字符串与类名组合而成:
native:
例如:
const QString id = "native:randompointsinpolygons";
创建算法实例的标准代码格式如下:
QgsProcessingAlgorithm *algorithm = QgsApplication::processingRegistry()->createAlgorithmById(id, conf);
createAlgorithmById
其中第二个参数 conf 是配置项,类型为键值对字典:
QVariantMap
该字典的构造逻辑可在各算法类的初始化函数中找到依据,如:
initAlgorithm
具体代码如下:
void QgsRandomPointsInPolygonsAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterFeatureSource( INPUT, QObject::tr( "Input polygon layer" ), QList< int >() << QgsProcessing::TypeVectorPolygon ) );
std::unique_ptr< QgsProcessingParameterNumber > numberPointsParam = std::make_unique< QgsProcessingParameterNumber >( POINTS_NUMBER, QObject::tr( "Number of points for each feature" ), QgsProcessingParameterNumber::Integer, 1, false, 1 );
numberPointsParam->setIsDynamic( true );
numberPointsParam->setDynamicPropertyDefinition( QgsPropertyDefinition( POINTS_NUMBER, QObject::tr( "Number of points for each feature" ), QgsPropertyDefinition::IntegerPositive ) );
numberPointsParam->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( numberPointsParam.release() );
std::unique_ptr< QgsProcessingParameterDistance > minDistParam = std::make_unique< QgsProcessingParameterDistance >( MIN_DISTANCE, QObject::tr( "Minimum distance between points" ), 0, INPUT, true, 0 );
minDistParam->setIsDynamic( true );
minDistParam->setDynamicPropertyDefinition( QgsPropertyDefinition( MIN_DISTANCE, QObject::tr( "Minimum distance between points" ), QgsPropertyDefinition::DoublePositive ) );
minDistParam->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( minDistParam.release() );
std::unique_ptr< QgsProcessingParameterDistance > minDistGlobalParam = std::make_unique< QgsProcessingParameterDistance >( MIN_DISTANCE_GLOBAL, QObject::tr( "Global minimum distance between points" ), 0, INPUT, true, 0 );
minDistGlobalParam->setFlags( minDistGlobalParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( minDistGlobalParam.release() );
std::unique_ptr< QgsProcessingParameterNumber > maxAttemptsParam = std::make_unique< QgsProcessingParameterNumber >( MAX_TRIES_PER_POINT, QObject::tr( "Maximum number of search attempts (for Min. dist. > 0)" ), QgsProcessingParameterNumber::Integer, 10, true, 1 );
maxAttemptsParam->setFlags( maxAttemptsParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
maxAttemptsParam->setIsDynamic( true );
maxAttemptsParam->setDynamicPropertyDefinition( QgsPropertyDefinition( MAX_TRIES_PER_POINT, QObject::tr( "Maximum number of attempts per point (for Min. dist. > 0)" ), QgsPropertyDefinition::IntegerPositiveGreaterZero ) );
maxAttemptsParam->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( maxAttemptsParam.release() );
std::unique_ptr< QgsProcessingParameterNumber > randomSeedParam = std::make_unique< QgsProcessingParameterNumber >( SEED, QObject::tr( "Random seed" ), QgsProcessingParameterNumber::Integer, QVariant(), true, 1 );
randomSeedParam->setFlags( randomSeedParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( randomSeedParam.release() );
std::unique_ptr< QgsProcessingParameterBoolean > includePolygonAttrParam = std::make_unique< QgsProcessingParameterBoolean >( INCLUDE_POLYGON_ATTRIBUTES, QObject::tr( "Include polygon attributes" ), true );
includePolygonAttrParam->setFlags( includePolygonAttrParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( includePolygonAttrParam.release() );
addParameter( new
QgsProcessingParameterFeatureSink( OUTPUT, QObject::tr( "Random points in polygons" ), QgsProcessing::TypeVectorPoint ) );
addOutput( new QgsProcessingOutputNumber( OUTPUT_POINTS, QObject::tr( "Total number of points generated" ) ) );
addOutput( new QgsProcessingOutputNumber( POINTS_MISSED, QObject::tr( "Number of missed points" ) ) );
addOutput( new QgsProcessingOutputNumber( POLYGONS_WITH_MISSED_POINTS, QObject::tr( "Number of polygons with missed points" ) ) );
addOutput( new QgsProcessingOutputNumber( FEATURES_WITH_EMPTY_OR_NO_GEOMETRY, QObject::tr( "Number of features with empty or no geometry" ) ) );
}
算法对象创建完成后,需执行运行流程才能获得输出结果。运行支持两种模式:主线程运行与后台线程运行。若算法耗时较长,推荐使用后台运行方式,以免造成界面无响应。
完整的测试调用代码如下:
void MainWindow::processingRandomPointsSlot()
{
QgsVectorLayer* layer = addTestShape(QStringLiteral("maps/processing/jilin_dist.shp"));
const QString id = "native:randompointsinpolygons";
QVariantMap conf;
conf.insert(QStringLiteral("INPUT"), layer->id()); // 支持图层ID或物理路径
conf.insert(QStringLiteral("POINTS_NUMBER"), "10");
QgsProcessingOutputLayerDefinition value("TEMPORARY_OUTPUT");
conf.insert(QStringLiteral("OUTPUT"), value);
QgsProcessingAlgorithm *algorithm = QgsApplication::processingRegistry()->createAlgorithmById(id, conf);
QgsProcessingContext *context = new QgsProcessingContext;
context->setProject(QgsProject::instance());
QgsProcessingFeedback *feedback = new QgsProcessingFeedback(false);
#if 0
// 此处采用主线程运行方式
algorithm->prepare(conf, *context, feedback);
#endif
}
void MainWindow::processingClipSlot()
{
QgsVectorLayer* layer = addTestShape(QStringLiteral("maps/processing/dongbei_roads.shp"));
const QString id = "native:clip";
QVariantMap conf;
// 使用图层ID作为输入,而非直接路径
conf.insert(QStringLiteral("INPUT"), layer->id());
conf.insert(QStringLiteral("OVERLAY"), "maps/processing/jilin_dist.shp");
QgsProcessingOutputLayerDefinition value("TEMPORARY_OUTPUT");
conf.insert(QStringLiteral("OUTPUT"), value);
auto algorithm = QgsApplication::processingRegistry()->createAlgorithmById(id, conf);
QgsProcessingContext *context = new QgsProcessingContext;
context->setProject(QgsProject::instance());
QgsProcessingFeedback *feedback = new QgsProcessingFeedback(false);
#if 0
// 主线程中执行算法
algorithm->prepare(conf, *context, feedback);
QVariantMap runResults = algorithm->run(conf, *context, feedback);
QgsMapLayer *tempLayer = context->getMapLayer(runResults["OUTPUT"].toString());
if(layer)
{
QgsProject::instance()->addMapLayer(tempLayer);
}
#else
// 在独立线程中运行处理任务
mContext = context;
QgsProcessingAlgRunnerTask *algTask = new QgsProcessingAlgRunnerTask(algorithm, conf, *mContext, feedback);
connect(algTask, &QgsProcessingAlgRunnerTask::executed, this, &MainWindow::algExecuted);
QgsApplication::taskManager()->addTask(algTask);
#endif
}
const QString id = "native:clip";
按参考面要素裁剪矢量数据说明
“按参考面要素裁剪”是一种基于矢量叠加分析的空间操作方法,其核心是利用一个面状参考图层对目标图层进行几何裁剪,保留目标图层中位于参考面范围内的部分。该功能在区域范围提取、局部数据生成等场景中具有广泛应用。
本示例以吉林省地级行政区划(jilin_dist.shp)作为裁剪范围,对东北地区公路网(dongbei_roads.shp)进行裁剪,最终生成仅包含吉林境内公路的矢量网络数据。原始地图数据来源于《QGIS软件及其应用教程》配套资源。
操作步骤
- 加载数据:首先将“jilin_dist.shp”与“dongbei_roads.shp”两个图层导入QGIS项目中。
- 打开处理工具箱:在“Processing Toolbox”面板中,定位至“Vector overlay”工具集,双击选择“Clip”工具,系统将弹出配置对话框。
- 设置参数:
- Input layer:选择待裁剪的目标图层“dongbei_roads”;
- Overlay layer:选择作为裁剪边界的参考面图层“jilin_dist”;
- Clipped:指定输出文件的保存路径或使用临时图层;
- 执行操作:点击“Run”按钮启动裁剪流程,完成后结果图层将自动加载至地图视图。
效果展示
裁剪完成后,原覆盖整个东北地区的公路网络被限制在吉林省行政边界内,形成精确的区域交通数据子集。此过程有效实现了空间数据的范围过滤与局部提取。
代码实现逻辑说明
上述C++代码实现了与图形界面相同的功能。其处理流程与“随机生成点”类似,均基于QGIS的Processing框架进行封装调用。关键在于指定正确的算法ID(本例为"native:clip"),并正确配置输入输出参数。当采用多线程模式时,通过QgsProcessingAlgRunnerTask将任务提交至任务管理器异步执行,避免阻塞主界面。
在QGis的二次开发过程中,调用QGIS(native c++)算法是一个关键环节。通过使用相关接口和任务管理机制,可以实现对原生算法的高效执行。
具体实现方式如下:当处于线程环境中时,首先将上下文信息赋值给成员变量 mContext,以便后续操作中使用。接着创建一个 QgsProcessingAlgRunnerTask 实例,传入目标算法、配置参数、上下文对象以及反馈对象。该任务对象用于异步运行指定的处理算法。
为确保任务执行完成后能及时响应结果,需将任务的 executed 信号与主窗口中的槽函数 algExecuted 进行连接。最后,通过 QgsApplication::taskManager() 获取任务管理器,并将新创建的任务添加进去,由系统负责调度执行。
上述方法实现了在不阻塞主线程的前提下运行复杂的地理处理算法,提升了应用的响应性和稳定性。
总结
本文详细阐述了如何在QGis平台进行二次开发时,调用其内置的 native C++ 算法模块。通过合理利用任务机制与信号槽体系,能够安全、高效地完成算法调用与结果处理,为构建功能丰富的插件或独立应用提供了技术支持。


雷达卡


京公网安备 11010802022788号







