Skip to content

Commit

Permalink
Merge pull request #39942 from rouault/ogrprovider_featurecount
Browse files Browse the repository at this point in the history
OGR provider: feature count improvements
  • Loading branch information
rouault committed Nov 12, 2020
2 parents 928c10a + 3260807 commit bd32aa8
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 22 deletions.
1 change: 1 addition & 0 deletions python/core/auto_generated/qgsdataprovider.sip.in
Expand Up @@ -73,6 +73,7 @@ Abstract base class for spatial data provider implementations.
enum ReadFlag
{
FlagTrustDataSource,
SkipFeatureCount,
};
typedef QFlags<QgsDataProvider::ReadFlag> ReadFlags;

Expand Down
45 changes: 26 additions & 19 deletions src/core/providers/ogr/qgsogrprovider.cpp
Expand Up @@ -241,10 +241,14 @@ void QgsOgrProvider::repack()

}

long oldcount = mFeaturesCounted;
recalculateFeatureCount();
if ( oldcount != mFeaturesCounted )
emit dataChanged();
if ( mFeaturesCounted != QgsVectorDataProvider::Uncounted &&
mFeaturesCounted != QgsVectorDataProvider::UnknownCount )
{
long oldcount = mFeaturesCounted;
recalculateFeatureCount();
if ( oldcount != mFeaturesCounted )
emit dataChanged();
}
}


Expand Down Expand Up @@ -949,7 +953,8 @@ void QgsOgrProvider::addSubLayerDetailsToSubLayerList( int i, QgsOgrLayer *layer

QStringList QgsOgrProvider::subLayers() const
{
return _subLayers( true );
const bool withFeatureCount = ( mReadFlags & QgsDataProvider::SkipFeatureCount ) == 0;
return _subLayers( withFeatureCount );
}

QgsLayerMetadata QgsOgrProvider::layerMetadata() const
Expand Down Expand Up @@ -1303,7 +1308,7 @@ QString QgsOgrProvider::storageType() const
}


void QgsOgrProvider::setRelevantFields( bool fetchGeometry, const QgsAttributeList &fetchAttributes )
void QgsOgrProvider::setRelevantFields( bool fetchGeometry, const QgsAttributeList &fetchAttributes ) const
{
QMutex *mutex = nullptr;
OGRLayerH ogrLayer = mOgrLayer->getHandleAndMutex( mutex );
Expand Down Expand Up @@ -1600,6 +1605,15 @@ QgsWkbTypes::Type QgsOgrProvider::wkbType() const
*/
long QgsOgrProvider::featureCount() const
{
if ( ( mReadFlags & QgsDataProvider::SkipFeatureCount ) != 0 )
{
return QgsVectorDataProvider::UnknownCount;
}
if ( mRefreshFeatureCount )
{
mRefreshFeatureCount = false;
recalculateFeatureCount();
}
return mFeaturesCounted;
}

Expand Down Expand Up @@ -2317,12 +2331,7 @@ bool QgsOgrProvider::_setSubsetString( const QString &theSQL, bool updateFeature

mOgrLayer->ResetReading();

// getting the total number of features in the layer
// TODO: This can be expensive, do we really need it!
if ( updateFeatureCount )
{
recalculateFeatureCount();
}
mRefreshFeatureCount = updateFeatureCount;

// check the validity of the layer
QgsDebugMsgLevel( QStringLiteral( "checking validity" ), 4 );
Expand Down Expand Up @@ -4643,7 +4652,7 @@ bool QgsOgrProvider::syncToDisc()
return true;
}

void QgsOgrProvider::recalculateFeatureCount()
void QgsOgrProvider::recalculateFeatureCount() const
{
if ( !mOgrLayer )
{
Expand Down Expand Up @@ -4671,7 +4680,6 @@ void QgsOgrProvider::recalculateFeatureCount()
else
{
mFeaturesCounted = 0;
mOgrLayer->ResetReading();
setRelevantFields( true, QgsAttributeList() );
mOgrLayer->ResetReading();
gdal::ogr_feature_unique_ptr fet;
Expand All @@ -4688,7 +4696,7 @@ void QgsOgrProvider::recalculateFeatureCount()
}
}
mOgrLayer->ResetReading();

setRelevantFields( true, attributeIndexes() );
}

if ( filter )
Expand Down Expand Up @@ -4906,7 +4914,7 @@ void QgsOgrProvider::open( OpenMode mode )

// WARNING if this is the initial open - we don't already have a connection ref, and will be creating one later. So we *mustn't* grab an extra connection ref
// while setting the subset string, or we'll be left with an extra reference which is never cleared.
mValid = _setSubsetString( origSubsetString, true, false, mode != OpenModeInitial );
mValid = _setSubsetString( origSubsetString, false, false, mode != OpenModeInitial );

blockSignals( false );
if ( mValid )
Expand Down Expand Up @@ -4968,15 +4976,12 @@ void QgsOgrProvider::open( OpenMode mode )

if ( !mSubsetString.isEmpty() )
{
int featuresCountedBackup = mFeaturesCounted;
mFeaturesCounted = -1;
// Do not update capabilities here
// but ensure subset is set (setSubsetString does nothing if the passed sql subset string is equal to
// mSubsetString, which is the case when reloading the dataset)
QString origSubsetString = mSubsetString;
mSubsetString.clear();
mValid = _setSubsetString( origSubsetString, false, false );
mFeaturesCounted = featuresCountedBackup;
}
}
}
Expand All @@ -4988,6 +4993,8 @@ void QgsOgrProvider::open( OpenMode mode )
setProperty( "_debug_open_mode", "read-write" );
else
setProperty( "_debug_open_mode", "read-only" );

