Skip to content

Commit

Permalink
Improve task management
Browse files Browse the repository at this point in the history
  • Loading branch information
troopa81 committed Sep 6, 2019
1 parent fbc88a2 commit 42be18c
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 61 deletions.
23 changes: 21 additions & 2 deletions python/core/auto_generated/qgspointlocator.sip.in
Expand Up @@ -103,8 +103,11 @@ Configure render context - if not ``None``, it will use to index only visible f
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 non blocking and index is built in another thread. Signals indexStarting and indexFinished
are triggered to let you know when index build is started and finished.

This method is either blocking or non blocking according to ``asynchronous`` parameter passed
in the constructor

.. seealso:: :py:class:`QgsPointLocator`
%End

bool hasIndex() const;
Expand Down Expand Up @@ -233,6 +236,14 @@ find out if the point is in any polygons
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 usefull if constructor parameter ``asynchronous`` is ``True``

.. seealso:: :py:class:`QgsPointLocator`
%End

signals:
Expand All @@ -247,6 +258,14 @@ Emitted whenever index has been built and initialization is finished

protected:
bool rebuildIndex( int maxFeaturesToIndex = -1 );

bool prepare();
%Docstring
prepare index if need and returns ``True`` if the index is ready to be used

.. versionadded:: 3.10
%End

protected slots:
void destroyIndex();
};
Expand Down
82 changes: 26 additions & 56 deletions src/core/qgspointlocator.cpp
Expand Up @@ -611,6 +611,7 @@ void QgsPointLocator::init( int maxFeaturesToIndex )
return;

mRenderer.reset( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
mSource.reset( new QgsVectorLayerFeatureSource( mLayer ) );

if ( mContext )
{
Expand Down Expand Up @@ -639,15 +640,27 @@ bool QgsPointLocator::hasIndex() const
return mIsIndexing || 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();

QgsVectorLayerFeatureSource source( mLayer );

QgsDebugMsg( QStringLiteral( "rebuildIndex %1" ).arg( source.id() ) );
QgsDebugMsg( QStringLiteral( "RebuildIndex start : %1" ).arg( mSource->id() ) );

destroyIndex();

Expand Down Expand Up @@ -684,13 +697,13 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex )
if ( mRenderer )
{
// setup scale for scale dependent visibility (rule based)
mRenderer->startRender( *ctx, source.fields() );
mRenderer->startRender( *ctx, mSource->fields() );
filter = mRenderer->capabilities() & QgsFeatureRenderer::Filter;
request.setSubsetOfAttributes( mRenderer->usedAttributes( *ctx ), source.fields() );
request.setSubsetOfAttributes( mRenderer->usedAttributes( *ctx ), mSource->fields() );
}
}

QgsFeatureIterator fi = source.getFeatures( request );
QgsFeatureIterator fi = mSource->getFeatures( request );
int indexedCount = 0;

while ( fi.nextFeature( f ) )
Expand Down Expand Up @@ -767,7 +780,7 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex )
mRenderer->stopRender( *ctx );
}

QgsDebugMsg( QStringLiteral( "RebuildIndex : %1 ms (%2)" ).arg( t.elapsed() ).arg( source.id() ) );
QgsDebugMsg( QStringLiteral( "RebuildIndex end : %1 ms (%2)" ).arg( t.elapsed() ).arg( mSource->id() ) );

