12.4 调用QGIS空间分析算法(native,qgis,gdal)

58 阅读3分钟

前言

  • 介绍在C++中如何调用各个平台(GDAL/QGIS/GRASS等)提供的算法,包含C++实现的算法和Python实现的算法
  • 说明:文章中的示例代码均来自开源项目qgis_cpp_api_apps中子项目ll_qgis_process

算法相关的类

  • QgsProcessingRegistry

Registry for various processing components, including providers, algorithms and various parameters and outputs.

An algorithm provider is a set of related algorithms, typically from the same external application or related to a common area of analysis.

Abstract base class for processing algorithms.

Contains information about the context in which a processing algorithm is executed. Contextual information includes settings such as the associated project, and expression context.

Base class for providing feedback from a processing algorithm.This base class implementation silently ignores all feedback reported by algorithms. Subclasses of QgsProcessingFeedback can be used to log this feedback or report it to users via the GUI.

  • 算法运行的反馈信息 在这里插入图片描述

算法分类

  • 常见的有native qgis gdal 在这里插入图片描述

native

  • native是以C++写的,以native:addxyfields为例 在这里插入图片描述

qgis

  • qgis是以python写的,以qgis:basicstatisticsforfields为例 在这里插入图片描述

gdal

  • gdal同样是python代码,以gdal:aspect为例 在这里插入图片描述

C++中如何调用python算法

  • 在C++中调用python算法需要加载python解释器,在qgis中有一个lib库libqgispython.so实现了这个功能 在这里插入图片描述

  • python实现 在这里插入图片描述 在这里插入图片描述

添加libqgispython.so

  • 以下分步骤说明增加python支持
  1. 在CMakeLists.txt增加qgispython
if (WITH_BINDINGS)
  target_link_libraries(ll_qgis_process qgispython)
  add_definitions(-DWITH_BINDINGS)
endif()

  1. main中增加初始化 QgsProviderRegistry::instance( QgsApplication::pluginPath() ); 在这里插入图片描述

  2. MainWIndows initialize 初始化python 在这里插入图片描述

#ifdef WITH_BINDINGS
std::unique_ptr<QgsPythonUtils> MainWindow::loadPythonSupport()
{
    QString pythonlibName( QStringLiteral( "qgispython" ) );
    #if defined(Q_OS_UNIX) && !defined(Q_OS_ANDROID)
    pythonlibName.prepend( QgsApplication::libraryPath() );
    #endif
    #ifdef __MINGW32__
    pythonlibName.prepend( "lib" );
    #endif
    QString version = QStringLiteral( "%1.%2.%3" ).arg( Qgis::versionInt() / 10000 ).arg( Qgis::versionInt() / 100 % 100 ).arg( Qgis::versionInt() % 100 );
    QgsDebugMsgLevel( QStringLiteral( "load library %1 (%2)" ).arg( pythonlibName, version ), 1 );
    QLibrary pythonlib( pythonlibName, version );
    // It's necessary to set these two load hints, otherwise Python library won't work correctly
    // see http://lists.kde.org/?l=pykde&m=117190116820758&w=2
    pythonlib.setLoadHints( QLibrary::ResolveAllSymbolsHint | QLibrary::ExportExternalSymbolsHint );
    if ( !pythonlib.load() )
    {
      pythonlib.setFileName( pythonlibName );
      if ( !pythonlib.load() )
      {
        std::cerr << QStringLiteral( "Couldn't load Python support library: %1\n" ).arg( pythonlib.errorString() ).toLocal8Bit().constData();
        return nullptr;
      }
    }

    typedef QgsPythonUtils*( *inst )();
    inst pythonlib_inst = reinterpret_cast< inst >( cast_to_fptr( pythonlib.resolve( "instance" ) ) );
    if ( !pythonlib_inst )
    {
      //using stderr on purpose because we want end users to see this [TS]
      std::cerr << "Couldn't resolve Python support library's instance() symbol.\n";
      return nullptr;
    }

    std::unique_ptr< QgsPythonUtils > pythonUtils( pythonlib_inst() );
    if ( pythonUtils )
    {
      pythonUtils->initPython( nullptr, false );
    }

    return pythonUtils;

}
#endif

4. initPython函数为libqgispython.so库中的函数 在这里插入图片描述

