Skip to content
Permalink
Browse files

Merge pull request #9118 from rouault/fix_20742

Correctly compute layer extent when zooming out layer [WFS provider]
  • Loading branch information
rouault committed Feb 7, 2019
2 parents 20d8528 + 32d9028 commit 2341fa86f6c314520dc87f6a9d17dd2ad3bd1485
Showing with 114 additions and 24 deletions.
  1. +26 −24 src/providers/wfs/qgswfsshareddata.cpp
  2. +88 −0 tests/src/python/test_provider_wfs.py
@@ -925,6 +925,32 @@ void QgsWFSSharedData::serializeFeatures( QVector<QgsWFSFeatureGmlIdPair> &featu
{
const QgsFeature &gmlFeature = featPair.first;

QgsFeature cachedFeature;
cachedFeature.initAttributes( dataProviderFields.size() );

// copy the geometry
// Do this now to update localComputedExtent, even if we skip the feature
// afterwards as being already downloaded.
QgsGeometry geometry = gmlFeature.geometry();
if ( !mGeometryAttribute.isEmpty() && !geometry.isNull() )
{
QByteArray array( geometry.asWkb() );

cachedFeature.setAttribute( hexwkbGeomIdx, QVariant( QString( array.toHex().data() ) ) );

QgsRectangle bBox( geometry.boundingBox() );
if ( localComputedExtent.isNull() )
localComputedExtent = bBox;
else
localComputedExtent.combineExtentWith( bBox );
QgsGeometry polyBoundingBox = QgsGeometry::fromRect( bBox );
cachedFeature.setGeometry( polyBoundingBox );
}
else
{
cachedFeature.setAttribute( hexwkbGeomIdx, QVariant( QString() ) );
}

const QString &gmlId = featPair.second;
QString md5;
if ( mDistinctSelect )
@@ -956,30 +982,6 @@ void QgsWFSSharedData::serializeFeatures( QVector<QgsWFSFeatureGmlIdPair> &featu

updatedFeatureList.push_back( featPair );

QgsFeature cachedFeature;
cachedFeature.initAttributes( dataProviderFields.size() );

//copy the geometry
QgsGeometry geometry = gmlFeature.geometry();
if ( !mGeometryAttribute.isEmpty() && !geometry.isNull() )
{
QByteArray array( geometry.asWkb() );

cachedFeature.setAttribute( hexwkbGeomIdx, QVariant( QString( array.toHex().data() ) ) );

QgsRectangle bBox( geometry.boundingBox() );
if ( localComputedExtent.isNull() )
localComputedExtent = bBox;
else
localComputedExtent.combineExtentWith( bBox );
QgsGeometry polyBoundingBox = QgsGeometry::fromRect( bBox );
cachedFeature.setGeometry( polyBoundingBox );
}
else
{
cachedFeature.setAttribute( hexwkbGeomIdx, QVariant( QString() ) );
}

//and the attributes
for ( int i = 0; i < mFields.size(); i++ )
{
@@ -1505,6 +1505,94 @@ def testWFSGetOnlyFeaturesInViewExtent(self):
vl_extent = QgsGeometry.fromRect(vl.extent())
assert QgsGeometry.compare(vl_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), 'Expected {}, got {}'.format(reference.asWkt(), vl_extent.asWkt())

def testWFSGetOnlyFeaturesInViewExtentZoomOut(self):
"""Test zoom out outside of declare extent in metadata (#20742) """

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

with open(sanitize(endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?ACCEPTVERSIONS=2.0.0,1.1.0,1.0.0'), 'wb') as f:
f.write("""
<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">
<OperationsMetadata>
<Operation name="GetFeature">
<Parameter name="resultType">
<Value>results</Value>
<Value>hits</Value>
</Parameter>
</Operation>
</OperationsMetadata>
<FeatureTypeList>
<FeatureType>
<Name>my:typename</Name>
<Title>Title</Title>
<Abstract>Abstract</Abstract>
<DefaultCRS>urn:ogc:def:crs:EPSG::4326</DefaultCRS>
<WGS84BoundingBox>
<LowerCorner>-80 60</LowerCorner>
<UpperCorner>-50 80</UpperCorner>
</WGS84BoundingBox>
</FeatureType>
</FeatureTypeList>
</wfs:WFS_Capabilities>""".encode('UTF-8'))

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

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')
getfeature_response = """
<wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs"
xmlns:gml="http://www.opengis.net/gml"
xmlns:my="http://my"
numberOfFeatures="2" timeStamp="2016-03-25T14:51:48.998Z">
<gml:featureMembers>
<my:typename gml:id="typename.200">
<my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>70 -65</gml:pos></gml:Point></my:geometryProperty>
<my:fid>2</my:fid>
</my:typename>
<my:typename gml:id="typename.400">
<my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>71 -64</gml:pos></gml:Point></my:geometryProperty>
<my:fid>4</my:fid>
</my:typename>
</gml:featureMembers>
</wfs:FeatureCollection>""".encode('UTF-8')
with open(last_url, 'wb') as f:
f.write(getfeature_response)

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')
with open(last_url, 'wb') as f:
f.write(getfeature_response)

vl = QgsVectorLayer("url='http://" + endpoint + "' typename='my:typename' restrictToRequestBBOX=1", 'test', 'WFS')
# First request with declared extent in metadata
extent = QgsRectangle(-80, 60, -50, 80)
request = QgsFeatureRequest().setFilterRect(extent)
self.assertEqual(len([f for f in vl.getFeatures(request)]), 2)
reference = QgsGeometry.fromRect(QgsRectangle(-65, 70, -64, 71))
vl_extent = QgsGeometry.fromRect(vl.extent())
assert QgsGeometry.compare(vl_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), 'Expected {}, got {}'.format(reference.asWkt(), vl_extent.asWkt())

# Second request: zoomed out
extent = QgsRectangle(-90, 50, -40, 90)
request = QgsFeatureRequest().setFilterRect(extent)
self.assertEqual(len([f for f in vl.getFeatures(request)]), 2)
vl_extent = QgsGeometry.fromRect(vl.extent())
assert QgsGeometry.compare(vl_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), 'Expected {}, got {}'.format(reference.asWkt(), vl_extent.asWkt())

def testWFS20TruncatedResponse(self):
"""Test WFS 2.0 truncatedResponse"""

0 comments on commit 2341fa8

Please sign in to comment.
You can’t perform that action at this time.