Skip to content
Permalink
Browse files
Add framework for querying for dataset sublayers via
QgsProviderRegistry/QgsProviderMetadata
  • Loading branch information
nyalldawson committed Jun 21, 2021
1 parent 2328f4f commit 469f967941fe41130628c4e48cea2cb83bf9028e
@@ -331,3 +331,10 @@
Qgis.UnplacedLabelVisibility.__doc__ = 'Unplaced label visibility.\n\n.. versionadded:: 3.20\n\n' + '* ``FollowEngineSetting``: ' + Qgis.UnplacedLabelVisibility.FollowEngineSetting.__doc__ + '\n' + '* ``NeverShow``: ' + Qgis.UnplacedLabelVisibility.NeverShow.__doc__
# --
Qgis.UnplacedLabelVisibility.baseClass = Qgis
# monkey patching scoped based enum
Qgis.SublayerQueryFlag.FastScan.__doc__ = "Indicates that the provider must scan for sublayers using the fastest possible approach -- e.g. by first checking that a uri has an extension which is known to be readable by the provider"
Qgis.SublayerQueryFlag.ResolveGeometryType.__doc__ = "Attempt to resolve the geometry type for vector sublayers"
Qgis.SublayerQueryFlag.CountFeatures.__doc__ = "Count features in vector sublayers"
Qgis.SublayerQueryFlag.__doc__ = 'Flags which control how data providers will scan for sublayers in a dataset.\n\n.. versionadded:: 3.22\n\n' + '* ``FastScan``: ' + Qgis.SublayerQueryFlag.FastScan.__doc__ + '\n' + '* ``ResolveGeometryType``: ' + Qgis.SublayerQueryFlag.ResolveGeometryType.__doc__ + '\n' + '* ``CountFeatures``: ' + Qgis.SublayerQueryFlag.CountFeatures.__doc__
# --
Qgis.SublayerQueryFlag.baseClass = Qgis
@@ -265,6 +265,24 @@ The default method returns ``False`` for all URIs.
ignore the specified ``uri``, not just the provider associated with this metadata!

.. versionadded:: 3.18
%End

virtual QList< QgsProviderSublayerDetails > querySublayers( const QString &uri, Qgis::SublayerQueryFlags flags = Qgis::SublayerQueryFlags(), QgsFeedback *feedback = 0 ) const;
%Docstring
Queries the specified ``uri`` and returns a list of any valid sublayers found in the dataset which can be handled by this provider.

The optional ``flags`` argument can be used to control the behavior of the query.

The optional ``feedback`` argument can be used to provide cancellation support for long-running queries.

.. note::

Providers which implement this method should return always return a list of sublayer details for any valid, even if the ``uri``
only relates to a single layer. Returning a non-empty list indicates that the provider is able to load at least one layer using the ``uri``,
and is used to collate a combined layer of all providers which support the URI (e.g. in the case that a URI may be readable by multiple
different providers).

.. versionadded:: 3.20
%End

virtual QgsDataProvider *createProvider( const QString &uri,
@@ -510,6 +510,19 @@ This method tests whether any of the registered providers return ``True`` for th
:py:func:`QgsProviderMetadata.uriIsBlocklisted()` implementation for the specified URI.

.. versionadded:: 3.18
%End

QList< QgsProviderSublayerDetails > querySublayers( const QString &uri, Qgis::SublayerQueryFlags flags = Qgis::SublayerQueryFlags(), QgsFeedback *feedback = 0 ) const;
%Docstring
Queries the specified ``uri`` and returns a list of any valid sublayers found in the dataset which can be handled by any registered data provider.

This method iteratively queries each registered data provider and returns the complete collated list of all valid sublayers found in the dataset which can be opened by the data providers.

The optional ``flags`` argument can be used to control the behavior of the query.

The optional ``feedback`` argument can be used to provide cancellation support for long-running queries.

.. versionadded:: 3.20
%End

QString fileVectorFilters() const;
@@ -242,6 +242,15 @@ The development version
NeverShow,
};

