[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