void QgsPythonUtilsImpl::initPython( QgisInterface *interface, const bool installErrorHook, const QString &faultHandlerLogPath )
{
  init();
  if ( !checkSystemImports() )
  {
    exitPython();
    return;
  }

  if ( !faultHandlerLogPath.isEmpty() )
  {
    runString( QStringLiteral( "import faulthandler" ) );
    QString escapedPath = faultHandlerLogPath;
    escapedPath.replace( '\\', QLatin1String( "\\\\" ) );
    escapedPath.replace( '\'', QLatin1String( "\\'" ) );
    runString( QStringLiteral( "qgis.utils.__qgis_fault_handler_file_path='%1'" ).arg( escapedPath ) );
    runString( QStringLiteral( "qgis.utils.__qgis_fault_handler_file=open('%1', 'wt')" ).arg( escapedPath ) );
    runString( QStringLiteral( "faulthandler.enable(file=qgis.utils.__qgis_fault_handler_file)" ) );
    mFaultHandlerLogPath = faultHandlerLogPath;
  }

  if ( interface )
  {
    // initialize 'iface' object
    runString( QStringLiteral( "qgis.utils.initInterface(%1)" ).arg( reinterpret_cast< quint64 >( interface ) ) );
  }

  if ( !checkQgisUser() )
  {
    exitPython();
    return;
  }
  doCustomImports();
  if ( installErrorHook )
    QgsPythonUtilsImpl::installErrorHook();
  finish();
}
  • 其中runString函数为执行python语句 如 runString( QStringLiteral( "qgis.utils.initInterface(%1)" ).arg( reinterpret_cast< quint64 >( interface ) ) );
  • qgis.utils.initInterface即python代码 在这里插入图片描述

测试qgis:basicstatisticsforfields算法

QGis中调用

  • toolbox中打开算法,设置输入、输出 在这里插入图片描述

  • 运行算法 可以看到结果 在这里插入图片描述

二次开发调用

  • 先查看算法帮助信息,可以使用qgsprocess 或者本文提供的界面化程序 在这里插入图片描述

在这里插入图片描述

  • 设置参数 可以从帮助信息中看到 在这里插入图片描述

  • 运行算法 完整代码如下

if(id.compare("qgis:basicstatisticsforfields") == 0)
    {
        //添加测试图层
        QString filename = QStringLiteral("maps/shapefile/myplaces.shp");
        QVariantMap conf;
        conf.insert(QStringLiteral("INPUT_LAYER"),filename);//直接用路径
        conf.insert(QStringLiteral("FIELD_NAME"),QStringLiteral("name"));
        QgsProcessingOutputLayerDefinition value( "TEMPORARY_OUTPUT" );
        conf.insert(QStringLiteral("OUTPUT_HTML_FILE"),value);
        auto algorithm = QgsApplication::processingRegistry()->createAlgorithmById(id,conf);
        QgsProcessingContext *context = new QgsProcessingContext;
        context->setProject(QgsProject::instance());
        QgsProcessingFeedback *feedback = new QgsProcessingFeedback(false);
        QVariantMap runResults = algorithm->run(conf,*context,feedback);
        QString htmlFilePath = runResults["OUTPUT_HTML_FILE"].toString();
        statusBar()->showMessage(QStringLiteral("统计结果文件路径: %1").arg(htmlFilePath), 10000);
        QDesktopServices::openUrl(QUrl::fromLocalFile(htmlFilePath));
    }
  • 效果如下图 在这里插入图片描述

测试gdal:aspect算法

QGis中调用

  • toolbox中打开算法,设置输入、输出 在这里插入图片描述

  • 运行算法 生成对应的坡向图层 在这里插入图片描述

二次开发中调用

  • 先查看算法帮助信息,可以使用qgsprocess 或者本文提供的界面化程序
  • 根据帮助信息 确定参数parameters outputs等 在这里插入图片描述
  • 运行算法即可 完整代码如下
else if(id.compare("gdal:aspect") == 0)
    {
        QString filename = QStringLiteral("maps/raster/3420C_2010_327_RGB_LATLNG.tif");
        QFileInfo ff(filename);
        QgsRasterLayer* layer = (QgsRasterLayer*)mApp->addRasterLayer(filename,ff.baseName());

        QVariantMap conf;
        conf.insert(QStringLiteral("INPUT"), layer->id());
        conf.insert(QStringLiteral("BAND"), 1);

        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);

        QVariantMap runResults = algorithm->run(conf,*context,feedback);

        QString tempFilename = runResults["OUTPUT"].toString();
        QFileInfo t(tempFilename);
        QgsRasterLayer* tempLayer = (QgsRasterLayer*)mApp->addRasterLayer(tempFilename,t.baseName());
    }
  • 效果如下 在这里插入图片描述

总结

  • 介绍了如何在C++程序中调用python算法