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

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

这里写图片描述

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

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

这里写图片描述


几个类之间的关系

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

它们的关系是这样的。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 );  
 
 
  • 1
  • 2
  • 3
  • 4

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

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

// 创建 svgMarkerSymbolLayer
QgsSvgMarkerSymbolLayerV2* svgMarker = new QgsSvgMarkerSymbolLayerV2( "money/money_bank2.svg" );
 
 
  • 1
  • 2

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

QgsSymbolLayerV2List symList;
 
 
  • 1

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

symList.append( svgMarker );
 
 
  • 1

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

QgsMarkerSymbolV2* markSym = new QgsMarkerSymbolV2( symList );
QgsSingleSymbolRendererV2* symRenderer = new QgsSingleSymbolRendererV2( markSym );
 
 
  • 1
  • 2

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

veclayer->setRendererV2( symRenderer );
 
 
  • 1

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

svgMarker->setSize( 10 );
 
 
  • 1

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

最后一点

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

全是问号,为什么?

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

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

这里只给了文件的名称,并没有指出文件的路径,因此程序找不到对应的 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" ) );
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

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

效果是一样的。

最后,还是给一个完整的 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();
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

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

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值