Index: src/plugins/wfs/qgswfssourceselect.h =================================================================== --- src/plugins/wfs/qgswfssourceselect.h (revision 13957) +++ src/plugins/wfs/qgswfssourceselect.h (working copy) @@ -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 > 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& 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& typenames, std::list< std::list >& crs, std::list& titles, std::list& abstracts ); - //encoding specific methods of getCapabilities - int getCapabilitiesGET( QString uri, std::list& typenames, std::list< std::list >& crs, std::list& titles, std::list& abstracts ); - int getCapabilitiesPOST( const QString& uri, std::list& typenames, std::list< std::list >& crs, std::list& titles, std::list& abstracts ); - int getCapabilitiesSOAP( const QString& uri, std::list& typenames, std::list< std::list >& crs, std::list& titles, std::list& 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() ); } }; Index: src/plugins/wfs/qgswfssourceselect.cpp =================================================================== --- src/plugins/wfs/qgswfssourceselect.cpp (revision 13957) +++ src/plugins/wfs/qgswfssourceselect.cpp (working copy) @@ -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 #include #include #include +#include +#include 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& typenames, std::list< std::list >& crs, std::list& titles, std::list& 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& typenames, std::list< std::list >& crs, std::list& titles, std::list& 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 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 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 typenames; + std::list< std::list > crs; + std::list titles; + std::list 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 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 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 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 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::const_iterator typeNameIter; + std::list< std::list >::const_iterator crsIter; + for ( typeNameIter = typenames.begin(), crsIter = crs.begin(); typeNameIter != typenames.end(); ++typeNameIter, ++crsIter ) + { + std::list currentCRSList; + for ( std::list::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& typenames, std::list< std::list >& crs, std::list& titles, std::list& abstracts ) -{ - return 1; //soon... + //insert the typenames, titles and abstracts into the tree view + std::list::const_iterator t_it = titles.begin(); + std::list::const_iterator n_it = typenames.begin(); + std::list::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& typenames, std::list< std::list >& crs, std::list& titles, std::list& 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 typenames; - std::list< std::list > crsList; - std::list titles; - std::list 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::const_iterator typeNameIter; - std::list< std::list >::const_iterator crsIter; - for ( typeNameIter = typenames.begin(), crsIter = crsList.begin(); typeNameIter != typenames.end(); ++typeNameIter, ++crsIter ) - { - std::list currentCRSList; - for ( std::list::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::const_iterator t_it = titles.begin(); - std::list::const_iterator n_it = typenames.begin(); - std::list::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 Index: src/providers/wfs/qgswfsdata.cpp =================================================================== --- src/providers/wfs/qgswfsdata.cpp (revision 13957) +++ src/providers/wfs/qgswfsdata.cpp (working copy) @@ -16,8 +16,8 @@ #include "qgsrectangle.h" #include "qgscoordinatereferencesystem.h" #include "qgsgeometry.h" -#include "qgshttptransaction.h" #include "qgslogger.h" + #include #include #include @@ -103,8 +103,6 @@ mHttp.setHost( requestUrl.host() ); } - QgsHttpTransaction::applyProxySettings( mHttp, mUri ); - //find out if there is a QGIS main window. If yes, display a progress dialog QProgressDialog* progressDialog = 0; QWidget* mainWindow = findMainWindow(); Index: src/providers/wfs/qgswfsprovider.cpp =================================================================== --- src/providers/wfs/qgswfsprovider.cpp (revision 13957) +++ src/providers/wfs/qgswfsprovider.cpp (working copy) @@ -15,18 +15,23 @@ * * ***************************************************************************/ +#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 #include +#include +#include #include #include #include @@ -38,7 +43,12 @@ 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 ) + , mReplyFinished( true ) + , mUseIntersect( false ) + , mSourceCRS( 0 ) + , mFeatureCount( 0 ) + , mValid( true ) { mSpatialIndex = new QgsSpatialIndex; if ( getFeature( uri ) == 0 ) @@ -110,8 +120,6 @@ } } - - QGis::WkbType QgsWFSProvider::geometryType() const { return mWKBType; @@ -222,10 +230,6 @@ { 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 ); } @@ -234,37 +238,6 @@ int QgsWFSProvider::getFeatureGET( const QString& uri, const QString& geometryAttribute ) { -#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 ); - - QDomDocument getFeatureDocument; - if ( !getFeatureDocument.setContent( result, true ) ) - { - return 1; //error - } - - QDomElement featureCollectionElement = getFeatureDocument.documentElement(); - - //get and set Extent - if ( getExtentFromGML2( &mExtent, featureCollectionElement ) != 0 ) - { - return 3; - } - - setCRSFromGML2( featureCollectionElement ); - - if ( getFeaturesFromGML2( featureCollectionElement, geometryAttribute ) != 0 ) - { - return 4; - } - - return 0; -#endif - //the new and faster method with the expat SAX parser //allows fast searchings with attribute name. Also needed is attribute Index and type infos @@ -315,16 +288,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 +325,27 @@ int QgsWFSProvider::describeFeatureTypeGET( const QString& uri, QString& geometryAttribute, QgsFieldMap& fields ) { - QByteArray result; - QgsHttpTransaction http( uri ); - if ( !http.getSynchronously( result ) ) + Q_ASSERT( mReplyFinished ); + mReplyFinished = false; + + QNetworkRequest request( uri ); + QNetworkReply *reply = QgsNetworkAccessManager::instance()->get( request ); + + connect( reply, SIGNAL( finished() ), this, SLOT( featureTypeFinished() ) ); + + while ( !mReplyFinished ) { - return 1; + QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents, WFS_THRESHOLD ); } - QDomDocument describeFeatureDocument; - if ( !describeFeatureDocument.setContent( result, true ) ) + QByteArray response = reply->readAll(); + + reply->deleteLater(); + + QgsDebugMsg( "feature type: " + response ); + + QDomDocument describeFeatureDocument; + if ( !describeFeatureDocument.setContent( response, true ) ) { return 2; } @@ -380,18 +355,16 @@ return 3; } + return 0; } -int QgsWFSProvider::describeFeatureTypePOST( const QString& uri, QString& geometryAttribute, QgsFieldMap& fields ) +void QgsWFSProvider::featureTypeFinished() { - return 1; //soon... + Q_ASSERT( !mReplyFinished ); + mReplyFinished = true; } -int QgsWFSProvider::describeFeatureTypeSOAP( const QString& uri, QString& geometryAttribute, QgsFieldMap& fields ) -{ - return 1; //soon... -} int QgsWFSProvider::describeFeatureTypeFile( const QString& uri, QString& geometryAttribute, QgsFieldMap& fields ) { Index: src/providers/wfs/qgswfsprovider.h =================================================================== --- src/providers/wfs/qgswfsprovider.h (revision 13957) +++ src/providers/wfs/qgswfsprovider.h (working copy) @@ -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 }; @@ -99,7 +97,11 @@ and emits the dataReadProgressMessage signal*/ void handleWFSProgressMessage( int done, int total ); + void featureTypeFinished(); + private: + bool mReplyFinished; + protected: QgsFieldMap mFields; /**The encoding used for request/response. Can be GET, POST or SOAP*/ @@ -126,7 +128,6 @@ /**Flag if provider is valid*/ bool mValid; - /**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*/ int describeFeatureType( const QString& uri, QString& geometryAttribute, QgsFieldMap& fields );