Allow bulk load of QgsSpatialIndex to be canceled via QgsFeedback

This commit is contained in:
Nyall Dawson 2017-07-15 16:43:44 +10:00
parent b441a4f2c9
commit 7baa623f6f
11 changed files with 46 additions and 21 deletions

View File

@ -28,19 +28,28 @@ class QgsSpatialIndex
Constructor - creates R-tree Constructor - creates R-tree
%End %End
explicit QgsSpatialIndex( const QgsFeatureIterator &fi ); explicit QgsSpatialIndex( const QgsFeatureIterator &fi, QgsFeedback *feedback = 0 );
%Docstring %Docstring
Constructor - creates R-tree and bulk loads it with features from the iterator. Constructor - creates R-tree and bulk loads it with features from the iterator.
This is much faster approach than creating an empty index and then inserting features one by one. This is much faster approach than creating an empty index and then inserting features one by one.
The optional ``feedback`` object can be used to allow cancelation of bulk feature loading. Ownership
of ``feedback`` is not transferred, and callers must take care that the lifetime of feedback exceeds
that of the spatial index construction.
.. versionadded:: 2.8 .. versionadded:: 2.8
%End %End
explicit QgsSpatialIndex( const QgsFeatureSource &source ); explicit QgsSpatialIndex( const QgsFeatureSource &source, QgsFeedback *feedback = 0 );
%Docstring %Docstring
Constructor - creates R-tree and bulk loads it with features from the source. Constructor - creates R-tree and bulk loads it with features from the source.
This is much faster approach than creating an empty index and then inserting features one by one. This is much faster approach than creating an empty index and then inserting features one by one.
The optional ``feedback`` object can be used to allow cancelation of bulk feature loading. Ownership
of ``feedback`` is not transferred, and callers must take care that the lifetime of feedback exceeds
that of the spatial index construction.
.. versionadded:: 3.0 .. versionadded:: 3.0
%End %End

View File

@ -83,7 +83,7 @@ class Difference(QgisAlgorithm):
featB = QgsFeature() featB = QgsFeature()
outFeat = QgsFeature() outFeat = QgsFeature()
indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs()))) indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs())), feedback)
total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount() and sourceB.featureCount() else 1 total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount() and sourceB.featureCount() else 1
count = 0 count = 0

View File

@ -95,7 +95,7 @@ class Intersection(QgisAlgorithm):
fields, geomType, sourceA.sourceCrs()) fields, geomType, sourceA.sourceCrs())
outFeat = QgsFeature() outFeat = QgsFeature()
indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs()))) indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs())), feedback)
total = 100.0 / sourceA.featureCount() if sourceA.featureCount() else 1 total = 100.0 / sourceA.featureCount() if sourceA.featureCount() else 1
count = 0 count = 0

View File

@ -94,7 +94,7 @@ class NearestNeighbourAnalysis(QgisAlgorithm):
source = self.parameterAsSource(parameters, self.INPUT, context) source = self.parameterAsSource(parameters, self.INPUT, context)
output_file = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context) output_file = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context)
spatialIndex = QgsSpatialIndex(source) spatialIndex = QgsSpatialIndex(source, feedback)
distance = QgsDistanceArea() distance = QgsDistanceArea()
distance.setSourceCrs(source.sourceCrs()) distance.setSourceCrs(source.sourceCrs())

View File

@ -111,7 +111,7 @@ class PointsInPolygon(QgisAlgorithm):
fields, poly_source.wkbType(), poly_source.sourceCrs()) fields, poly_source.wkbType(), poly_source.sourceCrs())
spatialIndex = QgsSpatialIndex(point_source.getFeatures( spatialIndex = QgsSpatialIndex(point_source.getFeatures(
QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(poly_source.sourceCrs()))) QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(poly_source.sourceCrs())), feedback)
point_attribute_indices = [] point_attribute_indices = []
if weight_field_index >= 0: if weight_field_index >= 0:

