QGis二次开发基础 -- 矢量图层的显示样式

来源:互联网 发布:递归算法迷宫问题 编辑:程序博客网 时间:2024/05/29 06:49

带坐标的矢量图层作为GIS的核心数据,具有非常丰富的用途。人们往往喜欢在地图上做各种标记,不仅美观,而且使地图清晰,一目了然。于是应运而生了使用各种各样的图标作为地图标记的功能需求,在很多GIS软件上,这早已不是什么新鲜事了。然而在QGis二次开发的时候,同学们的对于图层样式自定义的需求貌似还挺大的,今天就来与大家探讨一下这个功能的实现。

这里写图片描述

下面我将会很简略的介绍这个功能相关的类,并用一个简单的例子来展示这些类的调用方式。当然,这里面还有很多功能可以供我们使用,但只要明白了调用机理,再去看 API 文档应该就没什么大问题了。

说明:文中所使用的 svg 格式图标来自于 QGis 源码工程的 image 文件夹,如下图所示

这里写图片描述


几个类之间的关系

首先要讲的,是矢量图层样式相关的几个类,它们分别是

  • QgsVectorLayer
  • QgsSymbolV2
  • QgsSymbolLayerV2
  • QgsFeatureRendererV2

它们的关系是这样的。QgsFeatureRendererV2 控制着QgsVectorLayer 的“渲染”样式,而具体用什么样式来“渲染”,则是有 QgsSymbolV2来定义的,QgsSymbolLayerV2 是 QgsSymbolV2 的扩展。搞明白这几个类的关系有助于我们后面的理解。实际上,我们基本不会直接用到这几个类,大多数时候是在用它们的子类。

QgsVectorLayer 不必多说,只需要知道使用它的方法“setRendererV2()”来绑定它的 Renderer 就好了。

QgsFeatureRendererV2

来看看矢量图层都支持那些 Renderer,也就是 QgsFeatureRendererV2 这个类的派生关系,如下图
这里写图片描述
从图上可以看到,这里支持的渲染方式还是蛮多的,GIS的同学看到这些名字应该不会太陌生,就是我们设置图层属性面板时候的那个下拉菜单里面的内容,见下图。

这里写图片描述

这里内容太多,就不逐个介绍了,本篇文章中我们只关注一个最简单也是最常用的 QgsSingleSymbolRendererV2 ,也就是我们经常使用在点图层标记上的单个的样式。

QgsSymbolV2

这个类就是直接关系到图层显示的“造型”了,也就是在这里设置各种图层样式的属性。还是先来看看它的派生类关系。

这里写图片描述

简单说明一下,QgsFillSymbolV2 对应的是多边形矢量图层,QgsLineSymbolV2 对应的是线性矢量图层, 而 QgsMarkerSymbolV2 自然就是对应点矢量图层了。

关于这些类的设置大多集中在“颜色”、“大小”、“透明度”等属性上,每个派生类也都有自己的一些属性,详细的情况还是建议大家看看 API 文档 。

QgsSymbolLayerV2

QGis扩展了简单的 Symbol 图层样式,将原来单一的 Symbol 变成了“图层”方式,使得样式可定制的自由度开阔了许多, 当然派生关系也多了很多,见下图。

这里写图片描述

从这个图里面可以看到,分别对应这多边形、线和点都有各自的样式图层,将一个矢量图形的图标拆分,基本每个地方都有可定制的余地,都有专门的样式图层类来控制。这里很多同学应该特别关注一下 QgsSvgMarkerSymbolLayerV2 这个类,这个类允许我们使用自己做好的“svg”格式的图标来作为图层的显示样式,借助这个特性,我们能够将t做成图层显示成任何我们想要的样式,如下图效果。

这里写图片描述

调用机理

下面我将会通过一个非常简单的示例来阐述这些类的调用机理。

要做图层样式显示,自然需要一个矢量图层,于是

QString myLayerPath         = "D:/Data/qgis_sample_data/shapefiles/airports.shp";  // 修改为自己的文件路径QString myLayerBaseName     = "airports"; //图层名称;  QgsVectorLayer* vecLayer = new QgsVectorLayer( myLayerPath, myLayerBaseName, "ogr", false );  

这里我们以“”图层作为例子,直接copy代码的同学,需要将你的文件路径改为一个有效的“”图层文件路径。

然后,创建一个样式图层,也就是 QgsSymbolLayerV2 类型,但我们要使用“svg” 格式的图片作为显示样式,因此使用 QgsSymbolLayerV2 的派生类 QgsSvgMarkerSymbolLayerV2 。先不要太在意下面代码中构造函数里面的字符串,后面来讲它的作用。

// 创建 svgMarkerSymbolLayerQgsSvgMarkerSymbolLayerV2* svgMarker = new QgsSvgMarkerSymbolLayerV2( "money/money_bank2.svg" );

前面说过,这是一个样式图层,图层是可以叠加的,也就是说可以做好几个这样的样式,然后叠加上去显示,但是总归要有一个管理这些样式图层的集合,于是

QgsSymbolLayerV2List symList;

并将刚才创建的样式图层装到这个 List 里面去,喜欢的话你可以多装几个样式图层进去,这里就只装一个做演示。

symList.append( svgMarker );