mRefreshFeatureCount = true;
}

void QgsOgrProvider::close()
Expand Down
10 changes: 7 additions & 3 deletions src/core/providers/ogr/qgsogrprovider.h
Expand Up @@ -174,10 +174,10 @@ class QgsOgrProvider final: public QgsVectorDataProvider
void loadFields();

//! Find out the number of features of the whole layer
void recalculateFeatureCount();
void recalculateFeatureCount() const;

//! Tell OGR, which fields to fetch in nextFeature/featureAtId (ie. which not to ignore)
void setRelevantFields( bool fetchGeometry, const QgsAttributeList &fetchAttributes );
void setRelevantFields( bool fetchGeometry, const QgsAttributeList &fetchAttributes ) const;

//! Convert a QgsField to work with OGR
static bool convertField( QgsField &field, const QTextCodec &encoding );
Expand Down Expand Up @@ -294,7 +294,11 @@ class QgsOgrProvider final: public QgsVectorDataProvider
bool mValid = false;

OGRwkbGeometryType mOGRGeomType = wkbUnknown;
long mFeaturesCounted = QgsVectorDataProvider::Uncounted;

//! Whether the next call to featureCount() should refresh the feature count
mutable bool mRefreshFeatureCount = true;

mutable long mFeaturesCounted = QgsVectorDataProvider::Uncounted;

mutable QStringList mSubLayerList;

Expand Down
1 change: 1 addition & 0 deletions src/core/qgsdataprovider.h
Expand Up @@ -122,6 +122,7 @@ class CORE_EXPORT QgsDataProvider : public QObject
enum ReadFlag
{
FlagTrustDataSource = 1 << 0, //!< Trust datasource config (primary key unicity, geometry type and srid, etc). Improves provider load time by skipping expensive checks like primary key unicity, geometry type and srid and by using estimated metadata on data load. Since QGIS 3.16
SkipFeatureCount = 1 << 1, //!< Make featureCount() return -1 to indicate unknown, and subLayers() to return a unknown feature count as well. Since QGIS 3.18. Only implemented by OGR provider at time of writing.
};
Q_DECLARE_FLAGS( ReadFlags, ReadFlag )

Expand Down
20 changes: 20 additions & 0 deletions tests/src/python/test_provider_shapefile.py
Expand Up @@ -22,12 +22,14 @@
from osgeo import gdal
from qgis.core import (
QgsApplication,
QgsDataProvider,
QgsSettings,
QgsFeature,
QgsField,
QgsGeometry,
QgsVectorLayer,
QgsFeatureRequest,
QgsProviderRegistry,
QgsVectorDataProvider,
QgsWkbTypes,
QgsVectorLayerExporter,
Expand Down Expand Up @@ -1076,6 +1078,24 @@ def testEncoding(self):
self.assertTrue(vl.isValid())
self.assertEqual([f.attributes() for f in vl.dataProvider().getFeatures()], [['abcŐ'], ['abcŐabcŐabcŐ']])

def testSkipFeatureCountOnFeatureCount(self):
"""Test QgsDataProvider.SkipFeatureCount on featureCount()"""

testPath = TEST_DATA_DIR + '/' + 'lines.shp'
provider = QgsProviderRegistry.instance().createProvider('ogr', testPath, QgsDataProvider.ProviderOptions(), QgsDataProvider.SkipFeatureCount)
self.assertTrue(provider.isValid())
self.assertEqual(provider.featureCount(), QgsVectorDataProvider.UnknownCount)

def testSkipFeatureCountOnSubLayers(self):
"""Test QgsDataProvider.SkipFeatureCount on subLayers()"""

datasource = os.path.join(TEST_DATA_DIR, 'shapefile')
provider = QgsProviderRegistry.instance().createProvider('ogr', datasource, QgsDataProvider.ProviderOptions(), QgsDataProvider.SkipFeatureCount)
self.assertTrue(provider.isValid())
sublayers = provider.subLayers()
self.assertTrue(len(sublayers) > 1)
self.assertEqual(sublayers[0].split(QgsDataProvider.sublayerSeparator())[2], '-1')


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

0 comments on commit bd32aa8

Please sign in to comment.