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

提供: OSGeo.JP Wiki
移動: 案内検索
(ページの作成: <source lang="latex"> % vim: set textwidth=78 autoindent: \section{Writing a QGIS Plugin in C++}\label{cpp_plugin} % when the revision of a section has been finalized, % comme…)
 
行8: 行8:
 
% \updatedisclaimer
 
% \updatedisclaimer
  
In this section we provide a beginner's tutorial for writing a simple QGIS
+
このセクションは、シンプルなQGIS C++プラグインを記述するための初心者向けチュートリアルで、
C++ plugin. It is based on a workshop held by Dr. Marco Hugentobler.
+
Dr. Marco Hugentoblerによるワークショップに基づいています。
  
QGIS C++ plugins are dynamically linked libraries (.so or .dll). They are
+
QGIS C++プラグインはダイナミックリンクライブラリ(.soまたは.dll)です。
linked to QGIS at runtime when requested in the Plugin Manager, and extend the
+
これらはプラグインマネージャからの要求を受けたとき、起動時にQGISにリンクされ、QGIS GUIを通して
functionality of QGIS via access to the QGIS GUI. In general, they can be devided
+
QGISに機能を追加します。一般に、プラグインはコアプラグインと外部プラグインに分類されます。
into core and external plugins.
+
  
Technically the QGIS Plugin Manager looks in the lib/qgis directory for all
+
技術的には、QGISのプラグインマネージャが呼び出されると、lib/qgisディレクトリから全ての.soファイルを検索
.so files and loads them when it is started. When it is closed they are
+
し、ロードします。ユーザが選択したプラグイン以外は、プラグインマネージャが閉じられたときに再びアンロードさ
unloaded again, except the ones enabled by the user (See User Manual). For newly loaded plugins,
+
れます。新たにロードされたプラグインでは、\method{classFactory}メソッドによってプラグインクラスのインス
the \method{classFactory} method creates an instance of the plugin class and
+
タンスが生成され、またプラグイン内の\method{initGui}メソッドはプラグインメニュー及びツールバーへGUIを表示
the \method{initGui} method of the plugin is called to show the GUI elements
+
するために呼び出されます。プラグイン内の\method{unload()}関数はGUIリソースを開放するために呼び出されま
in the plugin menu and toolbar. The \method{unload()} function of the plugin
+
す。また、プラグインクラスそれ自体は、クラスのデストラクタによって削除されます。各プラグインは\method
is used to remove the allocated GUI elements and the plugin class itself is
+
{classFactory}はもちろんですが、プラグインマネージャがプラグインを列挙するために、自身の説明を記述するた
removed using the class destructor. To list the plugins, each plugin must
+
めの'C'関数をエクスポートする必要があります。
have a few external 'C' functions for description and of course the
+
\method{classFactory} method.
+
  
 
\subsection{Why C++ and what about licensing}
 
\subsection{Why C++ and what about licensing}

2009年9月4日 (金) 02:01時点における版

% 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{Why C++ and what about licensing}
 
QGIS itself is written in C++, so it makes sense to write plugins in C++
as well. It is an object-oriented programming (OOP) language that is prefered 
by many developers for creating large-scale applications.
 
QGIS C++ plugins take advantage of the functionalities provided by the libqgis*.so libraries. 
As these libraries licensed under the GNU GPL, QGIS C++ plugins must also be licenced under 
the GPL. This means that you may use your plugins for any purpose and you are not required to
publish them. If you do publish them however, they must be published under
the conditions of the GPL license. 
 
\subsection{Programming a QGIS C++ Plugin in four steps}
 
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 
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
be loaded into QGIS using the delimited text plugin (see User Manual).
 
\minisec{Step 1: Make the plugin manager recognise the plugin}
 
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}:
 
 
% 
%
% 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}