Skip to content

Commit

Permalink
Cleaner API for QgsProviderUtils::sublayerDetailsAreIncomplete,
Browse files Browse the repository at this point in the history
add flag to ignore unknown geometry types
  • Loading branch information
nyalldawson committed Jul 22, 2021
1 parent da03027 commit 5246257
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 22 deletions.
6 changes: 6 additions & 0 deletions python/core/auto_additions/qgsproviderutils.py
@@ -0,0 +1,6 @@
# The following has been generated automatically from src/core/providers/qgsproviderutils.h
# monkey patching scoped based enum
QgsProviderUtils.SublayerCompletenessFlag.IgnoreUnknownFeatureCount.__doc__ = "Indicates that an unknown feature count should not be considered as incomplete"
QgsProviderUtils.SublayerCompletenessFlag.IgnoreUnknownGeometryType.__doc__ = "Indicates that an unknown geometry type should not be considered as incomplete"
QgsProviderUtils.SublayerCompletenessFlag.__doc__ = 'Flags which control how :py:func:`QgsProviderUtils.sublayerDetailsAreIncomplete()` tests for completeness.\n\n' + '* ``IgnoreUnknownFeatureCount``: ' + QgsProviderUtils.SublayerCompletenessFlag.IgnoreUnknownFeatureCount.__doc__ + '\n' + '* ``IgnoreUnknownGeometryType``: ' + QgsProviderUtils.SublayerCompletenessFlag.IgnoreUnknownGeometryType.__doc__
# --
13 changes: 10 additions & 3 deletions python/core/auto_generated/providers/qgsproviderutils.sip.in
Expand Up @@ -22,16 +22,23 @@ Contains utility functions for working with data providers.
%End
public:

static bool sublayerDetailsAreIncomplete( const QList< QgsProviderSublayerDetails > &details, bool ignoreUnknownFeatureCount );
enum class SublayerCompletenessFlag
{
IgnoreUnknownFeatureCount,
IgnoreUnknownGeometryType,
};
typedef QFlags<QgsProviderUtils::SublayerCompletenessFlag> SublayerCompletenessFlags;


static bool sublayerDetailsAreIncomplete( const QList< QgsProviderSublayerDetails > &details, QgsProviderUtils::SublayerCompletenessFlags flags = QgsProviderUtils::SublayerCompletenessFlags() );
%Docstring
Returns ``True`` if the sublayer ``details`` are incomplete, and require a more in-depth
scan.

For instance, if the details contain any vector sublayers with unknown geometry types
then a query with the Qgis.SublayerQueryFlag.ResolveGeometryType flag is required.

If ``ignoreUnknownFeatureCount`` is ``True`` then sublayers with an unknown feature count
will not be considered as incomplete.
The ``flags`` argument can be used to control the level of completeness required during the test.
%End