View File

@ -83,7 +83,7 @@ class SelectByAttributeSum(QgisAlgorithm):
geom = ft.geometry() geom = ft.geometry()
attrSum = ft[fieldName] attrSum = ft[fieldName]
idx = QgsSpatialIndex(layer.getFeatures(QgsFeatureRequest.setSubsetOfAttributes([]))) idx = QgsSpatialIndex(layer.getFeatures(QgsFeatureRequest.setSubsetOfAttributes([])), feedback)
req = QgsFeatureRequest() req = QgsFeatureRequest()
completed = False completed = False
while not completed: while not completed:

View File

@ -101,7 +101,7 @@ class SumLines(QgisAlgorithm):
fields, poly_source.wkbType(), poly_source.sourceCrs()) fields, poly_source.wkbType(), poly_source.sourceCrs())
spatialIndex = QgsSpatialIndex(line_source.getFeatures( spatialIndex = QgsSpatialIndex(line_source.getFeatures(
QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(poly_source.sourceCrs()))) QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(poly_source.sourceCrs())), feedback)
distArea = QgsDistanceArea() distArea = QgsDistanceArea()
distArea.setSourceCrs(poly_source.sourceCrs()) distArea.setSourceCrs(poly_source.sourceCrs())

View File

@ -87,8 +87,8 @@ class SymmetricalDifference(QgisAlgorithm):
featB = QgsFeature() featB = QgsFeature()
outFeat = QgsFeature() outFeat = QgsFeature()
indexA = QgsSpatialIndex(sourceA) indexA = QgsSpatialIndex(sourceA, feedback)
indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs()))) indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs())), feedback)
total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount() and sourceB.featureCount() else 1 total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount() and sourceB.featureCount() else 1
count = 0 count = 0

View File

@ -97,8 +97,8 @@ class Union(QgisAlgorithm):
featB = QgsFeature() featB = QgsFeature()
outFeat = QgsFeature() outFeat = QgsFeature()
indexA = QgsSpatialIndex(sourceA) indexA = QgsSpatialIndex(sourceA, feedback)
indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs()))) indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs())), feedback)
total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount() and sourceB.featureCount() else 1 total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount() and sourceB.featureCount() else 1
count = 0 count = 0

View File

