QGIS API : C++アプリケーションを作成するには

QGISのCoding Compilation
guideにはC++アプリケーションのシンプルなサンプルコードが記載されています。 以下のコードはCoding compilation
guideからコードだけを抜き取ったものです。 コードについての詳細な説明は上記のドキュメントを参照してください。
なお、以下の例では、”C:/Program Files/qgis1.7.0″にQGISがインストールされているものとします。

   1: 
   2: //
   3: // QGIS Includes
   4: //
   5: #include <qgsapplication.h>
   6: #include <qgsproviderregistry.h>
   7: #include <qgssinglesymbolrenderer.h>
   8: #include <qgsmaplayerregistry.h>
   9: #include <qgsvectorlayer.h>
  10: #include <qgsmapcanvas.h>
  11: //
  12: // Qt Includes
  13: //
  14: #include <QString>
  15: #include <QApplication>
  16: #include <QWidget>
  17: 
  18: int main(int argc, char ** argv)
  19: {
  20: 	// Start the Application
  21: 	QgsApplication app(argc, argv, true);
  22:  
  23: 	QString myPluginsDir        = "C:/Program Files/qgis1.7.0/plugins";
  24: 	QString myLayerPath         = "C:/Data/testforest/test_forest.shp";
  25: 	QString myLayerBaseName     = "test_forest";
  26: 	QString myProviderName      = "ogr";
  27: 
  28: 	// Instantiate Provider Registry
  29: 	QgsProviderRegistry::instance(myPluginsDir);
  30: 	QgsVectorLayer * mypLayer = new QgsVectorLayer(myLayerPath, myLayerBaseName, myProviderName);
  31: 	QgsSingleSymbolRenderer *mypRenderer = new QgsSingleSymbolRenderer(mypLayer->geometryType());
  32: 	QList <QgsMapCanvasLayer> myLayerSet;
  33:  
  34: 	mypLayer->setRenderer(mypRenderer);
  35: 	if (mypLayer->isValid())
  36: 	{
  37: 		qDebug("Layer is valid");
  38: 	}
  39: 	else
  40: 	{
  41: 		qDebug("Layer is NOT valid");
  42: 	}
  43:  
  44: 	// Add the Vector Layer to the Layer Registry
  45: 	QgsMapLayerRegistry::instance()->addMapLayer(mypLayer, TRUE);
  46: 	// Add the Layer to the Layer Set
  47: 	myLayerSet.append(QgsMapCanvasLayer(mypLayer, TRUE));
  48:  
  49: 	// Create the Map Canvas
  50: 	QgsMapCanvas * mypMapCanvas = new QgsMapCanvas(0, 0);
  51: 	mypMapCanvas->setExtent(mypLayer->extent());
  52: 	mypMapCanvas->enableAntiAliasing(true);
  53: 	mypMapCanvas->setCanvasColor(QColor(0, 255, 255));
  54: 	mypMapCanvas->freeze(false);
  55: 	// Set the Map Canvas Layer Set
  56: 	mypMapCanvas->setLayerSet(myLayerSet);
  57: 	mypMapCanvas->setVisible(true);
  58: 	mypMapCanvas->refresh();
  59: 
  60: 	// Start the Application Event Loop
  61: 	return app.exec();
  62: }

このサンプルコードは一応正しく動作します。
ただし、このコードを実際に実行させるためには各コンパイラに対応した設定をあれこれ行う必要があります。
以下では、VC2010における設定について説明します。

どのバージョンからかは正確にはわかりませんが、少なくとも2.14ではQgsApplicationオブジェクト構築後に
QgsApplication::initQgis()を呼び出す必要があります。
これを忘れると、一見動作しているように見えるのですが色々と挙動がおかしくなります。

上記のコードであれば、22行目あたりで、

  QgsApplication::initQgis();

の一行を追加しておきましょう。

また、終了時にはQgsApplication::exitQgis();を呼び出す必要があります。
ただし、上記のコード内には適切な追加個所がありません。

実践的には、initQgis()exitQgis()はメインウィンドウクラスのコンストラクタとデストラクタでそれぞれ呼び出すことになります。
メインウィンドウクラスの実装は後述のコードを参照してください。

なお、initQgis()exitQgis()は、プロバイダレジストリや認証マネージャの構築及び破棄を行っているようです。

VC2010のプロジェクト設定

まず、空の新規プロジェクトを作成します。プリコンパイル済みヘッダは使用しないほうが無難かと思います。

次にmain.cppを新規作成した後、コーディングする前にインクルードパスを設定します。
こうするとコードアシストが使えるようになるためです。 追加するインクルードパスは以下のとおりです。
Qtが”C:/”の直下にインストールされているものとします。

