QGIS API Documentation  3.23.0-Master (eb871beae0)
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"
20 #include "qgsspatialindexutils.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 
32 class QgsRectangle;
33 
43 template <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  QgsDebugMsg( QStringLiteral( "Tools::Exception caught: " ).arg( e.what().c_str() ) );
81  }
82  catch ( const std::exception &e )
83  {
84  Q_UNUSED( e )
85  QgsDebugMsg( QStringLiteral( "std::exception caught: " ).arg( e.what() ) );
86  }
87  catch ( ... )
88  {
89  QgsDebugMsg( 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 QgsDebugMsg(str)
Definition: qgslogger.h:38