mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
Implement manual locks on QgsSpatialIndex
Since libspatialindex is not thread safe on all platforms, and have expressed desire to remove the thread safety that they DO have on remaining platforms, it's safer and easier for us to manually add locks to QgsSpatialIndex and be gauranteed that this class is thread safe on all platforms and libspatialindex versions. Also improve docs for the class.
This commit is contained in:
parent
426c72f28a
commit
8f902e7796
@ -16,6 +16,18 @@
|
||||
|
||||
class QgsSpatialIndex
|
||||
{
|
||||
%Docstring
|
||||
|
||||
A spatial index for QgsFeature objects.
|
||||
|
||||
QgsSpatialIndex objects are implicitly shared and can be inexpensively copied.
|
||||
|
||||
.. note::
|
||||
|
||||
While the underlying libspatialindex is not thread safe on some platforms, the QgsSpatialIndex
|
||||
class implements its own locks and accordingly, a single QgsSpatialIndex object can safely
|
||||
be used across multiple threads.
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsspatialindex.h"
|
||||
@ -25,7 +37,7 @@ class QgsSpatialIndex
|
||||
|
||||
QgsSpatialIndex();
|
||||
%Docstring
|
||||
Constructor - creates R-tree
|
||||
Constructor for QgsSpatialIndex. Creates an empty R-tree index.
|
||||
%End
|
||||
|
||||
explicit QgsSpatialIndex( const QgsFeatureIterator &fi, QgsFeedback *feedback = 0 );
|
||||
@ -60,24 +72,10 @@ Copy constructor
|
||||
~QgsSpatialIndex();
|
||||
|
||||
|
||||
void detach();
|
||||
|
||||
bool insertFeature( const QgsFeature &feature );
|
||||
%Docstring
|
||||
Detaches the index, forcing a deep copy of the underlying
|
||||
spatial index data.
|
||||
|
||||
Since the underlying libspatialindex is not thread safe on some platforms (e.g. Windows),
|
||||
manual calls to detach() must be made if a QgsSpatialIndex is to be accessed across multiple threads.
|
||||
|
||||
Note that for platforms on which libspatialindex is thread safe, calling
|
||||
detach() has no effect and does not force the deep copy.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
|
||||
bool insertFeature( const QgsFeature &f );
|
||||
%Docstring
|
||||
Add feature to index
|
||||
Adds a ``feature`` to the index.
|
||||
%End
|
||||
|
||||
bool insertFeature( QgsFeatureId id, const QgsRectangle &bounds );
|
||||
@ -89,21 +87,33 @@ Add a feature ``id`` to the index with a specified bounding box.
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
bool deleteFeature( const QgsFeature &f );
|
||||
bool deleteFeature( const QgsFeature &feature );
|
||||
%Docstring
|
||||
Remove feature from index
|
||||
Removes a ``feature`` from the index.
|
||||
%End
|
||||
|
||||
|
||||
|
||||
QList<QgsFeatureId> intersects( const QgsRectangle &rect ) const;
|
||||
QList<QgsFeatureId> intersects( const QgsRectangle &rectangle ) const;
|
||||
%Docstring
|
||||
Returns features that intersect the specified rectangle
|
||||
Returns a list of features with a bounding box which intersects the specified ``rectangle``.
|
||||
|
||||
.. note::
|
||||
|
||||
The intersection test is performed based on the feature bounding boxes only, so for non-point
|
||||
geometry features it is necessary to manually test the returned features for exact geometry intersection
|
||||
when required.
|
||||
%End
|
||||
|
||||
QList<QgsFeatureId> nearestNeighbor( const QgsPointXY &point, int neighbors ) const;
|
||||
%Docstring
|
||||
Returns nearest neighbors (their count is specified by second parameter)
|
||||
Returns nearest neighbors to a ``point``. The number of neighbours returned is specified
|
||||
by the ``neighbours`` argument.
|
||||
|
||||
.. note::
|
||||
|
||||
The nearest neighbour test is performed based on the feature bounding boxes only, so for non-point
|
||||
geometry features this method is not guaranteed to return the actual closest neighbours.
|
||||
%End
|
||||
|
||||
|
||||
|
@ -231,17 +231,13 @@ bool QgsMemoryFeatureIterator::close()
|
||||
QgsMemoryFeatureSource::QgsMemoryFeatureSource( const QgsMemoryProvider *p )
|
||||
: mFields( p->mFields )
|
||||
, mFeatures( p->mFeatures )
|
||||
, mSpatialIndex( p->mSpatialIndex ? qgis::make_unique< QgsSpatialIndex >( *p->mSpatialIndex ) : nullptr ) // this is just a shallow copy, but see below...
|
||||
, mSpatialIndex( p->mSpatialIndex ? qgis::make_unique< QgsSpatialIndex >( *p->mSpatialIndex ) : nullptr ) // just shallow copy
|
||||
, mSubsetString( p->mSubsetString )
|
||||
, mCrs( p->mCrs )
|
||||
{
|
||||
mExpressionContext << QgsExpressionContextUtils::globalScope()
|
||||
<< QgsExpressionContextUtils::projectScope( QgsProject::instance() );
|
||||
mExpressionContext.setFields( mFields );
|
||||
|
||||
// QgsSpatialIndex is not thread safe - so make spatial index safe to use across threads by forcing a full deep copy
|
||||
if ( mSpatialIndex && p->thread() != QThread::currentThread() )
|
||||
mSpatialIndex->detach();
|
||||
}
|
||||
|
||||
QgsFeatureIterator QgsMemoryFeatureSource::getFeatures( const QgsFeatureRequest &request )
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "qgsfeedback.h"
|
||||
|
||||
#include "SpatialIndex.h"
|
||||
#include <QMutex>
|
||||
#include <QMutexLocker>
|
||||
|
||||
using namespace SpatialIndex;
|
||||
|
||||
@ -183,6 +185,8 @@ class QgsSpatialIndexData : public QSharedData
|
||||
QgsSpatialIndexData( const QgsSpatialIndexData &other )
|
||||
: QSharedData( other )
|
||||
{
|
||||
QMutexLocker locker( &other.mMutex );
|
||||
|
||||
initTree();
|
||||
|
||||
// copy R-tree data one by one (is there a faster way??)
|
||||
@ -230,6 +234,8 @@ class QgsSpatialIndexData : public QSharedData
|
||||
//! R-tree containing spatial index
|
||||
SpatialIndex::ISpatialIndex *mRTree = nullptr;
|
||||
|
||||
mutable QMutex mMutex;
|
||||
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -266,14 +272,6 @@ QgsSpatialIndex &QgsSpatialIndex::operator=( const QgsSpatialIndex &other )
|
||||
return *this;
|
||||
}
|
||||
|
||||
void QgsSpatialIndex::detach()
|
||||
{
|
||||
// libspatialindex is not thread safe on windows - so force the deep copy
|
||||
#if defined(Q_OS_WIN)
|
||||
d.detach();
|
||||
#endif
|
||||
}
|
||||
|
||||
SpatialIndex::Region QgsSpatialIndex::rectToRegion( const QgsRectangle &rect )
|
||||
{
|
||||
double pt1[2] = { rect.xMinimum(), rect.yMinimum() },
|
||||
@ -315,6 +313,8 @@ bool QgsSpatialIndex::insertFeature( QgsFeatureId id, const QgsRectangle &rect )
|
||||
{
|
||||
SpatialIndex::Region r( rectToRegion( rect ) );
|
||||
|
||||
QMutexLocker locker( &d->mMutex );
|
||||
|
||||
// TODO: handle possible exceptions correctly
|
||||
try
|
||||
{
|
||||
@ -346,6 +346,7 @@ bool QgsSpatialIndex::deleteFeature( const QgsFeature &f )
|
||||
if ( !featureInfo( f, r, id ) )
|
||||
return false;
|
||||
|
||||
QMutexLocker locker( &d->mMutex );
|
||||
// TODO: handle exceptions
|
||||
return d->mRTree->deleteData( r, FID_TO_NUMBER( id ) );
|
||||
}
|
||||
@ -357,6 +358,7 @@ QList<QgsFeatureId> QgsSpatialIndex::intersects( const QgsRectangle &rect ) cons
|
||||
|
||||
SpatialIndex::Region r = rectToRegion( rect );
|
||||
|
||||
QMutexLocker locker( &d->mMutex );
|
||||
d->mRTree->intersectsWithQuery( r, visitor );
|
||||
|
||||
return list;
|
||||
@ -370,6 +372,7 @@ QList<QgsFeatureId> QgsSpatialIndex::nearestNeighbor( const QgsPointXY &point, i
|
||||
double pt[2] = { point.x(), point.y() };
|
||||
Point p( pt, 2 );
|
||||
|
||||
QMutexLocker locker( &d->mMutex );
|
||||
d->mRTree->nearestNeighborQuery( neighbors, p, visitor );
|
||||
|
||||
return list;
|
||||
|
@ -52,6 +52,14 @@ class QgsFeatureSource;
|
||||
/**
|
||||
* \ingroup core
|
||||
* \class QgsSpatialIndex
|
||||
*
|
||||
* A spatial index for QgsFeature objects.
|
||||
*
|
||||
* QgsSpatialIndex objects are implicitly shared and can be inexpensively copied.
|
||||
*
|
||||
* \note While the underlying libspatialindex is not thread safe on some platforms, the QgsSpatialIndex
|
||||
* class implements its own locks and accordingly, a single QgsSpatialIndex object can safely
|
||||
* be used across multiple threads.
|
||||
*/
|
||||
class CORE_EXPORT QgsSpatialIndex
|
||||
{
|
||||
@ -60,7 +68,9 @@ class CORE_EXPORT QgsSpatialIndex
|
||||
|
||||
/* creation of spatial index */
|
||||
|
||||
//! Constructor - creates R-tree
|
||||
/**
|
||||
* Constructor for QgsSpatialIndex. Creates an empty R-tree index.
|
||||
*/
|
||||
QgsSpatialIndex();
|
||||
|
||||
/**
|
||||
@ -97,24 +107,12 @@ class CORE_EXPORT QgsSpatialIndex
|
||||
//! Implement assignment operator
|
||||
QgsSpatialIndex &operator=( const QgsSpatialIndex &other );
|
||||
|
||||
/**
|
||||
* Detaches the index, forcing a deep copy of the underlying
|
||||
* spatial index data.
|
||||
*
|
||||
* Since the underlying libspatialindex is not thread safe on some platforms (e.g. Windows),
|
||||
* manual calls to detach() must be made if a QgsSpatialIndex is to be accessed across multiple threads.
|
||||
*
|
||||
* Note that for platforms on which libspatialindex is thread safe, calling
|
||||
* detach() has no effect and does not force the deep copy.
|
||||
*
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
void detach();
|
||||
|
||||
/* operations */
|
||||
|
||||
//! Add feature to index
|
||||
bool insertFeature( const QgsFeature &f );
|
||||
/**
|
||||
* Adds a \a feature to the index.
|
||||
*/
|
||||
bool insertFeature( const QgsFeature &feature );
|
||||
|
||||
/**
|
||||
* Add a feature \a id to the index with a specified bounding box.
|
||||
@ -123,16 +121,30 @@ class CORE_EXPORT QgsSpatialIndex
|
||||
*/
|
||||
bool insertFeature( QgsFeatureId id, const QgsRectangle &bounds );
|
||||
|
||||
//! Remove feature from index
|
||||
bool deleteFeature( const QgsFeature &f );
|
||||
/**
|
||||
* Removes a \a feature from the index.
|
||||
*/
|
||||
bool deleteFeature( const QgsFeature &feature );
|
||||
|
||||
|
||||
/* queries */
|
||||
|
||||
//! Returns features that intersect the specified rectangle
|
||||
QList<QgsFeatureId> intersects( const QgsRectangle &rect ) const;
|
||||
/**
|
||||
* Returns a list of features with a bounding box which intersects the specified \a rectangle.
|
||||
*
|
||||
* \note The intersection test is performed based on the feature bounding boxes only, so for non-point
|
||||
* geometry features it is necessary to manually test the returned features for exact geometry intersection
|
||||
* when required.
|
||||
*/
|
||||
QList<QgsFeatureId> intersects( const QgsRectangle &rectangle ) const;
|
||||
|
||||
//! Returns nearest neighbors (their count is specified by second parameter)
|
||||
/**
|
||||
* Returns nearest neighbors to a \a point. The number of neighbours returned is specified
|
||||
* by the \a neighbours argument.
|
||||
*
|
||||
* \note The nearest neighbour test is performed based on the feature bounding boxes only, so for non-point
|
||||
* geometry features this method is not guaranteed to return the actual closest neighbours.
|
||||
*/
|
||||
QList<QgsFeatureId> nearestNeighbor( const QgsPointXY &point, int neighbors ) const;
|
||||
|
||||
/* debugging */
|
||||
|
Loading…
x
Reference in New Issue
Block a user