4. qgis c++二次开发 map canvas介绍

前言

前几篇文章分别介绍了qgis二次开发的

在显示GIS数据之前,需要首先介绍一下map canvas,毕竟在将颜料(GIS地图等数据)画在画布(map canvas)之前,首先要创建画布(map canvas)。

Map canvas

QGis软件中的Map canvas

  • 官方文档training_manual 有一节Navigating the Map Canvas专门讲述了Map Canvas
  • 下图空白区域左侧为图层树(Layer tree),右侧为Map Canvas


    image.png

代码添加Map canvas

Map canvas is a class for displaying all GIS data types on a canvas.


image.png
  • QgsMapCanvas是Map canvas,类的详细内容见文档

  • QgsMapCanvas本质是一个QGraphicsViewQGraphicsView是Qt Graphics View Framework框架的一个类,Graphics View Framework是Qt提供的一个用于管理大量2D图形项目并与之交互的框架。

    image.png

    • QGraphicsView提供了视图,相当于画布
    • QGraphicsItem图形类的基类,可以显示在canvas的组件,QgsMapCanvasItem是该类的子类
    • QGraphicsScene用于管理item

Map Canvas创建和显示

  • Map Canvas创建代码如下
void ll_qgis_base_lib::initialize(QMainWindow *mainWindow)
{
    mMainWindow = mainWindow;

    mMapCanvas = new QgsMapCanvas;
    mMapCanvas->enableAntiAliasing(true);
    mMapCanvas->setCachingEnabled(true);
    mMapCanvas->setCanvasColor(QColor(255,255,255));
    mMapCanvas->setVisible(true);

    initLayerTreeView();
    initMaptools();
}
  • ll_qgis_base_lib类是qgis_cpp_api_apps项目的一个库,由于Map canvas和Layer tree很通用,所以将这两个放在库中
  • mMapCanvas添加到父Widget即可显示Map canvas
void MainWindow::initStackwidgetPageCanvas()
{
    auto gridLayout = new QGridLayout;
    gridLayout->addWidget((QWidget*)mApp->mapCanvas());
    ui->page_canvas->setLayout(gridLayout);
    addDockWidget(Qt::LeftDockWidgetArea,mApp->layerTreeDock());
}

QGis中的QGraphicsItem

image.png
  • Measure功能使用了QgsRubberBand类,上图中的三角形就是用QgsRubberBand类绘制的

二次开发中的Item

image.png
  • QgsMapCanvasItem是一个抽象类,用于放置在地图画布上的项目。

  • QgsRubberband类是一个用于在地图上绘制临时特征(例如数字化线条)的类。类图如下

    image.png

  • 如下代码实现在图层上画三角形

void MainWindow::rubberBandPolygonSlot()
{
    //添加shapefile
    QString filename = QStringLiteral("maps/shapefile/protected_areas.shp");
    QFileInfo ff(filename);
    mApp->addVectorLayer(filename,ff.baseName());
    zoomToFirstLayer<QgsVectorLayer*>();
    //定义三个点
    QgsPointXY point1(20.34013,-33.90453);
    QgsPointXY point2(20.49744,-33.91126);
    QgsPointXY point3(20.41396,-33.93079);
    //新建PolygonGeometry类型的RubberBand
    mRubberBandPolygon = new QgsRubberBand(mApp->mapCanvas(),QgsWkbTypes::PolygonGeometry);
    //添加三个点
    mRubberBandPolygon->addPoint(point1);
    mRubberBandPolygon->addPoint(point2);
    mRubberBandPolygon->addPoint(point3);
    //设置线宽颜色等属性
    mRubberBandPolygon->setWidth(4);
    mRubberBandPolygon->setColor(QColor(222,155,67));
    mRubberBandPoint = new QgsRubberBand(mApp->mapCanvas(),QgsWkbTypes::PointGeometry);
    mRubberBandPoint->addPoint(point1);
    mRubberBandPoint->addPoint(point2);
    mRubberBandPoint->addPoint(point3);
    mRubberBandPoint->setWidth(6);
    mRubberBandPoint->setColor(QColor(222,155,67));
    mRubberBandPolygon->show();
    mRubberBandPoint->show();
}
  • 以下代码实现删除QgsGraphicsItem
if(mVertexMarker)
    mApp->mapCanvas()->scene()->removeItem(mVertexMarker);

Layer Tree

QGis软件中的Layer Tree

image.png
  • Layer Tree用于管理图层
  • Layer Tree和Map canvas建立连接,可以对图层进行操作

代码实现layer tree

image.png
  • 代码如下
