Skip to content
Permalink
Browse files

Merge pull request #6407 from elpaso/bugfix-18099-wfs-operations-xref-2

[bugfix] Support Distributed Computing Platform (DCP) for WFS
  • Loading branch information
elpaso committed Feb 22, 2018
2 parents e06e95c + 31df608 commit 81d8625f73ca6f1067b81481357f2d1cdae1372d
@@ -28,14 +28,14 @@
#include <QStringList>

QgsWfsCapabilities::QgsWfsCapabilities( const QString &uri )
: QgsWfsRequest( uri )
: QgsWfsRequest( QgsWFSDataSourceURI( uri ) )
{
connect( this, &QgsWfsRequest::downloadFinished, this, &QgsWfsCapabilities::capabilitiesReplyFinished );
}

bool QgsWfsCapabilities::requestCapabilities( bool synchronous, bool forceRefresh )
{
QUrl url( baseURL() );
QUrl url( mUri.baseURL( ) );
url.addQueryItem( QStringLiteral( "REQUEST" ), QStringLiteral( "GetCapabilities" ) );

const QString &version = mUri.version();
@@ -249,7 +249,28 @@ void QgsWfsCapabilities::capabilitiesReplyFinished()
for ( int i = 0; i < operationList.size(); ++i )
{
QDomElement operation = operationList.at( i ).toElement();
if ( operation.attribute( QStringLiteral( "name" ) ) == QLatin1String( "GetFeature" ) )
QString name = operation.attribute( QStringLiteral( "name" ) );

// Search for DCP/HTTP
QDomNodeList operationHttpList = operation.elementsByTagName( QStringLiteral( "HTTP" ) );
for ( int j = 0; j < operationHttpList.size(); ++j )
{
QDomElement value = operationHttpList.at( j ).toElement();
QDomNodeList httpGetMethodList = value.elementsByTagName( QStringLiteral( "Get" ) );
QDomNodeList httpPostMethodList = value.elementsByTagName( QStringLiteral( "Post" ) );
if ( httpGetMethodList.size() > 0 )
{
mCaps.operationGetEndpoints[name] = httpGetMethodList.at( 0 ).toElement().attribute( QStringLiteral( "href" ) );
QgsDebugMsgLevel( QStringLiteral( "Adding DCP Get %1 %2" ).arg( name, mCaps.operationGetEndpoints[name] ), 3 );
}
if ( httpPostMethodList.size() > 0 )
{
mCaps.operationPostEndpoints[name] = httpPostMethodList.at( 0 ).toElement().attribute( QStringLiteral( "href" ) );
QgsDebugMsgLevel( QStringLiteral( "Adding DCP Post %1 %2" ).arg( name, mCaps.operationPostEndpoints[name] ), 3 );
}
}

if ( name == QLatin1String( "GetFeature" ) )
{
QDomNodeList operationContraintList = operation.elementsByTagName( QStringLiteral( "Constraint" ) );
for ( int j = 0; j < operationContraintList.size(); ++j )
@@ -98,6 +98,8 @@ class QgsWfsCapabilities : public QgsWfsRequest
QList<Function> functionList;
bool useEPSGColumnFormat; // whether to use EPSG:XXXX srsname
QList< QString > outputFormats;
QgsStringMap operationGetEndpoints;
QgsStringMap operationPostEndpoints;

QSet< QString > setAllTypenames;
QMap< QString, QString> mapUnprefixedTypenameToPrefixedTypename;
@@ -165,6 +165,28 @@ QUrl QgsWFSDataSourceURI::baseURL( bool bIncludeServiceWFS ) const
return url;
}

QUrl QgsWFSDataSourceURI::requestUrl( const QString &request, const Method &method ) const
{
QString endpoint;
switch ( method )
{
case Post:
endpoint = mPostEndpoints.contains( request ) ?
mPostEndpoints[ request ] : mURI.param( QgsWFSConstants::URI_PARAM_URL );
break;
default:
case Get:
endpoint = mGetEndpoints.contains( request ) ?
mGetEndpoints[ request ] : mURI.param( QgsWFSConstants::URI_PARAM_URL );
break;
}
QUrl url( endpoint );
url.addQueryItem( QStringLiteral( "SERVICE" ), QStringLiteral( "WFS" ) );
if ( ! request.isEmpty() )
url.addQueryItem( QStringLiteral( "REQUEST" ), request );
return url;
}

QString QgsWFSDataSourceURI::version() const
{
if ( !mURI.hasParam( QgsWFSConstants::URI_PARAM_VERSION ) )
@@ -298,3 +320,13 @@ QString QgsWFSDataSourceURI::build( const QString &baseUri,
uri.mURI.setParam( QgsWFSConstants::URI_PARAM_RESTRICT_TO_REQUEST_BBOX, QStringLiteral( "1" ) );
return uri.uri();
}

void QgsWFSDataSourceURI::setGetEndpoints( const QgsStringMap &map )
{
mGetEndpoints = map;
}

void QgsWFSDataSourceURI::setPostEndpoints( const QgsStringMap &map )
{
mPostEndpoints = map;
}
@@ -75,6 +75,13 @@ class QgsWFSDataSourceURI
{
public:

//! Http method for DCP URIs
enum Method
{
Get,
Post
};

explicit QgsWFSDataSourceURI( const QString &uri );

//! Return the URI, avoiding expansion of authentication configuration, which is handled during network access
@@ -83,6 +90,9 @@ class QgsWFSDataSourceURI
//! Return base URL (with SERVICE=WFS parameter if bIncludeServiceWFS=true)
QUrl baseURL( bool bIncludeServiceWFS = true ) const;

//! Return request URL (with SERVICE=WFS parameter)
QUrl requestUrl( const QString &request, const Method &method = Method::Get ) const;

//! Get WFS version. Can be auto, 1.0.0, 1.1.0 or 2.0.0.
QString version() const;

@@ -150,9 +160,17 @@ class QgsWFSDataSourceURI
const QString &sql = QString(),
bool restrictToCurrentViewExtent = false );

//! Set Get DCP endpoints
void setGetEndpoints( const QgsStringMap &map );

//! Set Post DCP endpoints
void setPostEndpoints( const QgsStringMap &map );

private:
QgsDataSourceUri mURI;
QgsWFSAuthorization mAuth;
QgsStringMap mGetEndpoints;
QgsStringMap mPostEndpoints;
};


@@ -16,16 +16,15 @@
#include "qgswfsdescribefeaturetype.h"
#include "qgswfsutils.h"

QgsWFSDescribeFeatureType::QgsWFSDescribeFeatureType( const QString &uri )
QgsWFSDescribeFeatureType::QgsWFSDescribeFeatureType( QgsWFSDataSourceURI &uri )
: QgsWfsRequest( uri )
{
}

bool QgsWFSDescribeFeatureType::requestFeatureType( const QString &WFSVersion,
const QString &typeName, bool forceSingularTypeName )
{
QUrl url( baseURL() );
url.addQueryItem( QStringLiteral( "REQUEST" ), QStringLiteral( "DescribeFeatureType" ) );
QUrl url( mUri.requestUrl( QStringLiteral( "DescribeFeatureType" ) ) );
url.addQueryItem( QStringLiteral( "VERSION" ), WFSVersion );
// The specs are not consistent: is it singular in 1.0.x and plural in 2.0.0?
// see http://docs.opengeospatial.org/is/09-025r2/09-025r2.html#147
@@ -22,7 +22,7 @@ class QgsWFSDescribeFeatureType : public QgsWfsRequest
{
Q_OBJECT
public:
explicit QgsWFSDescribeFeatureType( const QString &uri );
explicit QgsWFSDescribeFeatureType( QgsWFSDataSourceURI &uri );

//! Issue the request
bool requestFeatureType( const QString &WFSVersion, const QString &typeName,
@@ -35,7 +35,7 @@
#include <QStyle>

QgsWFSFeatureHitsAsyncRequest::QgsWFSFeatureHitsAsyncRequest( QgsWFSDataSourceURI &uri )
: QgsWfsRequest( uri.uri() )
: QgsWfsRequest( uri )
, mNumberMatched( -1 )
{
connect( this, &QgsWfsRequest::downloadFinished, this, &QgsWFSFeatureHitsAsyncRequest::hitsReplyFinished );
@@ -77,7 +77,7 @@ QString QgsWFSFeatureHitsAsyncRequest::errorMessageWithReason( const QString &re
// -------------------------

QgsWFSFeatureDownloader::QgsWFSFeatureDownloader( QgsWFSSharedData *shared )
: QgsWfsRequest( shared->mURI.uri() )
: QgsWfsRequest( shared->mURI )
, mShared( shared )
, mStop( false )
, mProgressDialogShowImmediately( false )
@@ -187,8 +187,7 @@ QString QgsWFSFeatureDownloader::sanitizeFilter( QString filter )

QUrl QgsWFSFeatureDownloader::buildURL( int startIndex, int maxFeatures, bool forHits )
{
QUrl getFeatureUrl( mShared->mURI.baseURL() );
getFeatureUrl.addQueryItem( QStringLiteral( "REQUEST" ), QStringLiteral( "GetFeature" ) );
QUrl getFeatureUrl( mShared->mURI.requestUrl( QStringLiteral( "GetFeature" ) ) );
getFeatureUrl.addQueryItem( QStringLiteral( "VERSION" ), mShared->mWFSVersion );

QString typenames;
@@ -375,6 +374,8 @@ QUrl QgsWFSFeatureDownloader::buildURL( int startIndex, int maxFeatures, bool fo
namespaces );
}

QgsDebugMsgLevel( QStringLiteral( "WFS GetFeature URL: %1" ).arg( getFeatureUrl.toDisplayString( ) ), 2 );

return getFeatureUrl;
}

@@ -419,7 +419,7 @@ bool QgsWFSProvider::processSQL( const QString &sqlString, QString &errorMsg, QS
concatenatedTypenames += typeName;
}

QgsWFSDescribeFeatureType describeFeatureType( mShared->mURI.uri() );
QgsWFSDescribeFeatureType describeFeatureType( mShared->mURI );
if ( !describeFeatureType.requestFeatureType( mShared->mWFSVersion,
concatenatedTypenames ) )
{
@@ -1168,7 +1168,7 @@ bool QgsWFSProvider::describeFeatureType( QString &geometryAttribute,
{
fields.clear();

QgsWFSDescribeFeatureType describeFeatureType( mShared->mURI.uri() );
QgsWFSDescribeFeatureType describeFeatureType( mShared->mURI );
if ( !describeFeatureType.requestFeatureType( mShared->mWFSVersion,
mShared->mURI.typeName(), forceSingularTypeNames ) )
{
@@ -1450,7 +1450,7 @@ bool QgsWFSProvider::sendTransactionDocument( const QDomDocument &doc, QDomDocum
return false;
}

QgsWFSTransactionRequest request( mShared->mURI.uri() );
QgsWFSTransactionRequest request( mShared->mURI );
return request.send( doc, serverResponse );
}

@@ -1464,11 +1464,13 @@ QDomElement QgsWFSProvider::createTransactionElement( QDomDocument &doc ) const
transactionElem.setAttribute( QStringLiteral( "service" ), QStringLiteral( "WFS" ) );
transactionElem.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );

QUrl describeFeatureTypeURL( mShared->mURI.baseURL() );
QUrl describeFeatureTypeURL = mShared->mURI.requestUrl( QStringLiteral( "DescribeFeatureType" ) );
// For tests (since the URL contains part of random data, we need to replace it with a fixed content)
if ( mShared->mURI.baseURL().toString().contains( QLatin1String( "fake_qgis_http_endpoint" ) ) )
if ( describeFeatureTypeURL.toString().contains( QLatin1String( "fake_qgis_http_endpoint" ) ) )
{
describeFeatureTypeURL = QUrl( QStringLiteral( "http://fake_qgis_http_endpoint" ) );
describeFeatureTypeURL.addQueryItem( QStringLiteral( "REQUEST" ), QStringLiteral( "DescribeFeatureType" ) );
describeFeatureTypeURL.addQueryItem( QStringLiteral( "REQUEST" ), QStringLiteral( "DescribeFeatureType" ) );
}
describeFeatureTypeURL.addQueryItem( QStringLiteral( "VERSION" ), QStringLiteral( "1.0.0" ) );
//TODO: proper support of 2.0.0, for now hardcoded
describeFeatureTypeURL.addQueryItem( QgsWFSUtils::typeNameParameterForVersion( WfsVersion ).toUpper(), mShared->mURI.typeName() );
@@ -1562,6 +1564,8 @@ bool QgsWFSProvider::getCapabilities()

const QgsWfsCapabilities::Capabilities caps = getCapabilities.capabilities();
mShared->mCaps = caps;
mShared->mURI.setGetEndpoints( caps.operationGetEndpoints );
mShared->mURI.setPostEndpoints( caps.operationPostEndpoints );
}

mShared->mWFSVersion = mShared->mCaps.version;
@@ -25,15 +25,15 @@
#include <QNetworkCacheMetaData>
#include <QCryptographicHash> // just for testin file:// fake_qgis_http_endpoint hack

QgsWfsRequest::QgsWfsRequest( const QString &uri )
QgsWfsRequest::QgsWfsRequest( const QgsWFSDataSourceURI &uri )
: mUri( uri )
, mErrorCode( QgsWfsRequest::NoError )
, mIsAborted( false )
, mForceRefresh( false )
, mTimedout( false )
, mGotNonEmptyResponse( false )
{
QgsDebugMsg( "theUri = " + uri );
QgsDebugMsg( "theUri = " + uri.uri( ) );
connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::requestTimedOut, this, &QgsWfsRequest::requestTimedOut );
}

@@ -48,6 +48,11 @@ void QgsWfsRequest::requestTimedOut( QNetworkReply *reply )
mTimedout = true;
}

QUrl QgsWfsRequest::requestUrl( const QString &request ) const
{
return mUri.requestUrl( request );
}

bool QgsWfsRequest::sendGET( const QUrl &url, bool synchronous, bool forceRefresh, bool cache )
{
abort(); // cancel previous
@@ -61,6 +66,8 @@ bool QgsWfsRequest::sendGET( const QUrl &url, bool synchronous, bool forceRefres
mResponse.clear();

QUrl modifiedUrl( url );

// Specific code for testing
if ( modifiedUrl.toString().contains( QLatin1String( "fake_qgis_http_endpoint" ) ) )
{
// Just for testing with local files instead of http:// resources
@@ -27,7 +27,7 @@ class QgsWfsRequest : public QObject
{
Q_OBJECT
public:
explicit QgsWfsRequest( const QString &uri );
explicit QgsWfsRequest( const QgsWFSDataSourceURI &uri );

~QgsWfsRequest() override;

@@ -54,6 +54,9 @@ class QgsWfsRequest : public QObject
//! \brief Return server response (after download/post)
QByteArray response() const { return mResponse; }

//! Return the url for a WFS request
QUrl requestUrl( const QString &request ) const;

public slots:
//! Abort network request immediately
void abort();
@@ -100,12 +103,10 @@ class QgsWfsRequest : public QObject

protected:

//! base service URL
QUrl baseURL() const { return mUri.baseURL(); }

/**
* Return (translated) error message, composed with a
(possibly translated, but sometimes coming from server) reason */
* (possibly translated, but sometimes coming from server) reason
*/
virtual QString errorMessageWithReason( const QString &reason ) = 0;

//! Return experiation delay in second
@@ -1170,15 +1170,14 @@ QgsGmlStreamingParser *QgsWFSSharedData::createParser()


QgsWFSFeatureHitsRequest::QgsWFSFeatureHitsRequest( QgsWFSDataSourceURI &uri )
: QgsWfsRequest( uri.uri() )
: QgsWfsRequest( uri )
{
}

int QgsWFSFeatureHitsRequest::getFeatureCount( const QString &WFSVersion,
const QString &filter )
{
QUrl getFeatureUrl( mUri.baseURL() );
getFeatureUrl.addQueryItem( QStringLiteral( "REQUEST" ), QStringLiteral( "GetFeature" ) );
QUrl getFeatureUrl( mUri.requestUrl( QStringLiteral( "GetFeature" ) ) );
getFeatureUrl.addQueryItem( QStringLiteral( "VERSION" ), WFSVersion );
if ( WFSVersion.startsWith( QLatin1String( "2.0" ) ) )
getFeatureUrl.addQueryItem( QStringLiteral( "TYPENAMES" ), mUri.typeName() );
@@ -1232,14 +1231,13 @@ QString QgsWFSFeatureHitsRequest::errorMessageWithReason( const QString &reason


QgsWFSSingleFeatureRequest::QgsWFSSingleFeatureRequest( QgsWFSSharedData *shared )
: QgsWfsRequest( shared->mURI.uri() ), mShared( shared )
: QgsWfsRequest( shared->mURI ), mShared( shared )
{
}

QgsRectangle QgsWFSSingleFeatureRequest::getExtent()
{
QUrl getFeatureUrl( mUri.baseURL() );
getFeatureUrl.addQueryItem( QStringLiteral( "REQUEST" ), QStringLiteral( "GetFeature" ) );
QUrl getFeatureUrl( mUri.requestUrl( QStringLiteral( "GetFeature" ) ) );
getFeatureUrl.addQueryItem( QStringLiteral( "VERSION" ), mShared->mWFSVersion );
if ( mShared->mWFSVersion .startsWith( QLatin1String( "2.0" ) ) )
getFeatureUrl.addQueryItem( QStringLiteral( "TYPENAMES" ), mUri.typeName() );

0 comments on commit 81d8625

Please sign in to comment.
You can’t perform that action at this time.