[QGIS Commit] r13985 - in trunk/qgis/src: plugins/wfs providers/wfs

svn_qgis at osgeo.org svn_qgis at osgeo.org
Fri Jul 30 09:01:48 EDT 2010


Author: mhugent
Date: 2010-07-30 13:01:48 +0000 (Fri, 30 Jul 2010)
New Revision: 13985

Modified:
   trunk/qgis/src/plugins/wfs/qgswfssourceselect.cpp
   trunk/qgis/src/plugins/wfs/qgswfssourceselect.h
   trunk/qgis/src/providers/wfs/qgswfsdata.cpp
   trunk/qgis/src/providers/wfs/qgswfsdata.h
   trunk/qgis/src/providers/wfs/qgswfsprovider.cpp
   trunk/qgis/src/providers/wfs/qgswfsprovider.h
Log:
[FEATURE]: WFS-T support (experimental). Additionally ported wfs to network manager (code from Juergen, patch #2892)

Modified: trunk/qgis/src/plugins/wfs/qgswfssourceselect.cpp
===================================================================
--- trunk/qgis/src/plugins/wfs/qgswfssourceselect.cpp	2010-07-29 12:33:04 UTC (rev 13984)
+++ trunk/qgis/src/plugins/wfs/qgswfssourceselect.cpp	2010-07-30 13:01:48 UTC (rev 13985)
@@ -19,20 +19,26 @@
 #include "qgswfssourceselect.h"
 #include "qgsnewhttpconnection.h"
 #include "qgsgenericprojectionselector.h"
-#include "qgshttptransaction.h"
 #include "qgscontexthelp.h"
 #include "qgsproject.h"
 #include "qgscoordinatereferencesystem.h"
 #include "qgslogger.h"
 #include "qgsmapcanvas.h" //for current view extent
+#include "qgsnetworkaccessmanager.h"
+
 #include <QDomDocument>
 #include <QListWidgetItem>
 #include <QMessageBox>
 #include <QSettings>
+#include <QNetworkRequest>
+#include <QNetworkReply>
 
 static const QString WFS_NAMESPACE = "http://www.opengis.net/wfs";
 
-QgsWFSSourceSelect::QgsWFSSourceSelect( QWidget* parent, QgisInterface* iface ): QDialog( parent ), mIface( iface )
+QgsWFSSourceSelect::QgsWFSSourceSelect( QWidget* parent, QgisInterface* iface )
+    : QDialog( parent )
+    , mIface( iface )
+    , mCapabilitiesReply( 0 )
 {
   setupUi( this );
   btnAdd = buttonBox->button( QDialogButtonBox::Ok );
@@ -128,127 +134,165 @@
   return *( crsSet.constBegin() );
 }
 
-int QgsWFSSourceSelect::getCapabilities( const QString& uri, QgsWFSSourceSelect::REQUEST_ENCODING e, std::list<QString>& typenames, std::list< std::list<QString> >& crs, std::list<QString>& titles, std::list<QString>& abstracts )
+void QgsWFSSourceSelect::capabilitiesReplyFinished()
 {
-  switch ( e )
+  if ( mCapabilitiesReply->error() == QNetworkReply::NoError )
   {
-    case QgsWFSSourceSelect::GET:
-      return getCapabilitiesGET( uri, typenames, crs, titles, abstracts );
-    case QgsWFSSourceSelect::POST:
-      return getCapabilitiesPOST( uri, typenames, crs, titles, abstracts );
-    case QgsWFSSourceSelect::SOAP:
-      return getCapabilitiesSOAP( uri, typenames, crs, titles, abstracts );
-  }
-  return 1;
-}
+    QVariant redirect = mCapabilitiesReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
+    if ( !redirect.isNull() )
+    {
+      QgsDebugMsg( "redirecting to " + redirect.toUrl().toString() );
+      QNetworkRequest request( redirect.toUrl() );
+      request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork );
+      request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
 
-int QgsWFSSourceSelect::getCapabilitiesGET( QString uri, std::list<QString>& typenames, std::list< std::list<QString> >& crs, std::list<QString>& titles, std::list<QString>& abstracts )
-{
-  QString request = uri + "SERVICE=WFS&REQUEST=GetCapabilities&VERSION=1.0.0";
+      mCapabilitiesReply->deleteLater();
+      mCapabilitiesReply = QgsNetworkAccessManager::instance()->get( request );
 
-  QByteArray result;
-  QgsHttpTransaction http( request );
-  if ( !http.getSynchronously( result ) )
-  {
-    QMessageBox::critical( 0, tr( "Error" ),
-      tr( "Could not download capabilities document: " ) + http.errorString() );
-    return 1;
-  }
+      connect( mCapabilitiesReply, SIGNAL( finished() ), this, SLOT( capabilitiesReplyFinished() ) );
+      connect( mCapabilitiesReply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( capabilitiesReplyProgress( qint64, qint64 ) ) );
+      return;
+    }
 
-  QDomDocument capabilitiesDocument;
-  QString capabilitiesDocError;
-  if ( !capabilitiesDocument.setContent( result, true, &capabilitiesDocError ) )
-  {
-    QMessageBox::critical( 0, tr( "Error" ),
-      tr( "Capabilities document is not valid: " ) + capabilitiesDocError );
-    return 1;
-  }
+    QByteArray buffer = mCapabilitiesReply->readAll();
 
-  QDomElement doc = capabilitiesDocument.documentElement();
-  if ( doc.tagName() == "ExceptionReport" )
-  {
-    QDomNode ex = doc.firstChild();
-    QString exc = ex.toElement().attribute("exceptionCode", "Exception");
-    QDomElement ext = ex.firstChild().toElement();
-    QMessageBox::critical( 0, tr( "Error" ),
-      exc + ": " + ext.firstChild().nodeValue() );
-    return 1;
-  }
+    QgsDebugMsg( "parsing capabilities: " + buffer );
 
-  //get the <FeatureType> elements
-  QDomNodeList featureTypeList = capabilitiesDocument.elementsByTagNameNS( WFS_NAMESPACE, "FeatureType" );
-  for ( unsigned int i = 0; i < featureTypeList.length(); ++i )
-  {
-    QString tname, title, abstract;
-    QDomElement featureTypeElem = featureTypeList.at( i ).toElement();
-    std::list<QString> featureCRSList; //CRS list for this feature
-
-    //Name
-    QDomNodeList nameList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "Name" );
-    if ( nameList.length() > 0 )
+    QString capabilitiesDocError;
+    QDomDocument capabilitiesDocument;
+    if ( capabilitiesDocument.setContent( buffer, true, &capabilitiesDocError ) )
     {
-      tname = nameList.at( 0 ).toElement().text();
-      //strip away namespace prefixes
-      /* if ( tname.contains( ":" ) )
-       {
-         tname = tname.section( ":", 1, 1 );
-       }*/
-    }
-    //Title
-    QDomNodeList titleList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "Title" );
-    if ( titleList.length() > 0 )
-    {
-      title = titleList.at( 0 ).toElement().text();
-    }
-    //Abstract
-    QDomNodeList abstractList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "Abstract" );
-    if ( abstractList.length() > 0 )
-    {
-      abstract = abstractList.at( 0 ).toElement().text();
-    }
+      QDomElement doc = capabilitiesDocument.documentElement();
+      if ( doc.tagName() != "ExceptionReport" )
+      {
+        std::list<QString> typenames;
+        std::list< std::list<QString> > crs;
+        std::list<QString> titles;
+        std::list<QString> abstracts;
 
-    //DefaultSRS is always the first entry in the feature srs list
-    QDomNodeList defaultCRSList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "DefaultSRS" );
-    if ( defaultCRSList.length() > 0 )
-    {
-      featureCRSList.push_back( defaultCRSList.at( 0 ).toElement().text() );
-    }
+        //get the <FeatureType> elements
+        QDomNodeList featureTypeList = capabilitiesDocument.elementsByTagNameNS( WFS_NAMESPACE, "FeatureType" );
+        for ( unsigned int i = 0; i < featureTypeList.length(); ++i )
+        {
+          QString tname, title, abstract;
+          QDomElement featureTypeElem = featureTypeList.at( i ).toElement();
+          std::list<QString> featureCRSList; //CRS list for this feature
 
-    //OtherSRS
-    QDomNodeList otherCRSList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "OtherSRS" );
-    for ( unsigned int i = 0; i < otherCRSList.length(); ++i )
-    {
-      featureCRSList.push_back( otherCRSList.at( i ).toElement().text() );
-    }
+          //Name
+          QDomNodeList nameList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "Name" );
+          if ( nameList.length() > 0 )
+          {
+            tname = nameList.at( 0 ).toElement().text();
+            //strip away namespace prefixes
+            /* if ( tname.contains( ":" ) )
+               {
+               tname = tname.section( ":", 1, 1 );
+               }*/
+          }
+          //Title
+          QDomNodeList titleList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "Title" );
+          if ( titleList.length() > 0 )
+          {
+            title = titleList.at( 0 ).toElement().text();
+          }
+          //Abstract
+          QDomNodeList abstractList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "Abstract" );
+          if ( abstractList.length() > 0 )
+          {
+            abstract = abstractList.at( 0 ).toElement().text();
+          }
 
-    //Support <SRS> for compatibility with older versions
-    QDomNodeList srsList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "SRS" );
-    for ( unsigned int i = 0; i < srsList.length(); ++i )
-    {
-      featureCRSList.push_back( srsList.at( i ).toElement().text() );
-    }
+          //DefaultSRS is always the first entry in the feature srs list
+          QDomNodeList defaultCRSList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "DefaultSRS" );
+          if ( defaultCRSList.length() > 0 )
+          {
+            featureCRSList.push_back( defaultCRSList.at( 0 ).toElement().text() );
+          }
 
-    crs.push_back( featureCRSList );
-    typenames.push_back( tname );
-    titles.push_back( title );
-    abstracts.push_back( abstract );
-  }
+          //OtherSRS
+          QDomNodeList otherCRSList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "OtherSRS" );
+          for ( unsigned int i = 0; i < otherCRSList.length(); ++i )
+          {
+            featureCRSList.push_back( otherCRSList.at( i ).toElement().text() );
+          }
 
+          //Support <SRS> for compatibility with older versions
+          QDomNodeList srsList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "SRS" );
+          for ( unsigned int i = 0; i < srsList.length(); ++i )
+          {
+            featureCRSList.push_back( srsList.at( i ).toElement().text() );
+          }
 
-  //print out result for a test
-  QgsDebugMsg( result );
+          crs.push_back( featureCRSList );
+          typenames.push_back( tname );
+          titles.push_back( title );
+          abstracts.push_back( abstract );
+        }
 
