QGis二次開發基礎 -- 向量圖層的顯示樣式
帶座標的向量圖層作為GIS的核心資料,具有非常豐富的用途。人們往往喜歡在地圖上做各種標記,不僅美觀,而且使地圖清晰,一目瞭然。於是應運而生了使用各種各樣的圖示作為地圖示記的功能需求,在很多GIS軟體上,這早已不是什麼新鮮事了。然而在QGis二次開發的時候,同學們的對於圖層樣式自定義的需求貌似還挺大的,今天就來與大家探討一下這個功能的實現。
下面我將會很簡略的介紹這個功能相關的類,並用一個簡單的例子來展示這些類的呼叫方式。當然,這裡面還有很多功能可以供我們使用,但只要明白了呼叫機理,再去看 API 文件應該就沒什麼大問題了。
說明:文中所使用的 svg 格式圖示來自於 QGis 原始碼工程的 image 資料夾,如下圖所示
幾個類之間的關係
首先要講的,是向量圖層樣式相關的幾個類,它們分別是
它們的關係是這樣的。QgsFeatureRendererV2 控制著QgsVectorLayer 的“渲染”樣式,而具體用什麼樣式來“渲染”,則是有 QgsSymbolV2來定義的,QgsSymbolLayerV2 是 QgsSymbolV2 的擴充套件。搞明白這幾個類的關係有助於我們後面的理解。實際上,我們基本不會直接用到這幾個類,大多數時候是在用它們的子類。
QgsVectorLayer 不必多說,只需要知道使用它的方法“setRendererV2()”來繫結它的 Renderer 就好了。
QgsFeatureRendererV2
來看看向量圖層都支援那些 Renderer,也就是 QgsFeatureRendererV2 這個類的派生關係,如下圖
從圖上可以看到,這裡支援的渲染方式還是蠻多的,GIS的同學看到這些名字應該不會太陌生,就是我們設定圖層屬性面板時候的那個下拉選單裡面的內容,見下圖。
這裡內容太多,就不逐個介紹了,本篇文章中我們只關注一個最簡單也是最常用的 QgsSingleSymbolRendererV2 ,也就是我們經常使用在點圖層標記上的單個的樣式。
QgsSymbolV2
這個類就是直接關係到圖層顯示的“造型”了,也就是在這裡設定各種圖層樣式的屬性。還是先來看看它的派生類關係。
關於這些類的設定大多集中在“顏色”、“大小”、“透明度”等屬性上,每個派生類也都有自己的一些屬性,詳細的情況還是建議大家看看 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 。先不要太在意下面程式碼中建構函式裡面的字串,後面來講它的作用。
// 建立 svgMarkerSymbolLayer
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 );
效果如下圖:
最後一點
上面的程式碼直接拷貝是會有問題的,效果應該是下面這樣
全是問號,為什麼?
這是沒有找到對應的 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();
}
如有錯誤請不吝指正,謝謝閱讀!