Skip to content

Commit

Permalink
[WFS provider] Include namespace prefix and URI in FILTER ...
Browse files Browse the repository at this point in the history
computed from setSubsetString() when a namespace is declared on <<wfs:FeatureType> in GetCapabilities response

Fixes #49121

Follow-up to #43957 / PR #45043
  • Loading branch information
rouault authored and nyalldawson committed Dec 20, 2022
1 parent 259a65c commit 50ad97d
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 43 deletions.
48 changes: 38 additions & 10 deletions src/core/qgsogcutils.cpp
Expand Up @@ -48,6 +48,8 @@
QgsOgcUtilsExprToFilter::QgsOgcUtilsExprToFilter( QDomDocument &doc,
QgsOgcUtils::GMLVersion gmlVersion,
QgsOgcUtils::FilterVersion filterVersion,
const QString &namespacePrefix,
const QString &namespaceURI,
const QString &geometryName,
const QString &srsName,
bool honourAxisOrientation,
Expand All @@ -56,6 +58,8 @@ QgsOgcUtilsExprToFilter::QgsOgcUtilsExprToFilter( QDomDocument &doc,
, mGMLUsed( false )
, mGMLVersion( gmlVersion )
, mFilterVersion( filterVersion )
, mNamespacePrefix( namespacePrefix )
, mNamespaceURI( namespaceURI )
, mGeometryName( geometryName )
, mSrsName( srsName )
, mInvertAxisOrientation( invertAxisOrientation )
Expand Down Expand Up @@ -1854,7 +1858,7 @@ QgsExpressionNodeBinaryOperator *QgsOgcUtils::nodePropertyIsNullFromOgcFilter( Q

QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression &exp, QDomDocument &doc, QString *errorMessage )
{
return expressionToOgcFilter( exp, doc, GML_2_1_2, FILTER_OGC_1_0,
return expressionToOgcFilter( exp, doc, GML_2_1_2, FILTER_OGC_1_0, QString(), QString(),
QStringLiteral( "geometry" ), QString(), false, false, errorMessage );
}

Expand All @@ -1868,6 +1872,8 @@ QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression &expression,
QDomDocument &doc,
GMLVersion gmlVersion,
FilterVersion filterVersion,
const QString &namespacePrefix,
const QString &namespaceURI,
const QString &geometryName,
const QString &srsName,
bool honourAxisOrientation,
Expand All @@ -1881,7 +1887,7 @@ QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression &expression,

QgsExpressionContext context;
context << QgsExpressionContextUtils::globalScope();
QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, namespacePrefix, namespaceURI, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
const QDomElement exprRootElem = utils.expressionNodeToOgcFilter( exp.rootNode(), &exp, &context );
if ( errorMessage )
*errorMessage = utils.errorMessage();
Expand All @@ -1901,6 +1907,14 @@ QDomElement QgsOgcUtils::expressionToOgcFilter( const QgsExpression &expression,
attr.setValue( GML_NAMESPACE );
filterElem.setAttributeNode( attr );
}

if ( !namespacePrefix.isEmpty() && !namespaceURI.isEmpty() )
{
QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:" ) + namespacePrefix );
attr.setValue( namespaceURI );
filterElem.setAttributeNode( attr );
}

filterElem.appendChild( exprRootElem );
return filterElem;
}
Expand Down Expand Up @@ -1930,7 +1944,7 @@ QDomElement QgsOgcUtils::expressionToOgcExpression( const QgsExpression &express
case QgsExpressionNode::ntLiteral:
case QgsExpressionNode::ntColumnRef:
{
QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
QgsOgcUtilsExprToFilter utils( doc, gmlVersion, filterVersion, QString(), QString(), geometryName, srsName, honourAxisOrientation, invertAxisOrientation );
const QDomElement exprRootElem = utils.expressionNodeToOgcFilter( node, &exp, &context );

if ( errorMessage )
Expand Down Expand Up @@ -2180,7 +2194,10 @@ QDomElement QgsOgcUtilsExprToFilter::expressionColumnRefToOgcFilter( const QgsEx
Q_UNUSED( expression )
Q_UNUSED( context )
QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
propElem.appendChild( mDoc.createTextNode( node->name() ) );
QString columnRef( node->name() );
if ( !mNamespacePrefix.isEmpty() && !mNamespaceURI.isEmpty() )
columnRef = mNamespacePrefix + QStringLiteral( ":" ) + columnRef;
propElem.appendChild( mDoc.createTextNode( columnRef ) );
return propElem;
}

Expand Down Expand Up @@ -2292,11 +2309,19 @@ QDomElement QgsOgcUtilsExprToFilter::expressionFunctionToOgcFilter( const QgsExp
QgsOgcUtils::rectangleToGMLBox( &rect, mDoc, mSrsName, mInvertAxisOrientation ) :
QgsOgcUtils::rectangleToGMLEnvelope( &rect, mDoc, mSrsName, mInvertAxisOrientation );

QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
geomProperty.appendChild( mDoc.createTextNode( mGeometryName ) );

QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":BBOX" );
funcElem.appendChild( geomProperty );

if ( !mGeometryName.isEmpty() )
{
// Geometry column is optional for a BBOX filter.
QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
QString columnRef( mGeometryName );
if ( !mNamespacePrefix.isEmpty() && !mNamespaceURI.isEmpty() )
columnRef = mNamespacePrefix + QStringLiteral( ":" ) + columnRef;
geomProperty.appendChild( mDoc.createTextNode( columnRef ) );

funcElem.appendChild( geomProperty );
}
funcElem.appendChild( elemBox );
return funcElem;
}
Expand Down Expand Up @@ -2378,7 +2403,10 @@ QDomElement QgsOgcUtilsExprToFilter::expressionFunctionToOgcFilter( const QgsExp

QDomElement funcElem = mDoc.createElement( mFilterPrefix + ":" + tagNameForSpatialOperator( fd->name() ) );
QDomElement geomProperty = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
geomProperty.appendChild( mDoc.createTextNode( mGeometryName ) );
QString columnRef( mGeometryName );
if ( !mNamespacePrefix.isEmpty() && !mNamespaceURI.isEmpty() )
columnRef = mNamespacePrefix + QStringLiteral( ":" ) + columnRef;
geomProperty.appendChild( mDoc.createTextNode( columnRef ) );
funcElem.appendChild( geomProperty );
funcElem.appendChild( otherGeomElem );
return funcElem;
Expand Down Expand Up @@ -2629,7 +2657,7 @@ QDomElement QgsOgcUtilsSQLStatementToFilter::toOgcFilter( const QgsSQLStatement:
QDomElement propElem = mDoc.createElement( mFilterPrefix + ":" + mPropertyName );
if ( node->tableName().isEmpty() || mLayerProperties.size() == 1 )
{
if ( mLayerProperties.size() == 1 && !mLayerProperties[0].mNamespacePrefix.isEmpty() )
if ( mLayerProperties.size() == 1 && !mLayerProperties[0].mNamespacePrefix.isEmpty() && !mLayerProperties[0].mNamespaceURI.isEmpty() )
propElem.appendChild( mDoc.createTextNode(
mLayerProperties[0].mNamespacePrefix + QStringLiteral( ":" ) + node->name() ) );
else
Expand Down
6 changes: 6 additions & 0 deletions src/core/qgsogcutils.h
Expand Up @@ -203,6 +203,8 @@ class CORE_EXPORT QgsOgcUtils
QDomDocument &doc,
QgsOgcUtils::GMLVersion gmlVersion,
FilterVersion filterVersion,
const QString &namespacePrefix,
const QString &namespaceURI,
const QString &geometryName,
const QString &srsName,
bool honourAxisOrientation,
Expand Down Expand Up @@ -370,6 +372,8 @@ class QgsOgcUtilsExprToFilter
QgsOgcUtilsExprToFilter( QDomDocument &doc,
QgsOgcUtils::GMLVersion gmlVersion,
QgsOgcUtils::FilterVersion filterVersion,
const QString &namespacePrefix,
const QString &namespaceURI,
const QString &geometryName,
const QString &srsName,
bool honourAxisOrientation,
Expand All @@ -389,6 +393,8 @@ class QgsOgcUtilsExprToFilter
bool mGMLUsed;
QgsOgcUtils::GMLVersion mGMLVersion;
QgsOgcUtils::FilterVersion mFilterVersion;
const QString &mNamespacePrefix;
const QString &mNamespaceURI;
const QString &mGeometryName;
const QString &mSrsName;
bool mInvertAxisOrientation;
Expand Down
33 changes: 22 additions & 11 deletions src/providers/wfs/qgswfsfeatureiterator.cpp
Expand Up @@ -211,10 +211,12 @@ QUrl QgsWFSFeatureDownloaderImpl::buildURL( qint64 startIndex, long long maxFeat
QString geometryAttribute( mShared->mGeometryAttribute );
if ( mShared->mLayerPropertiesList.size() > 1 )
geometryAttribute = mShared->mURI.typeName() + "/" + geometryAttribute;
else if ( mShared->mLayerPropertiesList.size() == 1 && !mShared->mLayerPropertiesList[0].mNamespacePrefix.isEmpty() )
geometryAttribute = mShared->mLayerPropertiesList[0].mNamespacePrefix + QStringLiteral( ":" ) + geometryAttribute;

QDomElement bboxElem = QgsOgcUtils::expressionToOgcFilter( bboxExp, doc,
gmlVersion, filterVersion, geometryAttribute, mShared->srsName(),
gmlVersion, filterVersion,
mShared->mLayerPropertiesList.size() == 1 ? mShared->mLayerPropertiesList[0].mNamespacePrefix : QString(),
mShared->mLayerPropertiesList.size() == 1 ? mShared->mLayerPropertiesList[0].mNamespaceURI : QString(),
geometryAttribute, mShared->srsName(),
honourAxisOrientation, mShared->mURI.invertAxisOrientation() );
doc.appendChild( bboxElem );
QDomNode bboxNode = bboxElem.firstChildElement();
Expand All @@ -230,16 +232,25 @@ QUrl QgsWFSFeatureDownloaderImpl::buildURL( qint64 startIndex, long long maxFeat
andElem.appendChild( filterNode );
doc.firstChildElement().appendChild( andElem );

QSet<QString> setNamespaceURI;
for ( const QgsOgcUtils::LayerProperties &props : std::as_const( mShared->mLayerPropertiesList ) )
if ( mShared->mLayerPropertiesList.size() == 1 &&
doc.firstChildElement().hasAttribute( QStringLiteral( "xmlns:" ) + mShared->mLayerPropertiesList[0].mNamespacePrefix ) )
{
// nothing to do
}
else
{
if ( !props.mNamespacePrefix.isEmpty() && !props.mNamespaceURI.isEmpty() &&
!setNamespaceURI.contains( props.mNamespaceURI ) )
// add xmls:PREFIX=URI attributes to top element
QSet<QString> setNamespaceURI;
for ( const QgsOgcUtils::LayerProperties &props : std::as_const( mShared->mLayerPropertiesList ) )
{
setNamespaceURI.insert( props.mNamespaceURI );
QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:" ) + props.mNamespacePrefix );
attr.setValue( props.mNamespaceURI );
doc.firstChildElement().setAttributeNode( attr );
if ( !props.mNamespacePrefix.isEmpty() && !props.mNamespaceURI.isEmpty() &&
!setNamespaceURI.contains( props.mNamespaceURI ) )
{
setNamespaceURI.insert( props.mNamespaceURI );
QDomAttr attr = doc.createAttribute( QStringLiteral( "xmlns:" ) + props.mNamespacePrefix );
attr.setValue( props.mNamespaceURI );
doc.firstChildElement().setAttributeNode( attr );
}
}
}

Expand Down
56 changes: 41 additions & 15 deletions src/providers/wfs/qgswfsprovider.cpp
Expand Up @@ -111,6 +111,7 @@ QgsWFSProvider::QgsWFSProvider( const QString &uri, const ProviderOptions &optio
return;
}
mThisTypenameFields = mShared->mFields;
mLayerPropertiesListWhenNoSqlRequest = mShared->mLayerPropertiesList;
}

if ( !mShared->computeFilter( mProcessSQLErrorMsg ) )
Expand Down Expand Up @@ -488,7 +489,6 @@ bool QgsWFSProvider::processSQL( const QString &sqlString, QString &errorMsg, QS
return false;
}

mShared->mLayerPropertiesList.clear();
QMap < QString, QgsFields > mapTypenameToFields;
QMap < QString, QString > mapTypenameToGeometryAttribute;
for ( const QString &typeName : std::as_const( typenameList ) )
Expand All @@ -507,28 +507,17 @@ bool QgsWFSProvider::processSQL( const QString &sqlString, QString &errorMsg, QS

mapTypenameToFields[typeName] = fields;
mapTypenameToGeometryAttribute[typeName] = geometryAttribute;

if ( typeName == mShared->mURI.typeName() )
{
mShared->mGeometryAttribute = geometryAttribute;
mShared->mWKBType = geomType;
mThisTypenameFields = fields;
}

QgsOgcUtils::LayerProperties layerProperties;
layerProperties.mName = typeName;
layerProperties.mGeometryAttribute = geometryAttribute;
if ( typeName == mShared->mURI.typeName() )
layerProperties.mSRSName = mShared->srsName();

if ( typeName.contains( ':' ) )
{
layerProperties.mNamespaceURI = mShared->mCaps.getNamespaceForTypename( typeName );
layerProperties.mNamespacePrefix = QgsWFSUtils::nameSpacePrefix( typeName );
}

mShared->mLayerPropertiesList << layerProperties;
}

setLayerPropertiesListFromDescribeFeature( describeFeatureDocument, typenameList, errorMsg );

const QString &defaultTypeName = mShared->mURI.typeName();
QgsWFSProviderSQLColumnRefValidator oColumnValidator(
mShared->mCaps,
Expand Down Expand Up @@ -692,6 +681,40 @@ bool QgsWFSProvider::processSQL( const QString &sqlString, QString &errorMsg, QS
return true;
}

bool QgsWFSProvider::setLayerPropertiesListFromDescribeFeature( QDomDocument &describeFeatureDocument, const QStringList &typenameList, QString &errorMsg )
{
mShared->mLayerPropertiesList.clear();
for ( const QString &typeName : typenameList )
{
QString geometryAttribute;
QgsFields fields;
QgsWkbTypes::Type geomType;
if ( !readAttributesFromSchema( describeFeatureDocument,
typeName,
geometryAttribute, fields, geomType, errorMsg ) )
{
errorMsg = tr( "Analysis of DescribeFeatureType response failed for url %1, typeName %2: %3" ).
arg( dataSourceUri(), typeName, errorMsg );
return false;
}

QgsOgcUtils::LayerProperties layerProperties;
layerProperties.mName = typeName;
layerProperties.mGeometryAttribute = geometryAttribute;
if ( typeName == mShared->mURI.typeName() )
layerProperties.mSRSName = mShared->srsName();

if ( typeName.contains( ':' ) )
{
layerProperties.mNamespaceURI = mShared->mCaps.getNamespaceForTypename( typeName );
layerProperties.mNamespacePrefix = QgsWFSUtils::nameSpacePrefix( typeName );
}

mShared->mLayerPropertiesList << layerProperties;
}
return true;
}

void QgsWFSProvider::pushErrorSlot( const QString &errorMsg )
{
pushError( errorMsg );
Expand Down Expand Up @@ -802,6 +825,7 @@ bool QgsWFSProvider::setSubsetString( const QString &theSQL, bool updateFeatureC
}
else
{
mShared->mLayerPropertiesList = mLayerPropertiesListWhenNoSqlRequest;
mShared->mURI.setSql( QString() );
mShared->mURI.setFilter( theSQL );
}
Expand Down Expand Up @@ -1372,6 +1396,8 @@ bool QgsWFSProvider::describeFeatureType( QString &geometryAttribute, QgsFields
return false;
}

setLayerPropertiesListFromDescribeFeature( describeFeatureDocument, {mShared->mURI.typeName()}, errorMsg );

return true;
}

Expand Down
7 changes: 7 additions & 0 deletions src/providers/wfs/qgswfsprovider.h
Expand Up @@ -23,6 +23,7 @@
#include "qgis.h"
#include "qgsrectangle.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsogcutils.h"
#include "qgsvectordataprovider.h"
#include "qgswfscapabilities.h"
#include "qgswfsfeatureiterator.h"
Expand Down Expand Up @@ -149,6 +150,12 @@ class QgsWFSProvider final: public QgsVectorDataProvider
*/
QDomElement geometryElement( const QgsGeometry &geometry, QDomDocument &transactionDoc );

//! Set mShared->mLayerPropertiesList from describeFeatureDocument
bool setLayerPropertiesListFromDescribeFeature( QDomDocument &describeFeatureDocument, const QStringList &typenameList, QString &errorMsg );

//! backup of mShared->mLayerPropertiesList on the feature type when there is no sql request
QList< QgsOgcUtils::LayerProperties > mLayerPropertiesListWhenNoSqlRequest;

protected:

//! String used to define a subset of the layer
Expand Down
7 changes: 5 additions & 2 deletions src/providers/wfs/qgswfsshareddata.cpp
Expand Up @@ -161,7 +161,10 @@ bool QgsWFSSharedData::computeFilter( QString &errorMsg )
const QgsExpression filterExpression( filter );

const QDomElement filterElem = QgsOgcUtils::expressionToOgcFilter(
filterExpression, filterDoc, gmlVersion, filterVersion, mGeometryAttribute,
filterExpression, filterDoc, gmlVersion, filterVersion,
mLayerPropertiesList.size() == 1 ? mLayerPropertiesList[0].mNamespacePrefix : QString(),
mLayerPropertiesList.size() == 1 ? mLayerPropertiesList[0].mNamespaceURI : QString(),
mGeometryAttribute,
srsName(), honourAxisOrientation, mURI.invertAxisOrientation(),
&errorMsg );

Expand Down Expand Up @@ -196,7 +199,7 @@ QgsGmlStreamingParser *QgsWFSSharedData::createParser() const
axisOrientationLogic = QgsGmlStreamingParser::Ignore_EPSG;
}

if ( !mLayerPropertiesList.isEmpty() )
if ( mLayerPropertiesList.size() > 1 )
{
QList< QgsGmlStreamingParser::LayerProperties > layerPropertiesList;
const auto constMLayerPropertiesList = mLayerPropertiesList;
Expand Down

0 comments on commit 50ad97d

Please sign in to comment.