1. 程式人生 > >QGis二次開發基礎 -- 向量圖層的顯示樣式

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

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