带坐标的矢量图层作为GIS的核心数据,具有非常丰富的用途。人们往往喜欢在地图上做各种标记,不仅美观,而且使地图清晰,一目了然。于是应运而生了使用各种各样的图标作为地图标记的功能需求,在很多GIS软件上,这早已不是什么新鲜事了。然而在QGis二次开发的时候,同学们的对于图层样式自定义的需求貌似还挺大的,今天就来与大家探讨一下这个功能的实现。
![这里写图片描述](https://img-blog.csdn.net/20160105212707680)
下面我将会很简略的介绍这个功能相关的类,并用一个简单的例子来展示这些类的调用方式。当然,这里面还有很多功能可以供我们使用,但只要明白了调用机理,再去看 API 文档应该就没什么大问题了。
说明:文中所使用的 svg 格式图标来自于 QGis 源码工程的 image 文件夹,如下图所示
![这里写图片描述](https://img-blog.csdn.net/20160105213702279)
几个类之间的关系
首先要讲的,是矢量图层样式相关的几个类,它们分别是
它们的关系是这样的。QgsFeatureRendererV2 控制着QgsVectorLayer 的“渲染”样式,而具体用什么样式来“渲染”,则是有 QgsSymbolV2来定义的,QgsSymbolLayerV2 是 QgsSymbolV2 的扩展。搞明白这几个类的关系有助于我们后面的理解。实际上,我们基本不会直接用到这几个类,大多数时候是在用它们的子类。
QgsVectorLayer 不必多说,只需要知道使用它的方法“setRendererV2()”来绑定它的 Renderer 就好了。
QgsFeatureRendererV2
来看看矢量图层都支持那些 Renderer,也就是 QgsFeatureRendererV2 这个类的派生关系,如下图
从图上可以看到,这里支持的渲染方式还是蛮多的,GIS的同学看到这些名字应该不会太陌生,就是我们设置图层属性面板时候的那个下拉菜单里面的内容,见下图。
![这里写图片描述](https://img-blog.csdn.net/20160105213135410)
这里内容太多,就不逐个介绍了,本篇文章中我们只关注一个最简单也是最常用的 QgsSingleSymbolRendererV2 ,也就是我们经常使用在点图层标记上的单个的样式。
QgsSymbolV2
这个类就是直接关系到图层显示的“造型”了,也就是在这里设置各种图层样式的属性。还是先来看看它的派生类关系。
![这里写图片描述](https://img-blog.csdn.net/20160105194547566)
简单说明一下,QgsFillSymbolV2 对应的是多边形矢量图层,QgsLineSymbolV2 对应的是线性矢量图层, 而 QgsMarkerSymbolV2 自然就是对应点矢量图层了。
关于这些类的设置大多集中在“颜色”、“大小”、“透明度”等属性上,每个派生类也都有自己的一些属性,详细的情况还是建议大家看看 API 文档 。
QgsSymbolLayerV2
QGis扩展了简单的 Symbol 图层样式,将原来单一的 Symbol 变成了“图层”方式,使得样式可定制的自由度开阔了许多, 当然派生关系也多了很多,见下图。
![这里写图片描述](https://img-blog.csdn.net/20160105195833266)
从这个图里面可以看到,分别对应这多边形、线和点都有各自的样式图层,将一个矢量图形的图标拆分,基本每个地方都有可定制的余地,都有专门的样式图层类来控制。这里很多同学应该特别关注一下 QgsSvgMarkerSymbolLayerV2 这个类,这个类允许我们使用自己做好的“svg”格式的图标来作为图层的显示样式,借助这个特性,我们能够将t做成图层显示成任何我们想要的样式,如下图效果。
![这里写图片描述](https://img-blog.csdn.net/20160105213022304)
调用机理
下面我将会通过一个非常简单的示例来阐述这些类的调用机理。
要做图层样式显示,自然需要一个矢量图层,于是
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 。先不要太在意下面代码中构造函数里面的字符串,后面来讲它的作用。
QgsSvgMarkerSymbolLayerV2* 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 );
效果如下图:
![这里写图片描述](https://img-blog.csdn.net/20160105203727224)
最后一点
上面的代码直接拷贝是会有问题的,效果应该是下面这样
![这里写图片描述](https://img-blog.csdn.net/20160105204051881)
全是问号,为什么?
这是没有找到对应的 svg 图标,也就是刚才上面的那一句
QgsSvgMarkerSymbolLayerV2* svgMarker = new QgsSvgMarkerSymbolLayerV2( "money/money_bank2.svg" );
这里只给了文件的名称,并没有指出文件的路径,因此程序找不到对应的 svg 格式文件。
打开QGis源码,不难发现这里有两种方式传文件路径,一种是在全局设置一个 svg 文件夹的默认路径,另一种则是直接在创建类之后给出完整的文件路径。
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 函数代码,方便各位测试。
#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 )
{
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
如有错误请不吝指正,谢谢阅读!