C:Program Filesqgis1.7.0include;
C:Qt4.7.1includeQtCore;
C:Qt4.7.1includeQtGui;
C:Qt4.7.1includeQtXml;
C:Qt4.7.1include

続いてプリプロセッサ定義を追加します。

WIN32;
DEBUG;
WINDOWS;
QT_GUI_LIB;
QT_CORE_LIB;
QT_DEBUG;_CRT_SECURE_NO_WARNINGS;
CRT_NONSTDC_NO_WARNINGS;
CORE_EXPORT=__declspec(dllimport);
GUI_EXPORT=__declspec(dllimport);
%(PreprocessorDefinitions)

重要なのは"CORE_EXPORT"、"GUI_EXPORT"の2つです。これらは全て
__declspec(dllimport)
を定義しています。
この定義は通常DLLのヘッダファイルにあったりすることが多いですが、QGISのライブラリでは定義されていないのでここで定義します。
なお、上記のコードを動作させるだけであればqgis_analysis.libはリンクしなくてもいいですが、analysis.libをリンクする必要がある場合は”ANALYSIS_EXPORT”の定義も追加します。

リンカの設定ではリンクするライブラリを指定します。 まず、追加のライブラリディレクトリを以下のように指定します。

c:Qt4.7.1lib;
C:Program Filesqgis1.7.0lib;
%(AdditionalLibraryDirectories)

さらにリンクするライブラリを「追加の依存ファイル」に列挙します。

QtCored4.lib;
QtGuid4.lib;
QtXmld4.lib;
qtmaind.lib;
qgis_core.lib;
qgis_gui.lib;
%(AdditionalDependencies)

上記のうち、Qt関連はデバッグ版のインポートライブラリです。リリースビルドの場合はQtCore4.lib、QtGui4.lib、QtXml4.lib、qtmain.libをリンクしてください。
あと筆者のお勧めは、デバッグ版はコンソールが表示されるほうが
qDebug()
の出力を表示できるので、 デバッグ版はコンソールアプリケーション、リリース版はウィンドウアプリケーションと分けて設定する作戦です。
この場合、デバッグビルドの設定ではqtmaind.libは不要です。

上記のように設定するには[リンカー]-[システム]を選択し、サブシステムを変更します。
デバッグビルドではサブシステムをコンソールに、リリースビルドではWindowsにそれぞれ設定します。

あと、[構成プロパティ]-[全般]にある文字セットはUnicodeのままで大丈夫です。

これで一応設定が整いました。以下のように表示されれば一応成功です。

実行結果

メインウィンドウに表示

上記のプログラムは動作はしますがこれではどうしようもないので、キャンバスをウィジェットの1つとして取り扱えるようにしたいところです。
ここでは例としてQtのメインウィンドウアプリケーションにキャンバスを表示するようにしてみましょう。

まず、QtDesignerでメインウィンドウを作成します。
下の図はメインウィンドウにファイルを開くアクションを1つ作成したものです。
アクションにはスロットとしてonFileOpen()を割り当てました。

メインウィンドウのデザイン

次にcentralWidgetを格上げします。図のようにcentralwidgetを右クリックして「格上げ先を指定」を選択します。

格上げ先を指定

下図のように格上げされたクラス名をQgsMapCanvasとし、ヘッダファイルを”qgsmapcanvas.h”とします。
入力後【追加】ボタンを押し、さらに【格上げ】ボタンを押します。

格上げダイアログ

格上げ後はオブジェクトインスペクタには下図のように表示されます。

格上げ後

UIファイルができたら、VCプロジェクトに追加してカスタムビルドツールを定義し、生成されたヘッダファイルをVCプロジェクトに追加しましょう。
UIファイルに対するカスタムビルドツールの設定はここを参照してください。

あとは、前記のコードを適当な箇所に分配していきます。
まず、main関数は以下のようになります。

main.cpp
   1: 
   2: //
   3: // QGIS Includes
   4: //
   5: #include <qgsapplication.h>
   6: #include <qgsproviderregistry.h>
   7: //
   8: // Qt Includes
   9: //
  10: #include <QString>
  11: #include <QApplication>
  12: #include <QWidget>
  13: 
  14: #include "MyMainWindow.h"
  15: 
  16: int main(int argc, char ** argv)
  17: {
  18: 	QgsApplication app(argc, argv, true);
  19: 
  20: 	QString myPluginsDir        = "C:/Program Files/qgis1.7.0/plugins";
  21: 	QgsProviderRegistry::instance(myPluginsDir);
  22: 
  23: 	MyMainWindow window;
  24: 	window.show();
  25: 
  26: 	return app.exec();
  27: }