@ -21,6 +21,7 @@
#include "qgsrectangle.h" #include "qgsrectangle.h"
#include "qgslogger.h" #include "qgslogger.h"
#include "qgsfeaturesource.h" #include "qgsfeaturesource.h"
#include "qgsfeedback.h"
#include "SpatialIndex.h" #include "SpatialIndex.h"
@ -92,9 +93,10 @@ class QgsFeatureIteratorDataStream : public IDataStream
{ {
public: public:
//! constructor - needs to load all data to a vector for later access when bulk loading //! constructor - needs to load all data to a vector for later access when bulk loading
explicit QgsFeatureIteratorDataStream( const QgsFeatureIterator &fi ) explicit QgsFeatureIteratorDataStream( const QgsFeatureIterator &fi, QgsFeedback *feedback = nullptr )
: mFi( fi ) : mFi( fi )
, mNextData( nullptr ) , mNextData( nullptr )
, mFeedback( feedback )
{ {
readNextEntry(); readNextEntry();
} }
@ -107,6 +109,9 @@ class QgsFeatureIteratorDataStream : public IDataStream
//! returns a pointer to the next entry in the stream or 0 at the end of the stream. //! returns a pointer to the next entry in the stream or 0 at the end of the stream.
IData *getNext() override IData *getNext() override
{ {
if ( mFeedback && mFeedback->isCanceled() )
return nullptr;
RTree::Data *ret = mNextData; RTree::Data *ret = mNextData;
mNextData = nullptr; mNextData = nullptr;
readNextEntry(); readNextEntry();
@ -141,6 +146,7 @@ class QgsFeatureIteratorDataStream : public IDataStream
private: private:
QgsFeatureIterator mFi; QgsFeatureIterator mFi;
RTree::Data *mNextData = nullptr; RTree::Data *mNextData = nullptr;
QgsFeedback *mFeedback = nullptr;
}; };
@ -157,9 +163,9 @@ class QgsSpatialIndexData : public QSharedData
initTree(); initTree();
} }
explicit QgsSpatialIndexData( const QgsFeatureIterator &fi ) explicit QgsSpatialIndexData( const QgsFeatureIterator &fi, QgsFeedback *feedback = nullptr )
{ {
QgsFeatureIteratorDataStream fids( fi ); QgsFeatureIteratorDataStream fids( fi, feedback );
initTree( &fids ); initTree( &fids );
} }
@ -224,14 +230,14 @@ QgsSpatialIndex::QgsSpatialIndex()
d = new QgsSpatialIndexData; d = new QgsSpatialIndexData;
} }
QgsSpatialIndex::QgsSpatialIndex( const QgsFeatureIterator &fi ) QgsSpatialIndex::QgsSpatialIndex( const QgsFeatureIterator &fi, QgsFeedback *feedback )
{ {
d = new QgsSpatialIndexData( fi ); d = new QgsSpatialIndexData( fi, feedback );
} }
QgsSpatialIndex::QgsSpatialIndex( const QgsFeatureSource &source ) QgsSpatialIndex::QgsSpatialIndex( const QgsFeatureSource &source, QgsFeedback *feedback )
{ {
d = new QgsSpatialIndexData( source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ) ) ); d = new QgsSpatialIndexData( source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ) ), feedback );
} }
QgsSpatialIndex::QgsSpatialIndex( const QgsSpatialIndex &other ) //NOLINT QgsSpatialIndex::QgsSpatialIndex( const QgsSpatialIndex &other ) //NOLINT

View File

@ -33,6 +33,7 @@ namespace SpatialIndex SIP_SKIP
} }
} }
class QgsFeedback;
class QgsFeature; class QgsFeature;
class QgsRectangle; class QgsRectangle;
class QgsPointXY; class QgsPointXY;
@ -64,17 +65,26 @@ class CORE_EXPORT QgsSpatialIndex
/** Constructor - creates R-tree and bulk loads it with features from the iterator. /** Constructor - creates R-tree and bulk loads it with features from the iterator.
* This is much faster approach than creating an empty index and then inserting features one by one. * This is much faster approach than creating an empty index and then inserting features one by one.
* *
* The optional \a feedback object can be used to allow cancelation of bulk feature loading. Ownership
* of \a feedback is not transferred, and callers must take care that the lifetime of feedback exceeds
* that of the spatial index construction.
*
* \since QGIS 2.8 * \since QGIS 2.8
*/ */
explicit QgsSpatialIndex( const QgsFeatureIterator &fi ); explicit QgsSpatialIndex( const QgsFeatureIterator &fi, QgsFeedback *feedback = nullptr );
/** /**
* Constructor - creates R-tree and bulk loads it with features from the source. * Constructor - creates R-tree and bulk loads it with features from the source.
* This is much faster approach than creating an empty index and then inserting features one by one. * This is much faster approach than creating an empty index and then inserting features one by one.
*
* The optional \a feedback object can be used to allow cancelation of bulk feature loading. Ownership
* of \a feedback is not transferred, and callers must take care that the lifetime of feedback exceeds
* that of the spatial index construction.
* *
* \since QGIS 3.0 * \since QGIS 3.0
*/ */
explicit QgsSpatialIndex( const QgsFeatureSource &source ); explicit QgsSpatialIndex( const QgsFeatureSource &source, QgsFeedback *feedback = nullptr );
//! Copy constructor //! Copy constructor
QgsSpatialIndex( const QgsSpatialIndex &other ); QgsSpatialIndex( const QgsSpatialIndex &other );