Skip to content

Commit 2341fa8

Browse files
authored
Merge pull request #9118 from rouault/fix_20742
Correctly compute layer extent when zooming out layer [WFS provider]
2 parents 20d8528 + 32d9028 commit 2341fa8

File tree

2 files changed

+114
-24
lines changed

2 files changed

+114
-24
lines changed

src/providers/wfs/qgswfsshareddata.cpp

+26-24
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,32 @@ void QgsWFSSharedData::serializeFeatures( QVector<QgsWFSFeatureGmlIdPair> &featu
925925
{
926926
const QgsFeature &gmlFeature = featPair.first;
927927

928+
QgsFeature cachedFeature;
929+
cachedFeature.initAttributes( dataProviderFields.size() );
930+
931+
// copy the geometry
932+
// Do this now to update localComputedExtent, even if we skip the feature
933+
// afterwards as being already downloaded.
934+
QgsGeometry geometry = gmlFeature.geometry();
935+
if ( !mGeometryAttribute.isEmpty() && !geometry.isNull() )
936+
{
937+
QByteArray array( geometry.asWkb() );
938+
939+
cachedFeature.setAttribute( hexwkbGeomIdx, QVariant( QString( array.toHex().data() ) ) );
940+
941+
QgsRectangle bBox( geometry.boundingBox() );
942+
if ( localComputedExtent.isNull() )
943+
localComputedExtent = bBox;
944+
else
945+
localComputedExtent.combineExtentWith( bBox );
946+
QgsGeometry polyBoundingBox = QgsGeometry::fromRect( bBox );
947+
cachedFeature.setGeometry( polyBoundingBox );
948+
}
949+
else
950+
{
951+
cachedFeature.setAttribute( hexwkbGeomIdx, QVariant( QString() ) );
952+
}
953+
928954
const QString &gmlId = featPair.second;
929955
QString md5;
930956
if ( mDistinctSelect )
@@ -956,30 +982,6 @@ void QgsWFSSharedData::serializeFeatures( QVector<QgsWFSFeatureGmlIdPair> &featu
956982

957983
updatedFeatureList.push_back( featPair );
958984

959-
QgsFeature cachedFeature;
960-
cachedFeature.initAttributes( dataProviderFields.size() );
961-
962-
//copy the geometry
963-
QgsGeometry geometry = gmlFeature.geometry();
964-
if ( !mGeometryAttribute.isEmpty() && !geometry.isNull() )
965-
{
966-
QByteArray array( geometry.asWkb() );
967-
968-
cachedFeature.setAttribute( hexwkbGeomIdx, QVariant( QString( array.toHex().data() ) ) );
969-
970-
QgsRectangle bBox( geometry.boundingBox() );
971-
if ( localComputedExtent.isNull() )
972-
localComputedExtent = bBox;
973-
else
974-
localComputedExtent.combineExtentWith( bBox );
975-
QgsGeometry polyBoundingBox = QgsGeometry::fromRect( bBox );
976-
cachedFeature.setGeometry( polyBoundingBox );
977-
}
978-
else
979-
{
980-
cachedFeature.setAttribute( hexwkbGeomIdx, QVariant( QString() ) );
981-
}
982-
983985
//and the attributes
984986
for ( int i = 0; i < mFields.size(); i++ )
985987
{

tests/src/python/test_provider_wfs.py

+88
Original file line numberDiff line numberDiff line change
@@ -1505,6 +1505,94 @@ def testWFSGetOnlyFeaturesInViewExtent(self):
15051505
vl_extent = QgsGeometry.fromRect(vl.extent())
15061506
assert QgsGeometry.compare(vl_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), 'Expected {}, got {}'.format(reference.asWkt(), vl_extent.asWkt())
15071507

1508+
def testWFSGetOnlyFeaturesInViewExtentZoomOut(self):
1509+
"""Test zoom out outside of declare extent in metadata (#20742) """
1510+
1511+
endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_20742'
1512+
1513+
with open(sanitize(endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?ACCEPTVERSIONS=2.0.0,1.1.0,1.0.0'), 'wb') as f:
1514+
f.write("""
1515+
<wfs:WFS_Capabilities version="1.1.0" xmlns="http://www.opengis.net/wfs" xmlns:wfs="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc" xmlns:ows="http://www.opengis.net/ows" xmlns:gml="http://schemas.opengis.net/gml">
1516+
<OperationsMetadata>
1517+
<Operation name="GetFeature">
1518+
<Parameter name="resultType">
1519+
<Value>results</Value>
1520+
<Value>hits</Value>
1521+
</Parameter>
1522+
</Operation>
1523+
</OperationsMetadata>
1524+
<FeatureTypeList>
1525+
<FeatureType>
1526+
<Name>my:typename</Name>
1527+
<Title>Title</Title>
1528+
<Abstract>Abstract</Abstract>
1529+
<DefaultCRS>urn:ogc:def:crs:EPSG::4326</DefaultCRS>
1530+
<WGS84BoundingBox>
1531+
<LowerCorner>-80 60</LowerCorner>
1532+
<UpperCorner>-50 80</UpperCorner>
1533+
</WGS84BoundingBox>
1534+
</FeatureType>
1535+
</FeatureTypeList>
1536+
</wfs:WFS_Capabilities>""".encode('UTF-8'))
1537+
1538+
with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.1.0&TYPENAME=my:typename'), 'wb') as f:
1539+
f.write("""
1540+
<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">
1541+
<xsd:import namespace="http://www.opengis.net/gml"/>
1542+
<xsd:complexType name="typenameType">
1543+
<xsd:complexContent>
1544+
<xsd:extension base="gml:AbstractFeatureType">
1545+
<xsd:sequence>
1546+
<xsd:element maxOccurs="1" minOccurs="0" name="fid" nillable="true" type="xsd:int"/>
1547+
<xsd:element maxOccurs="1" minOccurs="0" name="geometryProperty" nillable="true" type="gml:PointPropertyType"/>
1548+
</xsd:sequence>
1549+
</xsd:extension>
1550+
</xsd:complexContent>
1551+
</xsd:complexType>
1552+
<xsd:element name="typename" substitutionGroup="gml:_Feature" type="my:typenameType"/>
1553+
</xsd:schema>
1554+
""".encode('UTF-8'))
1555+
1556+
last_url = sanitize(endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.1.0&TYPENAME=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326&BBOX=60,-80,80,-50,urn:ogc:def:crs:EPSG::4326')
1557+
getfeature_response = """
1558+
<wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs"
1559+
xmlns:gml="http://www.opengis.net/gml"
1560+
xmlns:my="http://my"
1561+
numberOfFeatures="2" timeStamp="2016-03-25T14:51:48.998Z">
1562+
<gml:featureMembers>
1563+
<my:typename gml:id="typename.200">
1564+
<my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>70 -65</gml:pos></gml:Point></my:geometryProperty>
1565+
<my:fid>2</my:fid>
1566+
</my:typename>
1567+
<my:typename gml:id="typename.400">
1568+
<my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>71 -64</gml:pos></gml:Point></my:geometryProperty>
1569+
<my:fid>4</my:fid>
1570+
</my:typename>
1571+
</gml:featureMembers>
1572+
</wfs:FeatureCollection>""".encode('UTF-8')
1573+
with open(last_url, 'wb') as f:
1574+
f.write(getfeature_response)
1575+
1576+
last_url = sanitize(endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.1.0&TYPENAME=my:typename&SRSNAME=urn:ogc:def:crs:EPSG::4326&BBOX=50,-90,90,-40,urn:ogc:def:crs:EPSG::4326')
1577+
with open(last_url, 'wb') as f:
1578+
f.write(getfeature_response)
1579+
1580+
vl = QgsVectorLayer("url='http://" + endpoint + "' typename='my:typename' restrictToRequestBBOX=1", 'test', 'WFS')
1581+
# First request with declared extent in metadata
1582+
extent = QgsRectangle(-80, 60, -50, 80)
1583+
request = QgsFeatureRequest().setFilterRect(extent)
1584+
self.assertEqual(len([f for f in vl.getFeatures(request)]), 2)
1585+
reference = QgsGeometry.fromRect(QgsRectangle(-65, 70, -64, 71))
1586+
vl_extent = QgsGeometry.fromRect(vl.extent())
1587+
assert QgsGeometry.compare(vl_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), 'Expected {}, got {}'.format(reference.asWkt(), vl_extent.asWkt())
1588+
1589+
# Second request: zoomed out
1590+
extent = QgsRectangle(-90, 50, -40, 90)
1591+
request = QgsFeatureRequest().setFilterRect(extent)
1592+
self.assertEqual(len([f for f in vl.getFeatures(request)]), 2)
1593+
vl_extent = QgsGeometry.fromRect(vl.extent())
1594+
assert QgsGeometry.compare(vl_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), 'Expected {}, got {}'.format(reference.asWkt(), vl_extent.asWkt())
1595+
15081596
def testWFS20TruncatedResponse(self):
15091597
"""Test WFS 2.0 truncatedResponse"""
15101598

0 commit comments

Comments
 (0)