Skip to content

Commit

Permalink
Merge pull request #7016 from m-kuhn/featureSourceEmpty
Browse files Browse the repository at this point in the history
QgsFeatureSource::empty() method
  • Loading branch information
m-kuhn committed Jun 26, 2018
2 parents 7847421 + ae6bffd commit d7b85c6
Show file tree
Hide file tree
Showing 17 changed files with 254 additions and 2 deletions.
Expand Up @@ -260,6 +260,9 @@ An optional ``request`` can be used to optimise the returned
iterator, eg by restricting the returned attributes or geometry.
%End

virtual QgsFeatureSource::FeatureAvailability hasFeatures() const;


virtual QgsFeatureIterator getFeatures( const QgsFeatureRequest &request = QgsFeatureRequest() ) const;

virtual QgsCoordinateReferenceSystem sourceCrs() const;
Expand Down
15 changes: 14 additions & 1 deletion python/core/auto_generated/qgsfeaturesource.sip.in
Expand Up @@ -23,6 +23,13 @@ An interface for objects which provide features via a getFeatures method.
%End
public:

enum FeatureAvailability
{
NoFeaturesAvailable,
FeaturesAvailable,
FeaturesMaybeAvailable
};

virtual ~QgsFeatureSource();

virtual QgsFeatureIterator getFeatures( const QgsFeatureRequest &request = QgsFeatureRequest() ) const = 0;
Expand Down Expand Up @@ -66,6 +73,13 @@ if the feature count is unknown.
%Docstring
Returns the number of features contained in the source, or -1
if the feature count is unknown.
%End

virtual FeatureAvailability hasFeatures() const;
%Docstring
Determines if there are any features available in the source.

.. versionadded:: 3.2
%End

virtual QSet<QVariant> uniqueValues( int fieldIndex, int limit = -1 ) const;
Expand Down Expand Up @@ -139,7 +153,6 @@ for its ownership.

.. versionadded:: 3.0
%End

};


Expand Down
20 changes: 20 additions & 0 deletions python/core/auto_generated/qgsvectordataprovider.sip.in
Expand Up @@ -122,6 +122,26 @@ Returns the geometry type which is returned by this layer
Number of features in the layer

:return: long containing number of features
%End

virtual bool empty() const;
%Docstring
Returns true if the layer contains at least one feature.

.. versionadded:: 3.4
%End

virtual QgsFeatureSource::FeatureAvailability hasFeatures() const final;
%Docstring
Will always return FeatureAvailability.FeaturesAvailable or
FeatureAvailability.NoFeaturesAvailable.

Calls empty() internally. Providers should override empty()
instead if they provide an optimized version of this call.

.. seealso:: :py:func:`empty`

.. versionadded:: 3.4
%End

virtual QgsFields fields() const = 0;
Expand Down
15 changes: 15 additions & 0 deletions python/core/auto_generated/qgsvectorlayer.sip.in
Expand Up @@ -941,6 +941,21 @@ Number of features rendered with specified legend key. Features must be first
calculated by countSymbolFeatures()

:return: number of features rendered by symbol or -1 if failed or counts are not available
%End

virtual FeatureAvailability hasFeatures() const;

%Docstring
Determines if this vector layer has features.

.. warning::

when a layer is editable and some features
have been deleted, this will return
QgsFeatureSource.FeatureAvailability.FeaturesMayBeAvailable
to avoid a potentially expensive call to the dataprovider.

.. versionadded:: 3.4
%End

void setDataSource( const QString &dataSource, const QString &baseName, const QString &provider, bool loadDefaultStyleFlag = false ) /Deprecated/;
Expand Down
12 changes: 12 additions & 0 deletions src/core/processing/qgsprocessingutils.cpp
Expand Up @@ -721,6 +721,18 @@ QgsFeatureIterator QgsProcessingFeatureSource::getFeatures( const QgsFeatureRequ
return mSource->getFeatures( req );
}

QgsFeatureSource::FeatureAvailability QgsProcessingFeatureSource::hasFeatures() const
{
FeatureAvailability sourceAvailability = mSource->hasFeatures();
if ( sourceAvailability == NoFeaturesAvailable )
return NoFeaturesAvailable; // never going to be features if underlying source has no features
else if ( mInvalidGeometryCheck == QgsFeatureRequest::GeometryNoCheck )
return sourceAvailability;
else
// we don't know... source has features, but these may be filtered out by invalid geometry check
return FeaturesMaybeAvailable;
}

