Skip to content

Commit a023f55

Browse files
committed
Merge pull request #3148 from rouault/wfs_better_behaviour_with_wrong_capability_extent
[WFS Provider] Implement workarounds to better behave when extent reported by capabilities is wrong
2 parents 86f6e7e + 2ac20c6 commit a023f55

File tree

6 files changed

+333
-41
lines changed

6 files changed

+333
-41
lines changed

src/providers/wfs/qgswfsfeatureiterator.cpp

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -389,12 +389,6 @@ void QgsWFSFeatureDownloader::run( bool serializeFeatures, int maxFeatures )
389389
connect( &mFeatureHitsAsyncRequest, SIGNAL( downloadFinished() ), &loop, SLOT( quit() ) );
390390
}
391391

392-
QgsGmlStreamingParser::AxisOrientationLogic axisOrientationLogic( QgsGmlStreamingParser::Honour_EPSG_if_urn );
393-
if ( mShared->mURI.ignoreAxisOrientation() )
394-
{
395-
axisOrientationLogic = QgsGmlStreamingParser::Ignore_EPSG;
396-
}
397-
398392
bool interrupted = false;
399393
bool truncatedResponse = false;
400394
QSettings s;
@@ -404,32 +398,7 @@ void QgsWFSFeatureDownloader::run( bool serializeFeatures, int maxFeatures )
404398
while ( true )
405399
{
406400
success = true;
407-
QgsGmlStreamingParser* parser;
408-
if ( mShared->mLayerPropertiesList.size() )
409-
{
410-
QList< QgsGmlStreamingParser::LayerProperties > layerPropertiesList;
411-
Q_FOREACH ( QgsOgcUtils::LayerProperties layerProperties, mShared->mLayerPropertiesList )
412-
{
413-
QgsGmlStreamingParser::LayerProperties layerPropertiesOut;
414-
layerPropertiesOut.mName = layerProperties.mName;
415-
layerPropertiesOut.mGeometryAttribute = layerProperties.mGeometryAttribute;
416-
layerPropertiesList << layerPropertiesOut;
417-
}
418-
419-
parser = new QgsGmlStreamingParser( layerPropertiesList,
420-
mShared->mFields,
421-
mShared->mMapFieldNameToSrcLayerNameFieldName,
422-
axisOrientationLogic,
423-
mShared->mURI.invertAxisOrientation() );
424-
}
425-
else
426-
{
427-
parser = new QgsGmlStreamingParser( mShared->mURI.typeName(),
428-
mShared->mGeometryAttribute,
429-
mShared->mFields,
430-
axisOrientationLogic,
431-
mShared->mURI.invertAxisOrientation() );
432-
}
401+
QgsGmlStreamingParser* parser = mShared->createParser();
433402

434403
QUrl url( buildURL( mTotalDownloadedFeatureCount,
435404
maxFeatures ? maxFeatures : mShared->mMaxFeatures, false ) );

src/providers/wfs/qgswfsprovider.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ QgsWFSProvider::QgsWFSProvider( const QString& uri, const QgsWFSCapabilities::Ca
5959
{
6060
mShared->mCaps = caps;
6161
connect( mShared.data(), SIGNAL( raiseError( const QString& ) ), this, SLOT( pushErrorSlot( const QString& ) ) );
62+
connect( mShared.data(), SIGNAL( extentUpdated() ), this, SIGNAL( fullExtentCalculated() ) );
6263

6364
if ( uri.isEmpty() )
6465
{
@@ -684,7 +685,29 @@ QgsCoordinateReferenceSystem QgsWFSProvider::crs()
684685

685686
QgsRectangle QgsWFSProvider::extent()
686687
{
687-
return mExtent;
688+
// Some servers return completely buggy extent in their capabilities response
689+
// so mix it with the extent actually got from the downloaded features
690+
QgsRectangle computedExtent( mShared->computedExtent() );
691+
QgsDebugMsg( "computedExtent: " + computedExtent.toString() );
692+
QgsDebugMsg( "mCapabilityExtent: " + mShared->mCapabilityExtent.toString() );
693+
694+
// If we didn't get any feature, then return capabilities extent.
695+
if ( computedExtent.isNull() )
696+
return mShared->mCapabilityExtent;
697+
698+
// If the capabilities extent is completely off from the features, then
699+
// use feature extent.
700+
// Case of standplaats layer of http://geodata.nationaalgeoregister.nl/bag/wfs
701+
if ( !computedExtent.intersects( mShared->mCapabilityExtent ) )
702+
return computedExtent;
703+
704+
if ( mShared->downloadFinished() )
705+
{
706+
return computedExtent;
707+
}
708+
709+
computedExtent.combineExtentWith( &mShared->mCapabilityExtent );
710+
return computedExtent;
688711
}
689712

690713
bool QgsWFSProvider::isValid()
@@ -1378,9 +1401,9 @@ bool QgsWFSProvider::getCapabilities()
13781401
QgsDebugMsg( "src:" + src.authid() );
13791402
QgsDebugMsg( "dst:" + mShared->mSourceCRS.authid() );
13801403

1381-
mExtent = ct.transformBoundingBox( r, QgsCoordinateTransform::ForwardTransform );
1404+
mShared->mCapabilityExtent = ct.transformBoundingBox( r, QgsCoordinateTransform::ForwardTransform );
13821405

1383-
QgsDebugMsg( "layer ext:" + mExtent.toString() );
1406+
QgsDebugMsg( "layer ext:" + mShared->mCapabilityExtent.toString() );
13841407
}
13851408
if ( mShared->mCaps.featureTypes[i].insertCap )
13861409
{

src/providers/wfs/qgswfsprovider.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,6 @@ class QgsWFSProvider : public QgsVectorDataProvider
153153
//! String used to define a subset of the layer
154154
QString mSubsetString;
155155

156-
/** Bounding box for the layer*/
157-
QgsRectangle mExtent;
158156
/** Geometry type of the features in this layer*/
159157
mutable QGis::WkbType mWKBType;
160158
/** Flag if provider is valid*/

src/providers/wfs/qgswfsshareddata.cpp

Lines changed: 158 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
* *
1414
***************************************************************************/
1515

16+
#include <math.h> // M_PI
17+
1618
#include "qgswfsconstants.h"
1719
#include "qgswfsshareddata.h"
1820
#include "qgswfsutils.h"
@@ -34,6 +36,7 @@
3436

3537
#include <QCryptographicHash>
3638

39+
3740
QgsWFSSharedData::QgsWFSSharedData( const QString& uri )
3841
: mURI( uri )
3942
, mSourceCRS( 0 )
@@ -48,6 +51,7 @@ QgsWFSSharedData::QgsWFSSharedData( const QString& uri )
4851
, mFeatureCountExact( false )
4952
, mGetFeatureHitsIssued( false )
5053
, mTotalFeaturesAttemptedToBeCached( 0 )
54+
, mTryFetchingOneFeature( false )
5155
{
5256
// Needed because used by a signal
5357
qRegisterMetaType< QVector<QgsWFSFeatureGmlIdPair> >( "QVector<QgsWFSFeatureGmlIdPair>" );
@@ -514,6 +518,7 @@ int QgsWFSSharedData::registerToCache( QgsWFSFeatureIterator* iterator, QgsRecta
514518
delete mDownloader;
515519
mMutex.lock();
516520
mDownloadFinished = false;
521+
mComputedExtent = QgsRectangle();
517522
mDownloader = new QgsWFSThreadedFeatureDownloader( this );
518523
QEventLoop loop;
519524
connect( mDownloader, SIGNAL( ready() ), &loop, SLOT( quit() ) );
@@ -826,6 +831,7 @@ void QgsWFSSharedData::serializeFeatures( QVector<QgsWFSFeatureGmlIdPair>& featu
826831
existingGmlIds = getExistingCachedGmlIds( featureList );
827832
QVector<QgsWFSFeatureGmlIdPair> updatedFeatureList;
828833

834+
QgsRectangle localComputedExtent( mComputedExtent );
829835
Q_FOREACH ( QgsWFSFeatureGmlIdPair featPair, featureList )
830836
{
831837
const QgsFeature& gmlFeature = featPair.first;
@@ -874,8 +880,13 @@ void QgsWFSSharedData::serializeFeatures( QVector<QgsWFSFeatureGmlIdPair>& featu
874880

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

877-
QgsGeometry* polyBoudingBox = QgsGeometry::fromRect( geometry->boundingBox() );
878-
cachedFeature.setGeometry( polyBoudingBox );
883+
QgsRectangle bBox( geometry->boundingBox() );
884+
if ( localComputedExtent.isNull() )
885+
localComputedExtent = bBox;
886+
else
887+
localComputedExtent.combineExtentWith( &bBox );
888+
QgsGeometry* polyBoundingBox = QgsGeometry::fromRect( bBox );
889+
cachedFeature.setGeometry( polyBoundingBox );
879890
}
880891
else
881892
{
@@ -936,15 +947,30 @@ void QgsWFSSharedData::serializeFeatures( QVector<QgsWFSFeatureGmlIdPair>& featu
936947
if ( !mFeatureCountExact )
937948
mFeatureCount += featureListToCache.size();
938949
mTotalFeaturesAttemptedToBeCached += featureListToCache.size();
950+
if ( !localComputedExtent.isNull() && mComputedExtent.isNull() && !mTryFetchingOneFeature &&
951+
!localComputedExtent.intersects( mCapabilityExtent ) )
952+
{
953+
QgsMessageLog::logMessage( tr( "Layer extent reported by the server is not correct. "
954+
"You may need to zoom again on layer while features are being downloaded" ), tr( "WFS" ) );
955+
}
956+
mComputedExtent = localComputedExtent;
939957
}
940958
}
941959

942960
featureList = updatedFeatureList;
943961

962+
emit extentUpdated();
963+
944964
QgsDebugMsg( QString( "end %1" ).arg( featureList.size() ) );
945965

946966
}
947967

968+
QgsRectangle QgsWFSSharedData::computedExtent()
969+
{
970+
QMutexLocker locker( &mMutex );
971+
return mComputedExtent;
972+
}
973+
948974
void QgsWFSSharedData::pushError( const QString& errorMsg )
949975
{
950976
QgsMessageLog::logMessage( errorMsg, tr( "WFS" ) );
@@ -970,6 +996,36 @@ void QgsWFSSharedData::endOfDownload( bool success, int featureCount,
970996
mDownloadFinished = true;
971997
if ( success && !mRect.isEmpty() )
972998
{
999+
// In the case we requested an extent that includes the extent reported by GetCapabilities response,
1000+
// that we have no filter and we got no features, then it is not unlikely that the capabilities
1001+
// might be wrong. In which case, query one feature so that we got a beginning of extent from
1002+
// which the user will be able to zoom out. This is far from being ideal...
1003+
if ( featureCount == 0 && mRect.contains( mCapabilityExtent ) && mWFSFilter.isEmpty() &&
1004+
mCaps.supportsHits && !mGeometryAttribute.isEmpty() && !mTryFetchingOneFeature )
1005+
{
1006+
QgsDebugMsg( "Capability extent is probably wrong. Starting a new request with one feature limit to get at least one feature" );
1007+
mTryFetchingOneFeature = true;
1008+
QgsWFSSingleFeatureRequest request( this );
1009+
mComputedExtent = request.getExtent();
1010+
if ( !mComputedExtent.isNull() )
1011+
{
1012+
// Grow the extent by ~ 50 km (completely arbitrary number if you wonder!)
1013+
// so that it is sufficiently zoomed out
1014+
if ( mSourceCRS.mapUnits() == QGis::Meters )
1015+
mComputedExtent.grow( 50. * 1000. );
1016+
else if ( mSourceCRS.mapUnits() == QGis::Degrees )
1017+
mComputedExtent.grow( 50. / 110 );
1018+
QgsMessageLog::logMessage( tr( "Layer extent reported by the server is not correct. You may need to zoom on layer and then zoom out to see all fetchures" ), tr( "WFS" ) );
1019+
}
1020+
mMutex.unlock();
1021+
if ( !mComputedExtent.isNull() )
1022+
{
1023+
emit extentUpdated();
1024+
}
1025+
mMutex.lock();
1026+
return;
1027+
}
1028+
9731029
// Arbitrary threshold to avoid the cache of BBOX to grow out of control.
9741030
// Note: we could be smarter and keep some BBOXes, but the saturation is
9751031
// unlikely to happen in practice, so just clear everything.
@@ -1099,6 +1155,41 @@ int QgsWFSSharedData::getFeatureCount( bool issueRequestIfNeeded )
10991155
return mFeatureCount;
11001156
}
11011157

1158+
QgsGmlStreamingParser* QgsWFSSharedData::createParser()
1159+
{
1160+
QgsGmlStreamingParser::AxisOrientationLogic axisOrientationLogic( QgsGmlStreamingParser::Honour_EPSG_if_urn );
1161+
if ( mURI.ignoreAxisOrientation() )
1162+
{
1163+
axisOrientationLogic = QgsGmlStreamingParser::Ignore_EPSG;
1164+
}
1165+
1166+
if ( mLayerPropertiesList.size() )
1167+
{
1168+
QList< QgsGmlStreamingParser::LayerProperties > layerPropertiesList;
1169+
Q_FOREACH ( QgsOgcUtils::LayerProperties layerProperties, mLayerPropertiesList )
1170+
{
1171+
QgsGmlStreamingParser::LayerProperties layerPropertiesOut;
1172+
layerPropertiesOut.mName = layerProperties.mName;
1173+
layerPropertiesOut.mGeometryAttribute = layerProperties.mGeometryAttribute;
1174+
layerPropertiesList << layerPropertiesOut;
1175+
}
1176+
1177+
return new QgsGmlStreamingParser( layerPropertiesList,
1178+
mFields,
1179+
mMapFieldNameToSrcLayerNameFieldName,
1180+
axisOrientationLogic,
1181+
mURI.invertAxisOrientation() );
1182+
}
1183+
else
1184+
{
1185+
return new QgsGmlStreamingParser( mURI.typeName(),
1186+
mGeometryAttribute,
1187+
mFields,
1188+
axisOrientationLogic,
1189+
mURI.invertAxisOrientation() );
1190+
}
1191+
}
1192+
11021193

11031194
// -------------------------
11041195

@@ -1165,3 +1256,68 @@ QString QgsWFSFeatureHitsRequest::errorMessageWithReason( const QString& reason
11651256
return tr( "Download of feature count failed: %1" ).arg( reason );
11661257
}
11671258

1259+
1260+
// -------------------------
1261+
1262+
1263+
QgsWFSSingleFeatureRequest::QgsWFSSingleFeatureRequest( QgsWFSSharedData* shared )
1264+
: QgsWFSRequest( shared->mURI.uri() ), mShared( shared )
1265+
{
1266+
}
1267+
1268+
QgsWFSSingleFeatureRequest::~QgsWFSSingleFeatureRequest()
1269+
{
1270+
}
1271+
1272+
QgsRectangle QgsWFSSingleFeatureRequest::getExtent()
1273+
{
1274+
QUrl getFeatureUrl( mUri.baseURL() );
1275+
getFeatureUrl.addQueryItem( "REQUEST", "GetFeature" );
1276+
getFeatureUrl.addQueryItem( "VERSION", mShared->mWFSVersion );
1277+
if ( mShared->mWFSVersion .startsWith( "2.0" ) )
1278+
getFeatureUrl.addQueryItem( "TYPENAMES", mUri.typeName() );
1279+
else
1280+
getFeatureUrl.addQueryItem( "TYPENAME", mUri.typeName() );
1281+
if ( mShared->mWFSVersion .startsWith( "2.0" ) )
1282+
getFeatureUrl.addQueryItem( "COUNT", QString::number( 1 ) );
1283+
else
1284+
getFeatureUrl.addQueryItem( "MAXFEATURES", QString::number( 1 ) );
1285+
1286+
if ( !sendGET( getFeatureUrl, true ) )
1287+
return -1;
1288+
1289+
const QByteArray& buffer = response();
1290+
1291+
QgsDebugMsg( "parsing QgsWFSSingleFeatureRequest: " + buffer );
1292+
1293+
// parse XML
1294+
QgsGmlStreamingParser* parser = mShared->createParser();
1295+
QString gmlProcessErrorMsg;
1296+
QgsRectangle extent;
1297+
if ( parser->processData( buffer, true, gmlProcessErrorMsg ) )
1298+
{
1299+
QVector<QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair> featurePtrList =
1300+
parser->getAndStealReadyFeatures();
1301+
QVector<QgsWFSFeatureGmlIdPair> featureList;
1302+
for ( int i = 0;i < featurePtrList.size();i++ )
1303+
{
1304+
QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair& featPair = featurePtrList[i];
1305+
QgsFeature f( *( featPair.first ) );
1306+
const QgsGeometry* geometry = f.constGeometry();
1307+
if ( geometry )
1308+
{
1309+
extent = geometry->boundingBox();
1310+
}
1311+
delete featPair.first;
1312+
}
1313+
}
1314+
delete parser;
1315+
return extent;
1316+
}
1317+
1318+
QString QgsWFSSingleFeatureRequest::errorMessageWithReason( const QString& reason )
1319+
{
1320+
return tr( "Download of feature failed: %1" ).arg( reason );
1321+
}
1322+
1323+

0 commit comments

Comments
 (0)