mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Send feature counting to background task
This commit is contained in:
parent
f264370130
commit
964d9ac70a
@ -165,6 +165,7 @@
|
||||
%Include qgsvectorlayercache.sip
|
||||
%Include qgsvectorlayereditbuffer.sip
|
||||
%Include qgsvectorlayereditpassthrough.sip
|
||||
%Include qgsvectorlayerfeaturecounter.sip
|
||||
%Include qgsvectorlayerimport.sip
|
||||
%Include qgsvectorlayerjoinbuffer.sip
|
||||
%Include qgsvectorlayerjoininfo.sip
|
||||
|
@ -841,7 +841,7 @@ Return the provider type for this layer
|
||||
.. versionadded:: 2.10
|
||||
%End
|
||||
|
||||
bool countSymbolFeatures( bool showProgress = true );
|
||||
bool countSymbolFeatures();
|
||||
%Docstring
|
||||
Count features for symbols. Feature counts may be get by featureCount().
|
||||
\param showProgress show progress dialog
|
||||
@ -2000,6 +2000,12 @@ Signal emitted when setLayerTransparency() is called
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
void symbolFeatureCountMapChanged();
|
||||
%Docstring
|
||||
Emitted when the feature count for symbols on this layer has been recalculated.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
protected:
|
||||
virtual void setExtent( const QgsRectangle &rect );
|
||||
|
45
python/core/qgsvectorlayerfeaturecounter.sip
Normal file
45
python/core/qgsvectorlayerfeaturecounter.sip
Normal file
@ -0,0 +1,45 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/qgsvectorlayerfeaturecounter.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
class QgsVectorLayerFeatureCounter : QgsTask
|
||||
{
|
||||
%Docstring
|
||||
|
||||
Counts the features in a QgsVectorLayer in task.
|
||||
You should most likely not use this directly and instead call
|
||||
QgsVectorLayer.countSymbolFeatures() and connect to the signal
|
||||
QgsVectorLayer.symbolFeatureCountMapChanged().
|
||||
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsvectorlayerfeaturecounter.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsVectorLayerFeatureCounter( QgsVectorLayer *layer );
|
||||
%Docstring
|
||||
Create a new feature counter for ``layer``.
|
||||
%End
|
||||
|
||||
virtual bool run();
|
||||
|
||||
signals:
|
||||
void symbolsCounted( const QHash<QString, long> &symbolFeatureCountMap );
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/qgsvectorlayerfeaturecounter.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
@ -263,6 +263,7 @@ SET(QGIS_CORE_SRCS
|
||||
qgsvectorfilewriter.cpp
|
||||
qgsvectorfilewritertask.cpp
|
||||
qgsvectorlayer.cpp
|
||||
qgsvectorlayerfeaturecounter.cpp
|
||||
qgsvectorlayercache.cpp
|
||||
qgsvectorlayerdiagramprovider.cpp
|
||||
qgsvectorlayereditbuffer.cpp
|
||||
@ -569,6 +570,7 @@ SET(QGIS_CORE_MOC_HDRS
|
||||
qgsvectorlayereditbuffer.h
|
||||
qgsvectorlayereditpassthrough.h
|
||||
qgsvectorlayer.h
|
||||
qgsvectorlayerfeaturecounter.h
|
||||
qgsvectorlayerjoinbuffer.h
|
||||
qgsvectorlayertools.h
|
||||
qgsmapthemecollection.h
|
||||
|
@ -71,6 +71,7 @@
|
||||
#include "qgsvectorlayerlabeling.h"
|
||||
#include "qgsvectorlayerrenderer.h"
|
||||
#include "qgsvectorlayerundocommand.h"
|
||||
#include "qgsvectorlayerfeaturecounter.h"
|
||||
#include "qgspointv2.h"
|
||||
#include "qgsrenderer.h"
|
||||
#include "qgssymbollayer.h"
|
||||
@ -83,6 +84,7 @@
|
||||
#include "qgsfeedback.h"
|
||||
#include "qgsxmlutils.h"
|
||||
#include "qgsunittypes.h"
|
||||
#include "qgstaskmanager.h"
|
||||
|
||||
#include "diagram/qgsdiagram.h"
|
||||
|
||||
@ -126,6 +128,7 @@ typedef bool deleteStyleById_t(
|
||||
QString &errCause
|
||||
);
|
||||
|
||||
|
||||
QgsVectorLayer::QgsVectorLayer( const QString &vectorLayerPath,
|
||||
const QString &baseName,
|
||||
const QString &providerKey,
|
||||
@ -674,7 +677,7 @@ class QgsVectorLayerInterruptionCheckerDuringCountSymbolFeatures: public QgsInte
|
||||
QProgressDialog *mDialog = nullptr;
|
||||
};
|
||||
|
||||
bool QgsVectorLayer::countSymbolFeatures( bool showProgress )
|
||||
bool QgsVectorLayer::countSymbolFeatures()
|
||||
{
|
||||
if ( mSymbolFeatureCounted )
|
||||
return true;
|
||||
@ -697,103 +700,16 @@ bool QgsVectorLayer::countSymbolFeatures( bool showProgress )
|
||||
return false;
|
||||
}
|
||||
|
||||
QgsLegendSymbolList symbolList = mRenderer->legendSymbolItems();
|
||||
QgsLegendSymbolList::const_iterator symbolIt = symbolList.constBegin();
|
||||
|
||||
for ( ; symbolIt != symbolList.constEnd(); ++symbolIt )
|
||||
if ( !mFeatureCounter )
|
||||
{
|
||||
mSymbolFeatureCountMap.insert( symbolIt->first, 0 );
|
||||
mFeatureCounter.reset( new QgsVectorLayerFeatureCounter( this ) );
|
||||
connect( mFeatureCounter.get(), &QgsVectorLayerFeatureCounter::symbolsCounted, this, &QgsVectorLayer::onSymbolsCounted );
|
||||
connect( mFeatureCounter.get(), &QgsTask::taskCompleted, [ = ]() { mFeatureCounter.reset(); } );
|
||||
connect( mFeatureCounter.get(), &QgsTask::taskTerminated, [ = ]() { mFeatureCounter.reset(); } );
|
||||
|
||||
QgsApplication::taskManager()->addTask( mFeatureCounter.get() );
|
||||
}
|
||||
|
||||
long nFeatures = featureCount();
|
||||
|
||||
QWidget *mainWindow = nullptr;
|
||||
Q_FOREACH ( QWidget *widget, qApp->topLevelWidgets() )
|
||||
{
|
||||
if ( widget->objectName() == QLatin1String( "QgisApp" ) )
|
||||
{
|
||||
mainWindow = widget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QProgressDialog progressDialog( tr( "Updating feature count for layer %1" ).arg( name() ), tr( "Abort" ), 0, nFeatures, mainWindow );
|
||||
progressDialog.setWindowTitle( tr( "QGIS" ) );
|
||||
progressDialog.setWindowModality( Qt::WindowModal );
|
||||
if ( showProgress )
|
||||
{
|
||||
// Properly initialize to 0 as recommended in doc so that the evaluation
|
||||
// of the total time properly works
|
||||
progressDialog.setValue( 0 );
|
||||
}
|
||||
int featuresCounted = 0;
|
||||
|
||||
// Renderer (rule based) may depend on context scale, with scale is ignored if 0
|
||||
QgsRenderContext renderContext;
|
||||
renderContext.setRendererScale( 0 );
|
||||
renderContext.expressionContext().appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( this ) );
|
||||
|
||||
QgsFeatureRequest request;
|
||||
if ( !mRenderer->filterNeedsGeometry() )
|
||||
request.setFlags( QgsFeatureRequest::NoGeometry );
|
||||
request.setSubsetOfAttributes( mRenderer->usedAttributes( renderContext ), mFields );
|
||||
QgsFeatureIterator fit = getFeatures( request );
|
||||
QgsVectorLayerInterruptionCheckerDuringCountSymbolFeatures interruptionCheck( &progressDialog );
|
||||
if ( showProgress )
|
||||
{
|
||||
fit.setInterruptionChecker( &interruptionCheck );
|
||||
}
|
||||
|
||||
mRenderer->startRender( renderContext, fields() );
|
||||
|
||||
QgsFeature f;
|
||||
QTime time;
|
||||
time.start();
|
||||
while ( fit.nextFeature( f ) )
|
||||
{
|
||||
renderContext.expressionContext().setFeature( f );
|
||||
QSet<QString> featureKeyList = mRenderer->legendKeysForFeature( f, renderContext );
|
||||
Q_FOREACH ( const QString &key, featureKeyList )
|
||||
{
|
||||
mSymbolFeatureCountMap[key] += 1;
|
||||
}
|
||||
++featuresCounted;
|
||||
|
||||
if ( showProgress )
|
||||
{
|
||||
// Refresh progress every 50 features or second
|
||||
if ( ( featuresCounted % 50 == 0 ) || time.elapsed() > 1000 )
|
||||
{
|
||||
time.restart();
|
||||
if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
|
||||
{
|
||||
progressDialog.setMaximum( 0 );
|
||||
}
|
||||
progressDialog.setValue( featuresCounted );
|
||||
}
|
||||
// So that we get a chance of hitting the Abort button
|
||||
#ifdef Q_OS_LINUX
|
||||
// For some reason on Windows hasPendingEvents() always return true,
|
||||
// but one iteration is actually enough on Windows to get good interactivity
|
||||
// whereas on Linux we must allow for far more iterations.
|
||||
// For safety limit the number of iterations
|
||||
int nIters = 0;
|
||||
while ( QCoreApplication::hasPendingEvents() && ++nIters < 100 )
|
||||
#endif
|
||||
{
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
if ( progressDialog.wasCanceled() )
|
||||
{
|
||||
mSymbolFeatureCountMap.clear();
|
||||
mRenderer->stopRender( renderContext );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
mRenderer->stopRender( renderContext );
|
||||
progressDialog.setValue( nFeatures );
|
||||
mSymbolFeatureCounted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3983,6 +3899,13 @@ void QgsVectorLayer::onRelationsLoaded()
|
||||
mEditFormConfig.onRelationsLoaded();
|
||||
}
|
||||
|
||||
void QgsVectorLayer::onSymbolsCounted( const QHash<QString, long> &symbolFeatureCountMap )
|
||||
{
|
||||
mSymbolFeatureCountMap = symbolFeatureCountMap;
|
||||
mSymbolFeatureCounted = true;
|
||||
emit symbolFeatureCountMapChanged();
|
||||
}
|
||||
|
||||
QList<QgsRelation> QgsVectorLayer::referencingRelations( int idx ) const
|
||||
{
|
||||
return QgsProject::instance()->relationManager()->referencingRelations( this, idx );
|
||||
|
@ -65,6 +65,7 @@ class QgsSymbol;
|
||||
class QgsVectorLayerJoinInfo;
|
||||
class QgsVectorLayerEditBuffer;
|
||||
class QgsVectorLayerJoinBuffer;
|
||||
class QgsVectorLayerFeatureCounter;
|
||||
class QgsAbstractVectorLayerLabeling;
|
||||
class QgsPointV2;
|
||||
class QgsFeedback;
|
||||
@ -810,7 +811,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
|
||||
* \param showProgress show progress dialog
|
||||
* \returns true if calculated, false if failed or was canceled by user
|
||||
*/
|
||||
bool countSymbolFeatures( bool showProgress = true );
|
||||
bool countSymbolFeatures();
|
||||
|
||||
/**
|
||||
* Set the string (typically sql) used to define a subset of the layer
|
||||
@ -1846,11 +1847,18 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
|
||||
*/
|
||||
void readOnlyChanged();
|
||||
|
||||
/**
|
||||
* Emitted when the feature count for symbols on this layer has been recalculated.
|
||||
*
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
void symbolFeatureCountMapChanged();
|
||||
|
||||
private slots:
|
||||
void onJoinedFieldsChanged();
|
||||
void onFeatureDeleted( QgsFeatureId fid );
|
||||
void onRelationsLoaded();
|
||||
void onSymbolsCounted( const QHash<QString, long> &symbolFeatureCountMap );
|
||||
|
||||
protected:
|
||||
//! Set the extent
|
||||
@ -1883,7 +1891,6 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
|
||||
#endif
|
||||
|
||||
private: // Private attributes
|
||||
|
||||
QgsConditionalLayerStyles *mConditionalStyles = nullptr;
|
||||
|
||||
//! Pointer to data provider derived from the abastract base class QgsDataProvider
|
||||
@ -2001,6 +2008,8 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
|
||||
|
||||
mutable QMutex mFeatureSourceConstructorMutex;
|
||||
|
||||
std::unique_ptr<QgsVectorLayerFeatureCounter> mFeatureCounter;
|
||||
|
||||
friend class QgsVectorLayerFeatureSource;
|
||||
};
|
||||
|
||||
|
69
src/core/qgsvectorlayerfeaturecounter.cpp
Normal file
69
src/core/qgsvectorlayerfeaturecounter.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include "qgsvectorlayerfeaturecounter.h"
|
||||
|
||||
QgsVectorLayerFeatureCounter::QgsVectorLayerFeatureCounter( QgsVectorLayer *layer )
|
||||
: mSource( new QgsVectorLayerFeatureSource( layer ) )
|
||||
, mRenderer( layer->renderer()->clone() )
|
||||
, mExpressionContextScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) )
|
||||
, mFeatureCount( layer->featureCount() )
|
||||
{
|
||||
}
|
||||
|
||||
bool QgsVectorLayerFeatureCounter::run()
|
||||
{
|
||||
QgsLegendSymbolList symbolList = mRenderer->legendSymbolItems();
|
||||
QgsLegendSymbolList::const_iterator symbolIt = symbolList.constBegin();
|
||||
|
||||
for ( ; symbolIt != symbolList.constEnd(); ++symbolIt )
|
||||
{
|
||||
mSymbolFeatureCountMap.insert( symbolIt->first, 0 );
|
||||
}
|
||||
|
||||
int featuresCounted = 0;
|
||||
|
||||
// Renderer (rule based) may depend on context scale, with scale is ignored if 0
|
||||
QgsRenderContext renderContext;
|
||||
renderContext.setRendererScale( 0 );
|
||||
renderContext.expressionContext().appendScopes( mExpressionContextScopes );
|
||||
|
||||
QgsFeatureRequest request;
|
||||
if ( !mRenderer->filterNeedsGeometry() )
|
||||
request.setFlags( QgsFeatureRequest::NoGeometry );
|
||||
request.setSubsetOfAttributes( mRenderer->usedAttributes( renderContext ), mSource->fields() );
|
||||
QgsFeatureIterator fit = mSource->getFeatures( request );
|
||||
|
||||
// TODO: replace QgsInterruptionChecker with QgsFeedback
|
||||
// fit.setInterruptionChecker( mFeedback );
|
||||
|
||||
mRenderer->startRender( renderContext, mSource->fields() );
|
||||
|
||||
double progress = 0;
|
||||
QgsFeature f;
|
||||
while ( fit.nextFeature( f ) )
|
||||
{
|
||||
renderContext.expressionContext().setFeature( f );
|
||||
QSet<QString> featureKeyList = mRenderer->legendKeysForFeature( f, renderContext );
|
||||
Q_FOREACH ( const QString &key, featureKeyList )
|
||||
{
|
||||
mSymbolFeatureCountMap[key] += 1;
|
||||
}
|
||||
++featuresCounted;
|
||||
|
||||
double p = ( featuresCounted / mFeatureCount ) * 100;
|
||||
if ( p - progress > 1 )
|
||||
{
|
||||
progress = p;
|
||||
setProgress( progress );
|
||||
}
|
||||
|
||||
if ( isCanceled() )
|
||||
{
|
||||
mRenderer->stopRender( renderContext );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
mRenderer->stopRender( renderContext );
|
||||
setProgress( 100 );
|
||||
|
||||
emit symbolsCounted( mSymbolFeatureCountMap );
|
||||
return true;
|
||||
}
|
43
src/core/qgsvectorlayerfeaturecounter.h
Normal file
43
src/core/qgsvectorlayerfeaturecounter.h
Normal file
@ -0,0 +1,43 @@
|
||||
#ifndef QGSVECTORLAYERFEATURECOUNTER_H
|
||||
#define QGSVECTORLAYERFEATURECOUNTER_H
|
||||
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsvectorlayerfeatureiterator.h"
|
||||
#include "qgsrenderer.h"
|
||||
#include "qgstaskmanager.h"
|
||||
|
||||
/** \ingroup core
|
||||
*
|
||||
* Counts the features in a QgsVectorLayer in task.
|
||||
* You should most likely not use this directly and instead call
|
||||
* QgsVectorLayer::countSymbolFeatures() and connect to the signal
|
||||
* QgsVectorLayer::symbolFeatureCountMapChanged().
|
||||
*
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
class CORE_EXPORT QgsVectorLayerFeatureCounter : public QgsTask
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Create a new feature counter for \a layer.
|
||||
*/
|
||||
QgsVectorLayerFeatureCounter( QgsVectorLayer *layer );
|
||||
|
||||
virtual bool run() override;
|
||||
|
||||
signals:
|
||||
void symbolsCounted( const QHash<QString, long> &symbolFeatureCountMap );
|
||||
|
||||
private:
|
||||
std::unique_ptr<QgsVectorLayerFeatureSource> mSource;
|
||||
std::unique_ptr<QgsFeatureRenderer> mRenderer;
|
||||
QList<QgsExpressionContextScope *> mExpressionContextScopes;
|
||||
QHash<QString, long> mSymbolFeatureCountMap;
|
||||
int mFeatureCount;
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSVECTORLAYERFEATURECOUNTER_H
|
Loading…
x
Reference in New Issue
Block a user