Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUGFIX] [OGR provider] Make filter by id(s) requests work again on OSM datasets (fixes #20308) #8397

Merged
merged 1 commit into from
Nov 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 50 additions & 6 deletions src/providers/ogr/qgsogrfeatureiterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,16 @@

QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource *source, bool ownSource, const QgsFeatureRequest &request )
: QgsAbstractFeatureIteratorFromSource<QgsOgrFeatureSource>( source, ownSource, request )
, mFilterFids( mRequest.filterFids() )
, mFilterFidsIt( mFilterFids.constBegin() )
, mSharedDS( source->mSharedDS )
, mFirstFieldIsFid( source->mFirstFieldIsFid )
, mFieldsWithoutFid( source->mFieldsWithoutFid )
{
for ( const auto &id : mRequest.filterFids() )
{
mFilterFids.insert( id );
}
mFilterFidsIt = mFilterFids.begin();

// Since connection timeout for OGR connections is problematic and can lead to crashes, disable for now.
mRequest.setTimeout( -1 );
if ( mSharedDS )
Expand Down Expand Up @@ -235,7 +239,47 @@ bool QgsOgrFeatureIterator::fetchFeatureWithId( QgsFeatureId id, QgsFeature &fea
{
feature.setValid( false );
gdal::ogr_feature_unique_ptr fet;
fet.reset( OGR_L_GetFeature( mOgrLayer, FID_TO_NUMBER( id ) ) );

#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,2,0)
if ( !QgsOgrProviderUtils::canDriverShareSameDatasetAmongLayers( mSource->mDriverName ) )
{
OGRLayerH nextFeatureBelongingLayer;
bool found = false;
// First pass: try to read from the last feature, in the hope the dataset
// returns them in increasing feature id number (and as we use a std::set
// for mFilterFids, we get them in increasing number by the iterator)
// Second pass: reset before reading
for ( int passNumber = 0; passNumber < 2; passNumber++ )
{
while ( fet.reset( GDALDatasetGetNextFeature(
mConn->ds, &nextFeatureBelongingLayer, nullptr, nullptr, nullptr ) ), fet )
{
if ( nextFeatureBelongingLayer == mOgrLayer )
{
if ( OGR_F_GetFID( fet.get() ) == FID_TO_NUMBER( id ) )
{
found = true;
break;
}
}
}
if ( found || passNumber == 1 )
{
break;
}
GDALDatasetResetReading( mConn->ds );
}

if ( !found )
{
return false;
}
}
else
#endif
{
fet.reset( OGR_L_GetFeature( mOgrLayer, FID_TO_NUMBER( id ) ) );
}

if ( !fet )
{
Expand Down Expand Up @@ -281,10 +325,10 @@ bool QgsOgrFeatureIterator::fetchFeature( QgsFeature &feature )
}
else if ( mRequest.filterType() == QgsFeatureRequest::FilterFids )
{
while ( mFilterFidsIt != mFilterFids.constEnd() )
while ( mFilterFidsIt != mFilterFids.end() )
{
QgsFeatureId nextId = *mFilterFidsIt;
mFilterFidsIt++;
++mFilterFidsIt;

if ( fetchFeatureWithId( nextId, feature ) )
return true;
Expand Down Expand Up @@ -351,7 +395,7 @@ bool QgsOgrFeatureIterator::rewind()

resetReading();

mFilterFidsIt = mFilterFids.constBegin();
mFilterFidsIt = mFilterFids.begin();

return true;
}
Expand Down
6 changes: 4 additions & 2 deletions src/providers/ogr/qgsogrfeatureiterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <ogr_api.h>

#include <memory>
#include <set>

class QgsOgrFeatureIterator;
class QgsOgrProvider;
Expand Down Expand Up @@ -86,8 +87,9 @@ class QgsOgrFeatureIterator : public QgsAbstractFeatureIteratorFromSource<QgsOgr
bool mFetchGeometry = false;

bool mExpressionCompiled = false;
QgsFeatureIds mFilterFids;
QgsFeatureIds::const_iterator mFilterFidsIt;
// use std::set to get sorted ids (needed for efficient QgsFeatureRequest::FilterFids requests on OSM datasource)
std::set<QgsFeatureId> mFilterFids;
std::set<QgsFeatureId>::iterator mFilterFidsIt;

QgsRectangle mFilterRect;
QgsCoordinateTransform mTransform;
Expand Down
26 changes: 26 additions & 0 deletions tests/src/python/test_provider_ogr.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,32 @@ def testOSM(self):
iter_multipolygons = vl_multipolygons.getFeatures(QgsFeatureRequest())
self.assertTrue(iter_multipolygons.nextFeature(f))

# Test filter by id (#20308)
f = next(vl_multipolygons.getFeatures(QgsFeatureRequest().setFilterFid(8)))
self.assertTrue(f.isValid())
self.assertEqual(f.id(), 8)

f = next(vl_multipolygons.getFeatures(QgsFeatureRequest().setFilterFid(1)))
self.assertTrue(f.isValid())
self.assertEqual(f.id(), 1)

f = next(vl_multipolygons.getFeatures(QgsFeatureRequest().setFilterFid(5)))
self.assertTrue(f.isValid())
self.assertEqual(f.id(), 5)

# 6 doesn't exist
it = vl_multipolygons.getFeatures(QgsFeatureRequest().setFilterFids([1, 5, 6, 8]))
f = next(it)
self.assertTrue(f.isValid())
self.assertEqual(f.id(), 1)
f = next(it)
self.assertTrue(f.isValid())
self.assertEqual(f.id(), 5)
f = next(it)
self.assertTrue(f.isValid())
self.assertEqual(f.id(), 8)
del it


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