Skip to content

Commit

Permalink
Merge pull request #29946 from rouault/fix_redmine_21768
Browse files Browse the repository at this point in the history
WFS provider: fix issues with TYPENAME(S) and NAMESPACE(s) with WFS 2.0
  • Loading branch information
rouault committed May 27, 2019
2 parents b737588 + 0768dde commit 3dddfe4
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 199 deletions.
35 changes: 35 additions & 0 deletions src/providers/wfs/qgswfscapabilities.cpp
Expand Up @@ -88,6 +88,32 @@ QString QgsWfsCapabilities::Capabilities::addPrefixIfNeeded( const QString &name
return mapUnprefixedTypenameToPrefixedTypename[name];
}

QString QgsWfsCapabilities::Capabilities::getNamespaceForTypename( const QString &name ) const
{
Q_FOREACH ( const QgsWfsCapabilities::FeatureType &f, featureTypes )
{
if ( f.name == name )
{
return f.nameSpace;
}
}
return "";
}

QString QgsWfsCapabilities::Capabilities::getNamespaceParameterValue( const QString &WFSVersion, const QString &typeName ) const
{
QString namespaces = getNamespaceForTypename( typeName );
bool tryNameSpacing = ( !namespaces.isEmpty() && typeName.contains( ':' ) );
if ( tryNameSpacing )
{
QString prefixOfTypename = typeName.section( ':', 0, 0 );
return "xmlns(" + prefixOfTypename +
( WFSVersion.startsWith( QLatin1String( "2.0" ) ) ? "," : "=" ) +
namespaces + ")";
}
return QString();
}

class CPLXMLTreeUniquePointer
{
public:
Expand Down Expand Up @@ -405,6 +431,14 @@ void QgsWfsCapabilities::capabilitiesReplyFinished()
if ( psFeatureTypeIter )
{
featureType.nameSpace = CPLGetXMLValue( psFeatureTypeIter, ( "xmlns:" + prefixOfTypename ).toUtf8().constData(), "" );
if ( featureType.nameSpace.isEmpty() )
{
//Try to look for namespace in Name tag (Seen in GO Publisher)
//<wfs:FeatureType>
// <wfs:Name xmlns:dagi="http://data.gov.dk/schemas/dagi/2/gml3sfp">dagi:Menighedsraadsafstemningsomraade</wfs:Name>
// <wfs:Title>Menighedsraadsafstemningsomraade</wfs:Title>
featureType.nameSpace = CPLGetXMLValue( psFeatureTypeIter, ( "wfs:Name.xmlns:" + prefixOfTypename ).toUtf8().constData(), "" );
}
}
}
}
Expand Down Expand Up @@ -809,3 +843,4 @@ QString QgsWfsCapabilities::errorMessageWithReason( const QString &reason )
{
return tr( "Download of capabilities failed: %1" ).arg( reason );
}

2 changes: 2 additions & 0 deletions src/providers/wfs/qgswfscapabilities.h
Expand Up @@ -108,6 +108,8 @@ class QgsWfsCapabilities : public QgsWfsRequest

void clear();
QString addPrefixIfNeeded( const QString &name ) const;
QString getNamespaceForTypename( const QString &name ) const;
QString getNamespaceParameterValue( const QString &WFSVersion, const QString &typeName ) const;
};

//! Returns parsed capabilities - requestCapabilities() must be called before
Expand Down
22 changes: 19 additions & 3 deletions src/providers/wfs/qgswfsdescribefeaturetype.cpp
Expand Up @@ -14,19 +14,35 @@
***************************************************************************/

#include "qgswfsdescribefeaturetype.h"
#include "qgsmessagelog.h"

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

bool QgsWFSDescribeFeatureType::requestFeatureType( const QString &WFSVersion,
const QString &typeName, bool bUsePlural )
const QString &typeName, const QgsWfsCapabilities::Capabilities &caps )
{
QUrl url( mUri.requestUrl( QStringLiteral( "DescribeFeatureType" ) ) );
url.addQueryItem( QStringLiteral( "VERSION" ), WFSVersion );
url.addQueryItem( bUsePlural ?
QStringLiteral( "TYPENAMES" ) : QStringLiteral( "TYPENAME" ), typeName );

QString namespaceValue( caps.getNamespaceParameterValue( WFSVersion, typeName ) );

if ( WFSVersion.startsWith( QLatin1String( "2.0" ) ) )
{
url.addQueryItem( QStringLiteral( "TYPENAMES" ), typeName );
if ( !namespaceValue.isEmpty() )
{
url.addQueryItem( QStringLiteral( "NAMESPACES" ), namespaceValue );
}
}

url.addQueryItem( QStringLiteral( "TYPENAME" ), typeName );
if ( !namespaceValue.isEmpty() )
{
url.addQueryItem( QStringLiteral( "NAMESPACE" ), namespaceValue );
}

return sendGET( url, true, false );
}
Expand Down
5 changes: 4 additions & 1 deletion src/providers/wfs/qgswfsdescribefeaturetype.h
Expand Up @@ -16,6 +16,7 @@
#define QGSWFSDESCRIBEFEATURETYPE_H

