18#include <spatialindex/SpatialIndex.h>
30#include <QMutexLocker>
33using namespace Qt::StringLiterals;
51 void visitNode(
const INode &n )
override { Q_UNUSED( n ) }
53 void visitData(
const IData &d )
override { mList.append( d.getIdentifier() ); }
55 void visitData( std::vector<const IData *> &v )
override { Q_UNUSED( v ) }
58 QList<QgsFeatureId> &mList;
71 : mNewIndex( newIndex )
74 void visitNode(
const INode &n )
override { Q_UNUSED( n ) }
78 SpatialIndex::IShape *shape =
nullptr;
80 mNewIndex->insertData( 0,
nullptr, *shape, d.getIdentifier() );
84 void visitData( std::vector<const IData *> &v )
override { Q_UNUSED( v ) }
87 SpatialIndex::ISpatialIndex *mNewIndex =
nullptr;
91class QgsNearestNeighborComparator :
public INearestNeighborComparator
94 QgsNearestNeighborComparator(
const QHash< QgsFeatureId, QgsGeometry > *geometries,
const QgsPointXY &point,
double maxDistance )
95 : mGeometries( geometries )
96 , mGeom( QgsGeometry::fromPointXY( point ) )
97 , mMaxDistance( maxDistance )
100 QgsNearestNeighborComparator(
const QHash< QgsFeatureId, QgsGeometry > *geometries,
const QgsGeometry &geometry,
double maxDistance )
101 : mGeometries( geometries )
103 , mMaxDistance( maxDistance )
106 const QHash< QgsFeatureId, QgsGeometry > *mGeometries =
nullptr;
108 double mMaxDistance = 0;
109 QSet< QgsFeatureId > mFeaturesOutsideMaxDistance;
111 double getMinimumDistance(
const IShape &query,
const IShape &entry )
override {
return query.getMinimumDistance( entry ); }
113 double getMinimumDistance(
const IShape &query,
const IData &data )
override
117 data.getShape( &pS );
118 double dist = query.getMinimumDistance( *pS );
126 if ( mGeometries && ( mMaxDistance <= 0.0 || dist <= mMaxDistance ) )
128 const QgsGeometry other = mGeometries->value( data.getIdentifier() );
132 if ( mMaxDistance > 0 && dist > mMaxDistance )
142 mFeaturesOutsideMaxDistance.insert( data.getIdentifier() );
143 return mMaxDistance + 0.00000001;
155class QgsFeatureIteratorDataStream :
public IDataStream
159 explicit QgsFeatureIteratorDataStream(
163 , mFeedback( feedback )
165 , mCallback( callback )
170 ~QgsFeatureIteratorDataStream()
override {
delete mNextData; }
173 IData *getNext()
override
175 if ( mFeedback && mFeedback->isCanceled() )
178 RTree::Data *ret = mNextData;
185 bool hasNext()
override {
return nullptr != mNextData; }
188 uint32_t size()
override
190 Q_ASSERT(
false &&
"not available" );
195 void rewind()
override { Q_ASSERT(
false &&
"not available" ); }
197 QHash< QgsFeatureId, QgsGeometry > geometries;
203 SpatialIndex::Region r;
205 while ( mFi.nextFeature( f ) )
209 const bool res = ( *mCallback )( f );
216 if ( QgsSpatialIndex::featureInfo( f, r,
id ) )
218 mNextData =
new RTree::Data( 0,
nullptr, r,
id );
227 QgsFeatureIterator mFi;
228 RTree::Data *mNextData =
nullptr;
229 QgsFeedback *mFeedback =
nullptr;
231 const std::function< bool(
const QgsFeature & ) > *mCallback =
nullptr;
241class QgsSpatialIndexData :
public QSharedData
252 QHash< QgsFeatureId, QgsGeometry > mGeometries;
262 explicit QgsSpatialIndexData(
267 QgsFeatureIteratorDataStream fids( fi, feedback, mFlags, callback );
270 mGeometries = fids.geometries;
273 QgsSpatialIndexData(
const QgsSpatialIndexData &other )
274 : QSharedData( other )
275 , mFlags( other.mFlags )
276 , mGeometries( other.mGeometries )
278 const QMutexLocker locker( &other.mMutex );
283 double low[] = { std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest() };
284 double high[] = { std::numeric_limits<double>::max(), std::numeric_limits<double>::max() };
285 const SpatialIndex::Region query( low, high, 2 );
286 QgsSpatialIndexCopyVisitor visitor( mRTree );
287 other.mRTree->intersectsWithQuery( query, visitor );
290 ~QgsSpatialIndexData()
296 QgsSpatialIndexData &operator=(
const QgsSpatialIndexData &rh ) =
delete;
298 void initTree( IDataStream *inputStream =
nullptr )
301 mStorage = StorageManager::createNewMemoryStorageManager();
304 const double fillFactor = 0.7;
305 const unsigned long indexCapacity = 10;
306 const unsigned long leafCapacity = 10;
307 const unsigned long dimension = 2;
308 const RTree::RTreeVariant variant = RTree::RV_RSTAR;
311 SpatialIndex::id_type indexId;
313 if ( inputStream && inputStream->hasNext() )
314 mRTree = RTree::createAndBulkLoadNewRTree( RTree::BLM_STR, *inputStream, *mStorage, fillFactor, indexCapacity, leafCapacity, dimension, variant, indexId );
316 mRTree = RTree::createNewRTree( *mStorage, fillFactor, indexCapacity, leafCapacity, dimension, variant, indexId );
320 SpatialIndex::IStorageManager *mStorage =
nullptr;
323 SpatialIndex::ISpatialIndex *mRTree =
nullptr;
325 mutable QRecursiveMutex mMutex;
335 d =
new QgsSpatialIndexData( flags );
340 d =
new QgsSpatialIndexData( fi, feedback, flags );
346 d =
new QgsSpatialIndexData( fi,
nullptr, flags, &callback );
364 if (
this != &other )
372 if ( !featureInfo( f, rect,
id ) )
397 if ( !featureInfo( feature, rect,
id ) )
404 const QMutexLocker locker( &d->mMutex );
405 d->mGeometries.insert( feature.
id(), feature.
geometry() );
414 QgsFeatureList::iterator fIt = features.begin();
416 for ( ; fIt != features.end(); ++fIt )
438 const QMutexLocker locker( &d->mMutex );
446 catch ( Tools::Exception &e )
449 QgsDebugError( u
"Tools::Exception caught when adding feature to QgsSpatialIndex: %1"_s.arg( e.what().c_str() ) );
451 catch (
const std::exception &e )
454 QgsDebugError( u
"std::exception caught when adding feature to QgsSpatialIndex: %1"_s.arg( e.what() ) );
458 QgsDebugError( u
"unknown spatial index exception caught when adding feature to QgsSpatialIndex"_s );
466 SpatialIndex::Region r;
468 if ( !featureInfo( f, r,
id ) )
471 const QMutexLocker locker( &d->mMutex );
474 d->mGeometries.remove( f.
id() );
482 const QMutexLocker locker( &d->mMutex );
485 d->mGeometries.remove(
id );
491 QList<QgsFeatureId> list;
496 const QMutexLocker locker( &d->mMutex );
497 d->mRTree->intersectsWithQuery( r, visitor );
504 QList<QgsFeatureId> list;
507 double pt[2] = { point.
x(), point.
y() };
508 const Point p( pt, 2 );
510 const QMutexLocker locker( &d->mMutex );
512 d->mRTree->nearestNeighborQuery( neighbors, p, visitor, nnc );
514 if ( maxDistance > 0 )
517 list.erase( std::remove_if( list.begin(), list.end(), [&nnc](
QgsFeatureId id ) { return nnc.mFeaturesOutsideMaxDistance.contains( id ); } ), list.end() );
525 QList<QgsFeatureId> list;
530 const QMutexLocker locker( &d->mMutex );
532 d->mRTree->nearestNeighborQuery( neighbors, r, visitor, nnc );
534 if ( maxDistance > 0 )
537 list.erase( std::remove_if( list.begin(), list.end(), [&nnc](
QgsFeatureId id ) { return nnc.mFeaturesOutsideMaxDistance.contains( id ); } ), list.end() );
545 const QMutexLocker locker( &d->mMutex );
546 return d->mGeometries.value(
id );
Custom visitor that adds found features to list.
void visitNode(const INode &n) override
void visitData(std::vector< const IData * > &v) override
QgisVisitor(QList< QgsFeatureId > &list)
void visitData(const IData &d) override
Wrapper for iterator of features from vector data provider or vector layer.
Wraps a request for features to a vector layer (or directly its vector data provider).
An interface for objects which provide features via a getFeatures method.
virtual QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const =0
Returns an iterator for the features in the source.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
A geometry is the spatial representation of a feature.
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
A rectangle specified with double values.
bool isFinite() const
Returns true if the rectangle has finite boundaries.
void visitData(std::vector< const IData * > &v) override
void visitData(const IData &d) override
QgsSpatialIndexCopyVisitor(SpatialIndex::ISpatialIndex *newIndex)
void visitNode(const INode &n) override
static SpatialIndex::Region rectangleToRegion(const QgsRectangle &rectangle)
Converts a QGIS rectangle to a SpatialIndex region.
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features to the index.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
QgsSpatialIndex & operator=(const QgsSpatialIndex &other)
QgsSpatialIndex(QgsSpatialIndex::Flags flags=QgsSpatialIndex::Flags())
Constructor for QgsSpatialIndex.
QAtomicInt refs() const
Gets reference count - just for debugging!
QList< QgsFeatureId > nearestNeighbor(const QgsPointXY &point, int neighbors=1, double maxDistance=0) const
Returns nearest neighbors to a point.
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
~QgsSpatialIndex() override
Destructor finalizes work with spatial index.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a feature to the index.
Q_DECL_DEPRECATED bool insertFeature(const QgsFeature &feature)
Adds a feature to the index.
QgsGeometry geometry(QgsFeatureId id) const
Returns the stored geometry for the indexed feature with matching id.
bool deleteFeature(const QgsFeature &feature)
Removes a feature from the index.
QList< QgsFeature > QgsFeatureList
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
#define FID_TO_NUMBER(fid)
#define QgsDebugError(str)