メインページ/QGIS/coding-compilation guide/plugins writing in cpp/

提供: OSGeo.JP Wiki
移動: 案内検索
% vim: set textwidth=78 autoindent:
 
\section{Writing a QGIS Plugin in C++}\label{cpp_plugin}
 
% when the revision of a section has been finalized, 
% comment out the following line:
% \updatedisclaimer
 
このセクションは、シンプルなQGIS C++プラグインを記述するための初心者向けチュートリアルで、
Dr. Marco Hugentoblerによるワークショップに基づいています。
 
QGIS C++プラグインはダイナミックリンクライブラリ(.soまたは.dll)です。
これらはプラグインマネージャからの要求を受けたとき、起動時にQGISにリンクされ、QGIS GUIを通して
QGISに機能を追加します。一般に、プラグインはコアプラグインと外部プラグインに分類されます。
 
技術的には、QGISのプラグインマネージャが呼び出されると、lib/qgisディレクトリから全ての.soファイルを検索
し、ロードします。ユーザが選択したプラグイン以外は、プラグインマネージャが閉じられたときに再びアンロードさ
れます(ユーザマニュアル参照)。新たにロードされたプラグインでは、\method{classFactory}メソッドによってプラグインクラスのインス
タンスが生成され、またプラグイン内の\method{initGui}メソッドはプラグインメニュー及びツールバーへGUIを表示
するために呼び出されます。プラグイン内の\method{unload()}関数はGUIリソースを解放するために呼び出されま
す。また、プラグインクラスそれ自体は、クラスのデストラクタによって削除されます。各プラグインは\method
{classFactory}はもちろんですが、プラグインマネージャがプラグインを列挙するために、自身の説明を記述するた
めの'C'関数をエクスポートする必要があります。
 
\subsection{なぜC++なのか、ライセンスは?}
 
QGIS自体はC++で記述されているので、プラグインも同様にC++で記述するというのは理にかなっています。
C++はオブジェクト指向(OOP)プログラム言語であり、大規模なアプリケーションの開発に携わっている
多くの開発者に利用されています。
 
QGIS C++プラグインはlibqgis*.soライブラリが提供する機能群を利用します。
これらのライブラリはGNU GPLライセンスなので、QGIS C++プラグインも同様にGPLが適用されます。
これはすなわち、作成されたプラグインはどのような目的にも利用することができ、またそれらを公開する義務を負
わないことを意味します。もし公開する場合は、GPLライセンスの条件に従う必要があります。
 
\subsection{QGIS C++プラグインプログラミングの4つのステップ}
 
このマニュアルでは例として、できる限りシンプルに作成したポイントコンバータプラグインを取り上げます。
このプラグインは、QGISから現在アクティブなベクタレイヤーを探し出して、そのレイヤーの全ての頂点を点要素に変換し(属性は持ち越す)、
最終的には区切り文字で区切られたテキストファイルに書き出します。
新しいレイヤーとしてQGISに読み込むにはdelimited textプラグインを使用します(ユーザマニュアル参照)。
 
\minisec{Step 1: プラグインマネージャに認識させる}
 
まず最初に\filename{QgsPointConverter.h}\filename{QgsPointConverter.cpp}の2つのファイルを作成します。
次に、QgisPluginから継承した仮想関数(ただしここではまだ空の関数にしておきます)、必要なエクスポート'C'関数、
および.proファイル(これはメイクファイルを簡単に作成するためのQtの仕組みです)を作成します。
作成したらソースコードをコンパイルし、できたライブラリをプラグインフォルダへ移動した後、QGISのプラグインマネージャでロードします。
 
 
\textbf{a) Create new pointconverter.pro file and add}:
 
 
% 
%
% Note: the use of qmake / pro files for building plugins 
% is outdated and the reader should be made aware that this 
% chapter is not longer current. Tim will update this 
% chapter in the near future, but in the meantime this
% note serves as a reminder.
%
%
 
 
\begin{verbatim}
#base directory of the qgis installation
QGIS_DIR = /home/marco/src/qgis
 