#include "qgswfsrequest.h"
#include "qgswfscapabilities.h"

//! Manages the DescribeFeatureType request
class QgsWFSDescribeFeatureType : public QgsWfsRequest
Expand All @@ -25,7 +26,9 @@ class QgsWFSDescribeFeatureType : public QgsWfsRequest
explicit QgsWFSDescribeFeatureType( QgsWFSDataSourceURI &uri );

//! Issue the request
bool requestFeatureType( const QString &WFSVersion, const QString &typeName, bool bUsePlural );
bool requestFeatureType( const QString &WFSVersion,
const QString &typeName,
const QgsWfsCapabilities::Capabilities &caps );

protected:
QString errorMessageWithReason( const QString &reason ) override;
Expand Down
26 changes: 6 additions & 20 deletions src/providers/wfs/qgswfsfeatureiterator.cpp
Expand Up @@ -222,22 +222,7 @@ QUrl QgsWFSFeatureDownloader::buildURL( qint64 startIndex, int maxFeatures, bool
if ( mShared->mLayerPropertiesList.isEmpty() )
{
typenames = mShared->mURI.typeName();

// Add NAMESPACES parameter for server that declare a namespace in the FeatureType of GetCapabilities response
// See https://issues.qgis.org/issues/14685
Q_FOREACH ( const QgsWfsCapabilities::FeatureType &f, mShared->mCaps.featureTypes )
{
if ( f.name == typenames )
{
if ( !f.nameSpace.isEmpty() && f.name.contains( ':' ) )
{
QString prefixOfTypename = f.name.section( ':', 0, 0 );
namespaces = "xmlns(" + prefixOfTypename + "," + f.nameSpace + ")";
}
break;
}
}

namespaces = mShared->mCaps.getNamespaceParameterValue( mShared->mWFSVersion, typenames );
}
else
{
Expand All @@ -250,8 +235,8 @@ QUrl QgsWFSFeatureDownloader::buildURL( qint64 startIndex, int maxFeatures, bool
}
if ( mShared->mWFSVersion.startsWith( QLatin1String( "2.0" ) ) )
getFeatureUrl.addQueryItem( QStringLiteral( "TYPENAMES" ), typenames );
else
getFeatureUrl.addQueryItem( QStringLiteral( "TYPENAME" ), typenames );
getFeatureUrl.addQueryItem( QStringLiteral( "TYPENAME" ), typenames );

if ( forHits )
{
getFeatureUrl.addQueryItem( QStringLiteral( "RESULTTYPE" ), QStringLiteral( "hits" ) );
Expand Down Expand Up @@ -408,8 +393,9 @@ QUrl QgsWFSFeatureDownloader::buildURL( qint64 startIndex, int maxFeatures, bool

if ( !namespaces.isEmpty() )
{
getFeatureUrl.addQueryItem( QStringLiteral( "NAMESPACES" ),
namespaces );
if ( mShared->mWFSVersion.startsWith( QLatin1String( "2.0" ) ) )
getFeatureUrl.addQueryItem( QStringLiteral( "NAMESPACES" ), namespaces );
getFeatureUrl.addQueryItem( QStringLiteral( "NAMESPACE" ), namespaces );
}

QgsDebugMsgLevel( QStringLiteral( "WFS GetFeature URL: %1" ).arg( getFeatureUrl.toDisplayString( ) ), 2 );
Expand Down
73 changes: 16 additions & 57 deletions src/providers/wfs/qgswfsprovider.cpp
Expand Up @@ -425,32 +425,17 @@ bool QgsWFSProvider::processSQL( const QString &sqlString, QString &errorMsg, QS
}

QgsWFSDescribeFeatureType describeFeatureType( mShared->mURI );
bool bUsePlural = false;
QByteArray response;
for ( int i = 0; i < 2; i++ )
if ( !describeFeatureType.requestFeatureType( mShared->mWFSVersion,
concatenatedTypenames, mShared->mCaps ) )
{
if ( !describeFeatureType.requestFeatureType( mShared->mWFSVersion,
concatenatedTypenames, bUsePlural ) )
{
errorMsg = tr( "DescribeFeatureType failed for url %1: %2" ).
arg( dataSourceUri(), describeFeatureType.errorMessage() );
return false;
}

response = describeFeatureType.response();
// "http://geoportal.samregion.ru/wfs12?SERVICE=WFS&REQUEST=DescribeFeatureType&TYPENAME=EC_1_132&VERSION=2.0.0"
// returns a <ExceptionText><![CDATA[Missing typeNames parameter]]></ExceptionText>
if ( i == 0 && response.indexOf( "<![CDATA[Missing typeNames parameter]]>" ) >= 0 )
{
QgsDebugMsg( QStringLiteral( "Server does not accept TYPENAME parameter for DescribeFeatureType. Re-trying with TYPENAMES" ) );
bUsePlural = true;
}
else
{
break;
}
errorMsg = tr( "DescribeFeatureType failed for url %1: %2" ).
arg( dataSourceUri(), describeFeatureType.errorMessage() );
return false;
}

QByteArray response = describeFeatureType.response();


QDomDocument describeFeatureDocument;
errorMsg.clear();
if ( !describeFeatureDocument.setContent( response, true, &errorMsg ) )
Expand Down Expand Up @@ -1285,43 +1270,17 @@ bool QgsWFSProvider::describeFeatureType( QString &geometryAttribute, QgsFields
fields.clear();

QgsWFSDescribeFeatureType describeFeatureType( mShared->mURI );
bool bUsePlural = false;
QByteArray response;
for ( int i = 0; i < 2; i++ )
{
if ( !describeFeatureType.requestFeatureType( mShared->mWFSVersion,
mShared->mURI.typeName(), bUsePlural ) )
{
QgsMessageLog::logMessage( tr( "DescribeFeatureType network request failed for url %1: %2" ).
arg( dataSourceUri(), describeFeatureType.errorMessage() ), tr( "WFS" ) );
return false;
}

response = describeFeatureType.response();
// "http://geoportal.samregion.ru/wfs12?SERVICE=WFS&REQUEST=DescribeFeatureType&TYPENAME=EC_1_132&VERSION=2.0.0"
// returns a <ExceptionText><![CDATA[Missing typeNames parameter]]></ExceptionText>
if ( i == 0 && response.indexOf( "<![CDATA[Missing typeNames parameter]]>" ) >= 0 )
{
QgsDebugMsg( QStringLiteral( "Server does not accept TYPENAME parameter for DescribeFeatureType. Re-trying with TYPENAMES" ) );
bUsePlural = true;
}
// "http://services.cuzk.cz/wfs/inspire-cp-wfs.asp?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=cp:CadastralParcel" returns
// <!--Generated by Marushka, version 4.2.5.0, GEOVAP, spol. s r.o., 31.05.2018.-->
// <ExceptionReport xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="1.0.0" xml:lang="en-US" xmlns="http://www.opengis.net/ows/1.1">
// <Exception exceptionCode="OperationProcessingFailed" />
// </ExceptionReport>
else if ( i == 0 && response.indexOf( "<!--Generated by Marushka" ) >= 0 &&
response.indexOf( "OperationProcessingFailed" ) >= 0 )
{
QgsDebugMsg( QStringLiteral( "Server does not accept TYPENAME parameter for DescribeFeatureType. Re-trying with TYPENAMES" ) );
bUsePlural = true;
}
else
{
break;
}
if ( !describeFeatureType.requestFeatureType( mShared->mWFSVersion,
mShared->mURI.typeName(), mShared->mCaps ) )
{
QgsMessageLog::logMessage( tr( "DescribeFeatureType network request failed for url %1: %2" ).
arg( dataSourceUri(), describeFeatureType.errorMessage() ), tr( "WFS" ) );
return false;
}

QByteArray response = describeFeatureType.response();

QDomDocument describeFeatureDocument;
QString errorMsg;
if ( !describeFeatureDocument.setContent( response, true, &errorMsg ) )
Expand Down
35 changes: 27 additions & 8 deletions src/providers/wfs/qgswfsshareddata.cpp
Expand Up @@ -1274,8 +1274,7 @@ int QgsWFSSharedData::getFeatureCount( bool issueRequestIfNeeded )
{
mGetFeatureHitsIssued = true;
QgsWFSFeatureHitsRequest request( mURI );
int featureCount = request.getFeatureCount( mWFSVersion, mWFSFilter );

int featureCount = request.getFeatureCount( mWFSVersion, mWFSFilter, mCaps );
{
QMutexLocker locker( &mMutex );
// Check the return value. Might be -1 in case of error, or might be
Expand Down Expand Up @@ -1347,14 +1346,26 @@ QgsWFSFeatureHitsRequest::QgsWFSFeatureHitsRequest( QgsWFSDataSourceURI &uri )
}

int QgsWFSFeatureHitsRequest::getFeatureCount( const QString &WFSVersion,
const QString &filter )
const QString &filter, const QgsWfsCapabilities::Capabilities &caps )
{
QString typeName = mUri.typeName();

QUrl getFeatureUrl( mUri.requestUrl( QStringLiteral( "GetFeature" ) ) );
getFeatureUrl.addQueryItem( QStringLiteral( "VERSION" ), WFSVersion );
if ( WFSVersion.startsWith( QLatin1String( "2.0" ) ) )
getFeatureUrl.addQueryItem( QStringLiteral( "TYPENAMES" ), mUri.typeName() );
else
getFeatureUrl.addQueryItem( QStringLiteral( "TYPENAME" ), mUri.typeName() );
{
getFeatureUrl.addQueryItem( QStringLiteral( "TYPENAMES" ), typeName );
}
getFeatureUrl.addQueryItem( QStringLiteral( "TYPENAME" ), typeName );

QString namespaceValue( caps.getNamespaceParameterValue( WFSVersion, typeName ) );
if ( !namespaceValue.isEmpty() )
{
if ( WFSVersion.startsWith( QLatin1String( "2.0" ) ) )
getFeatureUrl.addQueryItem( QStringLiteral( "NAMESPACES" ), namespaceValue );
getFeatureUrl.addQueryItem( QStringLiteral( "NAMESPACE" ), namespaceValue );
}

if ( !filter.isEmpty() )
{
getFeatureUrl.addQueryItem( QStringLiteral( "FILTER" ), filter );
Expand Down Expand Up @@ -1413,8 +1424,16 @@ QgsRectangle QgsWFSSingleFeatureRequest::getExtent()
getFeatureUrl.addQueryItem( QStringLiteral( "VERSION" ), mShared->mWFSVersion );
if ( mShared->mWFSVersion .startsWith( QLatin1String( "2.0" ) ) )
getFeatureUrl.addQueryItem( QStringLiteral( "TYPENAMES" ), mUri.typeName() );
else
getFeatureUrl.addQueryItem( QStringLiteral( "TYPENAME" ), mUri.typeName() );
getFeatureUrl.addQueryItem( QStringLiteral( "TYPENAME" ), mUri.typeName() );

QString namespaceValue( mShared->mCaps.getNamespaceParameterValue( mShared->mWFSVersion, mUri.typeName() ) );
if ( !namespaceValue.isEmpty() )
{
if ( mShared->mWFSVersion.startsWith( QLatin1String( "2.0" ) ) )
getFeatureUrl.addQueryItem( QStringLiteral( "NAMESPACES" ), namespaceValue );
getFeatureUrl.addQueryItem( QStringLiteral( "NAMESPACE" ), namespaceValue );
}

if ( mShared->mWFSVersion .startsWith( QLatin1String( "2.0" ) ) )
getFeatureUrl.addQueryItem( QStringLiteral( "COUNT" ), QString::number( 1 ) );
else
Expand Down
2 changes: 1 addition & 1 deletion src/providers/wfs/qgswfsshareddata.h
Expand Up @@ -289,7 +289,7 @@ class QgsWFSFeatureHitsRequest: public QgsWfsRequest
explicit QgsWFSFeatureHitsRequest( QgsWFSDataSourceURI &uri );

//! Returns the feature count, or -1 in case of error
int getFeatureCount( const QString &WFSVersion, const QString &filter );
int getFeatureCount( const QString &WFSVersion, const QString &filter, const QgsWfsCapabilities::Capabilities &caps );

protected:
QString errorMessageWithReason( const QString &reason ) override;
Expand Down

0 comments on commit 3dddfe4

Please sign in to comment.