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 );  

這裏我們以“”圖層作爲例子,直接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();
}

如有錯誤請不吝指正,謝謝閱讀!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章