-  return 0;
-}
+        //insert the available CRS into mAvailableCRS
+        mAvailableCRS.clear();
+        std::list<QString>::const_iterator typeNameIter;
+        std::list< std::list<QString> >::const_iterator crsIter;
+        for ( typeNameIter = typenames.begin(), crsIter = crs.begin(); typeNameIter != typenames.end(); ++typeNameIter, ++crsIter )
+        {
+          std::list<QString> currentCRSList;
+          for ( std::list<QString>::const_iterator it = crsIter->begin(); it != crsIter->end(); ++it )
+          {
+            currentCRSList.push_back( *it );
+          }
+          mAvailableCRS.insert( std::make_pair( *typeNameIter, currentCRSList ) );
+        }
 
-int QgsWFSSourceSelect::getCapabilitiesPOST( const QString& uri, std::list<QString>& typenames, std::list< std::list<QString> >& crs, std::list<QString>& titles, std::list<QString>& abstracts )
-{
-  return 1; //soon...
+        //insert the typenames, titles and abstracts into the tree view
+        std::list<QString>::const_iterator t_it = titles.begin();
+        std::list<QString>::const_iterator n_it = typenames.begin();
+        std::list<QString>::const_iterator a_it = abstracts.begin();
+        for ( ; t_it != titles.end(); ++t_it, ++n_it, ++a_it )
+        {
+          QTreeWidgetItem* newItem = new QTreeWidgetItem();
+          newItem->setText( 0, *t_it );
+          newItem->setText( 1, *n_it );
+          newItem->setText( 2, *a_it );
+          treeWidget->addTopLevelItem( newItem );
+        }
+
+        if ( typenames.size() > 0 )
+        {
+          btnAdd->setEnabled( true );
+          treeWidget->setCurrentItem( treeWidget->topLevelItem( 0 ) );
+          btnChangeSpatialRefSys->setEnabled( true );
+        }
+        else
+        {
+          QMessageBox::information( 0, tr( "No Layers" ), tr( "capabilities document contained no layers." ) );
+          btnAdd->setEnabled( false );
+        }
+      }
+      else
+      {
+        QDomNode ex = doc.firstChild();
+        QString exc = ex.toElement().attribute( "exceptionCode", "Exception" );
+        QDomElement ext = ex.firstChild().toElement();
+        QMessageBox::critical( 0, tr( "Error" ), exc + ": " + ext.firstChild().nodeValue() );
+      }
+    }
+    else
+    {
+      QMessageBox::critical( 0, tr( "Capabilities document is not valid" ), capabilitiesDocError );
+    }
+  }
+  else
+  {
+    QMessageBox::critical( 0, tr( "GetCapabilities Error" ), mCapabilitiesReply->errorString() );
+  }
+
+  btnConnect->setEnabled( true );
+  mCapabilitiesReply->deleteLater();
+  mCapabilitiesReply = 0;
 }
 
-int QgsWFSSourceSelect::getCapabilitiesSOAP( const QString& uri, std::list<QString>& typenames, std::list< std::list<QString> >& crs, std::list<QString>& titles, std::list<QString>& abstracts )
+void QgsWFSSourceSelect::capabilitiesReplyProgress( qint64, qint64 )
 {
-  return 1; //soon...
 }
 
 void QgsWFSSourceSelect::addEntryToServerList()
@@ -296,11 +340,6 @@
   QgsDebugMsg( QString( "url is: %1" ).arg( mUri ) );
 
   //make a GetCapabilities request
-  std::list<QString> typenames;
-  std::list< std::list<QString> > crsList;
-  std::list<QString> titles;
-  std::list<QString> abstracts;
-
   //modify mUri to add '?' or '&' at the end if it is not already there
   if ( !( mUri.contains( "?" ) ) )
   {
@@ -311,51 +350,17 @@
     mUri.append( "&" );
   }
 
-  if ( getCapabilities( mUri, QgsWFSSourceSelect::GET, typenames, crsList, titles, abstracts ) != 0 )
-  {
-    QgsDebugMsg( "error during GetCapabilities request" );
-  }
-
-  //insert the available CRS into mAvailableCRS
-  mAvailableCRS.clear();
-  std::list<QString>::const_iterator typeNameIter;
-  std::list< std::list<QString> >::const_iterator crsIter;
-  for ( typeNameIter = typenames.begin(), crsIter = crsList.begin(); typeNameIter != typenames.end(); ++typeNameIter, ++crsIter )
-  {
-    std::list<QString> currentCRSList;
-    for ( std::list<QString>::const_iterator it = crsIter->begin(); it != crsIter->end(); ++it )
-    {
-      currentCRSList.push_back( *it );
-    }
-    mAvailableCRS.insert( std::make_pair( *typeNameIter, currentCRSList ) );
-  }
-
-  //insert the typenames, titles and abstracts into the tree view
+  btnConnect->setEnabled( false );
   treeWidget->clear();
-  std::list<QString>::const_iterator t_it = titles.begin();
-  std::list<QString>::const_iterator n_it = typenames.begin();
-  std::list<QString>::const_iterator a_it = abstracts.begin();
-  for ( ; t_it != titles.end(); ++t_it, ++n_it, ++a_it )
-  {
-    QTreeWidgetItem* newItem = new QTreeWidgetItem();
-    newItem->setText( 0, *t_it );
-    newItem->setText( 1, *n_it );
-    newItem->setText( 2, *a_it );
-    treeWidget->addTopLevelItem( newItem );
-  }
 
-  if ( typenames.size() > 0 )
-  {
-    btnAdd->setEnabled( true );
-    treeWidget->setCurrentItem( treeWidget->topLevelItem( 0 ) );
-    btnChangeSpatialRefSys->setEnabled( true );
-  }
-  else
-  {
-    btnAdd->setEnabled( false );
-  }
+  QNetworkRequest request( mUri + "SERVICE=WFS&REQUEST=GetCapabilities&VERSION=1.0.0" );
+  request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
+  mCapabilitiesReply = QgsNetworkAccessManager::instance()->get( request );
+  connect( mCapabilitiesReply, SIGNAL( finished() ), this, SLOT( capabilitiesReplyFinished() ) );
+  connect( mCapabilitiesReply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( capabilitiesReplyProgress( qint64, qint64 ) ) );
 }
 
+
 void QgsWFSSourceSelect::addLayer()
 {
   //get selected entry in lstWidget

Modified: trunk/qgis/src/plugins/wfs/qgswfssourceselect.h
===================================================================
--- trunk/qgis/src/plugins/wfs/qgswfssourceselect.h	2010-07-29 12:33:04 UTC (rev 13984)
+++ trunk/qgis/src/plugins/wfs/qgswfssourceselect.h	2010-07-30 13:01:48 UTC (rev 13985)
@@ -23,6 +23,7 @@
 
 class QgisInterface;
 class QgsGenericProjectionSelector;
+class QNetworkReply;
 
 class QgsWFSSourceSelect: public QDialog, private Ui::QgsWFSSourceSelectBase
 {
@@ -30,13 +31,6 @@
 
   public:
 
-    enum REQUEST_ENCODING
-    {
-      GET,
-      POST,
-      SOAP /*Note that this goes also through HTTP POST but additionally uses soap envelope and friends*/
-    };
-
     QgsWFSSourceSelect( QWidget* parent, QgisInterface* iface );
     ~QgsWFSSourceSelect();
 
@@ -50,6 +44,8 @@
     stores the CRS for the typename in the form 'EPSG:XXXX'*/
     std::map<QString, std::list<QString> > mAvailableCRS;
     QAbstractButton* btnAdd;
+    QNetworkReply *mCapabilitiesReply;
+
     void populateConnectionList();
 
     /**Returns the best suited CRS from a set of authority ids
@@ -59,19 +55,6 @@
     @return the authority id of the crs or an empty string in case of error*/
     QString getPreferredCrs( const QSet<QString>& crsSet ) const;
 
-    /**Makes a GetCapabilities and returns the typenamse and crs supported by the server.
-       @param typenames a list of layers provided by the server
-       @param crs a list of crs supported by the server. The place in the list corresponds to the
-       typenames list (means that the crs list at position 0 is a crs for typename at position 0 etc.)
-       @param title title list
-       @param abstract textual descriptions for the types
-       @return 0 in case of success*/
-    int getCapabilities( const QString& uri, QgsWFSSourceSelect::REQUEST_ENCODING e, std::list<QString>& typenames, std::list< std::list<QString> >& crs, std::list<QString>& titles, std::list<QString>& abstracts );
-    //encoding specific methods of getCapabilities
-    int getCapabilitiesGET( QString uri, std::list<QString>& typenames, std::list< std::list<QString> >& crs, std::list<QString>& titles, std::list<QString>& abstracts );
-    int getCapabilitiesPOST( const QString& uri, std::list<QString>& typenames, std::list< std::list<QString> >& crs, std::list<QString>& titles, std::list<QString>& abstracts );
-    int getCapabilitiesSOAP( const QString& uri, std::list<QString>& typenames, std::list< std::list<QString> >& crs, std::list<QString>& titles, std::list<QString>& abstracts );
-
   private slots:
     void addEntryToServerList();
     void modifyEntryOfServerList();
@@ -81,6 +64,8 @@
     void changeCRS();
     void changeCRSFilter();
     void on_cmbConnections_activated( int index );
+    void capabilitiesReplyFinished();
+    void capabilitiesReplyProgress( qint64, qint64 );
 
     void on_buttonBox_helpRequested() { QgsContextHelp::run( metaObject()->className() ); }
 };

Modified: trunk/qgis/src/providers/wfs/qgswfsdata.cpp
===================================================================
--- trunk/qgis/src/providers/wfs/qgswfsdata.cpp	2010-07-29 12:33:04 UTC (rev 13984)
+++ trunk/qgis/src/providers/wfs/qgswfsdata.cpp	2010-07-30 13:01:48 UTC (rev 13985)
@@ -16,18 +16,17 @@
 #include "qgsrectangle.h"
 #include "qgscoordinatereferencesystem.h"
 #include "qgsgeometry.h"
-#include "qgshttptransaction.h"
 #include "qgslogger.h"
+#include "qgsnetworkaccessmanager.h"
 #include <QBuffer>
-#include <QUrl>
 #include <QList>
+#include <QNetworkRequest>
+#include <QNetworkReply>
 #include <QProgressDialog>
 #include <QSet>
 #include <QSettings>
+#include <QUrl>
 
-//just for a test
-//#include <QProgressDialog>
-
 const char NS_SEPARATOR = '?';
 const QString GML_NAMESPACE = "http://www.opengis.net/gml";
 
@@ -35,7 +34,8 @@
   const QString& uri,
   QgsRectangle* extent,
   QgsCoordinateReferenceSystem* srs,
-  QList<QgsFeature*> &features,
+  QMap<int, QgsFeature*> &features,
+  QMap<int, QString > &idMap,
   const QString& geometryAttribute,
   const QMap<QString, QPair<int, QgsField> >& thematicAttributes,
   QGis::WkbType* wkbType )
