Use TaskManager to build index

This commit is contained in:
Julien Cabieces 2019-09-03 14:45:42 +02:00
parent ba867b46c6
commit 4081bea801
11 changed files with 52 additions and 91 deletions

View File

@ -42,10 +42,9 @@ do the searches on data reprojected to the given CRS. For accurate reprojection
to set the correct ``transformContext`` if a ``destinationCrs`` is specified. This is usually taken
from the current :py:func:`QgsProject.transformContext()`
:param asynchronous: if ``False``, point locator init() method will block until index is completely
finished. if ``True``, index building will be done in another thread and init() method returns
immediatly. initStarted() and initFinished() signals can be used to control current state of
the point locator.
:param asynchronous: if ``False``, point locator init() method will block until point locator index
is completely built. if ``True``, index building will be done in another thread and init() method returns
immediately. initFinished() signal will be emitted once the initialization is over.
If ``extent`` is not ``None``, the locator will index only a subset of the layer which falls within that extent.
%End
@ -246,11 +245,6 @@ Emitted whenever index has been built and initialization is finished
features, otherwise ``True``
%End
void initStarted();
%Docstring
Emitted whenever index initialization has started
%End
protected:
bool rebuildIndex( int maxFeaturesToIndex = -1 );
protected slots:

View File

@ -177,13 +177,15 @@ Emitted when the snapping settings object changes.
%End
protected:
virtual void prepareIndexStarting();
virtual void prepareIndexStarting( int count );
%Docstring
Called when starting to index
This methods is now deprecated and never called
%End
virtual void prepareIndexFinished();
virtual void prepareIndexProgress( int index );
%Docstring
Called when finished indexing a layer
This methods is now deprecated and never called
%End
void clearAllLocators();

View File

@ -25,12 +25,6 @@ Snapping utils instance that is connected to a canvas and updates the configurat
public:
QgsMapCanvasSnappingUtils( QgsMapCanvas *canvas, QObject *parent = 0 );
protected:
virtual void prepareIndexStarting();
virtual void prepareIndexFinished();
};

View File