ずいぶんとすっきりしたものになりました。
main()関数内でメインウィンドウ表示以前に行っているのはプロバイダプラグインのパスの登録だけ(20~21行目)になっています。
メインウィンドウのクラス名はMyMainWindowとしています。

そのメインウィンドウクラスですが、まず宣言は以下のようにします。

MyMainWindow.h
   1: #pragma once
   2: 
   3: #include "ui_mainwindow.h"
   4: 
   5: class MyMainWindow :
   6: 	public QMainWindow, private Ui::MainWindow
   7: {
   8: 	Q_OBJECT
   9: 
  10: public:
  11: 	MyMainWindow(void);
  12: 	~MyMainWindow(void);
  13: 
  14: private slots:
  15: 	void onFileOpen();
  16: };
  17: 

先ほど作成したUIファイルのファイル名は”ui_mainwindow.h”として保存し、3行目でインクルードしています。
5~6行目のクラス宣言ではQMainWindowとUIクラスを多重継承しています。
多重継承が気に入らない場合は必須ではありませんが、そうするとdesignerで設定したシグナル/スロットは自分でコードする必要があります(大した手間ではありませんが)。

また、シグナル/スロットを利用するので、クラス宣言内にQ_OBJECTを記述する必要があります。
したがって、mocの作成も行う必要があります。
mocの作成についてはここを参照してください。

クラス定義部は以下のようにします。

MyMainWindow.cpp
   1: #include "MyMainWindow.h"
   2: #include <qgssinglesymbolrenderer.h>
   3: #include <qgsmaplayerregistry.h>
   4: #include <qgsvectorlayer.h>
   5: #include <QtGui>
   6: 
   7: MyMainWindow::MyMainWindow(void)
   8: {
   9: 	setupUi( this );
  10: 
  11: 	centralwidget->enableAntiAliasing(true);
  12: 	centralwidget->setCanvasColor(QColor(255, 192, 255));
  13: 	centralwidget->freeze(false);
  14: 	centralwidget->setVisible(true);
  15:
  16:   // 2.14では以下のコードを追加する必要がある。
  17:   QgsApplication::initQgis();
  18: }
  19: 
  20: 
  21: MyMainWindow::~MyMainWindow(void)
  22: {
  23:     // 2.14では以下のコードを追加する必要がある。
  24:     QgsApplication::exitQgis();
  25: }
  26: 
  27: void MyMainWindow::onFileOpen()
  28: {
  29: 	QString strFName = QFileDialog::getOpenFileName( this, tr("File open"), "c:/", tr("Shape file (*.shp)" ) );
  30: 
  31: 	if ( !strFName.isEmpty() )
  32: 	{
  33: 		QgsVectorLayer * mypLayer = new QgsVectorLayer(strFName, QFileInfo(strFName).baseName(), "ogr" );
  34: 		QgsSingleSymbolRenderer *mypRenderer = new QgsSingleSymbolRenderer(mypLayer->geometryType());
  35: 		QList<QgsMapCanvasLayer> myLayerSet;
  36: 
  37: 		// Add the Vector Layer to the Layer Registry
  38: 		QgsMapLayerRegistry::instance()->addMapLayer(mypLayer, TRUE);
  39: 		// Add the Layer to the Layer Set
  40: 		myLayerSet.append(QgsMapCanvasLayer(mypLayer, TRUE));
  41: 
  42: 		centralwidget->setExtent(mypLayer->extent());
  43: 		centralwidget->setLayerSet(myLayerSet);
  44: 		centralwidget->refresh();
  45: 	}
  46: }

マップキャンバスの初期設定部分をコンストラクタ内に、またレイヤー登録・表示部をonFileOpen()内に記述しています。

以上でメインウィンドウアプリケーションの最低限の実装が完了しました。
実行すると以下のようになります。

実行結果

ここではキャンバスの色をピンキーにしてみました。

以上で最低限のアプリケーション実装は完了ですが、ズームイン・アウト、パンニングのマップツールはAPIで用意されているのでそれらの追加は簡単です。
また、ダイアログの一部としてキャンバスを貼り付けることももちろん可能です。

配布するには

配布するためには依存ライブラリを調べ上げて同梱する必要があろうかと思いますが、これが非常に大変です。
QGIS本体やコアライブラリだけでなく、一緒に配布するプロバイダプラグインが依存するライブラリも調べる必要があります。
最低でもgdalprovider.dll、ogrprovider.dllは同梱したいところで、
そうするとGDALが依存する膨大な量のライブラリを同梱する必要があるということになります。

Windows環境であればOSGeo4Wで大概はそろうかと思いますが、Linuxの場合は、、、。

とりあえず、WindowsであればDependency Walkerで、Linuxであればlddコマンドでこつこつ調べ上げましょう。

アーカイブ