% vim: set textwidth=78 autoindent:

% \section{Writing a QGIS Plugin in C++}\label{cpp_plugin}
\section{Écrire des extensions pour QGIS en C++}\label{cpp_plugin}

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

% In this section we provide a beginner's tutorial for writing a simple QGIS
% C++ plugin. It is based on a workshop held by Dr. Marco Hugentobler. 
Dans cette section nous fournissons un tutoriel pour débutant pour l'écriture d'
extension simple en C++ pour QGIS. Il est basé sur le workshop réalisé par Dr. 
Marco Hugentobler.

% QGIS C++ plugins are dynamically linked libraries (.so or .dll). They are
% linked to QGIS at runtime when requested in the plugin manager and extend the
% functionality of QGIS. They have access to the QGIS GUI and can be devided
% into core and external plugins.
Les extensions C++ de QGIS sont des bibliothèques dynamiquement liées (.so ou .dll). 
Elles sont liées à QGIS pendant son fonctionnement à la demande dans le 
gestionnaire d'extension et étendent les fonctionnalités de QGIS. Ils ont accès à 
l'interface de QGIS et peuvent être divisés en extensions principales et externes.

% Technically the QGIS plugin manager looks in the lib/qgis directory for all
% .so files and loads them when it is started. When it is closed they are
% unloaded again, except the ones with a checked box. For newly loaded plugins,
% the \method{classFactory} method creates an instance of the plugin class and
% the \method{initGui} method of the plugin is called to show the GUI elements
% in the plugin menu and toolbar. The \method{unload()} function of the plugin
% is used to remove the allocated GUI elements and the plugin class itself is
% removed using the class destructor. To list the plugins, each plugin must
% have a few external 'C' functions for description and of course the
% \method{classFactory} method.
Techniquement le gestionnaire d'extension de QGIS cherche tous les fichiers .so 
dans le répertoire lib/qgis et les charge lors du  démarrage. Lors de la 
fermeture, ils sont déchargés, sauf ceux avec une case cochée. Pour les plugins 
nouvellement chargés, la méthode \method{classFactory} du plugin est appelée 
pour afficher l'interface de l'extension dans le menu extension et la barre d'outils. 
La fonction \method{unload()} de l' est utilisée pour enlever les éléments 
de l'interface allouée et la classe d'extension est enlevée en utilisant le 
destructeur de classe. Pour lister les extensions, chaque extension doit avoir 
quelques fonctions 'C' externes pour la description et bien sur la méthode 
\method{classFactory}.

% \subsection{Why C++ and what about licensing}
\subsection{Pourquoi C++ et quelle licence est utilisée}

% QGIS itself is written in C++, so it also makes sense to write plugins in C++
% as well. It is an object-oriented programming (OOP) language that is viewed
% by many developers as a prefered language for creating large-scale
% applications.
QGIS lui-même est écrit en C++, il est donc logique d'écrire des extensions en C++.
C'est un langage orienté-objet (OOP) qui est considéré par beaucoup de 
développeurs comme langage à préférer pour la création d'applications de taille 
importantes.

% QGIS C++ plugins use functionalities of libqgis*.so libraries. As they are
% licensed under GNU GPL, QGIS C++ plugins must be licenced under the GPL, too.
% This means you may use your plugins for any purpose and you are not forced to
% publish them. If you do publish them however, they must be published under
% the conditions of the GPL license. 
Les extensions C++ de QGIS utilisent des fonctionnalités des bibliothèques 
libqgis*.so. Comme elles sont sous licence GNU GPL, les extensions C++ de QGIS 
doivent être aussi sous cette licence. Cela signifie que vous pouvez utiliser 
vos extensions comme vous le souhaitez et que vous n'êtes pas obligé de les 
publier. Cependant si vous les publiez, vous devez les publier sous les 
conditions de la licence GPL.

% \subsection{Programming a QGIS C++ Plugin in four steps}
\subsection{Programmer une extension en C++ pour QGIS en quatre étapes}

% The example plugin is a point converter plugin and intentionally kept simple. 
% The plugin searches the active vector layer in QGIS, converts all vertices of
% the layer features to point features keeping the attributes and finally
% writes the point features into a delimited text file. The new layer can then
% be loaded into QGIS using the delimited text plugin (see Section
% \ref{label_dltext}).
L'extension d'exemple est une extension de conversion de point et reste simple 
intentionnellement. L'extension recherche la couche vecteur active dans QGIS, 
convertit tous les sommets des géométries de la couche en objets ponctuels en 
gardant les attributs et enfin écrit les objets ponctuels dans un fichier csv. 
La nouvelle couche peut alors être chargée dans QGIS en utilisant l'extension de 
délimitation de texte (voir section~\ref{label_dltext}).

% \minisec{Step 1: Make the plugin manager recognise the plugin}
\minisec{Étape 1: Permettre au gestionnaire d'extensions de reconnaitre l'extension}