TEMPLATE = lib
CONFIG = qt
QT += xml qt3support
unix:LIBS += -L/$$QGIS_DIR/lib -lqgis_core -lqgis_gui
INCLUDEPATH += $$QGIS_DIR/src/ui $$QGIS_DIR/src/plugins  $$QGIS_DIR/src/gui \
	       $$QGIS_DIR/src/raster $$QGIS_DIR/src/core $$QGIS_DIR 
SOURCES = qgspointconverterplugin.cpp
HEADERS = qgspointconverterplugin.h
DEST = pointconverterplugin.so
DEFINES += GUI_EXPORT= CORE_EXPORT=
\end{verbatim}
 
\textbf{b) Create new qgspointconverterplugin.h file and add}:
 
\begin{verbatim}
#ifndef QGSPOINTCONVERTERPLUGIN_H
#define QGSPOINTCONVERTERPLUGIN_H
 
#include "qgisplugin.h"
 
/**A plugin that converts vector layers to delimited text point files.
 The vertices of polygon/line type layers are converted to point features*/
class QgsPointConverterPlugin: public QgisPlugin
{
  public:
  QgsPointConverterPlugin(QgisInterface* iface);
  ~QgsPointConverterPlugin();
  void initGui();
  void unload();
 
  private:
  QgisInterface* mIface;
};
#endif
\end{verbatim}
 
\textbf{c) Create new qgspointconverterplugin.cpp file and add}:
 
\begin{verbatim}
#include "qgspointconverterplugin.h"
 
#ifdef WIN32
#define QGISEXTERN extern "C" __declspec( dllexport )
#else
#define QGISEXTERN extern "C"
#endif
 
QgsPointConverterPlugin::QgsPointConverterPlugin(QgisInterface* iface): mIface(iface)
{
}
 
QgsPointConverterPlugin::~QgsPointConverterPlugin()
{
}
 
void QgsPointConverterPlugin::initGui()
{
}
 
void QgsPointConverterPlugin::unload()
{
}
 
QGISEXTERN QgisPlugin* classFactory(QgisInterface* iface)
{
  return new QgsPointConverterPlugin(iface);
}
 
QGISEXTERN QString name()
{
  return "point converter plugin";
}
 
QGISEXTERN QString description()
{
  return "A plugin that converts vector layers to delimited text point files";
}
 
QGISEXTERN QString version()
{
  return "0.00001";
}
 
// Return the type (either UI or MapLayer plugin)
QGISEXTERN int type()
{
  return QgisPlugin::UI;
}
 
// Delete ourself
QGISEXTERN void unload(QgisPlugin* theQgsPointConverterPluginPointer)
{
  delete theQgsPointConverterPluginPointer;
}
\end{verbatim}
 
\minisec{Step 2: プラグインのアイコン、ボタンおよびメニューの作成}
 
このステップではまず、プラグインのクラスにQgisInterfaceオブジェクトへのポインタを追加します。
次にQActionとそれに対応するコールバック(スロット)を作成し、それらをQgisInterface::addToolBarIcon()とQGisInterface::addPluginToMenu()関数を使って
QGIS GUIへ追加します。最後に、\method{unload()}メソッドを用いてQActionを削除します。
 
\textbf{d) Open qgspointconverterplugin.h again and extend existing content to}:
 
\begin{verbatim}
#ifndef QGSPOINTCONVERTERPLUGIN_H
#define QGSPOINTCONVERTERPLUGIN_H
 
#include "qgisplugin.h"
#include <QObject>
 
class QAction;
 
/**A plugin that converts vector layers to delimited text point files.
 The vertices of polygon/line type layers are converted to point features*/
class QgsPointConverterPlugin: public QObject, public QgisPlugin
{
  Q_OBJECT
 
 public:
  QgsPointConverterPlugin(QgisInterface* iface);
  ~QgsPointConverterPlugin();
  void initGui();
  void unload();
 
 private:
  QgisInterface* mIface;
  QAction* mAction;
 
   private slots:
   void convertToPoint();
};
 
#endif
\end{verbatim}
 
\textbf{e) Open qgspointconverterplugin.cpp again and extend existing content to}:
 
\begin{verbatim}
#include "qgspointconverterplugin.h"
#include "qgisinterface.h"
#include <QAction>
 
