Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
WFS: try harder guessing the layer geometry type when a filter return…
…s 0 features (fixes #43950)
  • Loading branch information
rouault authored and nyalldawson committed Sep 14, 2021
1 parent cf79561 commit 322181f
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 2 deletions.
19 changes: 17 additions & 2 deletions src/providers/wfs/qgswfsprovider.cpp
Expand Up @@ -120,8 +120,7 @@ QgsWFSProvider::QgsWFSProvider( const QString &uri, const ProviderOptions &optio
return;
}

//Failed to detect feature type from describeFeatureType -> get first feature from layer to detect type
if ( mShared->mWKBType == QgsWkbTypes::Unknown )
const auto GetGeometryTypeFromOneFeature = [&]()
{
const bool requestMadeFromMainThread = QThread::currentThread() == QApplication::instance()->thread();
auto downloader = std::make_unique<QgsFeatureDownloader>();
Expand All @@ -140,6 +139,22 @@ QgsWFSProvider::QgsWFSProvider( const QString &uri, const ProviderOptions &optio
}
downloader->run( false, /* serialize features */
1 /* maxfeatures */ );
};

//Failed to detect feature type from describeFeatureType -> get first feature from layer to detect type
if ( mShared->mWKBType == QgsWkbTypes::Unknown )
{
GetGeometryTypeFromOneFeature();

// If we still didn't get the geometry type, and have a filter, temporarily
// disable the filter.
// See https://github.com/qgis/QGIS/issues/43950
if ( mShared->mWKBType == QgsWkbTypes::Unknown && !mSubsetString.isEmpty() )
{
const QString oldFilter = mShared->setWFSFilter( QString() );
GetGeometryTypeFromOneFeature();
mShared->setWFSFilter( oldFilter );
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/providers/wfs/qgswfsshareddata.h
Expand Up @@ -48,6 +48,9 @@ class QgsWFSSharedData : public QObject, public QgsBackgroundCachedSharedData

const QgsWfsCapabilities::Capabilities &capabilities() const { return mCaps; }

//! Set a new filter and return the previous one. Only used to temporarily disable filtering when trying to get layer geometry type.
QString setWFSFilter( const QString &newFilter ) { QString oldFilter = mWFSFilter; mWFSFilter = newFilter; return oldFilter; }

signals:

//! Raise error
Expand Down
80 changes: 80 additions & 0 deletions tests/src/python/test_provider_wfs.py
Expand Up @@ -4776,6 +4776,86 @@ def testWFST11(self):
self.assertTrue(vl.dataProvider().deleteFeatures([1]))
self.assertEqual(vl.featureCount(), 0)

def testSelectZeroFeature(self):
"""Test a layer with a filter that returns 0 feature. See https://github.com/qgis/QGIS/issues/43950 """

endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_select_zero_feature'

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="http://www.opengis.net/wfs/2.0" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:gml="http://schemas.opengis.net/gml/3.2" xmlns:fes="http://www.opengis.net/fes/2.0">
<FeatureTypeList>
<FeatureType>
<Name>my:typename</Name>
<Title>Title</Title>
<Abstract>Abstract</Abstract>
<DefaultCRS>urn:ogc:def:crs:EPSG::4326</DefaultCRS>
<WGS84BoundingBox>
<LowerCorner>-71.123 66.33</LowerCorner>
<UpperCorner>-65.32 78.3</UpperCorner>
</WGS84BoundingBox>
</FeatureType>
</FeatureTypeList>
</wfs:WFS_Capabilities>""".encode('UTF-8'))

with open(sanitize(endpoint,
'?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=my:typename&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:element maxOccurs="1" minOccurs="0" name="geometry" nillable="true" type="gml:GeometryPropertyType"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="typename" substitutionGroup="gml:_Feature" type="my:typenameType"/>
</xsd:schema>
""".encode('UTF-8'))

with open(sanitize(endpoint,
"""?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326&FILTER=<fes:Filter xmlns:fes="http://www.opengis.net/fes/2.0">
<fes:PropertyIsEqualTo>
<fes:ValueReference>intfield</fes:ValueReference>
<fes:Literal>-1</fes:Literal>
</fes:PropertyIsEqualTo>
</fes:Filter>
"""),
'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">
</wfs:FeatureCollection>""".encode('UTF-8'))

with open(sanitize(endpoint,
"""?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326"""),
'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">
<wfs:member>
<my:typename gml:id="typename.0">
<my:intfield>1</my:intfield>
<my:geometry><gml:Point srsName="urn:ogc:def:crs:EPSG::4326" gml:id="typename.geom.0"><gml:pos>78.3 -65.32</gml:pos></gml:Point></my:geometry>
</my:typename>
</wfs:member>
</wfs:FeatureCollection>""".encode('UTF-8'))

vl = QgsVectorLayer(
"url='http://" + endpoint + "' typename='my:typename' version='2.0.0' sql=SELECT * FROM \"my:typename\" WHERE intfield = -1",
'test', 'WFS')
self.assertTrue(vl.isValid())
self.assertEqual(vl.wkbType(), QgsWkbTypes.Point)


if __name__ == '__main__':
unittest.main()

0 comments on commit 322181f

Please sign in to comment.