% vim: set textwidth=78 autoindent:

\section{Ein QGIS Plugin in C++ schreiben}\label{cpp_plugin}

% when the revision of a section has been finalized, 
% comment out the following line:
% \updatedisclaimer

In diesem Kapitel wird ein Anfängertutorial vorgestellt, um ein einfaches
QGIS Plugin in C++ zu schreiben. Es basiert auf einem Beispiel von Dr. Marco
Hugentobler.

QGIS C++ Plugins sind dynamisch eingebundene Bibliotheken (.so oder .dll).
Sie werden in QGIS zur Laufzeit eingebunden, wenn sie durch den Plugin
Manager aktiviert werden und erweitern die QGIS Funktionalitäten. Die Plugins
haben Zugriff auf die QGIS GUI und können in Kern- und externe Plugins
unterteilt werden.

Technisch gesehen, schaut der QGIS Plugin Manager unter GNU/Linux im Order
\filename{lib/qgis} nach allen .so Dateien und lädt diese, wenn der Plugin
Manager gestartet wird. Wenn dieser beendet wird, werden auch die .so Dateien
wieder entladen, ausser denen, die mit dem Kontrollkästchen aktiviert wurden.
Bei neu geladenen Plugins erstellt die \method{classFactory} Methode eine
Instanz der Plugin Klasse und die \method{initGui} Methode wird gestartet, um
die GUI Elemente in der Menü- und Werkzeugleiste anzuzeigen. Die
\method{unload()} Funktion des Plugins wird verwendet, um die GUI Elemente
wieder zu löschen. Die Plugin Klasse wird mit Hilfe des 'class destructors'
gelöscht. Um eine Liste der Plugins zu erstellen, muss jedes Plugin ein paar
externe 'C' Funktionen für die Beschreibung und natürlich auch die
\method{classFactory} Methode enthalten.

\subsection{Warum C++ und wie sieht es mit der Lizensierung aus}

QGIS ist in C++ geschrieben, daher macht es Sinn auch die Plugins in C++ zu
schreiben. Es ist eine objektorientierte Sprache (OOP), die von vielen
Entwicklern für die Programmierung umfangreicher Anwendungen bevorzugt wird. 

QGIS C++ Plugins verwenden die Funktionalitäten der libqgis*.so Bibliotheken.
Da diese unter der GNU GPL lizensiert sind, müssen QGIS C++ Plugins auch
unter der GPL lizensiert werden. Das bedeutet, dass das Plugin für jeglichen
Zweck verwendet werden kann und nicht publiziert werden muss. Wenn das Plugin
jedoch veröffentlicht wird, dann muss es unter den Bedingungen der GPL Lizens
stattfinden. 

\subsection{Ein QGIS C++ Plugin in vier Schritten programmieren}

Das Beispielplugin dieser Übung konvertiert Punktdaten und ist einfach
gehalten. Das Plugin sucht nach einem geladenen Vektorlayer in QGIS,
konvertiert alle vorhandenen Stützpunkte der Vektorobjekte in Punkte
inklusive der dazugehörigen Attribute und schreibt diese als ACSII-Tabelle in
eine Datei. Der neue Layer kann dann in QGIS mit dem 'getrennter Text' Plugin
geladen werden (siehe \ref{label_dltext}).

\minisec{Schritt 1: Erkennen des neuen Plugins im Plugin Manager}

Zu Beginn werden die beiden Dateien \filename{QgsPointConverter.h} und
\filename{QgsPointConverter.cpp} erstellt. Dann werden ein paar
virtuelle Methoden, vererbt von QgisPlugin, ergänzt (erstmal aber offen
gelassen), notwendige externe 'C' Methoden und eine .pro Datei, die ein Qt
Mechanismus darstellen, um auf einfache Art Makefiles zu erstellen. Dann
kompileren wir die Quellen, verschieben die kompilierte Bibliothek in den
Pluginordner und laden es mit dem Plugin Manager.  

\textbf{a) Erstellen Sie die Datei pointconverter.pro und fügen hinzu}:

\begin{verbatim}
#Basisordner der 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) Erstellen Sie die Datei qgspointconverterplugin.h und fügen hinzu}:

\begin{verbatim}
#ifndef QGSPOINTCONVERTERPLUGIN_H
#define QGSPOINTCONVERTERPLUGIN_H

#include "qgisplugin.h"

/**Ein Plugin, um Vertices eines Layers in eine ASCII-Tabelle zu schreiben.
Die Vertices einer Vektorlinie oder -fläche werden in eine ASCII-Tabelle
konvertiert.*/
class QgsPointConverterPlugin: public QgisPlugin
{
  public:
  QgsPointConverterPlugin(QgisInterface* iface);
  ~QgsPointConverterPlugin();
  void initGui();
  void unload();
  
  private:
  QgisInterface* mIface;
};
#endif
\end{verbatim}

\textbf{c) Erstellen Sie die Datei qgspointconverterplugin.cpp und fügen
hinzu}:

\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{Schritt 2: Ein Icon, Button und Menüeintrag für das Plugin erstellen}

An dieser Stelle fügen Sie einen Pointer zum QgisInterface Objekt in der
Plugin Klasse hinzu. Danach erstellen Sie eine QAction und eine 'callback'
Funktion (slot), fügen es der QGIS GUI mit Hilfe von
QgisIface::addToolBarIcon() anQgisInterfaced QgisIface::addPluginToMenu()
hinzu und löschen QAction schließlich in der \method{unload()} Methode.

\textbf{d) Öffnen Sie qgspointconverterplugin.h wieder und ergänzen}:

\begin{verbatim}
#ifndef QGSPOINTCONVERTERPLUGIN_H
#define QGSPOINTCONVERTERPLUGIN_H

#include "qgisplugin.h"
#include <QObject>

class QAction;

/**Ein Plugin, um Vertices eines Layers in eine ASCII-Tabelle zu schreiben.
Die Vertices einer Vektorlinie oder -fläche werden in eine ASCII-Tabelle
konvertiert.*/
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) Öffnen Sie qgspointconverterplugin.cpp wieder und ergänzen}:

\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{Schritt 3: Lese Punkte des aktiven Layers und schreibe sie in eine
ASCII-Tabelle}

Um die Vertices aus dem aktiven Layer zu lesen, muss der aktive Layer und der
Ort für die zu erstellende ASCII-Tabelle abgefragt werden. Dann werden nach
und nach alle Objekte des Vektorlayers durchsucht, die Vertices in Punkte
umgewandelt, eine neue Datei geöffnet und mit Hilfe von QTextStream die X-
und Y-Koordinaten hineingeschrieben. 

\textbf{f) Öffnen Sie qgspointconverterplugin.h wieder und ergänzen}

\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) Öffnen Sie qgspointconverterplugin.cpp wieder und ergänzen}:

\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: Kopieren der Objektattribute in die ASCII-Tabelle}

Zum Schluss extrahieren Sie die Attribute aus dem aktiven Layer mit
QgsVectorDataProvider::fieldNameMap(). Für jedes Objekt werden die Werte
mit QgsFeature::attributeMap() extrahiert und der Inhalt Komma-separiert
hinter die X- und Y-Koordinaten geschrieben. Für diesen Schritt ist es nicht
notwendig, weitere Veränderungen in \filename{qgspointconverterplugin.h}
vorzunehmen.

\textbf{h) Öffnen Sie qgspointconverterplugin.cpp wieder und ergänzen}:

\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{Weiterführende Informationen}

Wie zu sehen, brauchen Sie verschiedene Informationen aus unterschiedlichen
Quellen, um ein QGIS Plugin zu schreiben. Programmierer müssen C++ kennen,
die QGIS Programmierschnittstelle sowie die QT4 Klassen und Werkzeuge. Zu
Beginn ist es am einfachsten, von vorhandenen Beispielen zu lernen und
existierende Mechanismen von bereits existierenden Plugins zu kopieren. 

Es gibt eine Vielzahl von Online Dokumentationen, die nützlich für QGIS C++
Programmierer sind:

\begin{itemize}
\item QGIS Plugin Debugging: \url{http://wiki.qgis.org/qgiswiki/DebuggingPlugins}
\item QGIS API Dokumentation: \url{http://svn.qgis.org/api_doc/html/}
\item Qt Dokumentation: \url{http://doc.trolltech.com/4.3/index.html}
\end{itemize}