return true;
}
Expand Down Expand Up @@ -908,16 +921,9 @@ void QgsPointLocator::onAttributeValueChanged( QgsFeatureId fid, int idx, const

QgsPointLocator::Match QgsPointLocator::nearestVertex( const QgsPointXY &point, double tolerance, MatchFilter *filter )
{
if ( mIsIndexing )
if ( !prepare() )
return Match();

if ( !mRTree )
{
init();
if ( !mRTree ) // still invalid?
return Match();
}

Match m;
QgsPointLocator_VisitorNearestVertex visitor( this, m, point, filter );
QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
Expand All @@ -929,16 +935,9 @@ QgsPointLocator::Match QgsPointLocator::nearestVertex( const QgsPointXY &point,

QgsPointLocator::Match QgsPointLocator::nearestEdge( const QgsPointXY &point, double tolerance, MatchFilter *filter )
{
if ( mIsIndexing )
if ( !prepare() )
return Match();

if ( !mRTree )
{
init();
if ( !mRTree ) // still invalid?
return Match();
}

QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
if ( geomType == QgsWkbTypes::PointGeometry )
return Match();
Expand All @@ -954,16 +953,9 @@ QgsPointLocator::Match QgsPointLocator::nearestEdge( const QgsPointXY &point, do

QgsPointLocator::Match QgsPointLocator::nearestArea( const QgsPointXY &point, double tolerance, MatchFilter *filter )
{
if ( mIsIndexing )
if ( !prepare() )
return Match();

if ( !mRTree )
{
init();
if ( !mRTree ) // still invalid?
return Match();
}

MatchList mlist = pointInPolygon( point );
if ( !mlist.isEmpty() && mlist.at( 0 ).isValid() )
{
Expand Down Expand Up @@ -991,16 +983,9 @@ QgsPointLocator::Match QgsPointLocator::nearestArea( const QgsPointXY &point, do

QgsPointLocator::MatchList QgsPointLocator::edgesInRect( const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter )
{
if ( mIsIndexing )
if ( !prepare() )
return MatchList();

if ( !mRTree )
{
init();
if ( !mRTree ) // still invalid?
return MatchList();
}

QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
if ( geomType == QgsWkbTypes::PointGeometry )
return MatchList();
Expand All @@ -1020,16 +1005,9 @@ QgsPointLocator::MatchList QgsPointLocator::edgesInRect( const QgsPointXY &point

QgsPointLocator::MatchList QgsPointLocator::verticesInRect( const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter )
{
if ( mIsIndexing )
if ( !prepare() )
return MatchList();

if ( !mRTree )
{
init();
if ( !mRTree ) // still invalid?
return MatchList();
}

MatchList lst;
QgsPointLocator_VisitorVerticesInRect visitor( this, lst, rect, filter );
mRTree->intersectsWithQuery( rect2region( rect ), visitor );
Expand All @@ -1043,19 +1021,11 @@ QgsPointLocator::MatchList QgsPointLocator::verticesInRect( const QgsPointXY &po
return verticesInRect( rect, filter );
}


QgsPointLocator::MatchList QgsPointLocator::pointInPolygon( const QgsPointXY &point )
{
if ( mIsIndexing )
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 )
return MatchList();
Expand Down
26 changes: 23 additions & 3 deletions src/core/qgspointlocator.h
Expand Up @@ -24,6 +24,7 @@ class QgsVectorLayer;
class QgsFeatureRenderer;
class QgsRenderContext;
class QgsRectangle;
class QgsVectorLayerFeatureSource;

#include "qgis_core.h"
#include "qgspointxy.h"
Expand Down Expand Up @@ -129,9 +130,11 @@ 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 non blocking and index is built in another thread. Signals indexStarting and indexFinished
* are triggered to let you know when index build is started and finished.
*/
*
* This method is either blocking or non blocking according to \a asynchronous parameter passed
* in the constructor
* \see QgsPointLocator()
*/
void init( int maxFeaturesToIndex = -1 );

//! Indicate whether the data have been already indexed
Expand Down Expand Up @@ -291,6 +294,14 @@ 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 usefull if constructor parameter \a asynchronous is TRUE
*
* \see QgsPointLocator()
*/
bool isIndexing() const { return mIsIndexing; }

signals:

/**
Expand All @@ -302,6 +313,13 @@ class CORE_EXPORT QgsPointLocator : public QObject

protected:
bool rebuildIndex( int maxFeaturesToIndex = -1 );

/**
* prepare index if need and returns TRUE if the index is ready to be used
* \since 3.10
*/
bool prepare();

protected slots:
void destroyIndex();
private slots:
Expand Down Expand Up @@ -329,6 +347,7 @@ class CORE_EXPORT QgsPointLocator : public QObject

std::unique_ptr<QgsRenderContext> mContext;
std::unique_ptr<QgsFeatureRenderer> mRenderer;
std::unique_ptr<QgsVectorLayerFeatureSource> mSource;
int mMaxFeaturesToIndex = -1;
bool mAsynchronous = false;
bool mIsIndexing = false;
Expand All @@ -341,6 +360,7 @@ class CORE_EXPORT QgsPointLocator : public QObject
friend class QgsPointLocator_VisitorEdgesInRect;
friend class QgsPointLocator_VisitorVerticesInRect;
friend class QgsPointLocatorInitTask;
friend class TestQgsPointLocator;
};


Expand Down
3 changes: 3 additions & 0 deletions src/core/qgssnappingutils.cpp
Expand Up @@ -91,6 +91,9 @@ bool QgsSnappingUtils::isIndexPrepared( QgsVectorLayer *vl, const QgsRectangle &

QgsPointLocator *loc = locatorForLayer( vl );

if ( loc->isIndexing() )
return true;

if ( mStrategy == IndexAlwaysFull && loc->hasIndex() )
return true;

Expand Down
66 changes: 66 additions & 0 deletions tests/src/core/testqgspointlocator.cpp
Expand Up @@ -358,8 +358,13 @@ class TestQgsPointLocator : public QObject
// 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 an other thread
loop.exec();

QVERIFY( !loc.mIsIndexing );

// now locator is ready
m = loc.nearestVertex( pt, 999 );
QVERIFY( m.isValid() );
Expand All @@ -370,6 +375,67 @@ class TestQgsPointLocator : public QObject
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 an other 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 )
Expand Down

0 comments on commit 42be18c

Please sign in to comment.