diff --git a/src/providers/wfs/qgswfsshareddata.cpp b/src/providers/wfs/qgswfsshareddata.cpp index 3be7be339573..2d0dae89198d 100644 --- a/src/providers/wfs/qgswfsshareddata.cpp +++ b/src/providers/wfs/qgswfsshareddata.cpp @@ -925,6 +925,32 @@ void QgsWFSSharedData::serializeFeatures( QVector &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 &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++ ) { diff --git a/tests/src/python/test_provider_wfs.py b/tests/src/python/test_provider_wfs.py index 60712c46e603..05e45e552c82 100644 --- a/tests/src/python/test_provider_wfs.py +++ b/tests/src/python/test_provider_wfs.py @@ -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(""" + + + + + results + hits + + + + + + my:typename + Title + Abstract + urn:ogc:def:crs:EPSG::4326 + + -80 60 + -50 80 + + + +""".encode('UTF-8')) + + with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.1.0&TYPENAME=my:typename'), 'wb') as f: + f.write(""" + + + + + + + + + + + + + + +""".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 = """ + + + + 70 -65 + 2 + + + 71 -64 + 4 + + +""".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"""