@@ -44,15 +44,13 @@
     mExtent( extent ),
     mSrs( srs ),
     mFeatures( features ),
+    mIdMap( idMap ),
     mGeometryAttribute( geometryAttribute ),
     mThematicAttributes( thematicAttributes ),
     mWkbType( wkbType ),
     mFinished( false ),
     mFeatureCount( 0 )
 {
-  //qWarning("Name of the geometry attribute is:");
-  //qWarning(mGeometryAttribute.toLocal8Bit().data());
-
   //find out mTypeName from uri
   QStringList arguments = uri.split( "&" );
   QStringList::const_iterator it;
@@ -61,16 +59,17 @@
     if ( it->startsWith( "TYPENAME", Qt::CaseInsensitive ) )
     {
       mTypeName = it->section( "=", 1, 1 );
+      //and strip away namespace prefix
+      QStringList splitList = mTypeName.split( ":" );
+      if ( splitList.size() > 1 )
+      {
+        mTypeName = splitList.at( 1 );
+      }
       QgsDebugMsg( QString( "mTypeName is: %1" ).arg( mTypeName ) );
     }
   }
 
-  QSettings s;
-  mNetworkTimeoutMsec = s.value( "/qgis/networkAndProxy/networkTimeout", "60000" ).toInt();
-
   mEndian = QgsApplication::endian();
-  QObject::connect( &mHttp, SIGNAL( done( bool ) ), this, SLOT( setFinished( bool ) ) );
-  QObject::connect( &mNetworkTimeoutTimer, SIGNAL( timeout() ), this, SLOT( setFinished() ) );
 }
 
 QgsWFSData::~QgsWFSData()
@@ -91,19 +90,12 @@
     mExtent->set( 0, 0, 0, 0 );
   }
 
-  //separate host from query string
-  QUrl requestUrl( mUri );
-  int portNr = requestUrl.port();
-  if ( portNr != -1 )
-  {
-    mHttp.setHost( requestUrl.host(), portNr );
-  }
-  else
-  {
-    mHttp.setHost( requestUrl.host() );
-  }
+  //QUrl requestUrl( mUri );
+  QNetworkRequest request( mUri );
+  QNetworkReply* reply = QgsNetworkAccessManager::instance()->get( request );
 
-  QgsHttpTransaction::applyProxySettings( mHttp, mUri );
+  connect( reply, SIGNAL( finished() ), this, SLOT( setFinished() ) );
+  connect( reply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( handleProgressEvent( qint64, qint64 ) ) );
 
   //find out if there is a QGIS main window. If yes, display a progress dialog
   QProgressDialog* progressDialog = 0;
@@ -113,37 +105,29 @@
   {
     progressDialog = new QProgressDialog( tr( "Loading WFS data" ), tr( "Abort" ), 0, 0, mainWindow );
     progressDialog->setWindowModality( Qt::ApplicationModal );
-    connect( &mHttp, SIGNAL( dataReadProgress( int, int ) ), this, SLOT( handleProgressEvent( int, int ) ) );
     connect( this, SIGNAL( dataReadProgress( int ) ), progressDialog, SLOT( setValue( int ) ) );
     connect( this, SIGNAL( totalStepsUpdate( int ) ), progressDialog, SLOT( setMaximum( int ) ) );
-    connect( progressDialog, SIGNAL( canceled() ), &mHttp, SLOT( abort() ) );
+    connect( progressDialog, SIGNAL( canceled() ), this, SLOT( setFinished() ) );
     progressDialog->show();
   }
 
-  //setup timer
-  mNetworkTimeoutTimer.setSingleShot( true );
-  mNetworkTimeoutTimer.start( mNetworkTimeoutMsec );
-  mHttp.get( requestUrl.path() + "?" + QString( requestUrl.encodedQuery() ) );
-
-
-  //loop to read the data
   QByteArray readData;
   int atEnd = 0;
-  QgsDebugMsg( "Entering loop" );
-  while ( !mFinished || mHttp.bytesAvailable() > 0 )
+  while ( !atEnd )
   {
     if ( mFinished )
     {
       atEnd = 1;
     }
-    if ( mHttp.bytesAvailable() != 0 )
+    readData = reply->readAll();
+    if ( readData.size() > 0 )
     {
-      readData = mHttp.readAll();
       XML_Parse( p, readData.data(), readData.size(), atEnd );
     }
-    qApp->processEvents();
+    QCoreApplication::processEvents();
   }
 
+  delete reply;
   delete progressDialog;
 
   if ( mExtent )
@@ -155,28 +139,23 @@
     }
   }
 
-  return 0; //soon
+  return 0;
 }
 
-void QgsWFSData::setFinished( bool error )
+void QgsWFSData::setFinished( )
 {
-  if ( error )
-  {
-    //qWarning("Finished with error");
-    //qWarning(mHttp.errorString().toLocal8Bit().data());
-  }
-  else
-  {
-    //qWarning("Finished without error");
-  }
   mFinished = true;
 }
 
-void QgsWFSData::handleProgressEvent( int progress, int totalSteps )
+void QgsWFSData::handleProgressEvent( qint64 progress, qint64 totalSteps )
 {
   emit dataReadProgress( progress );
+  if ( totalSteps < 0 )
+  {
+    totalSteps = 0;
+  }
   emit totalStepsUpdate( totalSteps );
-  mNetworkTimeoutTimer.start( mNetworkTimeoutMsec );
+  emit dataProgressAndSteps( progress, totalSteps );
 }
 
 void QgsWFSData::startElement( const XML_Char* el, const XML_Char** attr )
@@ -187,8 +166,16 @@
   {
     mParseModeStack.push( QgsWFSData::coordinate );
     mStringCash.clear();
-    mCoordinateSeparator = readCsFromAttribute( attr );
-    mTupleSeparator = readTsFromAttribute( attr );
+    mCoordinateSeparator = readAttribute( "cs", attr );
+    if ( mCoordinateSeparator.isEmpty() )
+    {
+      mCoordinateSeparator = ",";
+    }
+    mTupleSeparator = readAttribute( "ts", attr );
+    if ( mTupleSeparator.isEmpty() )
+    {
+      mTupleSeparator = " ";
+    }
   }
   else if ( localName == mGeometryAttribute )
   {
@@ -203,6 +190,10 @@
     mCurrentFeature = new QgsFeature( mFeatureCount );
     mParseModeStack.push( QgsWFSData::featureMember );
   }
