QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsgenericspatialindex.h
Go to the documentation of this file.
1/***************************************************************************
2 qgsgenericspatialindex.h
3 ------------------------
4 Date : December 2019
5 Copyright : (C) 2019 by Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#ifndef QGSGENERICSPATIALINDEX_H
17#define QGSGENERICSPATIALINDEX_H
18
19#include "qgis_core.h"
21#include "qgslogger.h"
22
23#include <memory>
24#include <QMutex>
25#include <QString>
26
27#define SIP_NO_FILE
28
29#include <functional>
30#include <spatialindex/SpatialIndex.h>
31
32class QgsRectangle;
33
43template <typename T>
45{
46 public:
47
52 {
53 mStorageManager.reset( SpatialIndex::StorageManager::createNewMemoryStorageManager() );
54 mRTree = createSpatialIndex( *mStorageManager );
55 }
56
63 bool insert( T *data, const QgsRectangle &bounds )
64 {
65 const SpatialIndex::Region r( QgsSpatialIndexUtils::rectangleToRegion( bounds ) );
66
67 const QMutexLocker locker( &mMutex );
68
69 const qint64 id = mNextId++;
70 mIdToData.insert( id, data );
71 mDataToId.insert( data, id );
72 try
73 {
74 mRTree->insertData( 0, nullptr, r, static_cast< qint64 >( id ) );
75 return true;
76 }
77 catch ( Tools::Exception &e )
78 {
79 Q_UNUSED( e )
80 QgsDebugError( QStringLiteral( "Tools::Exception caught: " ).arg( e.what().c_str() ) );
81 }
82 catch ( const std::exception &e )
83 {
84 Q_UNUSED( e )
85 QgsDebugError( QStringLiteral( "std::exception caught: " ).arg( e.what() ) );
86 }
87 catch ( ... )
88 {
89 QgsDebugError( QStringLiteral( "unknown spatial index exception caught" ) );
90 }
91
92 return false;
93 }
94
101 bool remove( T *data, const QgsRectangle &bounds )
102 {
103 const SpatialIndex::Region r = QgsSpatialIndexUtils::rectangleToRegion( bounds );
104
105 const QMutexLocker locker( &mMutex );
106
107 const qint64 id = mDataToId.value( data, 0 );
108 if ( id == 0 )
109 return false;
110
111 // TODO: handle exceptions
112 const bool res = mRTree->deleteData( r, id );
113 mDataToId.remove( data );
114 mIdToData.remove( id );
115 return res;
116 }
117
123 bool intersects( const QgsRectangle &bounds, const std::function< bool( T *data )> &callback ) const
124 {
125 GenericIndexVisitor<T> visitor( callback, mIdToData );
126 const SpatialIndex::Region r = QgsSpatialIndexUtils::rectangleToRegion( bounds );
127
128 const QMutexLocker locker( &mMutex );
129 mRTree->intersectsWithQuery( r, visitor );
130 return true;
131 }
132
136 bool isEmpty( ) const
137 {
138 const QMutexLocker locker( &mMutex );
139 return mIdToData.isEmpty();
140 }
141
142 private:
143
144 std::unique_ptr< SpatialIndex::ISpatialIndex > createSpatialIndex( SpatialIndex::IStorageManager &storageManager )
145 {
146 // R-Tree parameters
147 constexpr double fillFactor = 0.7;
148 constexpr unsigned long indexCapacity = 10;
149 constexpr unsigned long leafCapacity = 10;
150 constexpr unsigned long dimension = 2;
151 constexpr SpatialIndex::RTree::RTreeVariant variant = SpatialIndex::RTree::RV_RSTAR;
152
153 // create R-tree
154 SpatialIndex::id_type indexId;
155 return std::unique_ptr< SpatialIndex::ISpatialIndex >( SpatialIndex::RTree::createNewRTree( storageManager, fillFactor, indexCapacity,
156 leafCapacity, dimension, variant, indexId ) );
157 }
158
159 std::unique_ptr< SpatialIndex::IStorageManager > mStorageManager;
160 std::unique_ptr< SpatialIndex::ISpatialIndex > mRTree;
161
162 mutable QMutex mMutex;
163
164 qint64 mNextId = 1;
165 QHash< qint64, T * > mIdToData;
166 QHash< T *, qint64 > mDataToId;
167
168 template <typename TT>
169 class GenericIndexVisitor : public SpatialIndex::IVisitor
170 {
171 public:
172 explicit GenericIndexVisitor( const std::function< bool( TT *data )> &callback, const QHash< qint64, TT * > &data )
173 : mCallback( callback )
174 , mData( data )
175 {}
176
177 void visitNode( const SpatialIndex::INode &n ) override
178 { Q_UNUSED( n ) }
179
180 void visitData( const SpatialIndex::IData &d ) override
181 {
182 const qint64 id = d.getIdentifier();
183 T *data = mData.value( id );
184 mCallback( data );
185 }
186
187 void visitData( std::vector<const SpatialIndex::IData *> &v ) override
188 { Q_UNUSED( v ) }
189
190 private:
191 const std::function< bool( TT *data )> &mCallback;
192 QHash< qint64, TT * > mData;
193 };
194
195};
196
197#endif // QGSGENERICSPATIALINDEX_H
A generic rtree spatial index based on a libspatialindex backend.
bool intersects(const QgsRectangle &bounds, const std::function< bool(T *data)> &callback) const
Performs an intersection check against the index, for data intersecting the specified bounds.
QgsGenericSpatialIndex()
Constructor for QgsGenericSpatialIndex.
bool isEmpty() const
Returns true if the index contains no items.
bool insert(T *data, const QgsRectangle &bounds)
Inserts new data into the spatial index, with the specified bounds.
bool remove(T *data, const QgsRectangle &bounds)
Removes existing data from the spatial index, with the specified bounds.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
static SpatialIndex::Region rectangleToRegion(const QgsRectangle &rectangle)
Converts a QGIS rectangle to a SpatialIndex region.
#define QgsDebugError(str)
Definition: qgslogger.h:38