QgsFeatureIterator QgsProcessingFeatureSource::getFeatures( const QgsFeatureRequest &request ) const
{
QgsFeatureRequest req( request );
Expand Down
2 changes: 2 additions & 0 deletions src/core/processing/qgsprocessingutils.h
Expand Up @@ -317,6 +317,8 @@ class CORE_EXPORT QgsProcessingFeatureSource : public QgsFeatureSource
*/
QgsFeatureIterator getFeatures( const QgsFeatureRequest &request, Flags flags ) const;

QgsFeatureSource::FeatureAvailability hasFeatures() const override;

QgsFeatureIterator getFeatures( const QgsFeatureRequest &request = QgsFeatureRequest() ) const override;
QgsCoordinateReferenceSystem sourceCrs() const override;
QgsFields fields() const override;
Expand Down
5 changes: 5 additions & 0 deletions src/core/qgsfeaturesource.cpp
Expand Up @@ -23,6 +23,11 @@
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"

QgsFeatureSource::FeatureAvailability QgsFeatureSource::hasFeatures() const
{
return FeaturesMaybeAvailable;
}

QSet<QVariant> QgsFeatureSource::uniqueValues( int fieldIndex, int limit ) const
{
if ( fieldIndex < 0 || fieldIndex >= fields().count() )
Expand Down
24 changes: 23 additions & 1 deletion src/core/qgsfeaturesource.h
Expand Up @@ -38,6 +38,22 @@ class CORE_EXPORT QgsFeatureSource
{
public:

/**
* Possible return value for hasFeatures() to determine if a source is empty.
* It is implemented as a three-value logic, so it can return if
* there are features available for sure, if there are no features
* available for sure or if there might be features available but
* there is no guarantee for this.
*
* \since QGIS 3.4
*/
enum FeatureAvailability
{
NoFeaturesAvailable, //!< There are certainly no features available in this source
FeaturesAvailable, //!< There is at least one feature available in this source
FeaturesMaybeAvailable //!< There may be features available in this source
};

virtual ~QgsFeatureSource() = default;

/**
Expand Down Expand Up @@ -85,6 +101,13 @@ class CORE_EXPORT QgsFeatureSource
*/
virtual long featureCount() const = 0;

/**
* Determines if there are any features available in the source.
*
* \since QGIS 3.2
*/
virtual FeatureAvailability hasFeatures() const;

/**
* Returns the set of unique values contained within the specified \a fieldIndex from this source.
* If specified, the \a limit option can be used to limit the number of returned values.
Expand Down Expand Up @@ -150,7 +173,6 @@ class CORE_EXPORT QgsFeatureSource
*/
QgsVectorLayer *materialize( const QgsFeatureRequest &request,
QgsFeedback *feedback = nullptr ) SIP_FACTORY;

};

Q_DECLARE_METATYPE( QgsFeatureSource * )
Expand Down
21 changes: 21 additions & 0 deletions src/core/qgsvectordataprovider.cpp
Expand Up @@ -46,6 +46,27 @@ QString QgsVectorDataProvider::storageType() const
return QStringLiteral( "Generic vector file" );
}

bool QgsVectorDataProvider::empty() const
{
QgsFeature f;
QgsFeatureRequest request;
request.setSubsetOfAttributes( QgsAttributeList() );
request.setFlags( QgsFeatureRequest::NoGeometry );
request.setLimit( 1 );
if ( getFeatures( request ).nextFeature( f ) )
return false;
else
return true;
}

QgsFeatureSource::FeatureAvailability QgsVectorDataProvider::hasFeatures() const
{
if ( empty() )
return QgsFeatureSource::FeatureAvailability::NoFeaturesAvailable;
else
return QgsFeatureSource::FeatureAvailability::FeaturesAvailable;
}

QgsCoordinateReferenceSystem QgsVectorDataProvider::sourceCrs() const
{
return crs();
Expand Down
19 changes: 19 additions & 0 deletions src/core/qgsvectordataprovider.h
Expand Up @@ -163,6 +163,25 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider, public QgsFeat
*/
long featureCount() const override = 0;

/**
* Returns true if the layer contains at least one feature.
*
* \since QGIS 3.4
*/
virtual bool empty() const;

/**
* Will always return FeatureAvailability::FeaturesAvailable or
* FeatureAvailability::NoFeaturesAvailable.
*
* Calls empty() internally. Providers should override empty()
* instead if they provide an optimized version of this call.
*
* \see empty()
* \since QGIS 3.4
*/
virtual QgsFeatureSource::FeatureAvailability hasFeatures() const final;

/**
* Returns the fields associated with this data provider.
*/
Expand Down
21 changes: 21 additions & 0 deletions src/core/qgsvectorlayer.cpp
Expand Up @@ -704,6 +704,8 @@ long QgsVectorLayer::featureCount( const QString &legendKey ) const
return mSymbolFeatureCountMap.value( legendKey );
}



QgsVectorLayerFeatureCounter *QgsVectorLayer::countSymbolFeatures()
{
if ( mSymbolFeatureCounted || mFeatureCounter )
Expand Down Expand Up @@ -2766,6 +2768,25 @@ long QgsVectorLayer::featureCount() const
( mEditBuffer ? mEditBuffer->mAddedFeatures.size() - mEditBuffer->mDeletedFeatureIds.size() : 0 );
}

QgsFeatureSource::FeatureAvailability QgsVectorLayer::hasFeatures() const
{
const QgsFeatureIds deletedFeatures( mEditBuffer ? mEditBuffer->deletedFeatureIds() : QgsFeatureIds() );
const QgsFeatureMap addedFeatures( mEditBuffer ? mEditBuffer->addedFeatures() : QgsFeatureMap() );

if ( mEditBuffer && !deletedFeatures.empty() )
{
if ( addedFeatures.size() > deletedFeatures.size() )
return QgsFeatureSource::FeatureAvailability::FeaturesAvailable;
else
return QgsFeatureSource::FeatureAvailability::FeaturesMaybeAvailable;
}

if ( ( !mEditBuffer || addedFeatures.empty() ) && mDataProvider->empty() )
return QgsFeatureSource::FeatureAvailability::NoFeaturesAvailable;
else
return QgsFeatureSource::FeatureAvailability::FeaturesAvailable;
}

bool QgsVectorLayer::commitChanges()
{
mCommitErrors.clear();
Expand Down
12 changes: 12 additions & 0 deletions src/core/qgsvectorlayer.h
Expand Up @@ -930,6 +930,18 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
*/
long featureCount( const QString &legendKey ) const;

/**
* Determines if this vector layer has features.
*
* \warning when a layer is editable and some features
* have been deleted, this will return
* QgsFeatureSource::FeatureAvailability::FeaturesMayBeAvailable
* to avoid a potentially expensive call to the dataprovider.
*
* \since QGIS 3.4
*/
FeatureAvailability hasFeatures() const override;

/**
* Update the data source of the layer. The layer's renderer and legend will be preserved only
* if the geometry type of the new data source matches the current geometry type of the layer.
Expand Down
13 changes: 13 additions & 0 deletions src/providers/postgres/qgspostgresprovider.cpp
Expand Up @@ -3190,6 +3190,19 @@ long QgsPostgresProvider::featureCount() const
return num;
}

bool QgsPostgresProvider::empty() const
{
QString sql = QStringLiteral( "SELECT EXISTS (SELECT * FROM %1%2 LIMIT 1)" ).arg( mQuery, filterWhereClause() );
QgsPostgresResult res( connectionRO()->PQexec( sql ) );
if ( res.PQresultStatus() != PGRES_TUPLES_OK )
{
pushError( res.PQresultErrorMessage() );
return false;
}

return res.PQgetvalue( 0, 0 ) != 't';
}

QgsRectangle QgsPostgresProvider::extent() const
{
if ( mGeometryColumn.isNull() )
Expand Down
10 changes: 10 additions & 0 deletions src/providers/postgres/qgspostgresprovider.h
Expand Up @@ -109,6 +109,16 @@ class QgsPostgresProvider : public QgsVectorDataProvider

long featureCount() const override;

/**
* Determines if there is at least one feature available on this table.
*
* \note In contrast to the featureCount() method, this method is not
* affected by estimated metadata.
*
* \since QGIS 3.4
*/
bool empty() const override;

/**
* Returns a string representation of the endian-ness for the layer
*/
Expand Down
21 changes: 21 additions & 0 deletions src/providers/wfs/qgswfsprovider.cpp
Expand Up @@ -1206,6 +1206,27 @@ QString QgsWFSProvider::translateMetadataValue( const QString &mdKey, const QVar
{
return value.toString();
}
}

bool QgsWFSProvider::empty() const
{
QgsFeature f;
QgsFeatureRequest request;
request.setSubsetOfAttributes( QgsAttributeList() );
request.setFlags( QgsFeatureRequest::NoGeometry );

// Whoops, the WFS provider returns an empty iterator when we are using
// a setLimit call in combination with a subsetString.
// Remove this method (and default to the QgsVectorDataProvider one)
// once this is fixed
#if 0
request.setLimit( 1 );
#endif
if ( getFeatures( request ).nextFeature( f ) )
return false;
else
return true;

};

bool QgsWFSProvider::describeFeatureType( QString &geometryAttribute, QgsFields &fields, QgsWkbTypes::Type &geomType )
Expand Down
2 changes: 2 additions & 0 deletions src/providers/wfs/qgswfsprovider.h
Expand Up @@ -111,6 +111,8 @@ class QgsWFSProvider : public QgsVectorDataProvider
QString translateMetadataKey( const QString &mdKey ) const override;
QString translateMetadataValue( const QString &mdKey, const QVariant &value ) const override;

bool empty() const override;

public slots:

void reloadData() override;
Expand Down

0 comments on commit d7b85c6

Please sign in to comment.