enum class SublayerQueryFlag
{
FastScan,
ResolveGeometryType,
CountFeatures,
};
typedef QFlags<Qgis::SublayerQueryFlag> SublayerQueryFlags;


static const double DEFAULT_SEARCH_RADIUS_MM;

static const float DEFAULT_MAPTOPIXEL_THRESHOLD;
@@ -315,6 +324,7 @@ QFlags<Qgis::SymbolPreviewFlag> operator|(Qgis::SymbolPreviewFlag f1, QFlags<Qgi

QFlags<Qgis::BrowserItemCapability> operator|(Qgis::BrowserItemCapability f1, QFlags<Qgis::BrowserItemCapability> f2);

QFlags<Qgis::SublayerQueryFlag> operator|(Qgis::SublayerQueryFlag f1, QFlags<Qgis::SublayerQueryFlag> f2);



@@ -21,6 +21,7 @@
#include "qgsmaplayer.h"
#include "qgsexception.h"
#include "qgsabstractdatabaseproviderconnection.h"
#include "qgsprovidersublayerdetails.h"

QgsProviderMetadata::QgsProviderMetadata( QString const &key,
QString const &description,
@@ -106,6 +107,11 @@ bool QgsProviderMetadata::uriIsBlocklisted( const QString & ) const
return false;
}

QList<QgsProviderSublayerDetails> QgsProviderMetadata::querySublayers( const QString &, Qgis::SublayerQueryFlags, QgsFeedback * ) const
{
return QList<QgsProviderSublayerDetails>();
}

QgsDataProvider *QgsProviderMetadata::createProvider( const QString &uri,
const QgsDataProvider::ProviderOptions &options,
QgsDataProvider::ReadFlags flags )
@@ -43,6 +43,8 @@ class QgsRasterDataProvider;
class QgsMeshDataProvider;
class QgsAbstractDatabaseProviderConnection;
class QgsLayerMetadata;
class QgsProviderSublayerDetails;
class QgsFeedback;

struct QgsMesh;

@@ -324,6 +326,22 @@ class CORE_EXPORT QgsProviderMetadata : public QObject
*/
virtual bool uriIsBlocklisted( const QString &uri ) const;

/**
* Queries the specified \a uri and returns a list of any valid sublayers found in the dataset which can be handled by this provider.
*
* The optional \a flags argument can be used to control the behavior of the query.
*
* The optional \a feedback argument can be used to provide cancellation support for long-running queries.
*
* \note Providers which implement this method should return always return a list of sublayer details for any valid, even if the \a uri
* only relates to a single layer. Returning a non-empty list indicates that the provider is able to load at least one layer using the \a uri,
* and is used to collate a combined layer of all providers which support the URI (e.g. in the case that a URI may be readable by multiple
* different providers).
*
* \since QGIS 3.20
*/
virtual QList< QgsProviderSublayerDetails > querySublayers( const QString &uri, Qgis::SublayerQueryFlags flags = Qgis::SublayerQueryFlags(), QgsFeedback *feedback = nullptr ) const;

/**
* Class factory to return a pointer to a newly created QgsDataProvider object
*
@@ -32,6 +32,7 @@
#include "qgsvectorlayer.h"
#include "qgsvectortileprovidermetadata.h"
#include "qgsproject.h"
#include "qgsprovidersublayerdetails.h"
#include "providers/memory/qgsmemoryprovider.h"
#include "providers/gdal/qgsgdalprovider.h"
#include "providers/ogr/qgsogrprovidermetadata.h"
@@ -894,3 +895,15 @@ bool QgsProviderRegistry::uriIsBlocklisted( const QString &uri ) const
}
return false;
}

QList<QgsProviderSublayerDetails> QgsProviderRegistry::querySublayers( const QString &uri, Qgis::SublayerQueryFlags flags, QgsFeedback *feedback ) const
{
QList<QgsProviderSublayerDetails> res;
for ( auto it = mProviders.begin(); it != mProviders.end(); ++it )
{
res.append( it->second->querySublayers( uri, flags ) );
if ( feedback && feedback->isCanceled() )
break;
}
return res;
}
@@ -40,6 +40,8 @@ class QgsDataItem;
class QgsRasterDataProvider;
class QgsTransaction;
class QgsFields;
class QgsProviderSublayerDetails;
class QgsFeedback;

/**
* \ingroup core
@@ -543,6 +545,19 @@ class CORE_EXPORT QgsProviderRegistry
*/
bool uriIsBlocklisted( const QString &uri ) const;

/**
* Queries the specified \a uri and returns a list of any valid sublayers found in the dataset which can be handled by any registered data provider.
*
* This method iteratively queries each registered data provider and returns the complete collated list of all valid sublayers found in the dataset which can be opened by the data providers.
*
* The optional \a flags argument can be used to control the behavior of the query.
*
* The optional \a feedback argument can be used to provide cancellation support for long-running queries.
*
* \since QGIS 3.20
*/
QList< QgsProviderSublayerDetails > querySublayers( const QString &uri, Qgis::SublayerQueryFlags flags = Qgis::SublayerQueryFlags(), QgsFeedback *feedback = nullptr ) const;

/**
* Returns a file filter string for supported vector files.
*
@@ -357,6 +357,20 @@ class CORE_EXPORT Qgis
};
Q_ENUM( UnplacedLabelVisibility )

/**
* Flags which control how data providers will scan for sublayers in a dataset.
*
* \since QGIS 3.22
*/
enum class SublayerQueryFlag : int
{
FastScan = 1 << 0, //!< Indicates that the provider must scan for sublayers using the fastest possible approach -- e.g. by first checking that a uri has an extension which is known to be readable by the provider
ResolveGeometryType = 1 << 1, //!< Attempt to resolve the geometry type for vector sublayers
CountFeatures = 1 << 2, //!< Count features in vector sublayers
};
Q_DECLARE_FLAGS( SublayerQueryFlags, SublayerQueryFlag )
Q_ENUM( SublayerQueryFlag )

/**
* Identify search radius in mm
* \since QGIS 2.3
@@ -474,7 +488,7 @@ Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SymbolRenderHints )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SymbolFlags )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SymbolPreviewFlags )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::BrowserItemCapabilities )

Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SublayerQueryFlags )

// hack to workaround warnings when casting void pointers
// retrieved from QLibrary::resolve to function pointers.
@@ -14,7 +14,10 @@

from qgis.core import (
QgsProviderRegistry,
QgsMapLayerType
QgsMapLayerType,
QgsProviderMetadata,
QgsProviderSublayerDetails,
Qgis
)
from qgis.testing import start_app, unittest

@@ -23,6 +26,20 @@
start_app()


class TestProviderMetadata(QgsProviderMetadata):
"""
Test metadata
"""

def __init__(self, key):
super().__init__(key, key)

def querySublayers(self, uri: str, flags=Qgis.SublayerQueryFlags(), feedback=None):
res = QgsProviderSublayerDetails()
res.setProviderKey(self.key())
return [res]


class TestQgsProviderRegistry(unittest.TestCase):

def testProviderList(self):
@@ -109,6 +126,18 @@ def testUnusableUriDetails(self):
self.assertTrue(res)
self.assertIn('LAZ', details.warning)

def testSublayerDetails(self):
provider1 = TestProviderMetadata('p1')
provider2 = TestProviderMetadata('p2')

self.assertFalse(QgsProviderRegistry.instance().querySublayers('test_uri'))

self.assertTrue(QgsProviderRegistry.instance().registerProvider(provider1))
self.assertTrue(QgsProviderRegistry.instance().registerProvider(provider2))

self.assertCountEqual([p.providerKey() for p in QgsProviderRegistry.instance().querySublayers('test_uri')],
['p1', 'p2'])


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

0 comments on commit 469f967

Please sign in to comment.