+  else if ( localName == mTypeName )
+  {
+    mCurrentFeatureId = readAttribute( "fid", attr );
+  }
   else if ( elementName == GML_NAMESPACE + NS_SEPARATOR + "Box" && mParseModeStack.top() == QgsWFSData::boundingBox )
   {
     //read attribute srsName="EPSG:26910"
@@ -211,7 +202,7 @@
     {
       QgsDebugMsg( "error, could not get epsg id" );
     }
-    //qWarning(("epsg id is: " + QString::number(epsgNr)).toLocal8Bit().data());
+
     if ( mSrs )
     {
       if ( !mSrs->createFromOgcWmsCrs( QString( "EPSG:%1" ).arg( epsgNr ) ) )
@@ -337,14 +328,18 @@
 
 
     mCurrentFeature->setGeometryAndOwnership( mCurrentWKB, mCurrentWKBSize );
-    mFeatures << mCurrentFeature;
+    mFeatures.insert( mCurrentFeature->id(), mCurrentFeature );
+    if ( !mCurrentFeatureId.isEmpty() )
+    {
+      mIdMap.insert( mCurrentFeature->id(), mCurrentFeatureId );
+    }
     ++mFeatureCount;
     mParseModeStack.pop();
   }
   else if ( elementName == GML_NAMESPACE + NS_SEPARATOR + "Point" )
   {
     std::list<QgsPoint> pointList;
-    if ( pointsFromCoordinateString( pointList, mStringCash, mCoordinateSeparator, mTupleSeparator ) != 0 )
+    if ( pointsFromCoordinateString( pointList, mStringCash ) != 0 )
     {
       //error
     }
@@ -381,7 +376,7 @@
     //add WKB point to the feature
 
     std::list<QgsPoint> pointList;
-    if ( pointsFromCoordinateString( pointList, mStringCash, mCoordinateSeparator, mTupleSeparator ) != 0 )
+    if ( pointsFromCoordinateString( pointList, mStringCash ) != 0 )
     {
       //error
     }
@@ -414,7 +409,7 @@
   else if ( elementName == GML_NAMESPACE + NS_SEPARATOR + "LinearRing" )
   {
     std::list<QgsPoint> pointList;
-    if ( pointsFromCoordinateString( pointList, mStringCash, mCoordinateSeparator, mTupleSeparator ) != 0 )
+    if ( pointsFromCoordinateString( pointList, mStringCash ) != 0 )
     {
       //error
     }
@@ -508,34 +503,20 @@
   return 2;
 }
 
-QString QgsWFSData::readCsFromAttribute( const XML_Char** attr ) const
+QString QgsWFSData::readAttribute( const QString& attributeName, const XML_Char** attr ) const
 {
   int i = 0;
   while ( attr[i] != NULL )
   {
-    if ( strcmp( attr[i], "cs" ) == 0 )
+    if ( attributeName.compare( attr[i] ) == 0 )
     {
       return QString( attr[i+1] );
     }
     ++i;
   }
-  return ",";
+  return QString();
 }
 
-QString QgsWFSData::readTsFromAttribute( const XML_Char** attr ) const
-{
-  int i = 0;
-  while ( attr[i] != NULL )
-  {
-    if ( strcmp( attr[i], "ts" ) == 0 )
-    {
-      return QString( attr[i+1] );
-    }
-    ++i;
-  }
-  return " ";
-}
-
 int QgsWFSData::createBBoxFromCoordinateString( QgsRectangle* bb, const QString& coordString ) const
 {
   if ( !bb )
@@ -544,9 +525,7 @@
   }
 
   std::list<QgsPoint> points;
-  //qWarning("string is: ");
-  //qWarning(coordString.toLocal8Bit().data());
-  if ( pointsFromCoordinateString( points, coordString, mCoordinateSeparator, mTupleSeparator ) != 0 )
+  if ( pointsFromCoordinateString( points, coordString ) != 0 )
   {
     return 2;
   }
@@ -562,10 +541,10 @@
   return 0;
 }
 
-int QgsWFSData::pointsFromCoordinateString( std::list<QgsPoint>& points, const QString& coordString, const QString& cs, const QString& ts ) const
+int QgsWFSData::pointsFromCoordinateString( std::list<QgsPoint>& points, const QString& coordString ) const
 {
   //tuples are separated by space, x/y by ','
-  QStringList tuples = coordString.split( ts, QString::SkipEmptyParts );
+  QStringList tuples = coordString.split( mTupleSeparator, QString::SkipEmptyParts );
   QStringList tuples_coordinates;
   double x, y;
   bool conversionSuccess;
@@ -573,7 +552,7 @@
   QStringList::const_iterator tupleIterator;
   for ( tupleIterator = tuples.constBegin(); tupleIterator != tuples.constEnd(); ++tupleIterator )
   {
-    tuples_coordinates = tupleIterator->split( cs, QString::SkipEmptyParts );
+    tuples_coordinates = tupleIterator->split( mCoordinateSeparator, QString::SkipEmptyParts );
     if ( tuples_coordinates.size() < 2 )
     {
       continue;

Modified: trunk/qgis/src/providers/wfs/qgswfsdata.h
===================================================================
--- trunk/qgis/src/providers/wfs/qgswfsdata.h	2010-07-29 12:33:04 UTC (rev 13984)
+++ trunk/qgis/src/providers/wfs/qgswfsdata.h	2010-07-30 13:01:48 UTC (rev 13985)
@@ -15,8 +15,6 @@
 #ifndef QGSWFSDATA_H
 #define QGSWFSDATA_H
 
-#include <QHttp>
-#include <QTimer>
 #include <expat.h>
 #include "qgis.h"
 #include "qgsapplication.h"
@@ -40,7 +38,8 @@
       const QString& uri,
       QgsRectangle* extent,
       QgsCoordinateReferenceSystem* srs,
-      QList<QgsFeature*> &features,
+      QMap<int, QgsFeature* > &features,
+      QMap<int, QString > &idMap,
       const QString& geometryAttribute,
       const QMap<QString, QPair<int, QgsField> >& thematicAttributes,
       QGis::WkbType* wkbType );
@@ -53,18 +52,18 @@
        @param features the features of the layer
     @return 0 in case of success*/
     int getWFSData();
-    /**Returns a pointer to the internal QHttp object (mainly for the purpose of making singal/slot connections*/
-    const QHttp* http() const {return &mHttp;}
 
   private slots:
-    void setFinished( bool error = true );
+    void setFinished();
 
     /**Takes progress value and total steps and emit signals 'dataReadProgress' and 'totalStepUpdate'*/
-    void handleProgressEvent( int progress, int totalSteps );
+    void handleProgressEvent( qint64 progress, qint64 totalSteps );
 
   signals:
     void dataReadProgress( int progress );
     void totalStepsUpdate( int totalSteps );
+    //also emit signal with progress and totalSteps together (this is better for the status message)
+    void dataProgressAndSteps( int progress, int totalSteps );
 
   private:
 
@@ -108,12 +107,9 @@
        @param attr attribute strings
        @return 0 in case of success*/
     int readEpsgFromAttribute( int& epsgNr, const XML_Char** attr ) const;
-    /**Reads the 'cs' (coordinate separator) attribute.
-     @return the cs attribute value or the default value ","*/
-    QString readCsFromAttribute( const XML_Char** attr ) const;
-    /**Reads the 'ts' (tuple separator) attribute.
-       @return the ts attribute value or the devault value " "*/
-    QString readTsFromAttribute( const XML_Char** attr ) const;
+    /**Reads attribute as string
+      @return attribute value or an empty string if no such attribute*/
+    QString readAttribute( const QString& attributeName, const XML_Char** attr ) const;
     /**Creates a rectangle from a coordinate string.
      @return 0 in case of success*/
     int createBBoxFromCoordinateString( QgsRectangle* bb, const QString& coordString ) const;
@@ -123,7 +119,8 @@
        @param cs coortinate separator
        @param ts tuple separator
        @return 0 in case of success*/
-    int pointsFromCoordinateString( std::list<QgsPoint>& points, const QString& coordString, const QString& cs, const QString& ts ) const;
+    int pointsFromCoordinateString( std::list<QgsPoint>& points, const QString& coordString ) const;
+
     int getPointWKB( unsigned char** wkb, int* size, const QgsPoint& ) const;
     int getLineWKB( unsigned char** wkb, int* size, const std::list<QgsPoint>& lineCoordinates ) const;
     int getRingWKB( unsigned char** wkb, int* size, const std::list<QgsPoint>& ringCoordinates ) const;
@@ -149,20 +146,21 @@
     /**Source srs of the layer*/
     QgsCoordinateReferenceSystem* mSrs;
     /**The features of the layer*/
-    QList<QgsFeature*> &mFeatures;
+    QMap<int, QgsFeature* > &mFeatures;
+    /**Stores the relation between provider ids and WFS server ids*/
+    QMap<int, QString > &mIdMap;
     /**Name of geometry attribute*/
     QString mGeometryAttribute;
     const QMap<QString, QPair<int, QgsField> > &mThematicAttributes;
     QGis::WkbType* mWkbType;
     /**True if the request is finished*/
     bool mFinished;
-    /**The HTTP client object*/
-    QHttp mHttp;
     /**Keep track about the most important nested elements*/
     std::stack<parseMode> mParseModeStack;
     /**This contains the character data if an important element has been encountered*/
     QString mStringCash;
     QgsFeature* mCurrentFeature;
+    QString mCurrentFeatureId;
     int mFeatureCount;
     /**The total WKB for a feature*/
     unsigned char* mCurrentWKB;
@@ -179,8 +177,6 @@
     QString mCoordinateSeparator;
     /**Tuple separator for coordinate strings. Usually " " */
     QString mTupleSeparator;
-    int mNetworkTimeoutMsec;
-    QTimer mNetworkTimeoutTimer;
 };
 
 #endif

Modified: trunk/qgis/src/providers/wfs/qgswfsprovider.cpp
===================================================================
--- trunk/qgis/src/providers/wfs/qgswfsprovider.cpp	2010-07-29 12:33:04 UTC (rev 13984)
+++ trunk/qgis/src/providers/wfs/qgswfsprovider.cpp	2010-07-30 13:01:48 UTC (rev 13985)
@@ -15,19 +15,25 @@
  *                                                                         *
  ***************************************************************************/
 
+#define WFS_THRESHOLD 200
+
 #include "qgsapplication.h"
 #include "qgsfeature.h"
 #include "qgsfield.h"
 #include "qgsgeometry.h"
-#include "qgshttptransaction.h"
 #include "qgscoordinatereferencesystem.h"
 #include "qgswfsdata.h"
 #include "qgswfsprovider.h"
 #include "qgsspatialindex.h"
 #include "qgslogger.h"
+#include "qgsnetworkaccessmanager.h"
 #include <QDomDocument>
+#include <QMessageBox>
 #include <QDomNodeList>
+#include <QNetworkRequest>
+#include <QNetworkReply>
 #include <QFile>
+#include <QUrl>
 #include <QWidget>
 #include <cfloat>
 
@@ -38,12 +44,18 @@
 static const QString GML_NAMESPACE = "http://www.opengis.net/gml";
 
 QgsWFSProvider::QgsWFSProvider( const QString& uri )
-    : QgsVectorDataProvider( uri ), mUseIntersect( false ), mSourceCRS( 0 ), mFeatureCount( 0 ), mValid( true )
+    : QgsVectorDataProvider( uri ),
+    mNetworkRequestFinished( true ),
+    mUseIntersect( false ),
+    mSourceCRS( 0 ),
+    mFeatureCount( 0 ),
+    mValid( true )
 {
   mSpatialIndex = new QgsSpatialIndex;
   if ( getFeature( uri ) == 0 )
   {
     mValid = true;
+    getLayerCapabilities();
 
     //set spatial filter to the whole extent
     //select(mExtent, false); //MH TODO: fix this in provider0_9-branch
@@ -191,7 +203,7 @@
   if ( mEncoding == QgsWFSProvider::FILE )
   {
     //guess geometry attribute and other attributes from schema or from .gml file
-    if ( describeFeatureTypeFile( uri, geometryAttribute, mFields ) != 0 )
+    if ( describeFeatureTypeFile( uri, mGeometryAttribute, mFields ) != 0 )
     {
       return 1;
     }
@@ -200,7 +212,7 @@
   {
     QString describeFeatureUri = uri;
     describeFeatureUri.replace( QString( "GetFeature" ), QString( "DescribeFeatureType" ) );
-    if ( describeFeatureType( describeFeatureUri, geometryAttribute, mFields ) != 0 )
+    if ( describeFeatureType( describeFeatureUri, mGeometryAttribute, mFields ) != 0 )
     {
       return 1;
     }
@@ -208,63 +220,380 @@
 
   if ( mEncoding == QgsWFSProvider::GET )
   {
-    return getFeatureGET( uri, geometryAttribute );
+    return getFeatureGET( uri, mGeometryAttribute );
   }
   else//local file
   {
-    return getFeatureFILE( uri, geometryAttribute ); //read the features from disk
+    return getFeatureFILE( uri, mGeometryAttribute ); //read the features from disk
   }
 }
 
-int QgsWFSProvider::describeFeatureType( const QString& uri, QString& geometryAttribute, QgsFieldMap& fields )
+bool QgsWFSProvider::addFeatures( QgsFeatureList &flist )
 {
-  switch ( mEncoding )
+  //create <Transaction> xml
+  QDomDocument transactionDoc;
+  QDomElement transactionElem = createTransactionElement( transactionDoc );
+  transactionDoc.appendChild( transactionElem );
+
+  //find out typename from uri and strip namespace prefix
+  QString tname = typeNameFromUrl();
+  if ( tname.isNull() )
   {
-    case QgsWFSProvider::GET:
-      return describeFeatureTypeGET( uri, geometryAttribute, fields );
-    case QgsWFSProvider::POST:
-      return describeFeatureTypePOST( uri, geometryAttribute, fields );
-    case QgsWFSProvider::SOAP:
-      return describeFeatureTypeSOAP( uri, geometryAttribute, fields );
-    case QgsWFSProvider::FILE:
-      return describeFeatureTypeFile( uri, geometryAttribute, fields );
+    return false;
   }
-  return 1;
+  removeNamespacePrefix( tname );
+
+  //Add the features
+  QgsFeatureList::iterator featureIt = flist.begin();
+  for ( ; featureIt != flist.end(); ++featureIt )
+  {
+    //Insert element
+    QDomElement insertElem = transactionDoc.createElementNS( "http://www.opengis.net/wfs", "Insert" );
+    transactionElem.appendChild( insertElem );
+
+    QDomElement featureElem = transactionDoc.createElementNS( mWfsNamespace, tname );
+
+    //add thematic attributes
+    QgsAttributeMap featureAttributes = featureIt->attributeMap();
+    QgsFieldMap::const_iterator fieldIt = mFields.constBegin();
+    for ( ; fieldIt != mFields.constEnd(); ++fieldIt )
+    {
+      QgsAttributeMap::const_iterator valueIt = featureAttributes.find( fieldIt.key() );
+      if ( valueIt != featureAttributes.constEnd() )
+      {
+        QDomElement fieldElem = transactionDoc.createElementNS( mWfsNamespace, fieldIt.value().name() );
+        QDomText fieldText = transactionDoc.createTextNode( valueIt.value().toString() );
+        fieldElem.appendChild( fieldText );
+        featureElem.appendChild( fieldElem );
+      }
+    }
+
+    //add geometry column (as gml)
+    QDomElement geomElem = transactionDoc.createElementNS( mWfsNamespace, mGeometryAttribute );
+    QDomElement gmlElem = createGeometryElem( featureIt->geometry(), transactionDoc );
+    if ( !gmlElem.isNull() )
+    {
+      geomElem.appendChild( gmlElem );
+      featureElem.appendChild( geomElem );
+    }
+
+    insertElem.appendChild( featureElem );
+  }
+
+  QDomDocument serverResponse;
+  bool success = sendTransactionDocument( transactionDoc, serverResponse );
+  if ( !success )
+  {
+    return false;
+  }
+
+  if ( transactionSuccess( serverResponse ) )
+  {
+    //transaction successful. Add the features to mSpatialIndex
+    if ( mSpatialIndex )
+    {
+      QStringList idList = insertedFeatureIds( serverResponse );
+      QStringList::const_iterator idIt = idList.constBegin();
+      featureIt = flist.begin();
+
+      for ( ; idIt != idList.constEnd() && featureIt != flist.end(); ++idIt, ++featureIt )
+      {
+        int newId = findNewKey();
+        featureIt->setFeatureId( newId );
+        mFeatures.insert( newId, new QgsFeature( *featureIt ) );
+        mIdMap.insert( newId, *idIt );
+        mSpatialIndex->insertFeature( *featureIt );
+        mFeatureCount = mFeatures.size();
+      }
+      return true;
+    }
+  }
+  else
+  {
+    handleException( serverResponse );
+    return false;
+  }
 }
 
-int QgsWFSProvider::getFeatureGET( const QString& uri, const QString& geometryAttribute )
+bool QgsWFSProvider::deleteFeatures( const QgsFeatureIds &id )
 {
-#if 0 //the old and slower method with DOM
-  //assemble request string
-  QString request = uri /*+ "&OUTPUTFORMAT=gml3"*/; //use gml2 as it is supported by most wfs servers
-  QByteArray result;
-  QgsHttpTransaction http( request );
-  http.getSynchronously( result );
+  if ( id.size() < 1 )
+  {
+    return true;
+  }
 
-  QDomDocument getFeatureDocument;
-  if ( !getFeatureDocument.setContent( result, true ) )
+  //find out typename from uri and strip namespace prefix
+  QString tname = typeNameFromUrl();
+  if ( tname.isNull() )
   {
-    return 1; //error
+    return false;
   }
 
-  QDomElement featureCollectionElement = getFeatureDocument.documentElement();
+  //create <Transaction> xml
+  QDomDocument transactionDoc;
+  QDomElement transactionElem = createTransactionElement( transactionDoc );
+  transactionDoc.appendChild( transactionElem );
+  //delete element
+  QDomElement deleteElem = transactionDoc.createElementNS( "http://www.opengis.net/wfs", "Delete" );
+  deleteElem.setAttribute( "typeName", tname );
+  QDomElement filterElem = transactionDoc.createElementNS( "http://www.opengis.net/ogc", "Filter" );
 
-  //get and set Extent
-  if ( getExtentFromGML2( &mExtent, featureCollectionElement ) != 0 )
+
+  QgsFeatureIds::const_iterator idIt = id.constBegin();
+  for ( ; idIt != id.constEnd(); ++idIt )
   {
-    return 3;
+    //find out feature id
+    QMap< int, QString >::const_iterator fidIt = mIdMap.find( *idIt );
+    if ( fidIt == mIdMap.constEnd() )
+    {
+      continue;
+    }
+    QDomElement featureIdElem = transactionDoc.createElementNS( "http://www.opengis.net/ogc", "FeatureId" );
+    featureIdElem.setAttribute( "fid", fidIt.value() );
+    filterElem.appendChild( featureIdElem );
   }
 
-  setCRSFromGML2( featureCollectionElement );
+  deleteElem.appendChild( filterElem );
+  transactionElem.appendChild( deleteElem );
 
-  if ( getFeaturesFromGML2( featureCollectionElement, geometryAttribute ) != 0 )
+  QDomDocument serverResponse;
+  bool success = sendTransactionDocument( transactionDoc, serverResponse );
+  if ( !success )
   {
-    return 4;
+    return false;
   }
 
-  return 0;
-#endif
+  if ( transactionSuccess( serverResponse ) )
+  {
+    idIt = id.constBegin();
+    for ( ; idIt != id.constEnd(); ++idIt )
+    {
+      QMap<int, QgsFeature* >::iterator fIt = mFeatures.find( *idIt );
+      if ( fIt != mFeatures.end() )
+      {
+        if ( mSpatialIndex )
+        {
+          mSpatialIndex->deleteFeature( *fIt.value() );
+        }
+        delete fIt.value();
+        mFeatures.remove( *idIt );
+      }
+    }
+    return true;
+  }
+  else
+  {
+    handleException( serverResponse );
+    return false;
+  }
+}
 
+bool QgsWFSProvider::changeGeometryValues( QgsGeometryMap & geometry_map )
+{
+  //find out typename from uri and strip namespace prefix
+  QString tname = typeNameFromUrl();
+  if ( tname.isNull() )
+  {
+    return false;
+  }
+
+  //create <Transaction> xml
+  QDomDocument transactionDoc;
+  QDomElement transactionElem = createTransactionElement( transactionDoc );
+  transactionDoc.appendChild( transactionElem );
+
+  QgsGeometryMap::iterator geomIt = geometry_map.begin();
+  for ( ; geomIt != geometry_map.end(); ++geomIt )
+  {
+    //find out feature id
+    QMap< int, QString >::const_iterator fidIt = mIdMap.find( geomIt.key() );
+    if ( fidIt == mIdMap.constEnd() )
+    {
+      continue;
+    }
+
+    QDomElement updateElem = transactionDoc.createElementNS( "http://www.opengis.net/wfs", "Update" );
+    updateElem.setAttribute( "typeName", tname );
+    //Property
+    QDomElement propertyElem = transactionDoc.createElementNS( "http://www.opengis.net/wfs", "Property" );
+    QDomElement nameElem = transactionDoc.createElementNS( "http://www.opengis.net/wfs", "Name" );
+    QDomText nameText = transactionDoc.createTextNode( mGeometryAttribute );
+    nameElem.appendChild( nameText );
+    propertyElem.appendChild( nameElem );
+    QDomElement valueElem = transactionDoc.createElementNS( "http://www.opengis.net/wfs", "Value" );
+    QDomElement gmlElem = createGeometryElem( &geomIt.value(), transactionDoc );
+    valueElem.appendChild( gmlElem );
+    propertyElem.appendChild( valueElem );
+    updateElem.appendChild( propertyElem );
+
+    //filter
+    QDomElement filterElem = transactionDoc.createElementNS( "http://www.opengis.net/ogc", "Filter" );
+    QDomElement featureIdElem = transactionDoc.createElementNS( "http://www.opengis.net/ogc", "FeatureId" );
+    featureIdElem.setAttribute( "fid", fidIt.value() );
+    filterElem.appendChild( featureIdElem );
+    updateElem.appendChild( filterElem );
+
+    transactionElem.appendChild( updateElem );
+  }
+
+  QDomDocument serverResponse;
+  bool success = sendTransactionDocument( transactionDoc, serverResponse );
+  if ( !success )
+  {
+    return false;
+  }
+
+  if ( transactionSuccess( serverResponse ) )
+  {
+    geomIt = geometry_map.begin();
+    for ( ; geomIt != geometry_map.end(); ++geomIt )
+    {
+      QMap<int, QgsFeature* >::iterator fIt = mFeatures.find( geomIt.key() );
+      if ( fIt == mFeatures.end() )
+      {
+        continue;
+      }
+      QgsFeature* currentFeature = fIt.value();
+      if ( !currentFeature )
+      {
+        continue;
+      }
+
+      if ( mSpatialIndex )
+      {
+        mSpatialIndex->deleteFeature( *currentFeature );
+        fIt.value()->setGeometry( geomIt.value() );
+        mSpatialIndex->insertFeature( *currentFeature );
+      }
+    }
+    return true;
+  }
+  else
+  {
+    handleException( serverResponse );
+    return false;
+  }
+}
+
+bool QgsWFSProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
+{
+  //find out typename from uri and strip namespace prefix
+  QString tname = typeNameFromUrl();
+  if ( tname.isNull() )
+  {
+    return false;
+  }
+
+  const QgsFieldMap& fieldMap = fields();
+
+  //create <Transaction> xml
+  QDomDocument transactionDoc;
+  QDomElement transactionElem = createTransactionElement( transactionDoc );
+  transactionDoc.appendChild( transactionElem );
+
+  QgsChangedAttributesMap::const_iterator attIt = attr_map.constBegin();
+  for ( ; attIt != attr_map.constEnd(); ++attIt )
+  {
+    //find out wfs server feature id
+    QMap< int, QString >::const_iterator fidIt = mIdMap.find( attIt.key() );
+    if ( fidIt == mIdMap.constEnd() )
+    {
+      continue;
+    }
+
+    QDomElement updateElem = transactionDoc.createElementNS( "http://www.opengis.net/wfs", "Update" );
+    updateElem.setAttribute( "typeName", tname );
+
+    QgsAttributeMap::const_iterator attMapIt = attIt.value().constBegin();
+    for ( ; attMapIt != attIt.value().constEnd(); ++attMapIt )
+    {
+      QString fieldName;
+      QgsFieldMap::const_iterator fieldIt = fieldMap.find( attMapIt.key() );
+      if ( fieldIt == fieldMap.constEnd() )
+      {
+        continue;
+      }
+      fieldName = fieldIt.value().name();
+
+      QDomElement propertyElem = transactionDoc.createElementNS( "http://www.opengis.net/wfs", "Property" );
+
+      QDomElement nameElem = transactionDoc.createElementNS( "http://www.opengis.net/wfs", "Name" );
+      QDomText nameText = transactionDoc.createTextNode( fieldName );
+      nameElem.appendChild( nameText );
+      propertyElem.appendChild( nameElem );
+
+      QDomElement valueElem = transactionDoc.createElementNS( "http://www.opengis.net/wfs", "Value" );
+      QDomText valueText = transactionDoc.createTextNode( attMapIt.value().toString() );
+      valueElem.appendChild( valueText );
+      propertyElem.appendChild( valueElem );
+
+      updateElem.appendChild( propertyElem );
+    }
+
+    //Filter
+    QDomElement filterElem = transactionDoc.createElementNS( "http://www.opengis.net/ogc", "Filter" );
+    QDomElement featureIdElem = transactionDoc.createElementNS( "http://www.opengis.net/ogc", "FeatureId" );
+    featureIdElem.setAttribute( "fid", fidIt.value() );
+    filterElem.appendChild( featureIdElem );
+    updateElem.appendChild( filterElem );
+
+    transactionElem.appendChild( updateElem );
+  }
+
+  QDomDocument serverResponse;
+  bool success = sendTransactionDocument( transactionDoc, serverResponse );
+  if ( !success )
+  {
+    return false;
+  }
+
+  if ( transactionSuccess( serverResponse ) )
+  {
+    //change attributes in mFeatures
+    attIt = attr_map.constBegin();
+    for ( ; attIt != attr_map.constEnd(); ++attIt )
+    {
+      QMap<int, QgsFeature*>::iterator fIt = mFeatures.find( attIt.key() );
+      if ( fIt == mFeatures.end() )
+      {
+        continue;
+      }
+
+      QgsFeature* currentFeature = fIt.value();
+      if ( !currentFeature )
+      {
+        continue;
+      }
+
+      QgsAttributeMap::const_iterator attMapIt = attIt.value().constBegin();
+      for ( ; attMapIt != attIt.value().constEnd(); ++attMapIt )
+      {
+        currentFeature->changeAttribute( attMapIt.key(), attMapIt.value() );
+      }
+    }
+    return true;
+  }
+  else
+  {
+    handleException( serverResponse );
+    return false;
+  }
+}
+
+int QgsWFSProvider::describeFeatureType( const QString& uri, QString& geometryAttribute, QgsFieldMap& fields )
+{
+  switch ( mEncoding )
+  {
+    case QgsWFSProvider::GET:
+      return describeFeatureTypeGET( uri, geometryAttribute, fields );
+    case QgsWFSProvider::FILE:
+      return describeFeatureTypeFile( uri, geometryAttribute, fields );
+  }
+  return 1;
+}
+
+int QgsWFSProvider::getFeatureGET( const QString& uri, const QString& geometryAttribute )
+{
   //the new and faster method with the expat SAX parser
 
   //allows fast searchings with attribute name. Also needed is attribute Index and type infos
@@ -274,8 +603,8 @@
     thematicAttributes.insert( it.value().name(), qMakePair( it.key(), it.value() ) );
   }
 
-  QgsWFSData dataReader( uri, &mExtent, &mSourceCRS, mFeatures, geometryAttribute, thematicAttributes, &mWKBType );
-  QObject::connect( dataReader.http(), SIGNAL( dataReadProgress( int, int ) ), this, SLOT( handleWFSProgressMessage( int, int ) ) );
+  QgsWFSData dataReader( uri, &mExtent, &mSourceCRS, mFeatures, mIdMap, geometryAttribute, thematicAttributes, &mWKBType );
+  QObject::connect( &dataReader, SIGNAL( dataProgressAndSteps( int , int ) ), this, SLOT( handleWFSProgressMessage( int, int ) ) );
 
   //also connect to statusChanged signal of qgisapp (if it exists)
   QWidget* mainWindow = 0;
@@ -304,10 +633,10 @@
   QgsDebugMsg( QString( "feature count after request is: %1" ).arg( mFeatures.size() ) );
   QgsDebugMsg( QString( "mExtent after request is: %1" ).arg( mExtent.toString() ) );
 
-  for ( QList<QgsFeature*>::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
+  for ( QMap<int, QgsFeature*>::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
   {
     QgsDebugMsg( "feature " + QString::number(( *it )->id() ) );
-    mSpatialIndex->insertFeature( **it );
+    mSpatialIndex->insertFeature( *( it.value() ) );
   }
 
   mFeatureCount = mFeatures.size();
@@ -315,16 +644,6 @@
   return 0;
 }
 
-int QgsWFSProvider::getFeaturePOST( const QString& uri, const QString& geometryAttribute )
-{
-  return 1; //soon...
-}
-
-int QgsWFSProvider::getFeatureSOAP( const QString& uri, const QString& geometryAttribute )
-{
-  return 1; //soon...
-}
-
 int QgsWFSProvider::getFeatureFILE( const QString& uri, const QString& geometryAttribute )
 {
   QFile gmlFile( uri );
@@ -362,15 +681,27 @@
 
 int QgsWFSProvider::describeFeatureTypeGET( const QString& uri, QString& geometryAttribute, QgsFieldMap& fields )
 {
-  QByteArray result;
-  QgsHttpTransaction http( uri );
-  if ( !http.getSynchronously( result ) )
+  if ( !mNetworkRequestFinished )
   {
     return 1;
   }
+
+  mNetworkRequestFinished = false;
+
+  QNetworkRequest request( uri );
+  QNetworkReply* reply = QgsNetworkAccessManager::instance()->get( request );
+  connect( reply, SIGNAL( finished() ), this, SLOT( networkRequestFinished() ) );
+  while ( !mNetworkRequestFinished )
+  {
+    QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents, WFS_THRESHOLD );
+  }
+
+  QByteArray response = reply->readAll();
+  reply->deleteLater();
+
   QDomDocument describeFeatureDocument;
 
-  if ( !describeFeatureDocument.setContent( result, true ) )
+  if ( !describeFeatureDocument.setContent( response, true ) )
   {
     return 2;
   }
@@ -383,16 +714,11 @@
   return 0;
 }
 
-int QgsWFSProvider::describeFeatureTypePOST( const QString& uri, QString& geometryAttribute, QgsFieldMap& fields )
+void QgsWFSProvider::networkRequestFinished()
 {
-  return 1; //soon...
+  mNetworkRequestFinished = true;
 }
 
-int QgsWFSProvider::describeFeatureTypeSOAP( const QString& uri, QString& geometryAttribute, QgsFieldMap& fields )
-{
-  return 1; //soon...
-}
-
 int QgsWFSProvider::describeFeatureTypeFile( const QString& uri, QString& geometryAttribute, QgsFieldMap& fields )
 {
   //first look in the schema file
@@ -434,7 +760,7 @@
   return 0;
 }
 
-int QgsWFSProvider::readAttributesFromSchema( QDomDocument& schemaDoc, QString& geometryAttribute, QgsFieldMap& fields ) const
+int QgsWFSProvider::readAttributesFromSchema( QDomDocument& schemaDoc, QString& geometryAttribute, QgsFieldMap& fields )
 {
   //get the <schema> root element
   QDomNodeList schemaNodeList = schemaDoc.elementsByTagNameNS( "http://www.w3.org/2001/XMLSchema", "schema" );
@@ -443,6 +769,7 @@
     return 1;
   }
   QDomElement schemaElement = schemaNodeList.at( 0 ).toElement();
+  mWfsNamespace = schemaElement.attribute( "targetNamespace" );
   QDomElement complexTypeElement; //the <complexType> element corresponding to the feature type
 
   //find out, on which lines the first <element> or the first <complexType> occur. If <element> occurs first (mapserver), read the type of the relevant <complexType> tag. If <complexType> occurs first (geoserver), search for information about the feature type directly under this first complexType element
@@ -792,7 +1119,7 @@
     {
       //insert bbox and pointer to feature into search tree
       mSpatialIndex->insertFeature( *f );
-      mFeatures << f;
+      mFeatures.insert( f->id(), f );
       ++mFeatureCount;
     }
     ++counter;
@@ -1387,7 +1714,213 @@
   emit dataReadProgressMessage( message );
 }
 
+QDomElement QgsWFSProvider::createGeometryElem( QgsGeometry* geom, QDomDocument& doc ) /*const*/
+{
+  if ( !geom )
+  {
+    return QDomElement();
+  }
 
+  QDomElement geomElement;
+
+  QString geomTypeName;
+  QGis::WkbType wkbType = geom->wkbType();
+  switch ( wkbType )
+  {
+    case QGis::WKBPoint:
+      geomElement = createPointElem( geom, doc );
+      break;
+    case QGis::WKBMultiPoint:
+      geomElement = createMultiPointElem( geom, doc );
+      break;
+    case QGis::WKBLineString:
+      geomElement = createLineStringElem( geom, doc );
+      break;
+    case QGis::WKBMultiLineString:
+      geomElement = createMultiLineStringElem( geom, doc );
+      break;
+    case QGis::WKBPolygon:
+      geomElement = createPolygonElem( geom, doc );
+      break;
+    case QGis::WKBMultiPolygon:
+      geomElement = createMultiPolygonElem( geom, doc );
+      break;
+    default:
+      return QDomElement();
+  }
+
+  if ( !geomElement.isNull() )
+  {
+    //append layer srs
+    QgsCoordinateReferenceSystem layerCrs = crs();
+    if ( layerCrs.isValid() )
+    {
+      geomElement.setAttribute( "srsName", QString( "EPSG:" ) + QString::number( layerCrs.epsg() ) );
+    }
+  }
+  return geomElement;
+}
+
+QDomElement QgsWFSProvider::createLineStringElem( QgsGeometry* geom, QDomDocument& doc ) const
+{
+  if ( !geom )
+  {
+    return QDomElement();
+  }
+
+  QDomElement lineStringElem = doc.createElementNS( "http://www.opengis.net/gml", "LineString" );
+  QDomElement coordElem = createCoordinateElem( geom->asPolyline(), doc );
+  lineStringElem.appendChild( coordElem );
+  return lineStringElem;
+}
+
+QDomElement QgsWFSProvider::createMultiLineStringElem( QgsGeometry* geom, QDomDocument& doc ) const
+{
+  if ( !geom )
+  {
+    return QDomElement();
+  }
+
+  QDomElement multiLineStringElem = doc.createElementNS( "http://www.opengis.net/gml", "MultiLineString" );
+  QgsMultiPolyline multiline = geom->asMultiPolyline();
+
+  QgsMultiPolyline::const_iterator multiLineIt = multiline.constBegin();
+  for ( ; multiLineIt != multiline.constEnd(); ++multiLineIt )
+  {
+    QgsGeometry* lineGeom = QgsGeometry::fromPolyline( *multiLineIt );
+    if ( lineGeom )
+    {
+      QDomElement lineStringMemberElem = doc.createElementNS( "http://www.opengis.net/gml", "lineStringMember" );
+      QDomElement lineElem = createLineStringElem( lineGeom, doc );
+      lineStringMemberElem.appendChild( lineElem );
+      multiLineStringElem.appendChild( lineStringMemberElem );
+    }
+    delete lineGeom;
+  }
+
+  return multiLineStringElem;
+}
+
+QDomElement QgsWFSProvider::createPointElem( QgsGeometry* geom, QDomDocument& doc ) const
+{
+  if ( !geom )
+  {
+    return QDomElement();
+  }
+
+  QDomElement pointElem = doc.createElementNS( "http://www.opengis.net/gml", "Point" );
+  QgsPoint p = geom->asPoint();
+  QVector<QgsPoint> v;
+  v.append( p );
+  QDomElement coordElem = createCoordinateElem( v, doc );
+  pointElem.appendChild( coordElem );
+  return pointElem;
+}
+
+QDomElement QgsWFSProvider::createMultiPointElem( QgsGeometry* geom, QDomDocument& doc ) const
+{
+  if ( !geom )
+  {
+    return QDomElement();
+  }
+
+  QDomElement multiPointElem = doc.createElementNS( "http://www.opengis.net/gml", "MultiPoint" );
+  QgsMultiPoint multiPoint = geom->asMultiPoint();
+
+  QgsMultiPoint::const_iterator multiPointIt = multiPoint.constBegin();
+  for ( ; multiPointIt != multiPoint.constEnd(); ++multiPointIt )
+  {
+    QgsGeometry* pointGeom = QgsGeometry::fromPoint( *multiPointIt );
+    if ( pointGeom )
+    {
+      QDomElement multiPointMemberElem = doc.createElementNS( "http://www.opengis.net/gml", "pointMember" );
+      QDomElement pointElem = createPointElem( pointGeom, doc );
+      multiPointMemberElem.appendChild( pointElem );
+      multiPointElem.appendChild( multiPointMemberElem );
+    }
+  }
+  return multiPointElem;
+}
+
+QDomElement QgsWFSProvider::createPolygonElem( QgsGeometry* geom, QDomDocument& doc ) const
+{
+  if ( !geom )
+  {
+    return QDomElement();
+  }
+
+  QDomElement polygonElem = doc.createElementNS( "http://www.opengis.net/gml", "Polygon" );
+  QgsPolygon poly = geom->asPolygon();
+  for ( int i = 0; i < poly.size(); ++i )
+  {
+    QString boundaryName;
+    if ( i == 0 )
+    {
+      boundaryName = "outerBoundaryIs";
+    }
+    else
+    {
+      boundaryName = "innerBoundaryIs";
+    }
+    QDomElement boundaryElem = doc.createElementNS( "http://www.opengis.net/gml", boundaryName );
+    QDomElement ringElem = doc.createElementNS( "http://www.opengis.net/gml", "LinearRing" );
+    QDomElement coordElem = createCoordinateElem( poly.at( i ), doc );
+    ringElem.appendChild( coordElem );
+    boundaryElem.appendChild( ringElem );
+    polygonElem.appendChild( boundaryElem );
+  }
+  return polygonElem;
+}
+
+QDomElement QgsWFSProvider::createMultiPolygonElem( QgsGeometry* geom, QDomDocument& doc ) const
+{
+  if ( !geom )
+  {
+    return QDomElement();
+  }
+  QDomElement multiPolygonElem = doc.createElementNS( "http://www.opengis.net/gml", "MultiPolygon" );
+  QgsMultiPolygon multipoly = geom->asMultiPolygon();
+
+  QgsMultiPolygon::const_iterator polyIt = multipoly.constBegin();
+  for ( ; polyIt != multipoly.constEnd(); ++polyIt )
+  {
+    QgsGeometry* polygonGeom = QgsGeometry::fromPolygon( *polyIt );
+    if ( polygonGeom )
+    {
+      QDomElement polygonMemberElem = doc.createElementNS( "http://www.opengis.net/gml", "polygonMember" );
+      QDomElement polygonElem = createPolygonElem( polygonGeom, doc );
+      delete polygonGeom;
+      polygonMemberElem.appendChild( polygonElem );
+      multiPolygonElem.appendChild( polygonMemberElem );
+    }
+  }
+  return multiPolygonElem;
+}
+
+QDomElement QgsWFSProvider::createCoordinateElem( const QVector<QgsPoint> points, QDomDocument& doc ) const
+{
+  QDomElement coordElem = doc.createElementNS( "http://www.opengis.net/gml", "coordinates" );
+  coordElem.setAttribute( "cs", "," );
+  coordElem.setAttribute( "ts", " " );
+
+  QString coordString;
+  QVector<QgsPoint>::const_iterator pointIt = points.constBegin();
+  for ( ; pointIt != points.constEnd(); ++pointIt )
+  {
+    if ( pointIt != points.constBegin() )
+    {
+      coordString += " ";
+    }
+    coordString += QString::number( pointIt->x() );
+    coordString += ",";
+    coordString += QString::number( pointIt->y() );
+  }
+
+  QDomText coordText = doc.createTextNode( coordString );
+  coordElem.appendChild( coordText );
+  return coordElem;
+}
+
 QString QgsWFSProvider::name() const
 {
   return TEXT_PROVIDER_KEY;
@@ -1398,7 +1931,284 @@
   return TEXT_PROVIDER_DESCRIPTION;
 }
 
+int QgsWFSProvider::capabilities() const
+{
+  return mCapabilities;
+}
 
+QString QgsWFSProvider::typeNameFromUrl() const
+{
+  QStringList urlSplit = dataSourceUri().split( "?" );
+  if ( urlSplit.size() > 1 )
+  {
+    QStringList keyValueSplit = urlSplit.at( 1 ).split( "&" );
+    QStringList::const_iterator kvIt = keyValueSplit.constBegin();
+    for ( ; kvIt != keyValueSplit.constEnd(); ++kvIt )
+    {
+      if ( kvIt->startsWith( "typename", Qt::CaseInsensitive ) )
+      {
+        QStringList equalSplit = kvIt->split( "=" );
+        if ( equalSplit.size() > 1 )
+        {
+          return equalSplit.at( 1 );
+        }
+      }
+    }
+  }
+
+  return QString();
+}
+
+void QgsWFSProvider::removeNamespacePrefix( QString& tname ) const
+{
+  if ( tname.contains( ":" ) )
+  {
+    QStringList splitList = tname.split( ":" );
+    if ( splitList.size() > 1 )
+    {
+      tname = splitList.at( 1 );
+    }
+  }
+}
+
+QString QgsWFSProvider::nameSpacePrefix( const QString& tname ) const
+{
+  QStringList splitList = tname.split( ":" );
+  if ( splitList.size() < 2 )
+  {
+    return QString();
+  }
+  return splitList.at( 0 );
+}
+
+bool QgsWFSProvider::sendTransactionDocument( const QDomDocument& doc, QDomDocument& serverResponse )
+{
+  if ( doc.isNull() || !mNetworkRequestFinished )
+  {
+    return false;
+  }
+
+  mNetworkRequestFinished = false;
+  QString serverUrl = dataSourceUri().split( "?" ).at( 0 );
+  QNetworkRequest request( serverUrl );
+  request.setHeader( QNetworkRequest::ContentTypeHeader, "text/xml" );
+  QNetworkReply* reply = QgsNetworkAccessManager::instance()->post( request, doc.toByteArray( -1 ) );
+
+  connect( reply, SIGNAL( finished() ), this, SLOT( networkRequestFinished() ) );
+  while ( !mNetworkRequestFinished )
+  {
+    QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents, WFS_THRESHOLD );
+  }
+
+  QByteArray response = reply->readAll();
+  reply->deleteLater();
+  serverResponse.setContent( response, true );
+  return true;
+}
+
+QDomElement QgsWFSProvider::createTransactionElement( QDomDocument& doc ) const
+{
+  QDomElement transactionElem = doc.createElementNS( "http://www.opengis.net/wfs", "Transaction" );
+  transactionElem.setAttribute( "version", "1.0.0" );
+  transactionElem.setAttribute( "service", "WFS" );
+  transactionElem.setAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
+  transactionElem.setAttribute( "xsi:schemaLocation", mWfsNamespace + " " \
+                                + dataSourceUri().replace( QString( "GetFeature" ), QString( "DescribeFeatureType" ) ) );
+
+  QString namespacePrefix = nameSpacePrefix( typeNameFromUrl() );
+  if ( !namespacePrefix.isEmpty() )
+  {
+    transactionElem.setAttribute( "xmlns:" + namespacePrefix, mWfsNamespace );
+  }
+
+  return transactionElem;
+}
+
+bool QgsWFSProvider::transactionSuccess( const QDomDocument& serverResponse ) const
+{
+  if ( serverResponse.isNull() )
+  {
+    return false;
+  }
+
+  QDomElement documentElem = serverResponse.documentElement();
+  if ( documentElem.isNull() )
+  {
+    return false;
+  }
+
+  QDomNodeList transactionResultList = documentElem.elementsByTagNameNS( "http://www.opengis.net/wfs", "TransactionResult" );
+  if ( transactionResultList.size() < 1 )
+  {
+    return false;
+  }
+
+  QDomNodeList statusList = transactionResultList.at( 0 ).toElement().elementsByTagNameNS( "http://www.opengis.net/wfs", "Status" );
+  if ( statusList.size() < 1 )
+  {
+    return false;
+  }
+
+  if ( statusList.at( 0 ).firstChildElement().localName() == "SUCCESS" )
+  {
+    return true;
+  }
+  else
+  {
+    return false;
+  }
+}
+
+QStringList QgsWFSProvider::insertedFeatureIds( const QDomDocument& serverResponse ) const
+{
+  QStringList ids;
+  if ( serverResponse.isNull() )
+  {
+    return ids;
+  }
+
+  QDomElement rootElem = serverResponse.documentElement();
+  if ( rootElem.isNull() )
+  {
+    return ids;
+  }
+
+  QDomNodeList insertResultList = rootElem.elementsByTagNameNS( "http://www.opengis.net/wfs", "InsertResult" );
+  for ( int i = 0; i < insertResultList.size(); ++i )
+  {
+    QDomNodeList featureIdList = insertResultList.at( i ).toElement().elementsByTagNameNS( "http://www.opengis.net/ogc", "FeatureId" );
+    for ( int j = 0; j < featureIdList.size(); ++j )
+    {
+      QString fidString = featureIdList.at( j ).toElement().attribute( "fid" );
+      if ( !fidString.isEmpty() )
+      {
+        ids << fidString;
+      }
+    }
+  }
+  return ids;
+}
+
+int QgsWFSProvider::findNewKey() const
+{
+  if ( mFeatures.isEmpty() )
+  {
+    return 0;
+  }
+
+  //else return highest key + 1
+  QMap<int, QgsFeature*>::const_iterator lastIt = mFeatures.end();
+  lastIt --;
+  return lastIt.key() + 1;
+}
+
+void QgsWFSProvider::getLayerCapabilities()
+{
+  int capabilities = 0;
+  if ( !mNetworkRequestFinished )
+  {
+    mCapabilities = 0;
+    return;
+  }
+  mNetworkRequestFinished = false;
+
+
+  //get capabilities document
+  QString uri = dataSourceUri();
+  uri.replace( QString( "GetFeature" ), QString( "GetCapabilities" ) );
+  QNetworkRequest request( uri );
+  QNetworkReply* reply = QgsNetworkAccessManager::instance()->get( request );
+
+  connect( reply, SIGNAL( finished() ), this, SLOT( networkRequestFinished() ) );
+  while ( !mNetworkRequestFinished )
+  {
+    QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents, WFS_THRESHOLD );
+  }
+  QByteArray response = reply->readAll();
+  reply->deleteLater();
+
+  QDomDocument capabilitiesDocument;
+  QString capabilitiesDocError;
+  if ( !capabilitiesDocument.setContent( response, true, &capabilitiesDocError ) )
+  {
+    mCapabilities = 0;
+    return;
+  }
+
+  //go to <FeatureTypeList>
+  QDomElement featureTypeListElem = capabilitiesDocument.documentElement().firstChildElement( "FeatureTypeList" );
+  if ( featureTypeListElem.isNull() )
+  {
+    mCapabilities = 0;
+    return;
+  }
+
+  QDomElement operationsElem = featureTypeListElem.firstChildElement( "Operations" );
+  if ( !operationsElem.isNull() )
+  {
+    appendSupportedOperations( operationsElem, capabilities );
+  }
+
+  //find the <FeatureType> for this layer
+  QString thisLayerName = typeNameFromUrl();
+  QDomNodeList featureTypeList = featureTypeListElem.elementsByTagName( "FeatureType" );
+  for ( int i = 0; i < featureTypeList.size(); ++i )
+  {
+    QString name = featureTypeList.at( i ).firstChildElement( "Name" ).text();
+    if ( name == thisLayerName )
+    {
+      appendSupportedOperations( featureTypeList.at( i ).firstChildElement( "Operations" ), capabilities );
+      break;
+    }
+  }
+
+  mCapabilities = capabilities;
+}
+
+void QgsWFSProvider::appendSupportedOperations( const QDomElement& operationsElem, int& capabilities ) const
+{
+  if ( operationsElem.isNull() )
+  {
+    return;
+  }
+
+  QDomNodeList childList = operationsElem.childNodes();
+  for ( int i = 0; i < childList.size(); ++i )
+  {
+    QString elemName = childList.at( i ).toElement().tagName();
+    if ( elemName == "Insert" )
+    {
+      capabilities |= QgsVectorDataProvider::AddFeatures;
+    }
+    else if ( elemName == "Update" )
+    {
+      capabilities |= QgsVectorDataProvider::ChangeAttributeValues;
+      capabilities |= QgsVectorDataProvider::ChangeGeometries;
+    }
+    else if ( elemName == "Delete" )
+    {
+      capabilities |= QgsVectorDataProvider::DeleteFeatures;
+    }
+  }
+}
+
+void QgsWFSProvider::handleException( const QDomDocument& serverResponse ) const
+{
+  QDomElement exceptionElem = serverResponse.documentElement();
+  if ( exceptionElem.isNull() || exceptionElem.tagName() != "ServiceExceptionReport" )
+  {
+    return;
+  }
+
+  //possibly this class is used not in a gui application
+  if ( QApplication::topLevelWidgets().size() < 1 )
+  {
+    return;
+  }
+  QString message = exceptionElem.firstChildElement( "ServiceException" ).text();
+  QMessageBox::critical( 0, tr( "Error" ), message );
+}
+
 QGISEXTERN QgsWFSProvider* classFactory( const QString *uri )
 {
   return new QgsWFSProvider( *uri );

Modified: trunk/qgis/src/providers/wfs/qgswfsprovider.h
===================================================================
--- trunk/qgis/src/providers/wfs/qgswfsprovider.h	2010-07-29 12:33:04 UTC (rev 13984)
+++ trunk/qgis/src/providers/wfs/qgswfsprovider.h	2010-07-30 13:01:48 UTC (rev 13985)
@@ -36,8 +36,6 @@
     enum REQUEST_ENCODING
     {
       GET,
-      POST,
-      SOAP,/*Note that this goes also through HTTP POST but additionally uses soap envelope and friends*/
       FILE  //reads from a file on disk
     };
 
@@ -80,6 +78,8 @@
     QString name() const;
     QString description() const;
 
+    virtual int capabilities() const;
+
     /* new functions */
 
     /**Sets the encoding type in which the provider makes requests and interprets
@@ -90,7 +90,36 @@
        stores them in a vector*/
     int getFeature( const QString& uri );
 
+    //Editing operations
+    /**
+     * Adds a list of features
+     * @return true in case of success and false in case of failure
+     */
+    virtual bool addFeatures( QgsFeatureList &flist );
 
+    /**
+     * Deletes one or more features
+     * @param id list containing feature ids to delete
+     * @return true in case of success and false in case of failure
+     */
+    virtual bool deleteFeatures( const QgsFeatureIds &id );
+
+    /**
+     * Changes geometries of existing features
+     * @param geometry_map   A QgsGeometryMap whose index contains the feature IDs
+     *                       that will have their geometries changed.
+     *                       The second map parameter being the new geometries themselves
+     * @return               True in case of success and false in case of failure
+     */
+    virtual bool changeGeometryValues( QgsGeometryMap & geometry_map );
+
+    /**
+     * Changes attribute values of existing features.
+     * @param attr_map a map containing changed attributes
+     * @return true in case of success and false in case of failure
+     */
+    virtual bool changeAttributeValues( const QgsChangedAttributesMap &attr_map );
+
   signals:
     void dataReadProgressMessage( QString message );
 
@@ -99,9 +128,17 @@
      and emits the dataReadProgressMessage signal*/
     void handleWFSProgressMessage( int done, int total );
 
+    /**Sets mNetworkRequestFinished flag to true*/
+    void networkRequestFinished();
 
+  private:
+    bool mNetworkRequestFinished;
+
   protected:
+    /**Thematic attributes*/
     QgsFieldMap mFields;
+    /**Name of geometry attribute*/
+    QString mGeometryAttribute;
     /**The encoding used for request/response. Can be GET, POST or SOAP*/
     REQUEST_ENCODING mEncoding;
     /**Bounding box for the layer*/
@@ -116,8 +153,10 @@
     QList<int> mSelectedFeatures;
     /**Iterator on the feature vector for use in rewind(), nextFeature(), etc...*/
     QList<int>::iterator mFeatureIterator;
-    /**Vector where the features are inserted*/
-    QList<QgsFeature*> mFeatures;
+    /**Map <feature Id / feature> */
+    QMap<int, QgsFeature* > mFeatures;
+    /**Stores the relation between provider ids and WFS server ids*/
+    QMap<int, QString > mIdMap;
     /**Geometry type of the features in this layer*/
     mutable QGis::WkbType mWKBType;
     /**Source CRS*/
@@ -125,6 +164,10 @@
     int mFeatureCount;
     /**Flag if provider is valid*/
     bool mValid;
+    /**Namespace URL of the server (comes from DescribeFeatureDocument)*/
+    QString mWfsNamespace;
+    /**Server capabilities for this layer (generated from capabilities document)*/
+    int mCapabilities;
 
 
     /**Collects information about the field types. Is called internally from QgsWFSProvider::getFeature. The method delegates the work to request specific ones and gives back the name of the geometry attribute and the thematic attributes with their types*/
@@ -142,7 +185,7 @@
     int describeFeatureTypeFile( const QString& uri, QString& geometryAttribute, QgsFieldMap& fields );
 
     /**Reads the name of the geometry attribute, the thematic attributes and their types from a dom document. Returns 0 in case of success*/
-    int readAttributesFromSchema( QDomDocument& schemaDoc, QString& geometryAttribute, QgsFieldMap& fields ) const;
+    int readAttributesFromSchema( QDomDocument& schemaDoc, QString& geometryAttribute, QgsFieldMap& fields );
     /**This method tries to guess the geometry attribute and the other attribute names from the .gml file if no schema is present. Returns 0 in case of success*/
     int guessAttributesFromFile( const QString& uri, QString& geometryAttribute, std::list<QString>& thematicAttributes ) const;
 
@@ -171,6 +214,55 @@
     int readGML2Coordinates( std::list<QgsPoint>& coords, const QDomElement elem ) const;
     /**Tries to create a QgsCoordinateReferenceSystem object and assign it to mSourceCRS. Returns 0 in case of success*/
     int setCRSFromGML2( const QDomElement& wfsCollectionElement );
+
+
+    //methods to write GML2
+
+    QDomElement createGeometryElem( QgsGeometry* g, QDomDocument& doc ) /*const*/;
+    QDomElement createLineStringElem( QgsGeometry* geom, QDomDocument& doc ) const;
+    QDomElement createMultiLineStringElem( QgsGeometry* geom, QDomDocument& doc ) const;
+    QDomElement createPointElem( QgsGeometry* geom, QDomDocument& doc ) const;
+    QDomElement createMultiPointElem( QgsGeometry* geom, QDomDocument& doc ) const;
+    QDomElement createPolygonElem( QgsGeometry* geom, QDomDocument& doc ) const;
+    QDomElement createMultiPolygonElem( QgsGeometry* geom, QDomDocument& doc ) const;
+
+    /**Create a GML coordinate string from a point list.
+      @param points list of data points
+      @param coordString out: GML coord string
+      @return 0 in case of success*/
+    QDomElement createCoordinateElem( const QVector<QgsPoint> points, QDomDocument& doc ) const;
+
+    //helper methods for WFS-T
+
+    /**Extracts the typename from the providers url
+      @return typename or a null string in case of error*/
+    QString typeNameFromUrl() const;
+
+    /**Removes a possible namespace prefix from a typename*/
+    void removeNamespacePrefix( QString& tname ) const;
+    /**Returns namespace prefix (or an empty string if there is no prefix)*/
+    QString nameSpacePrefix( const QString& tname ) const;
+
+    /**Sends the transaction document to the server using HTTP POST
+      @return true if transmission to the server succeeded, otherwise false
+        note: true does not automatically mean that the transaction succeeded*/
+    bool sendTransactionDocument( const QDomDocument& doc, QDomDocument& serverResponse );
+
+    /**Creates a transaction element and adds it (normally as first element) to the document*/
+    QDomElement createTransactionElement( QDomDocument& doc ) const;
+
+    /**True if the server response means success*/
+    bool transactionSuccess( const QDomDocument& serverResponse ) const;
+    /**Returns the inserted ids*/
+    QStringList insertedFeatureIds( const QDomDocument& serverResponse ) const;
+    /**Returns a key suitable for new items*/
+    int findNewKey() const;
+    /**Retrieve capabilities for this layer from GetCapabilities document (will be stored in mCapabilites)*/
+    void getLayerCapabilities();
+    /**Takes <Operations> element and updates the capabilities*/
+    void appendSupportedOperations( const QDomElement& operationsElem, int& capabilities ) const;
+    /**Shows a message box with the exception string (or does nothing if the xml document is not an exception)*/
+    void handleException( const QDomDocument& serverResponse ) const;
 };
 
 #endif



More information about the QGIS-commit mailing list