diff --git a/python/core/auto_generated/qgspointlocator.sip.in b/python/core/auto_generated/qgspointlocator.sip.in index 255d5806d34..30eb1ea0734 100644 --- a/python/core/auto_generated/qgspointlocator.sip.in +++ b/python/core/auto_generated/qgspointlocator.sip.in @@ -31,8 +31,7 @@ Works with one layer. explicit QgsPointLocator( QgsVectorLayer *layer, const QgsCoordinateReferenceSystem &destinationCrs = QgsCoordinateReferenceSystem(), const QgsCoordinateTransformContext &transformContext = QgsCoordinateTransformContext(), - const QgsRectangle *extent = 0, - bool asynchronous = false ); + const QgsRectangle *extent = 0 ); %Docstring Construct point locator for a ``layer``. @@ -41,10 +40,6 @@ 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()` -if ``asynchronous`` is ``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 @@ -101,13 +96,8 @@ Configure render context - if not ``None``, it will use to index only visible f %Docstring Prepare the index for queries. Does nothing if the index already exists. If the number of features is greater than the value of maxFeaturesToIndex, creation of index is stopped -to make sure we do not run out of memory. If maxFeaturesToIndex is -1, no limits are used. - -This method is either blocking or non blocking according to ``asynchronous`` parameter passed -in the constructor. -Returns false if the creation of index is blocking and has been prematurely stopped due to the limit of features, otherwise true - -.. seealso:: :py:class:`QgsPointLocator` +to make sure we do not run out of memory. If maxFeaturesToIndex is -1, no limits are used. Returns +``False`` if the creation of index has been prematurely stopped due to the limit of features, otherwise ``True`` %End bool hasIndex() const; @@ -238,27 +228,8 @@ Returns how many geometries are cached in the index .. versionadded:: 2.14 %End - bool isIndexing() const; -%Docstring -Returns ``True`` if the point locator is currently indexing the data. -This method is useful if constructor parameter ``asynchronous`` is ``True`` - -.. seealso:: :py:class:`QgsPointLocator` -%End - - signals: - - void initFinished( bool ok ); -%Docstring -Emitted whenever index has been built and initialization is finished - -:param ok: ``False`` if the creation of index has been prematurely stopped due to the limit of - features, otherwise ``True`` -%End - protected: bool rebuildIndex( int maxFeaturesToIndex = -1 ); - protected slots: void destroyIndex(); }; diff --git a/python/core/auto_generated/qgssnappingutils.sip.in b/python/core/auto_generated/qgssnappingutils.sip.in index 4d9e7243f69..acf32de8a4b 100644 --- a/python/core/auto_generated/qgssnappingutils.sip.in +++ b/python/core/auto_generated/qgssnappingutils.sip.in @@ -35,14 +35,9 @@ which keeps the configuration in sync with map canvas (e.g. current view, active %End public: - QgsSnappingUtils( QObject *parent /TransferThis/ = 0, bool enableSnappingForInvisibleFeature = true, - bool asynchronous = false ); + QgsSnappingUtils( QObject *parent /TransferThis/ = 0, bool enableSnappingForInvisibleFeature = true ); %Docstring Constructor for QgsSnappingUtils - -:param parent: parent object -:param enableSnappingForInvisibleFeature: ``True`` if we want to snap feature even if there are not visible -:param asynchronous: indicated if point locator creation has to be made asynchronously (see :py:class:`QgsPointLocator`()) %End ~QgsSnappingUtils(); @@ -177,19 +172,13 @@ Emitted when the snapping settings object changes. %End protected: - - virtual void prepareIndexStarting( int count ); + virtual void prepareIndexStarting( int count ); %Docstring -This methods is now deprecated and never called - -.. deprecated:: QGIS 3.10 +Called when starting to index - can be overridden and e.g. progress dialog can be provided %End - - virtual void prepareIndexProgress( int index ); + virtual void prepareIndexProgress( int index ); %Docstring -This methods is now deprecated and never called - -.. deprecated:: QGIS 3.10 +Called when finished indexing a layer. When index == count the indexing is complete %End void clearAllLocators(); diff --git a/python/gui/auto_generated/qgsmapcanvassnappingutils.sip.in b/python/gui/auto_generated/qgsmapcanvassnappingutils.sip.in index 1256657b2ce..ccd1e12238c 100644 --- a/python/gui/auto_generated/qgsmapcanvassnappingutils.sip.in +++ b/python/gui/auto_generated/qgsmapcanvassnappingutils.sip.in @@ -23,16 +23,13 @@ Snapping utils instance that is connected to a canvas and updates the configurat #include "qgsmapcanvassnappingutils.h" %End public: + QgsMapCanvasSnappingUtils( QgsMapCanvas *canvas, QObject *parent = 0 ); - QgsMapCanvasSnappingUtils( QgsMapCanvas *canvas, QObject *parent = 0, bool asynchronous = false ); -%Docstring -Construct map canvas snapping utils object + protected: + virtual void prepareIndexStarting( int count ); + + virtual void prepareIndexProgress( int index ); -:param canvas: map canvas -:param parent: parent object -:param asynchronous: if ``True`` snapping cache index will be non blocking and done in another thread, - if ``False`` it will block until indexing is done -%End }; diff --git a/resources/data/world_map.gpkg b/resources/data/world_map.gpkg index 3c0005d51cf..bb32da5b0dd 100644 Binary files a/resources/data/world_map.gpkg and b/resources/data/world_map.gpkg differ diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index f5240233324..4b2e3fe4059 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -961,7 +961,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh endProfile(); startProfile( QStringLiteral( "Snapping utils" ) ); - mSnappingUtils = new QgsMapCanvasSnappingUtils( mMapCanvas, this, true ); + mSnappingUtils = new QgsMapCanvasSnappingUtils( mMapCanvas, this ); mMapCanvas->setSnappingUtils( mSnappingUtils ); connect( QgsProject::instance(), &QgsProject::snappingConfigChanged, mSnappingUtils, &QgsSnappingUtils::setConfig ); connect( QgsProject::instance(), &QgsProject::collectAttachedFiles, this, &QgisApp::generateProjectAttachedFiles ); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5b82e5c3ddc..d3d7f7188f3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -317,7 +317,6 @@ SET(QGIS_CORE_SRCS qgspluginlayerregistry.cpp qgspointxy.cpp qgspointlocator.cpp - qgspointlocatorinittask.cpp qgsproject.cpp qgsprojectbadlayerhandler.cpp qgsprojectfiletransform.cpp @@ -713,7 +712,6 @@ SET(QGIS_CORE_MOC_HDRS qgspluginlayer.h qgspointxy.h qgspointlocator.h - qgspointlocatorinittask.h qgsproject.h qgsproxyprogresstask.h qgsrelationmanager.h diff --git a/src/core/qgspointlocator.cpp b/src/core/qgspointlocator.cpp index 627caace07b..3ba87a0b7fa 100644 --- a/src/core/qgspointlocator.cpp +++ b/src/core/qgspointlocator.cpp @@ -22,15 +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 #include -#include using namespace SpatialIndex; @@ -526,10 +522,8 @@ class QgsPointLocator_DumpTree : public SpatialIndex::IQueryStrategy //////////////////////////////////////////////////////////////////////////// -QgsPointLocator::QgsPointLocator( QgsVectorLayer *layer, const QgsCoordinateReferenceSystem &destCRS, const QgsCoordinateTransformContext &transformContext, const QgsRectangle *extent, - bool asynchronous ) +QgsPointLocator::QgsPointLocator( QgsVectorLayer *layer, const QgsCoordinateReferenceSystem &destCRS, const QgsCoordinateTransformContext &transformContext, const QgsRectangle *extent ) : mLayer( layer ) - , mAsynchronous( asynchronous ) { if ( destCRS.isValid() ) { @@ -560,10 +554,6 @@ QgsCoordinateReferenceSystem QgsPointLocator::destinationCrs() const void QgsPointLocator::setExtent( const QgsRectangle *extent ) { - if ( mIsIndexing ) - // already indexing, return! - return; - mExtent.reset( extent ? new QgsRectangle( *extent ) : nullptr ); destroyIndex(); @@ -571,10 +561,6 @@ void QgsPointLocator::setExtent( const QgsRectangle *extent ) void QgsPointLocator::setRenderContext( const QgsRenderContext *context ) { - if ( mIsIndexing ) - // already indexing, return! - return; - disconnect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex ); destroyIndex(); @@ -588,93 +574,27 @@ void QgsPointLocator::setRenderContext( const QgsRenderContext *context ) } -void QgsPointLocator::onInitTaskTerminated() -{ - mIsIndexing = false; - mRenderer.reset(); - mSource.reset(); -} - -void QgsPointLocator::onRebuildIndexFinished( bool ok ) -{ - onInitTaskTerminated(); - - // treat added and deleted feature while indexing - for ( QgsFeatureId fid : mAddedFeatures ) - onFeatureAdded( fid ); - - for ( QgsFeatureId fid : mDeletedFeatures ) - onFeatureDeleted( fid ); - - emit initFinished( ok ); -} - bool QgsPointLocator::init( int maxFeaturesToIndex ) { - const QgsWkbTypes::GeometryType geomType = mLayer->geometryType(); - if ( geomType == QgsWkbTypes::NullGeometry // nothing to index - || hasIndex() - || mIsIndexing ) // already indexing, return! - return true; - - mRenderer.reset( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr ); - mSource.reset( new QgsVectorLayerFeatureSource( mLayer ) ); - - if ( mContext ) - { - mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer ); - } - - mIsIndexing = true; - - if ( mAsynchronous ) - { - QgsPointLocatorInitTask *task = new QgsPointLocatorInitTask( this ); - connect( task, &QgsPointLocatorInitTask::rebuildIndexFinished, this, &QgsPointLocator::onRebuildIndexFinished ); - connect( task, &QgsPointLocatorInitTask::taskTerminated, this, &QgsPointLocator::onInitTaskTerminated ); - QgsApplication::taskManager()->addTask( task ); - return true; - } - else - { - const bool ok = rebuildIndex( maxFeaturesToIndex ); - mIsIndexing = false; - emit initFinished( ok ); - return ok; - } + return hasIndex() ? true : rebuildIndex( maxFeaturesToIndex ); } + bool QgsPointLocator::hasIndex() const { - return mIsIndexing || mRTree || mIsEmptyLayer; + return mRTree || mIsEmptyLayer; } -bool QgsPointLocator::prepare() -{ - if ( mIsIndexing ) - return false; - - if ( !mRTree ) - { - init(); - if ( mIsIndexing || !mRTree ) // asynchronous mode and currently indexing or still invalid? - return false; - } - - return true; -} bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex ) { - QTime t; - t.start(); - - QgsDebugMsg( QStringLiteral( "RebuildIndex start : %1" ).arg( mSource->id() ) ); - destroyIndex(); QLinkedList dataList; QgsFeature f; + QgsWkbTypes::GeometryType geomType = mLayer->geometryType(); + if ( geomType == QgsWkbTypes::NullGeometry ) + return true; // nothing to index QgsFeatureRequest request; request.setNoAttributes(); @@ -699,20 +619,22 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex ) } bool filter = false; + std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr ); QgsRenderContext *ctx = nullptr; if ( mContext ) { + mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer ); ctx = mContext.get(); - if ( mRenderer ) + if ( renderer ) { // setup scale for scale dependent visibility (rule based) - mRenderer->startRender( *ctx, mSource->fields() ); - filter = mRenderer->capabilities() & QgsFeatureRenderer::Filter; - request.setSubsetOfAttributes( mRenderer->usedAttributes( *ctx ), mSource->fields() ); + renderer->startRender( *ctx, mLayer->fields() ); + filter = renderer->capabilities() & QgsFeatureRenderer::Filter; + request.setSubsetOfAttributes( renderer->usedAttributes( *ctx ), mLayer->fields() ); } } - QgsFeatureIterator fi = mSource->getFeatures( request ); + QgsFeatureIterator fi = mLayer->getFeatures( request ); int indexedCount = 0; while ( fi.nextFeature( f ) ) @@ -720,10 +642,10 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex ) if ( !f.hasGeometry() ) continue; - if ( filter && ctx && mRenderer ) + if ( filter && ctx && renderer ) { ctx->expressionContext().setFeature( f ); - if ( !mRenderer->willRenderFeature( f, *ctx ) ) + if ( !renderer->willRenderFeature( f, *ctx ) ) { continue; } @@ -784,13 +706,10 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex ) mRTree.reset( RTree::createAndBulkLoadNewRTree( RTree::BLM_STR, stream, *mStorage, fillFactor, indexCapacity, leafCapacity, dimension, variant, indexId ) ); - if ( ctx && mRenderer ) + if ( ctx && renderer ) { - mRenderer->stopRender( *ctx ); + renderer->stopRender( *ctx ); } - - QgsDebugMsg( QStringLiteral( "RebuildIndex end : %1 ms (%2)" ).arg( t.elapsed() ).arg( mSource->id() ) ); - return true; } @@ -808,17 +727,10 @@ void QgsPointLocator::destroyIndex() void QgsPointLocator::onFeatureAdded( QgsFeatureId fid ) { - if ( mIsIndexing ) - { - // will modify index once current indexing is finished - mAddedFeatures << fid; - return; - } - if ( !mRTree ) { if ( mIsEmptyLayer ) - init(); // first feature - let's built the index + rebuildIndex(); // first feature - let's built the index return; // nothing to do if we are not initialized yet } @@ -884,20 +796,6 @@ void QgsPointLocator::onFeatureAdded( QgsFeatureId fid ) void QgsPointLocator::onFeatureDeleted( QgsFeatureId fid ) { - if ( mIsIndexing ) - { - if ( mAddedFeatures.contains( fid ) ) - { - mAddedFeatures.remove( fid ); - } - else - { - // will modify index once current indexing is finished - mDeletedFeatures << fid; - } - return; - } - if ( !mRTree ) return; // nothing to do if we are not initialized yet @@ -930,8 +828,12 @@ void QgsPointLocator::onAttributeValueChanged( QgsFeatureId fid, int idx, const QgsPointLocator::Match QgsPointLocator::nearestVertex( const QgsPointXY &point, double tolerance, MatchFilter *filter ) { - if ( !prepare() ) - return Match(); + if ( !mRTree ) + { + init(); + if ( !mRTree ) // still invalid? + return Match(); + } Match m; QgsPointLocator_VisitorNearestVertex visitor( this, m, point, filter ); @@ -944,8 +846,12 @@ QgsPointLocator::Match QgsPointLocator::nearestVertex( const QgsPointXY &point, QgsPointLocator::Match QgsPointLocator::nearestEdge( const QgsPointXY &point, double tolerance, MatchFilter *filter ) { - if ( !prepare() ) - return Match(); + if ( !mRTree ) + { + init(); + if ( !mRTree ) // still invalid? + return Match(); + } QgsWkbTypes::GeometryType geomType = mLayer->geometryType(); if ( geomType == QgsWkbTypes::PointGeometry ) @@ -962,8 +868,12 @@ QgsPointLocator::Match QgsPointLocator::nearestEdge( const QgsPointXY &point, do QgsPointLocator::Match QgsPointLocator::nearestArea( const QgsPointXY &point, double tolerance, MatchFilter *filter ) { - if ( !prepare() ) - return Match(); + if ( !mRTree ) + { + init(); + if ( !mRTree ) // still invalid? + return Match(); + } MatchList mlist = pointInPolygon( point ); if ( !mlist.isEmpty() && mlist.at( 0 ).isValid() ) @@ -992,8 +902,12 @@ QgsPointLocator::Match QgsPointLocator::nearestArea( const QgsPointXY &point, do QgsPointLocator::MatchList QgsPointLocator::edgesInRect( const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter ) { - if ( !prepare() ) - return MatchList(); + if ( !mRTree ) + { + init(); + if ( !mRTree ) // still invalid? + return MatchList(); + } QgsWkbTypes::GeometryType geomType = mLayer->geometryType(); if ( geomType == QgsWkbTypes::PointGeometry ) @@ -1014,8 +928,12 @@ QgsPointLocator::MatchList QgsPointLocator::edgesInRect( const QgsPointXY &point QgsPointLocator::MatchList QgsPointLocator::verticesInRect( const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter ) { - if ( !prepare() ) - return MatchList(); + if ( !mRTree ) + { + init(); + if ( !mRTree ) // still invalid? + return MatchList(); + } MatchList lst; QgsPointLocator_VisitorVerticesInRect visitor( this, lst, rect, filter ); @@ -1030,10 +948,15 @@ QgsPointLocator::MatchList QgsPointLocator::verticesInRect( const QgsPointXY &po return verticesInRect( rect, filter ); } + QgsPointLocator::MatchList QgsPointLocator::pointInPolygon( const QgsPointXY &point ) { - if ( !prepare() ) - return MatchList(); + if ( !mRTree ) + { + init(); + if ( !mRTree ) // still invalid? + return MatchList(); + } QgsWkbTypes::GeometryType geomType = mLayer->geometryType(); if ( geomType == QgsWkbTypes::PointGeometry || geomType == QgsWkbTypes::LineGeometry ) diff --git a/src/core/qgspointlocator.h b/src/core/qgspointlocator.h index be0c192c41d..615b8cbc631 100644 --- a/src/core/qgspointlocator.h +++ b/src/core/qgspointlocator.h @@ -21,7 +21,6 @@ class QgsVectorLayer; class QgsFeatureRenderer; class QgsRenderContext; class QgsRectangle; -class QgsVectorLayerFeatureSource; #include "qgis_core.h" #include "qgspointxy.h" @@ -66,16 +65,11 @@ 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(). * - * if \a asynchronous is 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. */ explicit QgsPointLocator( QgsVectorLayer *layer, const QgsCoordinateReferenceSystem &destinationCrs = QgsCoordinateReferenceSystem(), const QgsCoordinateTransformContext &transformContext = QgsCoordinateTransformContext(), - const QgsRectangle *extent = nullptr, - bool asynchronous = false ); + const QgsRectangle *extent = nullptr ); ~QgsPointLocator() override; @@ -126,14 +120,9 @@ class CORE_EXPORT QgsPointLocator : public QObject /** * Prepare the index for queries. Does nothing if the index already exists. * If the number of features is greater than the value of maxFeaturesToIndex, creation of index is stopped - * to make sure we do not run out of memory. If maxFeaturesToIndex is -1, no limits are used. - * - * This method is either blocking or non blocking according to \a asynchronous parameter passed - * in the constructor. - * Returns false if the creation of index is blocking and has been prematurely stopped due to the limit of features, otherwise true - * - * \see QgsPointLocator() - */ + * to make sure we do not run out of memory. If maxFeaturesToIndex is -1, no limits are used. Returns + * FALSE if the creation of index has been prematurely stopped due to the limit of features, otherwise TRUE + */ bool init( int maxFeaturesToIndex = -1 ); //! Indicate whether the data have been already indexed @@ -293,41 +282,17 @@ class CORE_EXPORT QgsPointLocator : public QObject */ int cachedGeometryCount() const { return mGeoms.count(); } - /** - * Returns TRUE if the point locator is currently indexing the data. - * This method is useful if constructor parameter \a asynchronous is TRUE - * - * \see QgsPointLocator() - */ - bool isIndexing() const { return mIsIndexing; } - - signals: - - /** - * Emitted whenever index has been built and initialization is finished - * \param ok FALSE if the creation of index has been prematurely stopped due to the limit of - * features, otherwise TRUE - */ - void initFinished( bool ok ); - protected: bool rebuildIndex( int maxFeaturesToIndex = -1 ); - protected slots: void destroyIndex(); private slots: - void onInitTaskTerminated(); - void onRebuildIndexFinished( bool ok ); void onFeatureAdded( QgsFeatureId fid ); void onFeatureDeleted( QgsFeatureId fid ); void onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom ); void onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value ); private: - - //! prepare index if need and returns TRUE if the index is ready to be used - bool prepare(); - //! Storage manager std::unique_ptr< SpatialIndex::IStorageManager > mStorage; @@ -344,21 +309,12 @@ class CORE_EXPORT QgsPointLocator : public QObject std::unique_ptr< QgsRectangle > mExtent; std::unique_ptr mContext; - std::unique_ptr mRenderer; - std::unique_ptr mSource; - int mMaxFeaturesToIndex = -1; - bool mAsynchronous = false; - bool mIsIndexing = false; - QgsFeatureIds mAddedFeatures; - QgsFeatureIds mDeletedFeatures; friend class QgsPointLocator_VisitorNearestVertex; friend class QgsPointLocator_VisitorNearestEdge; friend class QgsPointLocator_VisitorArea; friend class QgsPointLocator_VisitorEdgesInRect; friend class QgsPointLocator_VisitorVerticesInRect; - friend class QgsPointLocatorInitTask; - friend class TestQgsPointLocator; }; diff --git a/src/core/qgspointlocatorinittask.cpp b/src/core/qgspointlocatorinittask.cpp deleted file mode 100644 index bb13b19488a..00000000000 --- a/src/core/qgspointlocatorinittask.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/*************************************************************************** - qgspointlocatorinittask.cpp - -------------------------------------- - Date : September 2019 - Copyright : (C) 2019 by Julien Cabieces - Email : julien dot cabieces at oslandia dot com - *************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgspointlocatorinittask.h" - -#include "qgsvectorlayer.h" - -/// @cond PRIVATE - -QgsPointLocatorInitTask::QgsPointLocatorInitTask( QgsPointLocator *loc ) - : QgsTask( tr( "Indexing %1" ).arg( loc->layer()->id() ), QgsTask::Flags() ) - , mLoc( loc ) -{} - -bool QgsPointLocatorInitTask::run() -{ - const bool ok = mLoc->rebuildIndex(); - emit rebuildIndexFinished( ok ); - return true; -} - -/// @endcond diff --git a/src/core/qgspointlocatorinittask.h b/src/core/qgspointlocatorinittask.h deleted file mode 100644 index 83a791d2093..00000000000 --- a/src/core/qgspointlocatorinittask.h +++ /dev/null @@ -1,56 +0,0 @@ -/*************************************************************************** - qgspointlocatorinittask.h - -------------------------------------- - Date : September 2019 - Copyright : (C) 2019 by Julien Cabieces - Email : julien dot cabieces at oslandia dot com - *************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef QGSPOINTLOCATORINITTASK_H -#define QGSPOINTLOCATORINITTASK_H - -/// @cond PRIVATE - -// -// W A R N I N G -// ------------- -// -// This file is not part of the QGIS API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// - -#define SIP_NO_FILE - -#include "qgstaskmanager.h" -#include "qgspointlocator.h" - -class QgsPointLocatorInitTask : public QgsTask -{ - Q_OBJECT - - public: - - QgsPointLocatorInitTask( QgsPointLocator *loc ); - - bool run(); - - signals: - - void rebuildIndexFinished( bool ok ); - - private: - - QgsPointLocator *mLoc = nullptr; -}; - -/// @endcond - -#endif // QGSPOINTLOCATORINITTASK_H diff --git a/src/core/qgssnappingutils.cpp b/src/core/qgssnappingutils.cpp index b05c0b3015c..cd99ea655b2 100644 --- a/src/core/qgssnappingutils.cpp +++ b/src/core/qgssnappingutils.cpp @@ -21,12 +21,10 @@ #include "qgslogger.h" #include "qgsrenderer.h" -QgsSnappingUtils::QgsSnappingUtils( QObject *parent, bool enableSnappingForInvisibleFeature, - bool asynchronous ) +QgsSnappingUtils::QgsSnappingUtils( QObject *parent, bool enableSnappingForInvisibleFeature ) : QObject( parent ) , mSnappingConfig( QgsProject::instance() ) , mEnableSnappingForInvisibleFeature( enableSnappingForInvisibleFeature ) - , mAsynchronous( asynchronous ) { } @@ -43,8 +41,7 @@ QgsPointLocator *QgsSnappingUtils::locatorForLayer( QgsVectorLayer *vl ) if ( !mLocators.contains( vl ) ) { - QgsPointLocator *vlpl = new QgsPointLocator( vl, destinationCrs(), mMapSettings.transformContext(), nullptr, mAsynchronous ); - connect( vlpl, &QgsPointLocator::initFinished, this, &QgsSnappingUtils::onInitFinished ); + QgsPointLocator *vlpl = new QgsPointLocator( vl, destinationCrs(), mMapSettings.transformContext() ); mLocators.insert( vl, vlpl ); } return mLocators.value( vl ); @@ -77,9 +74,7 @@ QgsPointLocator *QgsSnappingUtils::temporaryLocatorForLayer( QgsVectorLayer *vl, QgsRectangle rect( pointMap.x() - tolerance, pointMap.y() - tolerance, pointMap.x() + tolerance, pointMap.y() + tolerance ); - QgsPointLocator *vlpl = new QgsPointLocator( vl, destinationCrs(), mMapSettings.transformContext(), &rect, mAsynchronous ); - connect( vlpl, &QgsPointLocator::initFinished, this, &QgsSnappingUtils::onInitFinished ); - + QgsPointLocator *vlpl = new QgsPointLocator( vl, destinationCrs(), mMapSettings.transformContext(), &rect ); mTemporaryLocators.insert( vl, vlpl ); return mTemporaryLocators.value( vl ); } @@ -91,9 +86,6 @@ bool QgsSnappingUtils::isIndexPrepared( QgsVectorLayer *vl, const QgsRectangle & QgsPointLocator *loc = locatorForLayer( vl ); - if ( loc->isIndexing() ) - return true; - if ( mStrategy == IndexAlwaysFull && loc->hasIndex() ) return true; @@ -346,20 +338,12 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPointXY &pointMap, return QgsPointLocator::Match(); } -void QgsSnappingUtils::onInitFinished( bool ok ) -{ - QgsPointLocator *loc = static_cast( sender() ); - - // point locator init didn't work out - too many features! - // let's make the allowed area smaller for the next time - if ( !ok ) - { - mHybridMaxAreaPerLayer[loc->layer()->id()] /= 4; - } -} - void QgsSnappingUtils::prepareIndex( const QList &layers ) { + if ( mIsIndexing ) + return; + mIsIndexing = true; + // check if we need to build any index QList layersToIndex; const auto constLayers = layers; @@ -378,6 +362,8 @@ void QgsSnappingUtils::prepareIndex( const QList &layers // build indexes QTime t; t.start(); + int i = 0; + prepareIndexStarting( layersToIndex.count() ); const auto constLayersToIndex = layersToIndex; for ( const LayerAndAreaOfInterest &entry : constLayersToIndex ) { @@ -436,14 +422,24 @@ void QgsSnappingUtils::prepareIndex( const QList &layers loc->setExtent( &rect ); // see if it's possible build index for this area - loc->init( mHybridPerLayerFeatureLimit ); + if ( !loc->init( mHybridPerLayerFeatureLimit ) ) + { + // hmm that didn't work out - too many features! + // let's make the allowed area smaller for the next time + mHybridMaxAreaPerLayer[vl->id()] /= 4; + } } } else // full index strategy loc->init(); + + QgsDebugMsg( QStringLiteral( "Index init: %1 ms (%2)" ).arg( tt.elapsed() ).arg( vl->id() ) ); + prepareIndexProgress( ++i ); } + QgsDebugMsg( QStringLiteral( "Prepare index total: %1 ms" ).arg( t.elapsed() ) ); } + mIsIndexing = false; } QgsSnappingConfig QgsSnappingUtils::config() const diff --git a/src/core/qgssnappingutils.h b/src/core/qgssnappingutils.h index 4dc47618c3f..ddb69d7eb76 100644 --- a/src/core/qgssnappingutils.h +++ b/src/core/qgssnappingutils.h @@ -52,14 +52,8 @@ class CORE_EXPORT QgsSnappingUtils : public QObject public: - /** - * Constructor for QgsSnappingUtils - * \param parent parent object - * \param enableSnappingForInvisibleFeature TRUE if we want to snap feature even if there are not visible - * \param asynchronous indicated if point locator creation has to be made asynchronously (see QgsPointLocator()) - */ - QgsSnappingUtils( QObject *parent SIP_TRANSFERTHIS = nullptr, bool enableSnappingForInvisibleFeature = true, - bool asynchronous = false ); + //! Constructor for QgsSnappingUtils + QgsSnappingUtils( QObject *parent SIP_TRANSFERTHIS = nullptr, bool enableSnappingForInvisibleFeature = true ); ~QgsSnappingUtils() override; // main actions @@ -196,27 +190,14 @@ class CORE_EXPORT QgsSnappingUtils : public QObject void configChanged( const QgsSnappingConfig &snappingConfig ); protected: - - /** - * This methods is now deprecated and never called - * \deprecated since QGIS 3.10 - */ - Q_DECL_DEPRECATED virtual void prepareIndexStarting( int count ) { Q_UNUSED( count ); } - - /** - * This methods is now deprecated and never called - * \deprecated since QGIS 3.10 - */ - Q_DECL_DEPRECATED virtual void prepareIndexProgress( int index ) { Q_UNUSED( index ); } + //! Called when starting to index - can be overridden and e.g. progress dialog can be provided + virtual void prepareIndexStarting( int count ) { Q_UNUSED( count ) } + //! Called when finished indexing a layer. When index == count the indexing is complete + 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(); - private slots: - - //! called whenever a point locator has finished - void onInitFinished( bool ok ); - private: void onIndividualLayerSettingsChanged( const QHash &layerSettings ); //! Gets destination CRS from map settings, or an invalid CRS if projections are disabled @@ -268,11 +249,12 @@ class CORE_EXPORT QgsSnappingUtils : public QObject //! if using hybrid strategy, how many features of one layer may be indexed (to limit amount of consumed memory) int mHybridPerLayerFeatureLimit = 50000; + //! internal flag that an indexing process is going on. Prevents starting two processes in parallel. + bool mIsIndexing = false; + //! Disable or not the snapping on all features. By default is always TRUE except for non visible features on map canvas. bool mEnableSnappingForInvisibleFeature = true; - //! true if we have to build point locator asynchronously - bool mAsynchronous = false; }; diff --git a/src/gui/qgsmapcanvassnappingutils.cpp b/src/gui/qgsmapcanvassnappingutils.cpp index 6835fa818f3..3b119b8e9b3 100644 --- a/src/gui/qgsmapcanvassnappingutils.cpp +++ b/src/gui/qgsmapcanvassnappingutils.cpp @@ -21,8 +21,8 @@ #include #include -QgsMapCanvasSnappingUtils::QgsMapCanvasSnappingUtils( QgsMapCanvas *canvas, QObject *parent, bool asynchronous ) - : QgsSnappingUtils( parent, QgsSettings().value( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), false ).toBool(), asynchronous ) +QgsMapCanvasSnappingUtils::QgsMapCanvasSnappingUtils( QgsMapCanvas *canvas, QObject *parent ) + : QgsSnappingUtils( parent, QgsSettings().value( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), false ).toBool() ) , mCanvas( canvas ) { @@ -58,3 +58,24 @@ void QgsMapCanvasSnappingUtils::canvasMapToolChanged() { setEnableSnappingForInvisibleFeature( QgsSettings().value( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), false ).toBool() ); } + +void QgsMapCanvasSnappingUtils::prepareIndexStarting( int count ) +{ + QApplication::setOverrideCursor( Qt::WaitCursor ); + mProgress = new QProgressDialog( tr( "Indexing data…" ), QString(), 0, count, mCanvas->topLevelWidget() ); + mProgress->setWindowModality( Qt::WindowModal ); +} + +void QgsMapCanvasSnappingUtils::prepareIndexProgress( int index ) +{ + if ( !mProgress ) + return; + + mProgress->setValue( index ); + if ( index == mProgress->maximum() ) + { + delete mProgress; + mProgress = nullptr; + QApplication::restoreOverrideCursor(); + } +} diff --git a/src/gui/qgsmapcanvassnappingutils.h b/src/gui/qgsmapcanvassnappingutils.h index def0d18e3eb..74f4e270a60 100644 --- a/src/gui/qgsmapcanvassnappingutils.h +++ b/src/gui/qgsmapcanvassnappingutils.h @@ -34,16 +34,11 @@ class GUI_EXPORT QgsMapCanvasSnappingUtils : public QgsSnappingUtils { Q_OBJECT public: + QgsMapCanvasSnappingUtils( QgsMapCanvas *canvas, QObject *parent = nullptr ); - /** - * Construct map canvas snapping utils object - * - * \param canvas map canvas - * \param parent parent object - * \param asynchronous if TRUE snapping cache index will be non blocking and done in another thread, - * if FALSE it will block until indexing is done - */ - QgsMapCanvasSnappingUtils( QgsMapCanvas *canvas, QObject *parent = nullptr, bool asynchronous = false ); + protected: + void prepareIndexStarting( int count ) override; + void prepareIndexProgress( int index ) override; private slots: void canvasMapSettingsChanged(); diff --git a/tests/src/app/testqgsmaptoolreshape.cpp b/tests/src/app/testqgsmaptoolreshape.cpp index ee46aae0330..41b7b8a4857 100644 --- a/tests/src/app/testqgsmaptoolreshape.cpp +++ b/tests/src/app/testqgsmaptoolreshape.cpp @@ -162,6 +162,7 @@ void TestQgsMapToolReshape::initTestCase() cfg.setType( QgsSnappingConfig::VertexAndSegment ); cfg.setEnabled( true ); mCanvas->snappingUtils()->setConfig( cfg ); + mCanvas->setLayers( QList() << mLayerLineZ << mLayerPointZ << mLayerPolygonZ ); mCanvas->setCurrentLayer( mLayerLineZ ); diff --git a/tests/src/app/testqgsmaptoolreverseline.cpp b/tests/src/app/testqgsmaptoolreverseline.cpp index cf8a25a70c9..1590d72fcff 100644 --- a/tests/src/app/testqgsmaptoolreverseline.cpp +++ b/tests/src/app/testqgsmaptoolreverseline.cpp @@ -25,7 +25,7 @@ #include "testqgsmaptoolutils.h" #include "qgsmaptoolreverseline.h" #include "qgsmapmouseevent.h" -#include "qgssnappingutils.h" + class TestQgsMapToolReverseLine : public QObject { @@ -60,6 +60,7 @@ void TestQgsMapToolReverseLine::initTestCase() mCanvas = new QgsMapCanvas(); mCanvas->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3946" ) ) ); + } void TestQgsMapToolReverseLine::cleanupTestCase() diff --git a/tests/src/app/testqgsvertextool.cpp b/tests/src/app/testqgsvertextool.cpp index 8640ac33e60..83cbe76fc4c 100644 --- a/tests/src/app/testqgsvertextool.cpp +++ b/tests/src/app/testqgsvertextool.cpp @@ -247,8 +247,9 @@ void TestQgsVertexTool::initTestCase() mCanvas->setLayers( QList() << mLayerLine << mLayerPolygon << mLayerPoint << mLayerLineZ ); - QgsMapCanvasSnappingUtils *snappingUtils = new QgsMapCanvasSnappingUtils( mCanvas, this ); - mCanvas->setSnappingUtils( snappingUtils ); + // TODO: set up snapping + + mCanvas->setSnappingUtils( new QgsMapCanvasSnappingUtils( mCanvas, this ) ); // create vertex tool mVertexTool = new QgsVertexTool( mCanvas, mAdvancedDigitizingDockWidget ); diff --git a/tests/src/core/testqgspointlocator.cpp b/tests/src/core/testqgspointlocator.cpp index c9251b726ba..8b7138e848f 100644 --- a/tests/src/core/testqgspointlocator.cpp +++ b/tests/src/core/testqgspointlocator.cpp @@ -346,96 +346,6 @@ class TestQgsPointLocator : public QObject delete vlEmptyGeom; } - - void testAsynchronousMode() - { - QgsPointLocator loc( mVL, QgsCoordinateReferenceSystem(), QgsCoordinateTransformContext(), nullptr, true ); - QgsPointXY pt( 2, 2 ); - - QEventLoop loop; - connect( &loc, &QgsPointLocator::initFinished, &loop, &QEventLoop::quit ); - - // locator is not ready yet - QgsPointLocator::Match m = loc.nearestVertex( pt, 999 ); - QVERIFY( !m.isValid() ); - QVERIFY( loc.mIsIndexing ); - - // we block until initFinished is called from another thread - loop.exec(); - - QVERIFY( !loc.mIsIndexing ); - - // now locator is ready - m = loc.nearestVertex( pt, 999 ); - QVERIFY( m.isValid() ); - QVERIFY( m.hasVertex() ); - QCOMPARE( m.layer(), mVL ); - QCOMPARE( m.featureId(), ( QgsFeatureId )1 ); - QCOMPARE( m.point(), QgsPointXY( 1, 1 ) ); - QCOMPARE( m.distance(), std::sqrt( 2.0 ) ); - QCOMPARE( m.vertexIndex(), 2 ); - } - - void testLayerUpdatesAsynchronous() - { - - QgsPointLocator loc( mVL, QgsCoordinateReferenceSystem(), QgsCoordinateTransformContext(), nullptr, true ); - - QEventLoop loop; - connect( &loc, &QgsPointLocator::initFinished, &loop, &QEventLoop::quit ); - - // trigger locator initialization - QgsPointLocator::Match mAddV0 = loc.nearestVertex( QgsPointXY( 12, 12 ), 999 ); - - // locator is not ready yet - QVERIFY( !mAddV0.isValid() ); - QVERIFY( loc.mIsIndexing ); - - mVL->startEditing(); - - // add a new feature - QgsFeature ff( 0 ); - QgsPolygonXY polygon; - QgsPolylineXY polyline; - polyline << QgsPointXY( 10, 11 ) << QgsPointXY( 11, 10 ) << QgsPointXY( 11, 11 ) << QgsPointXY( 10, 11 ); - polygon << polyline; - QgsGeometry ffGeom = QgsGeometry::fromPolygonXY( polygon ) ; - ff.setGeometry( ffGeom ); - QgsFeatureList flist; - flist << ff; - bool resA = mVL->addFeature( ff ); - QVERIFY( resA ); - - // indexing is still running, change geometry - QgsGeometry *newGeom = new QgsGeometry( ff.geometry() ); - newGeom->moveVertex( 10, 10, 2 ); // change 11,11 to 10,10 - mVL->changeGeometry( ff.id(), *newGeom ); - delete newGeom; - - // we block until initFinished is called from another thread - loop.exec(); - - QVERIFY( !loc.mIsIndexing ); - - // verify it is changed in the point locator - QgsPointLocator::Match mChV = loc.nearestVertex( QgsPointXY( 12, 12 ), 999 ); - QVERIFY( mChV.isValid() ); - QVERIFY( mChV.point() != QgsPointXY( 11, 11 ) ); // that point does not exist anymore - mChV = loc.nearestVertex( QgsPointXY( 9, 9 ), 999 ); - QVERIFY( mChV.isValid() ); - QVERIFY( mChV.point() == QgsPointXY( 10, 10 ) ); // updated point - - // delete feature while no indexing is running - bool resD = mVL->deleteFeature( ff.id() ); - QVERIFY( resD ); - - // verify it is deleted from the point locator - QgsPointLocator::Match mDelV = loc.nearestVertex( QgsPointXY( 12, 12 ), 999 ); - QVERIFY( mDelV.isValid() ); - QCOMPARE( mDelV.point(), QgsPointXY( 1, 1 ) ); - - mVL->rollBack(); - } }; QGSTEST_MAIN( TestQgsPointLocator )