@ -317,6 +317,7 @@ SET(QGIS_CORE_SRCS
qgspluginlayerregistry.cpp
qgspointxy.cpp
qgspointlocator.cpp
qgspointlocatorinittask.cpp
qgsproject.cpp
qgsprojectbadlayerhandler.cpp
qgsprojectfiletransform.cpp
@ -708,6 +709,7 @@ SET(QGIS_CORE_MOC_HDRS
qgspluginlayer.h
qgspointxy.h
qgspointlocator.h
qgspointlocatorinittask.h
qgsproject.h
qgsproxyprogresstask.h
qgsrelationmanager.h

View File

@ -22,9 +22,11 @@
#include "qgis.h"
#include "qgslogger.h"
#include "qgsrenderer.h"
#include "qgsapplication.h"
#include "qgsvectorlayerfeatureiterator.h"
#include "qgsexpressioncontextutils.h"
#include "qgslinestring.h"
#include "qgspointlocatorinittask.h"
#include <spatialindex/SpatialIndex.h>
#include <QLinkedListIterator>
@ -543,8 +545,6 @@ QgsPointLocator::QgsPointLocator( QgsVectorLayer *layer, const QgsCoordinateRefe
connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsPointLocator::onGeometryChanged );
connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsPointLocator::onAttributeValueChanged );
connect( mLayer, &QgsVectorLayer::dataChanged, this, &QgsPointLocator::destroyIndex );
connect( &mFutureWatcher, &QFutureWatcher<bool>::finished, this, &QgsPointLocator::onRebuildIndexFinished );
}
@ -560,8 +560,8 @@ QgsCoordinateReferenceSystem QgsPointLocator::destinationCrs() const
void QgsPointLocator::setExtent( const QgsRectangle *extent )
{
if ( mFuture.isRunning() )
// already running, return!
if ( mIsIndexing )
// already indexing, return!
return;
mExtent.reset( extent ? new QgsRectangle( *extent ) : nullptr );
@ -571,8 +571,8 @@ void QgsPointLocator::setExtent( const QgsRectangle *extent )
void QgsPointLocator::setRenderContext( const QgsRenderContext *context )
{
if ( mFuture.isRunning() )
// already running, return!
if ( mIsIndexing )
// already indexing, return!
return;
disconnect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex );
@ -588,10 +588,10 @@ void QgsPointLocator::setRenderContext( const QgsRenderContext *context )
}
void QgsPointLocator::onRebuildIndexFinished()
void QgsPointLocator::onRebuildIndexFinished( bool ok )
{
emit initFinished( mFuture.result() );
mIsIndexing = false;
emit initFinished( ok );
}
void QgsPointLocator::init( int maxFeaturesToIndex )
@ -599,7 +599,7 @@ void QgsPointLocator::init( int maxFeaturesToIndex )
const QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
if ( geomType == QgsWkbTypes::NullGeometry // nothing to index
|| hasIndex()
|| mFuture.isRunning() ) // already running, return!
|| mIsIndexing ) // already indexing, return!
return;
mRenderer.reset( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
@ -609,23 +609,26 @@ void QgsPointLocator::init( int maxFeaturesToIndex )
mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
}
emit initStarted();
mIsIndexing = true;
if ( mAsynchronous )
{
mFuture = QtConcurrent::run( this, &QgsPointLocator::rebuildIndex, maxFeaturesToIndex );
mFutureWatcher.setFuture( mFuture );
QgsPointLocatorInitTask *task = new QgsPointLocatorInitTask( this );
connect( task, &QgsPointLocatorInitTask::rebuildIndexFinished, this, &QgsPointLocator::onRebuildIndexFinished );
connect( task, &QgsPointLocatorInitTask::taskTerminated, this, [ = ] { mIsIndexing = false; } );
QgsApplication::taskManager()->addTask( task );
}
else
{
const bool ok = rebuildIndex( maxFeaturesToIndex );
mIsIndexing = false;
emit initFinished( ok );
}
}
bool QgsPointLocator::hasIndex() const
{
return mFuture.isRunning() || mRTree || mIsEmptyLayer;
return mIsIndexing || mRTree || mIsEmptyLayer;
}
@ -876,7 +879,7 @@ void QgsPointLocator::onAttributeValueChanged( QgsFeatureId fid, int idx, const
QgsPointLocator::Match QgsPointLocator::nearestVertex( const QgsPointXY &point, double tolerance, MatchFilter *filter )
{
if ( mFuture.isRunning() )
if ( mIsIndexing )
return Match();
if ( !mRTree )
@ -897,7 +900,7 @@ QgsPointLocator::Match QgsPointLocator::nearestVertex( const QgsPointXY &point,
QgsPointLocator::Match QgsPointLocator::nearestEdge( const QgsPointXY &point, double tolerance, MatchFilter *filter )
{
if ( mFuture.isRunning() )
if ( mIsIndexing )
return Match();
if ( !mRTree )
@ -922,7 +925,7 @@ QgsPointLocator::Match QgsPointLocator::nearestEdge( const QgsPointXY &point, do
QgsPointLocator::Match QgsPointLocator::nearestArea( const QgsPointXY &point, double tolerance, MatchFilter *filter )
{
if ( mFuture.isRunning() )
if ( mIsIndexing )
return Match();
if ( !mRTree )
@ -959,7 +962,7 @@ QgsPointLocator::Match QgsPointLocator::nearestArea( const QgsPointXY &point, do
QgsPointLocator::MatchList QgsPointLocator::edgesInRect( const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter )
{
if ( mFuture.isRunning() )
if ( mIsIndexing )
return MatchList();
if ( !mRTree )
@ -988,7 +991,7 @@ QgsPointLocator::MatchList QgsPointLocator::edgesInRect( const QgsPointXY &point
QgsPointLocator::MatchList QgsPointLocator::verticesInRect( const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter )
{
if ( mFuture.isRunning() )
if ( mIsIndexing )
return MatchList();
if ( !mRTree )
@ -1014,7 +1017,7 @@ QgsPointLocator::MatchList QgsPointLocator::verticesInRect( const QgsPointXY &po
QgsPointLocator::MatchList QgsPointLocator::pointInPolygon( const QgsPointXY &point )
{
if ( mFuture.isRunning() )
if ( mIsIndexing )
return MatchList();
if ( !mRTree )

View File

@ -68,10 +68,9 @@ class CORE_EXPORT QgsPointLocator : public QObject
* to set the correct \a transformContext if a \a destinationCrs is specified. This is usually taken
* from the current QgsProject::transformContext().
*
* \param asynchronous if FALSE, point locator init() method will block until index is completely
* finished. if TRUE, index building will be done in another thread and init() method returns
* immediatly. initStarted() and initFinished() signals can be used to control current state of
* the point locator.
* \param asynchronous if FALSE, point locator init() method will block until point locator index
* is completely built. if TRUE, index building will be done in another thread and init() method returns
* immediately. initFinished() signal will be emitted once the initialization is over.
*
* If \a extent is not NULLPTR, the locator will index only a subset of the layer which falls within that extent.
*/
@ -301,17 +300,12 @@ class CORE_EXPORT QgsPointLocator : public QObject
*/
void initFinished( bool ok );
/**
* Emitted whenever index initialization has started
*/
void initStarted();
protected:
bool rebuildIndex( int maxFeaturesToIndex = -1 );
protected slots:
void destroyIndex();
private slots:
void onRebuildIndexFinished();
void onRebuildIndexFinished( bool ok );
void onFeatureAdded( QgsFeatureId fid );
void onFeatureDeleted( QgsFeatureId fid );
void onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom );
@ -335,16 +329,16 @@ class CORE_EXPORT QgsPointLocator : public QObject
std::unique_ptr<QgsRenderContext> mContext;
std::unique_ptr<QgsFeatureRenderer> mRenderer;
QFuture<bool> mFuture;
QFutureWatcher<bool> mFutureWatcher;
int mMaxFeaturesToIndex = -1;
bool mAsynchronous = false;
bool mIsIndexing = false;
friend class QgsPointLocator_VisitorNearestVertex;
friend class QgsPointLocator_VisitorNearestEdge;
friend class QgsPointLocator_VisitorArea;
friend class QgsPointLocator_VisitorEdgesInRect;
friend class QgsPointLocator_VisitorVerticesInRect;
friend class QgsPointLocatorInitTask;
};

View File

@ -45,7 +45,6 @@ QgsPointLocator *QgsSnappingUtils::locatorForLayer( QgsVectorLayer *vl )
{
QgsPointLocator *vlpl = new QgsPointLocator( vl, destinationCrs(), mMapSettings.transformContext(), nullptr, mAsynchronous );
connect( vlpl, &QgsPointLocator::initFinished, this, &QgsSnappingUtils::onInitFinished );
connect( vlpl, &QgsPointLocator::initStarted, this, &QgsSnappingUtils::onInitStarted );
mLocators.insert( vl, vlpl );
}
return mLocators.value( vl );
@ -80,7 +79,6 @@ QgsPointLocator *QgsSnappingUtils::temporaryLocatorForLayer( QgsVectorLayer *vl,
pointMap.x() + tolerance, pointMap.y() + tolerance );
QgsPointLocator *vlpl = new QgsPointLocator( vl, destinationCrs(), mMapSettings.transformContext(), &rect, mAsynchronous );
connect( vlpl, &QgsPointLocator::initFinished, this, &QgsSnappingUtils::onInitFinished );
connect( vlpl, &QgsPointLocator::initStarted, this, &QgsSnappingUtils::onInitStarted );
mTemporaryLocators.insert( vl, vlpl );
return mTemporaryLocators.value( vl );
@ -355,13 +353,6 @@ void QgsSnappingUtils::onInitFinished( bool ok )
{
mHybridMaxAreaPerLayer[loc->layer()->id()] /= 4;
}
prepareIndexFinished();
}
void QgsSnappingUtils::onInitStarted()
{
prepareIndexStarting();
}
void QgsSnappingUtils::prepareIndex( const QList<LayerAndAreaOfInterest> &layers )

View File

@ -196,10 +196,16 @@ class CORE_EXPORT QgsSnappingUtils : public QObject
void configChanged( const QgsSnappingConfig &snappingConfig );
protected:
//! Called when starting to index
virtual void prepareIndexStarting() {}
//! Called when finished indexing a layer
virtual void prepareIndexFinished() {}
/**
* This methods is now deprecated and never called
*/
Q_DECL_DEPRECATED virtual void prepareIndexStarting( int count ) { Q_UNUSED( count ); }
/**
* This methods is now deprecated and never called
*/
Q_DECL_DEPRECATED virtual void prepareIndexProgress( int index ) { Q_UNUSED( index ); }
//! Deletes all existing locators (e.g. when destination CRS has changed and we need to reindex)
void clearAllLocators();
@ -209,9 +215,6 @@ class CORE_EXPORT QgsSnappingUtils : public QObject
//! called whenever a point locator has finished
void onInitFinished( bool ok );
//! called whenever a point locator has started
void onInitStarted();
private:
void onIndividualLayerSettingsChanged( const QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings> &layerSettings );
//! Gets destination CRS from map settings, or an invalid CRS if projections are disabled
@ -266,13 +269,11 @@ class CORE_EXPORT QgsSnappingUtils : public QObject
//! Disable or not the snapping on all features. By default is always TRUE except for non visible features on map canvas.
bool mEnableSnappingForInvisibleFeature = true;
//! Number of index currently being prepared
int mPreparingIndexCount = 0;
//! true if we have to build point locator asynchronously
bool mAsynchronous = false;
friend class TestQgsVertexTool;
friend class TestQgsMapToolTrimExtendFeature;
};

View File

@ -58,19 +58,3 @@ void QgsMapCanvasSnappingUtils::canvasMapToolChanged()
{
setEnableSnappingForInvisibleFeature( QgsSettings().value( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), false ).toBool() );
}
void QgsMapCanvasSnappingUtils::prepareIndexStarting()
{
if ( !mPreparingIndexCount )
QApplication::setOverrideCursor( Qt::BusyCursor );
mPreparingIndexCount++;
}
void QgsMapCanvasSnappingUtils::prepareIndexFinished()
{
mPreparingIndexCount--;
if ( !mPreparingIndexCount )
QApplication::restoreOverrideCursor();
}

View File

@ -36,10 +36,6 @@ class GUI_EXPORT QgsMapCanvasSnappingUtils : public QgsSnappingUtils
public:
QgsMapCanvasSnappingUtils( QgsMapCanvas *canvas, QObject *parent = nullptr );
protected:
void prepareIndexStarting() override;
void prepareIndexFinished() override;
private slots:
void canvasMapSettingsChanged();
void canvasTransformContextChanged();
@ -49,7 +45,6 @@ class GUI_EXPORT QgsMapCanvasSnappingUtils : public QgsSnappingUtils
private:
QgsMapCanvas *mCanvas = nullptr;
QProgressDialog *mProgress = nullptr;
int mPreparingIndexCount = 0;
};

View File

@ -164,6 +164,7 @@ class TestQgsMapToolTrimExtendFeature : public QObject
mapSettings.setLayers( QList<QgsMapLayer *>() << vlPolygon.get() << vlMultiLine.get() << vlLineZ.get() << vlTopoEdit.get() << vlTopoLimit.get() );
QgsSnappingUtils *mSnappingUtils = new QgsMapCanvasSnappingUtils( mCanvas, this );
mSnappingUtils->mAsynchronous = false;
QgsSnappingConfig snappingConfig = mSnappingUtils->config();
mSnappingUtils->setMapSettings( mapSettings );
snappingConfig.setEnabled( true );