#ifdef WIN32
#define QGISEXTERN extern "C" __declspec( dllexport )
#else
#define QGISEXTERN extern "C"
#endif
 
QgsPointConverterPlugin::QgsPointConverterPlugin(QgisInterface* iface): \
    mIface(iface), mAction(0)
{
 
}
 
QgsPointConverterPlugin::~QgsPointConverterPlugin()
{
 
}
 
void QgsPointConverterPlugin::initGui()
{
  mAction = new QAction(tr("&Convert to point"), this);
  connect(mAction, SIGNAL(activated()), this, SLOT(convertToPoint()));
  mIface->addToolBarIcon(mAction);
  mIface->addPluginToMenu(tr("&Convert to point"), mAction);
}
 
void QgsPointConverterPlugin::unload()
{
  mIface->removeToolBarIcon(mAction);
  mIface->removePluginMenu(tr("&Convert to point"), mAction);
  delete mAction;
}
 
void QgsPointConverterPlugin::convertToPoint()
{
  qWarning("in method convertToPoint");
}
 
QGISEXTERN QgisPlugin* classFactory(QgisInterface* iface)
{
  return new QgsPointConverterPlugin(iface);
}
 
QGISEXTERN QString name()
{
  return "point converter plugin";
}
 
QGISEXTERN QString description()
{
  return "A plugin that converts vector layers to delimited text point files";
}
 
QGISEXTERN QString version()
{
  return "0.00001";
}
 
// Return the type (either UI or MapLayer plugin)
QGISEXTERN int type()
{
  return QgisPlugin::UI;
}
 
// Delete ourself
QGISEXTERN void unload(QgisPlugin* theQgsPointConverterPluginPointer)
{
  delete theQgsPointConverterPluginPointer;
}
\end{verbatim}
 
 
\minisec{Step 3: アクティブなレイヤーから頂点を読み込み、テキストファイルへ書き出し}
 
アクティブなレイヤーから頂点を読み込むためには、現在のレイヤーと新規作成するテキストファイルの場所を問い合わせる必要があります。
その後、現在のレイヤーの全要素を順次処理して、それらの座標(頂点の)を点に変換し、新規作成したテキストファイルを開いてQTextStreamクラスを用いて
x、y座標を書き込みます。
 
\textbf{f) Open qgspointconverterplugin.h again and extend existing content to}
 
\begin{verbatim}
class QgsGeometry;
class QTextStream;
 
private:
 
void convertPoint(QgsGeometry* geom, const QString& attributeString, \
		  QTextStream& stream) const;
void convertMultiPoint(QgsGeometry* geom, const QString& attributeString, \
		  QTextStream& stream) const;
void convertLineString(QgsGeometry* geom, const QString& attributeString, \
		  QTextStream& stream) const;
void convertMultiLineString(QgsGeometry* geom, const QString& attributeString, \
		  QTextStream& stream) const;
void convertPolygon(QgsGeometry* geom, const QString& attributeString, \
		  QTextStream& stream) const;
void convertMultiPolygon(QgsGeometry* geom, const QString& attributeString, \
		  QTextStream& stream) const;
\end{verbatim}
 
\textbf{g) Open qgspointconverterplugin.cpp again and extend existing content to}:
 
\begin{verbatim}
#include "qgsgeometry.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QTextStream>
 