% As a first step we create the \filename{QgsPointConverter.h} and
% \filename{QgsPointConverter.cpp} files. Then we add virtual methods inherited
% from QgisPlugin (but leave them empty for now), create 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.
Pour commencer nous créons les fichiers \filename{QgsPointConverter.h} et 
\filename{QgsPointConverter.cpp}. Puis nous ajoutons les méthodes héritées de 
QgisPlugin (mais les laissons vides pour l'instant), créons les méthodes 'C' 
externes nécessaires et un fichier .pro, qui est un mécanisme de Qt pour créer 
facilement un Makefiles. Puis nous compilons les sources, déplaçons la 
bibliothèque compilée dans le répertoire des extensions et le chargeons dans le 
gestionnaire d'extensions de QGIS.

% \textbf{a) Create new pointconverter.pro file and add}:
\textbf{a) Créez un nouveau fichier pointconverter.pro et ajoutez}:

\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}:
\textbf{b) Créez un nouveau fichier qgspointconverterplugin.h et ajoutez}:

\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}:
\textbf{b) Créez un nouveau fichier qgspointconverterplugin.cpp et ajoutez}:

\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}
\minisec{Étape 2 : Créer un icône et un menu pour le 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 QgisIface::addToolBarIcon() and QgisIface::addPluginToMenu()
% and finally remove the QAction in the \method{unload()} method.
Cette étape inclut l'ajout d'un pointeur à l'objet QgisInterface dans la classe 
d'extension. Puis nous créons une fonction QAction et un callback (slot), 
l'ajoutons à l'interface de QGIS en utilisant  QgisIface::addToolBarIcon() et
 QgisIface::addPluginToMenu() et enfin nous enlevons QAction dans la méthode 
 \method{unload()}.

% \textbf{d) Open qgspointconverterplugin.h again and extend existing content to}:
\textbf{d) Ouvrez de nouveau le fichier qgspointconverterplugin.h et ajoutez au 
contenu existant} :

\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}:
\textbf{e) Ouvrez de nouveau le fichier qgspointconverterplugin.cpp et ajoutez 
au contenu existant} :

\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}
\minisec{Étape 3: Lire des géométries ponctuelles à partir de la couche active et écrire dans un fichier texte}

% 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.
Pour lire la géométrie ponctuelle à partir de la couche active nous devons 
demander la couche en court et la localisation du fichier texte. Puis nous 
itérons sur toutes les géométries de la couche actuelle, convertissons les 
géométries (sommets) en points, ouvrons un nouveau fichier et utilisons 
QTextStream pour écrire les coordonnées x et y à l'intérieur.

% \textbf{f) Open qgspointconverterplugin.h again and extend existing content to}
\textbf{f) Ouvrez de nouveau qgspointconverterplugin.h et ajoutez ceci au contenu existant}

\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}:
\textbf{g) Ouvrez qgspointconverterplugin.cpp et rajoutez ceci au contenu existant} :

\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}
\minisec{Étape 4 : Copier les attributs des géométries dans le fichier texte}

% 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} 
Finalement, nous récuperons les attributs de la couche active en utilisant 
QgsVectorDataProvider::fieldNameMap(). Pour chaque géométrie nous récupérons les 
valeurs des champs en utilisant QgsFeature::attributeMap() et ajoutons le 
contenu séparé d'une virgule après les coordonnées x et y pour chaque nouvel 
objet ponctuel. Pour cette étape il n'y a pas besoin de changement supplémentaire
 dans le fichier \filename{qgspointconverterplugin.h}.

% \textbf{h) Open qgspointconverterplugin.cpp again and extend existing content
% to}:
\textbf{h) Ouvrez de nouveau le fichier qgspointconverterplugin.cpp et rajoutez 
ceci au contenu}:

\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}
\subsection{Plus d'informations}

% As you can see, you need information from 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. 
Comme vous pouvez le voir, vous avez besoin d'informations à partir de différentes
sources pour écrire des extensions C++ pour QGIS. Les développeurs d'extensions doivent 
connaître le C++, l'interface des extensions de QGIS ainsi que les classes et 
outils Qt4. Au début il est plus enrichissant d'apprendre à partir des exemples 
et de copier les mécanismes d'extensions existants.

% There is a a collection of online documentation that may be usefull for
% QGIS C++ programers:
Il y a un certain nombre de documentations qui peut être utile pour les programmeurs
C++ pour QGIS :

\begin{itemize}
% \item QGIS Plugin Debugging: \url{http://wiki.qgis.org/qgiswiki/DebuggingPlugins}
\item Debuguer des extensions de QGIS : \url{http://wiki.qgis.org/qgiswiki/DebuggingPlugins}
% \item QGIS API Documentation: \url{http://svn.qgis.org/api_doc/html/}
\item Documentation de l'API de QGIS : \url{http://svn.qgis.org/api_doc/html/}
% \item Qt documentation: \url{http://doc.trolltech.com/4.3/index.html}
\item Documentation de Qt : \url{http://doc.trolltech.com/4.3/index.html}
\end{itemize}
