「メインページ/QGIS/coding-compilation guide/plugins writing in cpp/」の版間の差分

提供: OSGeo.JP Wiki
移動: 案内検索
行35: 行35:
 
わないことを意味します。もし公開する場合は、GPLライセンスの条件に従う必要があります。
 
わないことを意味します。もし公開する場合は、GPLライセンスの条件に従う必要があります。
  
\subsection{Programming a QGIS C++ Plugin in four steps}
+
\subsection{QGIS C++プラグインプログラミングの4つのステップ}
  
The C++ plugin example covered in this manual is a point converter plugin and intentionally kept simple.
+
このマニュアルでは例として、できる限りシンプルに作成したポイントコンバータプラグインを取り上げます。
The plugin searches the active vector layer in QGIS, converts all vertices of
+
このプラグインは、QGISから現在アクティブなベクタレイヤーを探し出して、そのレイヤーの全ての頂点を点要素に変換し(属性は持ち越す)、
the layer's features to point features (keeping the attributes), and finally
+
最終的には区切り文字で区切られたテキストファイルに書き出します。
writes the point features to a delimited text file. The new layer can then
+
新しいレイヤーとしてQGISに読み込むにはdelimited textプラグインを使用します(ユーザマニュアル参照)。
be loaded into QGIS using the delimited text plugin (see User Manual).
+
  
\minisec{Step 1: Make the plugin manager recognise the plugin}
+
\minisec{Step 1: プラグインマネージャに認識させる}
 +
 
 +
まず最初に\filename{QgsPointConverter.h}と\filename{QgsPointConverter.cpp}の2つのファイルを作成します。
 +
次に、QgisPluginから継承した仮想関数(ただしここではまだ空の関数にしておきます)、必要なエクスポート'C'関数、
 +
および.proファイル(これはメイクファイルを簡単に作成するためのQtの仕組みです)を作成します。
 +
作成したらソースコードをコンパイルし、できたライブラリをプラグインフォルダへ移動した後、QGISのプラグインマネージャでロードします。
  
As a first step we create the \filename{QgsPointConverter.h} and
 
\filename{QgsPointConverter.cpp} files. We then add virtual methods inherited
 
from QgisPlugin (but leave them empty for now), create the necessary external 'C'
 
methods, and a .pro file (which is a Qt mechanism to easily create Makefiles).
 
Then we compile the sources, move the compiled library into the plugin folder,
 
and load it in the QGIS Plugin Manager.
 
  
 
\textbf{a) Create new pointconverter.pro file and add}:
 
\textbf{a) Create new pointconverter.pro file and add}:

2009年9月5日 (土) 02:44時点における版

% 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: Create an icon, a button and a menu for the plugin}
 
This step includes adding a pointer to the QgisInterface object in the plugin
class. Then we create a QAction and a callback function (slot), add it to the
QGIS GUI using QgisInterface::addToolBarIcon() and QgisInterface::addPluginToMenu()
and finally remove the QAction in the \method{unload()} method.
 
\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: Read point features from the active layer and write to text file}
 
To read the point features from the active layer we need to query the current
layer and the location for the new text file. Then we iterate through all
features of the current layer, convert the geometries (vertices) to points,
open a new file and use QTextStream to write the x- and y-coordinates
into it.
 
\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: Copy the feature attributes to the text file}
 
At the end we extract the attributes from the active layer using 
QgsVectorDataProvider::fieldNameMap(). For each feature we extract the field 
values using QgsFeature::attributeMap() and add the contents (comma separated) 
behind the x- and y-coordinates for each new point feature. For this step 
there is no need for any furter change in \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{Further information}
 
As you can see, you need information from many different sources to write QGIS C++ 
plugins. Plugin writers need to know C++, the QGIS plugin interface as 
well as Qt4 classes and tools. At the beginning it is best to learn from 
examples and copy the mechanism of existing plugins. 
 
There is a a collection of online documentation that may be usefull for
QGIS C++ programers:
 
\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}