void ll_qgis_base_lib::initLayerTreeView()
{
    //添加DockWidget作为图层树的容器
    mLayerTreeDock = new QgsDockWidget(tr("Layer Tree"));
    mLayerTreeDock->setObjectName( QStringLiteral( "Layers" ) );
    mLayerTreeDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea );

    //创建Model
    QgsLayerTreeModel* model = new QgsLayerTreeModel(QgsProject::instance()->layerTreeRoot(),this);
    model->setFlag( QgsLayerTreeModel::AllowNodeReorder );
    model->setFlag( QgsLayerTreeModel::AllowNodeRename );
    model->setFlag( QgsLayerTreeModel::AllowNodeChangeVisibility );
    model->setFlag( QgsLayerTreeModel::ShowLegendAsTree );
    model->setFlag( QgsLayerTreeModel::UseEmbeddedWidgets );
    model->setFlag( QgsLayerTreeModel::UseTextFormatting );
    model->setAutoCollapseLegendNodes( 10 );

    //创建View,mLayerTreeView会在库外使用,所以需要添加getter方法,
    mLayerTreeView = new QgsLayerTreeView();
    mLayerTreeView->setModel(model);

    //Map Canvas和Layer Tree View建立联系,这样通过QgsProject::instance()->addMapLayer添加图层后,会自动添加在图层树以及会在canvas渲染图层
    mLayerTreeMapCanvasBridge = new QgsLayerTreeMapCanvasBridge(QgsProject::instance()->layerTreeRoot(),mMapCanvas,this);
    connect( mLayerTreeMapCanvasBridge, &QgsLayerTreeMapCanvasBridge::canvasLayersChanged, mMapCanvas, &QgsMapCanvas::setLayers );

    //Layer Tree View右键菜单实现
    mLayerTreeView->setMenuProvider(new ll_qgis_base_layertreeview_menu(mMainWindow,mLayerTreeView,mMapCanvas));
    connect(QgsProject::instance()->layerTreeRegistryBridge(),SIGNAL(addedLayersToLayerTree(QList<QgsMapLayer*>)),
            this,SLOT(slot_autoSelectAddedLayer(QList<QgsMapLayer*>)));
}
  • 主要涉及了以下类QgsDockWidget,QgsLayerTreeModel,QgsLayerTreeView,QgsLayerTreeMapCanvasBridge
  • QgsDockWidget是一个Qt Dock Widget,用于显示图层树
  • QgsLayerTreeViewQgsLayerTreeModel实现了树的功能
  • QgsLayerTreeMapCanvasBridge类负责从图层树中更新QgsMapCanvas的图层

QgsProject(项目管理)

QGis的项目可以保存为项目文档,后缀名为“qgz”或“qgs”。在QGIS 3以前,只能通过“qgs”文件保存项目。“qgs”文件的本质是存储图层的信息等的XML文件。在QGIS 3以后,“qgz”格式采用ZIP压缩方法,不仅包含“qgs”文件,还包括附属数据库(Auxiliary Storage)文件(后缀名为“qgd”)。自QGIS 3.2以来,“qgz”文件成为QGIS项目的默认存储格式,本书也使用“qgz”文件存储QGIS项目。

QGis软件中的项目管理

  • 新建项目,在Project菜单中可以新建一个空工程


    image.png
  • 保存项目 可以选择项目名称和位置


    image.png

    image.png

代码实现

Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
QgsProject is available both as a singleton (QgsProject::instance()) and for use as standalone objects. The QGIS project singleton always gives access to the canonical project reference open within the main QGIS application.

  • QgsProject代表QGis中的一个项目,包含项目管理、图层管理以及其他功能,QgsProject的详细内容见官方文档
  • QgsProject是一个单例类,通过方法QgsProject::instance()即可调用其方法,以下是QgsProject的一些常用函数
  • QgsProject的最常用的功能是添加图层,调用QgsProject::instance()->addMapLayer(vecLayer);后会在Map Canvas显示,也会添加到Layer tree中,如下代码添加一个shape文件
void MainWindow::addShpSlot()
{
    QString filename = QStringLiteral("maps/shapefile/protected_areas.shp");
    QFileInfo ff(filename);
    //创建图层
    QgsVectorLayer* vecLayer = new QgsVectorLayer(filename,ff.baseName(),"ogr");
    if(!vecLayer->isValid())
    {
        QMessageBox::critical(this,tr("error"),tr("invalid layer"));
        return;
    }
    QgsProject::instance()->addMapLayer(vecLayer);
    zoomToFirstLayer<QgsVectorLayer*>();
}
  • 以下是QgsProject的常用函数
//读取项目文件
bool    read (const QString &filename, Qgis::ProjectReadFlags flags=Qgis::ProjectReadFlags())
bool    read (Qgis::ProjectReadFlags flags=Qgis::ProjectReadFlags())
//保存项目文件
bool    write ()
bool    write (const QString &filename)
//添加单个图层
QgsMapLayer *   addMapLayer (QgsMapLayer *mapLayer, bool addToLegend=true, bool takeOwnership=true)
//添加多个图层
QList< QgsMapLayer * >  addMapLayers (const QList< QgsMapLayer * > &mapLayers, bool addToLegend=true, bool takeOwnership=true)
Q_INVOKABLE QgsMapLayer *mapLayer (const QString &layerId) const
template<class T >
T mapLayer (const QString &layerId) const
QMap< QString, QgsMapLayer * >  mapLayers (const bool validOnly=false) const
QList< QgsMapLayer * >  mapLayersByShortName (const QString &shortName) const
template<typename T >
QVector< T >    mapLayersByShortName (const QString &shortName) const
void    removeAllMapLayers ()
void    removeMapLayer (const QString &layerId)
void    removeMapLayer (QgsMapLayer *layer)
void    removeMapLayers (const QList< QgsMapLayer * > &layers)
void    removeMapLayers (const QStringList &layerIds)

总结

  • QgsMapCanvas作为视图类提供了绘制的画布
  • QgsMapCanvasItem可以绘制在画布上的Item
  • QgsProject图层管理,尤其在添加图层时常用
  • 了解了这几个类,就可以学习如何显示GIS数据了
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容