% vim: set textwidth=78 autoindent:
\pagenumbering{arabic}
\setcounter{page}{1}

\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

Σε αυτή την ενότητα σας παραχωρείται ένα μάθημα (tutorial) αρχαρίων για το πως να γράψετε ένα απλό QGIS plugin σε C++.  Είναι βασισμένο σε ένα workshop δημιουργημένο απο τον Dr. Marco Hugentobler . 

Τα QGIS plugins γραμμένα σε C++ είναι δυναμικά συνδεδεμένες βιβλιοθήκες (.so ή και .dll). Συνδέονται με το QGIS κατά τη διάρκεια εκτέλεσης (runtime) όταν ζητείται απο τον Διαχειριστή του plugin (Plugin manager), και επεκτείνουν τη λειτουργικότητα του QGIS  μέσω πρόσβασης στο Γραφικό Περιβάλλον Εργασίας (GUI) του QGIS. Γενικά μπορούν να χωριστούν σε εσωτερικά (core) και εξωτερικά pluggins. 

Τεχνικά ο Διαχειριστής Plugin (Plugin Manager) του QGIS αναζητεί για .so αρχεία στο φάκελο ib/qgis και τα φορτώνει όταν ξεκινάει. Όταν κλείνει τα αφαιρεί πάλι, εκτός απο εκείνα που είναι εξουσιοδοτημένα απο το χρήστη. (βλ. Εγχειρίδιο Χρήστη – User Manual).  Για προσφάτως φορτωμένα plugin
η \method{classFactory} μέθοδος, δημιουργεί ένα αντικείμενο της κλάσης του plugin ενώ η μέθοδος \method{initGui} καλείται για να δείξει τα στοιχεία του Γραφικού Περιβάλλοντος Εργασίας (GUI) στο μενού του pluggin και στην εργαλειοθήκη. Η λειτουργία \method{unload()} του plugin 
χρησιμοποιείται για να αφαιρέσει τα μοιρασμένα στοιχεία του Γραφικού Περιβάλλοντος Εργασίας (GUI) και η ίδια η κλάση του plugin αφαιρείται χρησιμοποιώντας τον class destructor. Για να δημιουργηθεί μια λίστα των plugins, κάθε ένα απο αυτά πρέπει να έχει μερικές εξωτερικές “C” λειτουργίες για περιγραφή και φυσικά τη μέθοδο
\method{classFactory}.

\subsection{1.1 Γιατί C++ και σχετικά με την άδεια χρήσης.}

 Το QGIS είναι γραμμένο σε C++, οπότε είναι λογικό και τα plugin να γράφονται επίσης σε C++. Είναι μία αντικειμενοστραφής γλώσσα προγραμματισμού (OPP) και προτιμάται απο πολλούς προγραμματιστές για τη δημιουργία μεγάλων εφαρμογών.



\subsection{1.2 Προγραμματίζοντας ένα plugin σε QGIS και C++ σε τέσσερα βήματα}

Το παράδειγμα του plugin του καλύπτεται σε αυτό το εγχειρίδιο είναι ένα plugin ενός σημειακού μετατροπέα και επιτειδευμένα είναι απλό. Το plugin, αναζητεί το ενεργό επίπεδο vector στο QGIS,   μετατρέπει όλες τις γωνίες του επιπέδου πληροφορίας (layer) σε σημεία (κρατώντας τις ιδιότητες της βάσης δεδομένων) και μετάφερει τα χαρακτηριστικά των σημείων σε ένα αρχείο κειμένου. Το καινούργιο επίπεδο πληροφορίας (layer) μπορεί μετά να ανοιχτεί στο QGIS χρησιμοποιώντας το plugin του οριοθετημένου κειμένου (dilimited text plugin) (βλ. Εγχηρίδιο χρήστη).

\minisec{Βήμα 1: Βάζοντας το διαχειριστή του plugin να αναγνωρίσει το plugin.}

Σαν πρώτο βήμα δημιουργούμε τα αρχεία \filename{QgsPointConverter.h} και
\filename{QgsPointConverter.cpp}. Έπειτα προσθέτουμε εικονικές μεθόδους  που έχουν «κληρονομήσει» τις ιδιότητές τους απο το QgisPlugin (αλλά αφήστε κενά προς το παρόν), δημιουργούμε τις απαραίτητες εξωτερικές “C” μεθόδους, και ένα αρχείο a .pro (το οποίο είναι ένας Qt μηχανισμός για να δημιουργηθούν εύκολα τα Makefiles. Έπειτα συντάσουμε (compile) τους πηγαίους κώδικες (sources) μετακινούμε τη συνταγμένη βιβλιοθήκη στο φάκελο του plugin Και τη φορτώνουμε στον Διαχειριστη plugin (plugin Manager) του QGIS.

\textbf{α) Δημιουργήστε καινούργια pointconverter.pro αρχεία και προσθέστε }:


% 
%
% 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{β) Δημιουργήστε καινούργιο qgspointconverterplugin.h αρχείο και προσθέστε }:

\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{γ)  Δημιουργήστε καινούργιο qgspointconverterplugin.cpp αρχείο και προσθέστε }:

\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{Βημα 2: Δημιουργήστε ένα εικονίδιο, ένα κουμπί και ένα μενού για το plugin.}

Το βήμα αυτό συμπεριλαμβάνει την πρόσθεση ενός δείκτη στο αντικείμενο QgisInterface μέσα στην κλάση του plugin. Έπειτα δημιουργούμε ένα Qaction και μία λειτουργία callback, το προσθέτουμε στο Γραφικό Περιβάλλον Εργασίας χρησιμοποιώντας το QgisInterface::addToolBarIcon() και QgisInterface::addPluginToMenu()
και τελικά αφαιρούμε το Qaction που βρίσκεται στη μέθοδο \method{unload()}.

\textbf{δ) Ανοίξτε το qgspointconverterplugin.h και επεκτείνετε το υπάρχον περιεχόμενο σε}:

\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{ε) Ανοίξτο το qgspointconverterplugin.cpp πάλι και επεκτείνετε το περιεχόμενο του σε:}:

\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{Βήμα 3ο: Ανάγνωση των σημείων απο το ενεργό επίπεδο πληροφορίας και η εγγραφή του σε  αρχείο κειμένου.}

Για να διαβάσουμε τα χαρακτηριστικα των σημείων απο το ενεργό επίπεδο πληροφορίας πρέπει να υποβάλουμε ένα ερωτημα (query) στο ενεργό επίπεδο και στην τοποθεσία για το νέο αρχείο κειμένου. Έπειτα εναλλάσουμε ανάμεσα σε όλα τα τα χαρακτηριστικά του ενεργού επιπέδου, μετατρέπουμε τις γεωμετρίες (vertices) σε σημεία, ανοίγουμε ένα καινούργιο αρχείο και χρησιμοποιούμε το QtextStream για να εγγράψουμε τις χ και ψ συντεταγμένες μέσα του.

\textbf{στ)  Aνοίξτε το qgspointconverterplugin.h πάλι και επεκτείνετε το υπάρχων περιεχόμενο σε:}

\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{ζ) Ανοίξτε το qgspointconverterplugin.cpp και επεκτείνετε το υπάρχων περιεχόμενο σε:}:

\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}

Στο τέλος εξάγουμε τις ιδότητες απο το ενεργό επίπεδο πληροφορίας χρησιμοποιώντας το QgsVectorDataProvider::fieldNameMap() Για κάθε χραρακτηριστικό εξάγουμε τις τιμές του πεδίου χρησιμοποιώντας το QgsFeature::attributeMap() και προσθέτουμε το περιεχόμενο (χωρισμένο με κόμμα) πίσω απο τις χ και ψ συντεταγμένες πίσω απο κάθε νέο σημείο των χαρακτηριστικών (features). Σε αυτό το βήμα δεν χρειάζεται να γίνει καμία αλλαγή στο\filename{qgspointconverterplugin.h} 

\textbf{η) Ανοίγουμε το qgspointconverterplugin.cpp ξανά και επεκτείνουμε το υπάρχων περιεχόμενο σε:}:

\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{1.3 Περαιτέρω πληροφορίες}

Όπως βλέπετε, χρειάζεστε πληροφορίες απο πολλές διαφορετικές πηγές για να γράψετε QGIS plugins σε C++.  Οι προγραμματιστές plugin πρέπει να γνωρίζουν C++, το περιβάλλον του QGIS plugin όπως επίσης και τις κλάσεις και τα εργαλεία του Qt4. Στην αρχή είναι καλύτερα να μαθαίνετε απο παραδείγματα και να αντιγράφετε τους μηχανισμούς των ήδη υπαρχόντων plugins.  

Υπάρχει απο μία συλλογή απο βιβλιογραφία στο διαδίκτυο που μπορεί να είναι χρήσιμα για τους προγραμματιστές C++ του QGIS.

\begin{itemize}
\item Debugin για τους προγραμματιστές των QGIS plugins: \url{http://www.qgis.org/wiki/How_to_debug_QGIS_Plugins}
\item Βιβλιογραφία για τα API του QGIS: \url{http://svn.qgis.org/api_doc/html/}
\item Βιβλιογραφία για Qt: \url{http://doc.trolltech.com/4.3/index.html}
\end{itemize}