做好了显示样式,还需要将这个样式传给图层”渲染器“才行,需要建立一个点图层的 Renderer,并且这个 Renderer 还只接受 QgsSymbolV2 这个类型的参数,于是需要将样式图层 QgsSymbolLayerV2 转换为 QgsSymbolV2 类型。由于这里是点图层,就用到 QgsSymbolV2 的子类 QgsMarkerSymbolV2 , 又通过 API 文档了解到这个类的构造函数接受 QgsSymbolLayerV2List 这个样式图层的集合,于是也就将 Symbol 和 SymbolLayer 联系了起来。代码如下。

QgsMarkerSymbolV2* markSym = new QgsMarkerSymbolV2( symList );QgsSingleSymbolRendererV2* symRenderer = new QgsSingleSymbolRendererV2( markSym );

最后,就只需要将这个图层渲染器连接到之前建立的矢量图层就好了

veclayer->setRendererV2( symRenderer );

这样,MapCanvas 刷新以后,就可以看到图层的显示效果了。如果觉得图标小,还可以加一句

svgMarker->setSize( 10 );

效果如下图:
这里写图片描述

最后一点

上面的代码直接拷贝是会有问题的,效果应该是下面这样
这里写图片描述

全是问号,为什么?

这是没有找到对应的 svg 图标,也就是刚才上面的那一句

QgsSvgMarkerSymbolLayerV2* svgMarker = new QgsSvgMarkerSymbolLayerV2( "money/money_bank2.svg" );

这里只给了文件的名称,并没有指出文件的路径,因此程序找不到对应的 svg 格式文件。

打开QGis源码,不难发现这里有两种方式传文件路径,一种是在全局设置一个 svg 文件夹的默认路径,另一种则是直接在创建类之后给出完整的文件路径。

  • 先设置默认路径,再传文件名作为构造函数的参数
// 放在 main 函数里设置路径QgsApplication myApp( argc, argv, true );QgsApplication::setPrefixPath( "C:/Program Files/qgis2.9.0", true );QgsApplication::initQgis();myApp.setDefaultSvgPaths( QStringList( "../images/svg" ) );

之后就可以按照上文的方式传文件名 “money/money_bank2.svg” 进去了

QgsSvgMarkerSymbolLayerV2* svgMarker = new QgsSvgMarkerSymbolLayerV2( "money/money_bank2.svg" );
  • 直接给出完成的文件路径
QgsSvgMarkerSymbolLayerV2* svgMarker = new QgsSvgMarkerSymbolLayerV2();svgMarker->setPath( "C:/Program Files/qgis2.9.0/images/svg/money/money_bank2.svg" );

效果是一样的。

最后,还是给一个完整的 main 函数代码,方便各位测试。

// main.cpp#include<QtGui/QApplication>#include<qgsapplication.h>#include<qgsproviderregistry.h>#include<qgsmaplayerregistry.h>#include<qgsvectorlayer.h>#include<qgsmapcanvas.h>#include<QString>#include<QApplication>#include<QWidget>#include <QStringList>#include<QMessageBox>#include<QObject>#include <QList>#include <QFileInfoList>#include <QDir>#include <QLibrary>#include <QDebug>#include <qgssymbollayerv2.h>#include <qgssymbolv2.h>#include <qgsmarkersymbollayerv2.h>#include <qgsvectorlayerrenderer.h>#include <qgsrendercontext.h>#include <qgssinglesymbolrendererv2.h>#include <qgssymbollayerv2.h>int main( int argc, char *argv[] ){    QgsApplication myApp( argc, argv, true );    QgsApplication::setPrefixPath( "C:/Program Files/qgis2.9.0", true );    QgsApplication::initQgis();    QgsProviderRegistry* provider = QgsProviderRegistry::instance();// 改成你自己的点矢量文件路径    QString myLayerPath         = "//psf/Home/Documents/qgis_sample_data/shapefiles/airports.shp";    QString myLayerBaseName     = "airports"; //图层名称;    QList<QgsMapLayer*> myList;    QgsVectorLayer* veclayer = new QgsVectorLayer( myLayerPath, myLayerBaseName, "ogr", false );    if ( !veclayer )    {        return 0;    }    if ( veclayer->isValid() )    {        QMessageBox::information( 0, "", "layer is valid" );        veclayer->setProviderEncoding( "System" );        myList << veclayer;    }    if ( veclayer->geometryType() == QGis::Point )    {        // 创建 svgMarkerSymbolLayer        QgsSvgMarkerSymbolLayerV2* svgMarker = new QgsSvgMarkerSymbolLayerV2();        svgMarker->setPath( "C:/Program Files/qgis2.9.0/images/svg/money/money_bank2.svg" );        QgsSymbolLayerV2List symList;        symList.append( svgMarker );        QgsMarkerSymbolV2* markSym = new QgsMarkerSymbolV2( symList );        QgsSingleSymbolRendererV2* symRenderer = new QgsSingleSymbolRendererV2( markSym );        svgMarker->setSize( 10 );        veclayer->setRendererV2( symRenderer );    }    QgsMapLayerRegistry::instance()->addMapLayer( veclayer );    QList<QgsMapCanvasLayer> myLayerSet;    myLayerSet.append( QgsMapCanvasLayer( veclayer ) );    QgsMapCanvas* mypMapCanvas = new QgsMapCanvas( 0, 0 );    mypMapCanvas->setExtent( veclayer->extent() );    mypMapCanvas->enableAntiAliasing( true );    mypMapCanvas->setCanvasColor( QColor( 255, 255, 255 ) );    mypMapCanvas->freeze( false );    mypMapCanvas->setLayerSet( myLayerSet );    mypMapCanvas->setVisible( true );    mypMapCanvas->refresh();    return myApp.exec();}

如有错误请不吝指正,谢谢阅读!

5 0