static QString suggestLayerNameFromFilePath( const QString &path );
Expand Down
8 changes: 4 additions & 4 deletions src/app/qgisapp.cpp
Expand Up @@ -5521,7 +5521,7 @@ bool QgisApp::addVectorLayersPrivate( const QStringList &layers, const QString &
{
userAskedToAddLayers = true;

const bool detailsAreIncomplete = QgsProviderUtils::sublayerDetailsAreIncomplete( sublayers, true );
const bool detailsAreIncomplete = QgsProviderUtils::sublayerDetailsAreIncomplete( sublayers, QgsProviderUtils::SublayerCompletenessFlag::IgnoreUnknownFeatureCount );
const bool singleSublayerOnly = sublayers.size() == 1;
QString groupName;

Expand Down Expand Up @@ -5901,7 +5901,7 @@ bool QgisApp::askUserForZipItemLayers( const QString &path, const QList< QgsMapL
if ( sublayers.empty() )
return false;

const bool detailsAreIncomplete = QgsProviderUtils::sublayerDetailsAreIncomplete( sublayers, true );
const bool detailsAreIncomplete = QgsProviderUtils::sublayerDetailsAreIncomplete( sublayers, QgsProviderUtils::SublayerCompletenessFlag::IgnoreUnknownFeatureCount );
const bool singleSublayerOnly = sublayers.size() == 1;
QString groupName;

Expand Down Expand Up @@ -7382,7 +7382,7 @@ bool QgisApp::openLayer( const QString &fileName, bool allowInteractive )

if ( !sublayers.empty() || !nonLayerItems.empty() )
{
const bool detailsAreIncomplete = QgsProviderUtils::sublayerDetailsAreIncomplete( sublayers, true );
const bool detailsAreIncomplete = QgsProviderUtils::sublayerDetailsAreIncomplete( sublayers, QgsProviderUtils::SublayerCompletenessFlag::IgnoreUnknownFeatureCount );
const bool singleSublayerOnly = sublayers.size() == 1;
QString groupName;

Expand Down Expand Up @@ -13153,7 +13153,7 @@ T *QgisApp::addLayerPrivate( QgsMapLayerType type, const QString &uri, const QSt
// since the layer is bad, stomp on it
return nullptr;
}
else if ( sublayers.size() > 1 || QgsProviderUtils::sublayerDetailsAreIncomplete( sublayers, true ) )
else if ( sublayers.size() > 1 || QgsProviderUtils::sublayerDetailsAreIncomplete( sublayers, QgsProviderUtils::SublayerCompletenessFlag::IgnoreUnknownFeatureCount ) )
{
// ask user for sublayers (unless user settings dictate otherwise!)
switch ( shouldAskUserForSublayers( sublayers ) )
Expand Down
2 changes: 1 addition & 1 deletion src/app/qgsprovidersublayersdialog.cpp
Expand Up @@ -142,7 +142,7 @@ QgsProviderSublayersDialog::QgsProviderSublayersDialog( const QString &uri, cons
mLayersTree->setColumnWidth( 1, mLayersTree->columnWidth( 1 ) + 10 );
}

if ( QgsProviderUtils::sublayerDetailsAreIncomplete( initialDetails, false ) )
if ( QgsProviderUtils::sublayerDetailsAreIncomplete( initialDetails ) )
{
// initial details are incomplete, so fire up a task in the background to fully populate the model...
mTask = new QgsProviderSublayerTask( uri );
Expand Down
7 changes: 5 additions & 2 deletions src/core/providers/qgsproviderutils.cpp
Expand Up @@ -19,14 +19,17 @@

#include <QFileInfo>

bool QgsProviderUtils::sublayerDetailsAreIncomplete( const QList<QgsProviderSublayerDetails> &details, bool ignoreUnknownFeatureCount )
bool QgsProviderUtils::sublayerDetailsAreIncomplete( const QList<QgsProviderSublayerDetails> &details, SublayerCompletenessFlags flags )
{
const bool ignoreUnknownGeometryTypes = flags & SublayerCompletenessFlag::IgnoreUnknownGeometryType;
const bool ignoreUnknownFeatureCount = flags & SublayerCompletenessFlag::IgnoreUnknownFeatureCount;

for ( const QgsProviderSublayerDetails &sublayer : details )
{
switch ( sublayer.type() )
{
case QgsMapLayerType::VectorLayer:
if ( sublayer.wkbType() == QgsWkbTypes::Unknown
if ( ( !ignoreUnknownGeometryTypes && sublayer.wkbType() == QgsWkbTypes::Unknown )
|| ( !ignoreUnknownFeatureCount &&
( sublayer.featureCount() == static_cast< long long >( Qgis::FeatureCountState::Uncounted )
|| sublayer.featureCount() == static_cast< long long >( Qgis::FeatureCountState::UnknownCount ) ) ) )
Expand Down
15 changes: 12 additions & 3 deletions src/core/providers/qgsproviderutils.h
Expand Up @@ -34,17 +34,26 @@ class CORE_EXPORT QgsProviderUtils
{
public:

/**
* Flags which control how QgsProviderUtils::sublayerDetailsAreIncomplete() tests for completeness.
*/
enum class SublayerCompletenessFlag : int
{
IgnoreUnknownFeatureCount = 1 << 0, //!< Indicates that an unknown feature count should not be considered as incomplete
IgnoreUnknownGeometryType = 1 << 1, //!< Indicates that an unknown geometry type should not be considered as incomplete
};
Q_DECLARE_FLAGS( SublayerCompletenessFlags, SublayerCompletenessFlag )

/**
* Returns TRUE if the sublayer \a details are incomplete, and require a more in-depth
* scan.
*
* For instance, if the details contain any vector sublayers with unknown geometry types
* then a query with the Qgis::SublayerQueryFlag::ResolveGeometryType flag is required.
*
* If \a ignoreUnknownFeatureCount is TRUE then sublayers with an unknown feature count
* will not be considered as incomplete.
* The \a flags argument can be used to control the level of completeness required during the test.
*/
static bool sublayerDetailsAreIncomplete( const QList< QgsProviderSublayerDetails > &details, bool ignoreUnknownFeatureCount );
static bool sublayerDetailsAreIncomplete( const QList< QgsProviderSublayerDetails > &details, QgsProviderUtils::SublayerCompletenessFlags flags = QgsProviderUtils::SublayerCompletenessFlags() );

/**
* Suggests a suitable layer name given only a file \a path.
Expand Down
49 changes: 40 additions & 9 deletions tests/src/python/test_qgsproviderutils.py
Expand Up @@ -16,11 +16,11 @@
QgsProviderRegistry,
QgsProviderUtils
)

from qgis.testing import (
unittest,
start_app
)

from utilities import unitTestDataPath

app = start_app()
Expand All @@ -40,15 +40,36 @@ def test_sublayerDetailsAreIncomplete(self):
self.assertEqual(sublayers[0].wkbType(), QgsWkbTypes.Unknown)

# need to resolve geometry types for complete details about this uri!
self.assertTrue(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers, False))
self.assertTrue(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers, True))
self.assertTrue(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers))
self.assertTrue(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers,
QgsProviderUtils.SublayerCompletenessFlags(
QgsProviderUtils.SublayerCompletenessFlag.IgnoreUnknownFeatureCount)))
self.assertTrue(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers,
QgsProviderUtils.SublayerCompletenessFlags(
QgsProviderUtils.SublayerCompletenessFlag.IgnoreUnknownGeometryType)))
# ...unless we are ignoring both unknown feature count and unknown geometry types
self.assertFalse(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers,
QgsProviderUtils.SublayerCompletenessFlags(
QgsProviderUtils.SublayerCompletenessFlag.IgnoreUnknownFeatureCount |
QgsProviderUtils.SublayerCompletenessFlag.IgnoreUnknownGeometryType)))

# fake feature count, now we have complete details if we ignore unknown geometry type
sublayers[0].setFeatureCount(5)
self.assertFalse(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers,
QgsProviderUtils.SublayerCompletenessFlags(
QgsProviderUtils.SublayerCompletenessFlag.IgnoreUnknownGeometryType)))

# retry with retrieving geometry types
sublayers = QgsProviderRegistry.instance().querySublayers(uri, Qgis.SublayerQueryFlag.ResolveGeometryType)
# now we have all the details
self.assertEqual(len(sublayers), 3)
self.assertFalse(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers, False))
self.assertFalse(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers, True))
self.assertFalse(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers))
self.assertFalse(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers,
QgsProviderUtils.SublayerCompletenessFlags(
QgsProviderUtils.SublayerCompletenessFlag.IgnoreUnknownFeatureCount)))
self.assertFalse(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers,
QgsProviderUtils.SublayerCompletenessFlags(
QgsProviderUtils.SublayerCompletenessFlag.IgnoreUnknownGeometryType)))

# this geopackage file requires manually requesting feature counts
uri = unitTestDataPath() + '/mixed_layers.gpkg'
Expand All @@ -64,16 +85,26 @@ def test_sublayerDetailsAreIncomplete(self):
self.assertEqual(sublayers[3].featureCount(), Qgis.FeatureCountState.Uncounted)

# need to count features for complete details about this uri!
self.assertTrue(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers, False))
self.assertTrue(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers))
self.assertTrue(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers,
QgsProviderUtils.SublayerCompletenessFlags(
QgsProviderUtils.SublayerCompletenessFlag.IgnoreUnknownGeometryType)))
# ...unless we are ignoring unknown feature counts, that is...
self.assertFalse(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers, True))
self.assertFalse(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers,
QgsProviderUtils.SublayerCompletenessFlags(
QgsProviderUtils.SublayerCompletenessFlag.IgnoreUnknownFeatureCount)))

# retry with retrieving feature count
sublayers = QgsProviderRegistry.instance().querySublayers(uri, Qgis.SublayerQueryFlag.CountFeatures)
# now we have all the details
self.assertEqual(len(sublayers), 4)
self.assertFalse(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers, True))
self.assertFalse(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers, False))
self.assertFalse(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers,
QgsProviderUtils.SublayerCompletenessFlags(
QgsProviderUtils.SublayerCompletenessFlag.IgnoreUnknownFeatureCount)))
self.assertFalse(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers,
QgsProviderUtils.SublayerCompletenessFlags(
QgsProviderUtils.SublayerCompletenessFlag.IgnoreUnknownGeometryType)))
self.assertFalse(QgsProviderUtils.sublayerDetailsAreIncomplete(sublayers))
self.assertEqual(sublayers[0].name(), 'band1')
self.assertEqual(sublayers[1].name(), 'band2')
self.assertEqual(sublayers[2].name(), 'points')
Expand Down

0 comments on commit 5246257

Please sign in to comment.