void QgsPointConverterPlugin::convertToPoint()
{
  qWarning("in method convertToPoint");
  QgsMapLayer* theMapLayer = mIface->activeLayer();
  if(!theMapLayer)
    {
      QMessageBox::information(0, tr("no active layer"), \
      tr("this plugin needs an active point vector layer to make conversions \ 
          to points"), QMessageBox::Ok);
      return;
    }
  QgsVectorLayer* theVectorLayer = dynamic_cast<QgsVectorLayer*>(theMapLayer);
  if(!theVectorLayer)
    {
      QMessageBox::information(0, tr("no vector layer"), \
      tr("this plugin needs an active point vector layer to make conversions \
          to points"), QMessageBox::Ok);
      return;
    }
 
  QString fileName = QFileDialog::getSaveFileName();
  if(!fileName.isNull())
    {
      qWarning("The selected filename is: " + fileName);
      QFile f(fileName);
      if(!f.open(QIODevice::WriteOnly))
      {
	QMessageBox::information(0, "error", "Could not open file", QMessageBox::Ok);
	return;
      }
      QTextStream theTextStream(&f);
      theTextStream.setRealNumberNotation(QTextStream::FixedNotation);
 
      QgsFeature currentFeature;
      QgsGeometry* currentGeometry = 0;
 
      QgsVectorDataProvider* provider = theVectorLayer->dataProvider();
      if(!provider)
      {
          return;
      }
 
      theVectorLayer->select(provider->attributeIndexes(), \
      theVectorLayer->extent(), true, false);
 
      //write header
      theTextStream << "x,y";
      theTextStream << endl;
 
      while(theVectorLayer->nextFeature(currentFeature))
      {
	 QString featureAttributesString;
 
        currentGeometry = currentFeature.geometry();
        if(!currentGeometry)
        {
            continue;
        }
 
        switch(currentGeometry->wkbType())
        {
            case QGis::WKBPoint:
            case QGis::WKBPoint25D:
                convertPoint(currentGeometry, featureAttributesString, \
		theTextStream);
                break;
 
            case QGis::WKBMultiPoint:
            case QGis::WKBMultiPoint25D:
                convertMultiPoint(currentGeometry, featureAttributesString, \
		theTextStream);
                break;
 
            case QGis::WKBLineString:
            case QGis::WKBLineString25D:
                convertLineString(currentGeometry, featureAttributesString, \
		theTextStream);
                break;
 
            case QGis::WKBMultiLineString:
            case QGis::WKBMultiLineString25D:
                convertMultiLineString(currentGeometry, featureAttributesString \
		theTextStream);
                break;
 
            case QGis::WKBPolygon:
            case QGis::WKBPolygon25D:
                convertPolygon(currentGeometry, featureAttributesString, \
		theTextStream);
                break;
 
            case QGis::WKBMultiPolygon:
            case QGis::WKBMultiPolygon25D:
                convertMultiPolygon(currentGeometry, featureAttributesString, \
		theTextStream);
                break;
        }
      }
    }
}
 
//geometry converter functions
void QgsPointConverterPlugin::convertPoint(QgsGeometry* geom, const QString& \
attributeString, QTextStream& stream) const
{
    QgsPoint p = geom->asPoint();
    stream << p.x() << "," << p.y();
    stream << endl;
}
 
void QgsPointConverterPlugin::convertMultiPoint(QgsGeometry* geom, const QString& \
attributeString, QTextStream& stream) const
{
    QgsMultiPoint mp = geom->asMultiPoint();
    QgsMultiPoint::const_iterator it = mp.constBegin();
    for(; it != mp.constEnd(); ++it)
    {
        stream << (*it).x() << "," << (*it).y();
        stream << endl;
    }
}
 
void QgsPointConverterPlugin::convertLineString(QgsGeometry* geom, const QString& \
attributeString, QTextStream& stream) const
{
    QgsPolyline line = geom->asPolyline();
    QgsPolyline::const_iterator it = line.constBegin();
    for(; it != line.constEnd(); ++it)
    {
        stream << (*it).x() << "," << (*it).y();
        stream << endl;
    }
}
 
void QgsPointConverterPlugin::convertMultiLineString(QgsGeometry* geom, const QString& \
attributeString, QTextStream& stream) const
{
    QgsMultiPolyline ml = geom->asMultiPolyline();
    QgsMultiPolyline::const_iterator lineIt = ml.constBegin();
    for(; lineIt != ml.constEnd(); ++lineIt)
    {
        QgsPolyline currentPolyline = *lineIt;
        QgsPolyline::const_iterator vertexIt = currentPolyline.constBegin();
        for(; vertexIt != currentPolyline.constEnd(); ++vertexIt)
        {
            stream << (*vertexIt).x() << "," << (*vertexIt).y();
            stream << endl;
        }
    }
}
 
void QgsPointConverterPlugin::convertPolygon(QgsGeometry* geom, const QString& \
attributeString, QTextStream& stream) const
{
    QgsPolygon polygon = geom->asPolygon();
    QgsPolygon::const_iterator it = polygon.constBegin();
    for(; it != polygon.constEnd(); ++it)
    {
        QgsPolyline currentRing = *it;
        QgsPolyline::const_iterator vertexIt = currentRing.constBegin();
        for(; vertexIt != currentRing.constEnd(); ++vertexIt)
        {
            stream << (*vertexIt).x() << "," << (*vertexIt).y();
            stream << endl;
        }
    }
}
 
void QgsPointConverterPlugin::convertMultiPolygon(QgsGeometry* geom, const QString& \
attributeString, QTextStream& stream) const
{
    QgsMultiPolygon mp = geom->asMultiPolygon();
    QgsMultiPolygon::const_iterator polyIt = mp.constBegin();
    for(; polyIt != mp.constEnd(); ++polyIt)
    {
        QgsPolygon currentPolygon = *polyIt;
        QgsPolygon::const_iterator ringIt = currentPolygon.constBegin();
        for(; ringIt != currentPolygon.constEnd(); ++ringIt)
        {
            QgsPolyline currentPolyline = *ringIt;
            QgsPolyline::const_iterator vertexIt = currentPolyline.constBegin();
            for(; vertexIt != currentPolyline.constEnd(); ++vertexIt)
            {
                stream << (*vertexIt).x() << "," << (*vertexIt).y();
                stream << endl;
            }
        }
    }
}
\end{verbatim}
 
\minisec{Step 4: 要素の属性をテキストファイルへコピー}
 
最後に、QgsVectorDataProvider::fieldNameMap()メソッドを用いてアクティブなレイヤーから属性を取得します。
QgsFeature::attributeMap()メソッドを用いて、各要素からフィールドの値を取得し、その内容を新しい点要素のx、y座標の後ろに追加します(カンマ区切り)。
このステップでは\filename{qgspointconverterplugin.h}に対してはこれ以上変更するところはありません。
 
\textbf{h) Open qgspointconverterplugin.cpp again and extend existing content
to}:
 
\begin{verbatim} 
#include "qgspointconverterplugin.h"
#include "qgisinterface.h"
#include "qgsgeometry.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"
#include <QAction>
#include <QFileDialog>
#include <QMessageBox>
#include <QTextStream>
 
#ifdef WIN32
#define QGISEXTERN extern "C" __declspec( dllexport )
#else
#define QGISEXTERN extern "C"
#endif
 
QgsPointConverterPlugin::QgsPointConverterPlugin(QgisInterface* iface): \
mIface(iface), mAction(0)
{
 
}
 
QgsPointConverterPlugin::~QgsPointConverterPlugin()
{
 
}
 
void QgsPointConverterPlugin::initGui()
{
  mAction = new QAction(tr("&Convert to point"), this);
  connect(mAction, SIGNAL(activated()), this, SLOT(convertToPoint()));
  mIface->addToolBarIcon(mAction);
  mIface->addPluginToMenu(tr("&Convert to point"), mAction);
}
 
void QgsPointConverterPlugin::unload()
{
  mIface->removeToolBarIcon(mAction);
  mIface->removePluginMenu(tr("&Convert to point"), mAction);
  delete mAction;
}
 
void QgsPointConverterPlugin::convertToPoint()
{
  qWarning("in method convertToPoint");
  QgsMapLayer* theMapLayer = mIface->activeLayer();
  if(!theMapLayer)
    {
      QMessageBox::information(0, tr("no active layer"), \
      tr("this plugin needs an active point vector layer to make conversions \
          to points"), QMessageBox::Ok);
      return;
    }
  QgsVectorLayer* theVectorLayer = dynamic_cast<QgsVectorLayer*>(theMapLayer);
  if(!theVectorLayer)
    {
      QMessageBox::information(0, tr("no vector layer"), \
      tr("this plugin needs an active point vector layer to make conversions \
          to points"), QMessageBox::Ok);
      return;
    }
 
  QString fileName = QFileDialog::getSaveFileName();
  if(!fileName.isNull())
    {
      qWarning("The selected filename is: " + fileName);
      QFile f(fileName);
      if(!f.open(QIODevice::WriteOnly))
      {
	QMessageBox::information(0, "error", "Could not open file", QMessageBox::Ok);
	return;
      }
      QTextStream theTextStream(&f);
      theTextStream.setRealNumberNotation(QTextStream::FixedNotation);
 
      QgsFeature currentFeature;
      QgsGeometry* currentGeometry = 0;
 
      QgsVectorDataProvider* provider = theVectorLayer->dataProvider();
      if(!provider)
      {
          return;
      }
 
      theVectorLayer->select(provider->attributeIndexes(), \
      theVectorLayer->extent(), true, false);
 
      //write header
      theTextStream << "x,y";
      QMap<QString, int> fieldMap = provider->fieldNameMap();
      //We need the attributes sorted by index.
      //Therefore we insert them in a second map where key / values are exchanged
      QMap<int, QString> sortedFieldMap;
      QMap<QString, int>::const_iterator fieldIt = fieldMap.constBegin();
      for(; fieldIt != fieldMap.constEnd(); ++fieldIt)
      {
        sortedFieldMap.insert(fieldIt.value(), fieldIt.key());
      }
 
      QMap<int, QString>::const_iterator sortedFieldIt = sortedFieldMap.constBegin();
      for(; sortedFieldIt != sortedFieldMap.constEnd(); ++sortedFieldIt)
      {
          theTextStream << "," << sortedFieldIt.value();
      }
 
      theTextStream << endl;
 
      while(theVectorLayer->nextFeature(currentFeature))
      {
        QString featureAttributesString;
         const QgsAttributeMap& map = currentFeature.attributeMap();
         QgsAttributeMap::const_iterator attributeIt = map.constBegin();
         for(; attributeIt != map.constEnd(); ++attributeIt)
         {
            featureAttributesString.append(",");
            featureAttributesString.append(attributeIt.value().toString());
         }
 
 
        currentGeometry = currentFeature.geometry();
        if(!currentGeometry)
        {
            continue;
        }
 
        switch(currentGeometry->wkbType())
        {
            case QGis::WKBPoint:
            case QGis::WKBPoint25D:
                convertPoint(currentGeometry, featureAttributesString, \
		theTextStream);
                break;
 
            case QGis::WKBMultiPoint:
            case QGis::WKBMultiPoint25D:
                convertMultiPoint(currentGeometry, featureAttributesString, \
		theTextStream);
                break;
 
            case QGis::WKBLineString:
            case QGis::WKBLineString25D:
                convertLineString(currentGeometry, featureAttributesString, \
		theTextStream);
                break;
 
            case QGis::WKBMultiLineString:
            case QGis::WKBMultiLineString25D:
                convertMultiLineString(currentGeometry, featureAttributesString \
		theTextStream);
                break;
 
            case QGis::WKBPolygon:
            case QGis::WKBPolygon25D:
                convertPolygon(currentGeometry, featureAttributesString, \
		theTextStream);
                break;
 
            case QGis::WKBMultiPolygon:
            case QGis::WKBMultiPolygon25D:
                convertMultiPolygon(currentGeometry, featureAttributesString, \
		theTextStream);
                break;
        }
      }
    }
}
 
//geometry converter functions
void QgsPointConverterPlugin::convertPoint(QgsGeometry* geom, const QString& \
attributeString, QTextStream& stream) const
{
    QgsPoint p = geom->asPoint();
    stream << p.x() << "," << p.y();
    stream << attributeString;
    stream << endl;
}
 
void QgsPointConverterPlugin::convertMultiPoint(QgsGeometry* geom, const QString& \
attributeString, QTextStream& stream) const
{
    QgsMultiPoint mp = geom->asMultiPoint();
    QgsMultiPoint::const_iterator it = mp.constBegin();
    for(; it != mp.constEnd(); ++it)
    {
        stream << (*it).x() << "," << (*it).y();
        stream << attributeString;
        stream << endl;
    }
}
 
void QgsPointConverterPlugin::convertLineString(QgsGeometry* geom, const QString& \
attributeString, QTextStream& stream) const
{
    QgsPolyline line = geom->asPolyline();
    QgsPolyline::const_iterator it = line.constBegin();
    for(; it != line.constEnd(); ++it)
    {
        stream << (*it).x() << "," << (*it).y();
        stream << attributeString;
        stream << endl;
    }
}
 
void QgsPointConverterPlugin::convertMultiLineString(QgsGeometry* geom, const QString& \
attributeString, QTextStream& stream) const
{
    QgsMultiPolyline ml = geom->asMultiPolyline();
    QgsMultiPolyline::const_iterator lineIt = ml.constBegin();
    for(; lineIt != ml.constEnd(); ++lineIt)
    {
        QgsPolyline currentPolyline = *lineIt;
        QgsPolyline::const_iterator vertexIt = currentPolyline.constBegin();
        for(; vertexIt != currentPolyline.constEnd(); ++vertexIt)
        {
            stream << (*vertexIt).x() << "," << (*vertexIt).y();
            stream << attributeString;
            stream << endl;
        }
    }
}
 
void QgsPointConverterPlugin::convertPolygon(QgsGeometry* geom, const QString& \
attributeString, QTextStream& stream) const
{
    QgsPolygon polygon = geom->asPolygon();
    QgsPolygon::const_iterator it = polygon.constBegin();
    for(; it != polygon.constEnd(); ++it)
    {
        QgsPolyline currentRing = *it;
        QgsPolyline::const_iterator vertexIt = currentRing.constBegin();
        for(; vertexIt != currentRing.constEnd(); ++vertexIt)
        {
            stream << (*vertexIt).x() << "," << (*vertexIt).y();
            stream << attributeString;
            stream << endl;
        }
    }
}
 
void QgsPointConverterPlugin::convertMultiPolygon(QgsGeometry* geom, const QString& \
attributeString, QTextStream& stream) const
{
    QgsMultiPolygon mp = geom->asMultiPolygon();
    QgsMultiPolygon::const_iterator polyIt = mp.constBegin();
    for(; polyIt != mp.constEnd(); ++polyIt)
    {
        QgsPolygon currentPolygon = *polyIt;
        QgsPolygon::const_iterator ringIt = currentPolygon.constBegin();
        for(; ringIt != currentPolygon.constEnd(); ++ringIt)
        {
            QgsPolyline currentPolyline = *ringIt;
            QgsPolyline::const_iterator vertexIt = currentPolyline.constBegin();
            for(; vertexIt != currentPolyline.constEnd(); ++vertexIt)
            {
                stream << (*vertexIt).x() << "," << (*vertexIt).y();
                stream << attributeString;
                stream << endl;
            }
        }
    }
}
 
QGISEXTERN QgisPlugin* classFactory(QgisInterface* iface)
{
  return new QgsPointConverterPlugin(iface);
}
 
QGISEXTERN QString name()
{
  return "point converter plugin";
}
 
QGISEXTERN QString description()
{
  return "A plugin that converts vector layers to delimited text point files";
}
 
QGISEXTERN QString version()
{
  return "0.00001";
}
 
// Return the type (either UI or MapLayer plugin)
QGISEXTERN int type()
{
  return QgisPlugin::UI;
}
 
// Delete ourself
QGISEXTERN void unload(QgisPlugin* theQgsPointConverterPluginPointer)
{
  delete theQgsPointConverterPluginPointer;
}
 
\end{verbatim}
 
\subsection{より詳しい情報}
 
以上からわかる通り、QGIS C++プラグインを作成するには多くの様々な情報を得なければなりません。
プラグインの製作者はC++、QGISプラグインのインターフェースと同様にQt4のクラス群とツールについても知る必要があります。
最初は例題から学び、既存のプラグインの機能を真似るところから始めるのがいいでしょう。
 
以下にQGIS C++プログラマ向けの有用なオラインドキュメントを集めてみました:
 
\begin{itemize}
\item QGIS Plugin Debugging: \url{http://wiki.qgis.org/qgiswiki/DebuggingPlugins}
\item QGIS API Documentation: \url{http://svn.qgis.org/api_doc/html/}
\item Qt documentation: \url{http://doc.trolltech.com/4.3/index.html}
\end{itemize}