mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-16 00:03:12 -04:00
[WFS provider] Add NAMESPACES= parameter to GetFeature requests when needed (fixes #14685)
This commit is contained in:
parent
fe4f1500d9
commit
94fa450efd
@ -66,6 +66,7 @@ INCLUDE_DIRECTORIES(SYSTEM
|
||||
${QTKEYCHAIN_INCLUDE_DIR}
|
||||
${GDAL_INCLUDE_DIR} # needed by qgsvectorfilewriter.h
|
||||
${SQLITE3_INCLUDE_DIR}
|
||||
${GDAL_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
IF (WITH_GUI)
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include "qgsogcutils.h"
|
||||
#include "qgssettings.h"
|
||||
|
||||
#include <cpl_minixml.h>
|
||||
|
||||
#include <QDomDocument>
|
||||
#include <QStringList>
|
||||
|
||||
@ -81,6 +83,35 @@ QString QgsWfsCapabilities::Capabilities::addPrefixIfNeeded( const QString &name
|
||||
return mapUnprefixedTypenameToPrefixedTypename[name];
|
||||
}
|
||||
|
||||
class CPLXMLTreeUniquePointer
|
||||
{
|
||||
public:
|
||||
//! Constructor
|
||||
explicit CPLXMLTreeUniquePointer( CPLXMLNode *data ) { the_data_ = data; }
|
||||
|
||||
//! Destructor
|
||||
~CPLXMLTreeUniquePointer()
|
||||
{
|
||||
if ( the_data_ ) CPLDestroyXMLNode( the_data_ );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node pointer/
|
||||
* Modifying the contents pointed to by the return is allowed.
|
||||
* @return the node pointer */
|
||||
CPLXMLNode *get() const { return the_data_; }
|
||||
|
||||
/**
|
||||
* Returns the node pointer/
|
||||
* Modifying the contents pointed to by the return is allowed.
|
||||
* @return the node pointer */
|
||||
CPLXMLNode *operator->() const { return get(); }
|
||||
|
||||
private:
|
||||
CPLXMLNode *the_data_;
|
||||
};
|
||||
|
||||
|
||||
void QgsWfsCapabilities::capabilitiesReplyFinished()
|
||||
{
|
||||
const QByteArray &buffer = mResponse;
|
||||
@ -98,6 +129,8 @@ void QgsWfsCapabilities::capabilitiesReplyFinished()
|
||||
return;
|
||||
}
|
||||
|
||||
CPLXMLTreeUniquePointer oCPLXML( CPLParseXMLString( buffer.constData() ) );
|
||||
|
||||
QDomElement doc = capabilitiesDocument.documentElement();
|
||||
|
||||
// handle exceptions
|
||||
@ -303,6 +336,20 @@ void QgsWfsCapabilities::capabilitiesReplyFinished()
|
||||
}
|
||||
}
|
||||
|
||||
// This is messy, but there's apparently no way to get the xmlns:ci attribute value with QDom API
|
||||
// in snippets like
|
||||
// <wfs:FeatureType xmlns:ci="http://www.interactive-instruments.de/namespaces/demo/cities/4.0/cities">
|
||||
// <wfs:Name>ci:City</wfs:Name>
|
||||
// so fallback to using GDAL XML parser for that...
|
||||
|
||||
CPLXMLNode *psFeatureTypeIter = nullptr;
|
||||
if ( oCPLXML.get() )
|
||||
{
|
||||
psFeatureTypeIter = CPLGetXMLNode( oCPLXML.get(), "=wfs:WFS_Capabilities.wfs:FeatureTypeList" );
|
||||
if ( psFeatureTypeIter )
|
||||
psFeatureTypeIter = psFeatureTypeIter->psChild;
|
||||
}
|
||||
|
||||
// get the <FeatureType> elements
|
||||
QDomNodeList featureTypeList = featureTypeListElem.elementsByTagName( QStringLiteral( "FeatureType" ) );
|
||||
for ( int i = 0; i < featureTypeList.size(); ++i )
|
||||
@ -310,12 +357,35 @@ void QgsWfsCapabilities::capabilitiesReplyFinished()
|
||||
FeatureType featureType;
|
||||
QDomElement featureTypeElem = featureTypeList.at( i ).toElement();
|
||||
|
||||
for ( ; psFeatureTypeIter; psFeatureTypeIter = psFeatureTypeIter->psNext )
|
||||
{
|
||||
if ( psFeatureTypeIter->eType != CXT_Element )
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
//Name
|
||||
QDomNodeList nameList = featureTypeElem.elementsByTagName( QStringLiteral( "Name" ) );
|
||||
if ( nameList.length() > 0 )
|
||||
{
|
||||
featureType.name = nameList.at( 0 ).toElement().text();
|
||||
|
||||
QgsDebugMsg( QString( "featureType.name = %1" ) . arg( featureType.name ) );
|
||||
if ( featureType.name.contains( ':' ) )
|
||||
{
|
||||
QString prefixOfTypename = featureType.name.section( ':', 0, 0 );
|
||||
|
||||
// for some Deegree servers that requires a NAMESPACES parameter for GetFeature
|
||||
if ( psFeatureTypeIter )
|
||||
{
|
||||
featureType.nameSpace = CPLGetXMLValue( psFeatureTypeIter, ( "xmlns:" + prefixOfTypename ).toUtf8().constData(), "" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( psFeatureTypeIter )
|
||||
psFeatureTypeIter = psFeatureTypeIter->psNext;
|
||||
|
||||
//Title
|
||||
QDomNodeList titleList = featureTypeElem.elementsByTagName( QStringLiteral( "Title" ) );
|
||||
if ( titleList.length() > 0 )
|
||||
|
@ -38,6 +38,7 @@ class QgsWfsCapabilities : public QgsWfsRequest
|
||||
FeatureType() = default;
|
||||
|
||||
QString name;
|
||||
QString nameSpace; // for some Deegree servers that requires a NAMESPACES parameter for GetFeature
|
||||
QString title;
|
||||
QString abstract;
|
||||
QList<QString> crslist; // first is default
|
||||
|
@ -191,9 +191,26 @@ QUrl QgsWFSFeatureDownloader::buildURL( int startIndex, int maxFeatures, bool fo
|
||||
getFeatureUrl.addQueryItem( QStringLiteral( "VERSION" ), mShared->mWFSVersion );
|
||||
|
||||
QString typenames;
|
||||
QString namespaces;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -351,6 +368,12 @@ QUrl QgsWFSFeatureDownloader::buildURL( int startIndex, int maxFeatures, bool fo
|
||||
}
|
||||
}
|
||||
|
||||
if ( !namespaces.isEmpty() )
|
||||
{
|
||||
getFeatureUrl.addQueryItem( QStringLiteral( "NAMESPACES" ),
|
||||
namespaces );
|
||||
}
|
||||
|
||||
return getFeatureUrl;
|
||||
}
|
||||
|
||||
|
@ -2571,6 +2571,65 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase):
|
||||
got = got_f[0].geometry().constGet()
|
||||
self.assertEqual((got.x(), got.y()), (426858.0, 5427937.0))
|
||||
|
||||
def testGetFeatureWithNamespaces(self):
|
||||
''' test https://issues.qgis.org/issues/14685 '''
|
||||
|
||||
endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_getfeature_with_namespaces'
|
||||
|
||||
with open(sanitize(endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?VERSION=2.0.0'), 'wb') as f:
|
||||
f.write("""
|
||||
<wfs:WFS_Capabilities version="2.0.0" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:ows="http://www.opengis.net/ows/1.1">
|
||||
<wfs:FeatureTypeList>
|
||||
<wfs:FeatureType xmlns:my="http://my">
|
||||
<wfs:Name>my:typename</wfs:Name>
|
||||
<wfs:Title>Title</wfs:Title>
|
||||
<wfs:Abstract>Abstract</wfs:Abstract>
|
||||
<wfs:SRS>EPSG:32631</wfs:SRS>
|
||||
<ows:WGS84BoundingBox>
|
||||
<ows:LowerCorner>0 40</ows:LowerCorner>
|
||||
<ows:UpperCorner>15 50</ows:UpperCorner>
|
||||
</ows:WGS84BoundingBox>
|
||||
</wfs:FeatureType>
|
||||
</wfs:FeatureTypeList>
|
||||
</wfs:WFS_Capabilities>""".encode('UTF-8'))
|
||||
|
||||
with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename'), 'wb') as f:
|
||||
f.write("""
|
||||
<xsd:schema xmlns:my="http://my" xmlns:gml="http://www.opengis.net/gml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://my">
|
||||
<xsd:import namespace="http://www.opengis.net/gml"/>
|
||||
<xsd:complexType name="typenameType">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="gml:AbstractFeatureType">
|
||||
<xsd:sequence>
|
||||
<xsd:element maxOccurs="1" minOccurs="0" name="intfield" nillable="true" type="xsd:int"/>
|
||||
</xsd:sequence>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
<xsd:element name="typename" substitutionGroup="gml:_Feature" type="my:typenameType"/>
|
||||
</xsd:schema>
|
||||
""".encode('UTF-8'))
|
||||
|
||||
vl = QgsVectorLayer("url='http://" + endpoint + "' typename='my:typename' version='2.0.0'", 'test', 'WFS')
|
||||
self.assertTrue(vl.isValid())
|
||||
self.assertEqual(len(vl.fields()), 1)
|
||||
|
||||
with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::32631&NAMESPACES=xmlns(my,http://my)'), 'wb') as f:
|
||||
f.write("""
|
||||
<wfs:FeatureCollection
|
||||
xmlns:wfs="http://www.opengis.net/wfs/2.0"
|
||||
xmlns:gml="http://www.opengis.net/gml/3.2"
|
||||
xmlns:my="http://my">
|
||||
<gml:featureMember>
|
||||
<my:typename fid="typename.0">
|
||||
<my:intfield>1</my:intfield>
|
||||
</my:typename>
|
||||
</gml:featureMember>
|
||||
</wfs:FeatureCollection>""".encode('UTF-8'))
|
||||
|
||||
values = [f['intfield'] for f in vl.getFeatures()]
|
||||
self.assertEqual(values, [1])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
Loading…
x
Reference in New Issue
Block a user