QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsmeshspatialindex.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmeshspatialindex.cpp
3  -----------------------
4  begin : January 2019
5  copyright : (C) 2019 by Peter Petrik
6  email : zilolv 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 #include "qgsmeshspatialindex.h"
17 #include "qgsrectangle.h"
18 #include "qgslogger.h"
19 #include "qgsfeedback.h"
20 
21 #include <spatialindex/SpatialIndex.h>
22 #include <QMutex>
23 #include <QMutexLocker>
24 #include <memory>
25 
26 using namespace SpatialIndex;
27 
29 
30 static Region faceToRegion( const QgsMesh &mesh, int id, bool &ok )
31 {
32  const QgsMeshFace face = mesh.face( id );
33 
34  if ( face.isEmpty() )
35  {
36  ok = false;
37  return Region();
38  }
39 
40  const QVector<QgsMeshVertex> &vertices = mesh.vertices;
41 
42  double xMinimum = vertices[face[0]].x();
43  double yMinimum = vertices[face[0]].y();
44  double xMaximum = vertices[face[0]].x();
45  double yMaximum = vertices[face[0]].y();
46 
47  for ( int i = 1; i < face.size(); ++i )
48  {
49  xMinimum = std::min( vertices[face[i]].x(), xMinimum );
50  yMinimum = std::min( vertices[face[i]].y(), yMinimum );
51  xMaximum = std::max( vertices[face[i]].x(), xMaximum );
52  yMaximum = std::max( vertices[face[i]].y(), yMaximum );
53  }
54 
55  double pt1[2] = { xMinimum, yMinimum };
56  double pt2[2] = { xMaximum, yMaximum };
57 
58  ok = true;
59  return SpatialIndex::Region( pt1, pt2, 2 );
60 }
61 
62 static Region edgeToRegion( const QgsMesh &mesh, int id, bool &ok )
63 {
64  const QgsMeshEdge edge = mesh.edge( id );
65  const QgsMeshVertex firstVertex = mesh.vertices[edge.first];
66  const QgsMeshVertex secondVertex = mesh.vertices[edge.second];
67  const double xMinimum = std::min( firstVertex.x(), secondVertex.x() );
68  const double yMinimum = std::min( firstVertex.y(), secondVertex.y() );
69  const double xMaximum = std::max( firstVertex.x(), secondVertex.x() );
70  const double yMaximum = std::max( firstVertex.y(), secondVertex.y() );
71  double pt1[2] = { xMinimum, yMinimum };
72  double pt2[2] = { xMaximum, yMaximum };
73  ok = true;
74  return SpatialIndex::Region( pt1, pt2, 2 );
75 }
76 
77 static Region rectToRegion( const QgsRectangle &rect )
78 {
79  double pt1[2] = { rect.xMinimum(), rect.yMinimum() };
80  double pt2[2] = { rect.xMaximum(), rect.yMaximum() };
81  return SpatialIndex::Region( pt1, pt2, 2 );
82 }
83 
90 class QgisMeshVisitor : public SpatialIndex::IVisitor
91 {
92  public:
93  explicit QgisMeshVisitor( QList<int> &list )
94  : mList( list ) {}
95 
96  void visitNode( const INode &n ) override
97  { Q_UNUSED( n ) }
98 
99  void visitData( const IData &d ) override
100  {
101  mList.append( static_cast<int>( d.getIdentifier() ) );
102  }
103 
104  void visitData( std::vector<const IData *> &v ) override
105  { Q_UNUSED( v ) }
106 
107  private:
108  QList<int> &mList;
109 };
110 
117 class QgsMeshSpatialIndexCopyVisitor : public SpatialIndex::IVisitor
118 {
119  public:
120  explicit QgsMeshSpatialIndexCopyVisitor( SpatialIndex::ISpatialIndex *newIndex )
121  : mNewIndex( newIndex ) {}
122 
123  void visitNode( const INode &n ) override
124  { Q_UNUSED( n ) }
125 
126  void visitData( const IData &d ) override
127  {
128  SpatialIndex::IShape *shape = nullptr;
129  d.getShape( &shape );
130  mNewIndex->insertData( 0, nullptr, *shape, d.getIdentifier() );
131  delete shape;
132  }
133 
134  void visitData( std::vector<const IData *> &v ) override
135  { Q_UNUSED( v ) }
136 
137  private:
138  SpatialIndex::ISpatialIndex *mNewIndex = nullptr;
139 };
140 
141 
148 class QgsMeshIteratorDataStream : public IDataStream
149 {
150  public:
152  explicit QgsMeshIteratorDataStream( const QgsMesh &mesh,
153  int featuresCount,
154  std::function<Region( const QgsMesh &mesh, int id, bool &ok )> featureToRegionFunction,
155  QgsFeedback *feedback = nullptr )
156  : mMesh( mesh )
157  , mFeaturesCount( featuresCount )
158  , mFeatureToRegionFunction( featureToRegionFunction )
159  , mFeedback( feedback )
160  {
161  readNextEntry();
162  }
163 
164  ~QgsMeshIteratorDataStream() override
165  {
166  delete mNextData;
167  }
168 
170  IData *getNext() override
171  {
172  if ( mFeedback && mFeedback->isCanceled() )
173  return nullptr;
174 
175  RTree::Data *ret = mNextData;
176  mNextData = nullptr;
177  readNextEntry();
178  return ret;
179  }
180 
182  bool hasNext() override
183  {
184  return nullptr != mNextData;
185  }
186 
188  uint32_t size() override
189  {
190  return static_cast<uint32_t>( mFeaturesCount );
191  }
192 
194  void rewind() override
195  {
196  mIterator = 0;
197  }
198 
199  protected:
200  void readNextEntry()
201  {
202  SpatialIndex::Region r;
203  while ( mIterator < mFeaturesCount )
204  {
205  bool ok = false;
206  r = mFeatureToRegionFunction( mMesh, mIterator, ok );
207  if ( ok )
208  {
209  mNextData = new RTree::Data( 0, nullptr, r, mIterator );
210  ++mIterator;
211  return;
212  }
213  else
214  {
215  ++mIterator;
216  continue;
217  }
218  }
219  }
220 
221  private:
222  int mIterator = 0;
223  const QgsMesh &mMesh;
224  int mFeaturesCount = 0;
225  std::function<Region( const QgsMesh &mesh, int id, bool &ok )> mFeatureToRegionFunction;
226  RTree::Data *mNextData = nullptr;
227  QgsFeedback *mFeedback = nullptr;
228 };
229 
236 class QgsMeshSpatialIndexData : public QSharedData
237 {
238  public:
239  QgsMeshSpatialIndexData()
240  {
241  initTree();
242  }
243 
252  explicit QgsMeshSpatialIndexData( const QgsMesh &fi, QgsFeedback *feedback, QgsMesh::ElementType elementType )
253  {
254  switch ( elementType )
255  {
256  case QgsMesh::ElementType::Edge:
257  {
258  QgsMeshIteratorDataStream fids( fi, fi.edgeCount(), edgeToRegion, feedback );
259  initTree( &fids );
260  }
261  break;
262  case QgsMesh::ElementType::Face:
263  {
264  QgsMeshIteratorDataStream fids( fi, fi.faceCount(), faceToRegion, feedback );
265  initTree( &fids );
266  }
267  break;
268  default:
269  // vertices are not supported
270  Q_ASSERT( false );
271  break;
272  }
273  }
274 
275  QgsMeshSpatialIndexData( const QgsMeshSpatialIndexData &other )
276  : QSharedData( other )
277  {
278  const QMutexLocker locker( &other.mMutex );
279 
280  initTree();
281 
282  // copy R-tree data one by one (is there a faster way??)
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  QgsMeshSpatialIndexCopyVisitor visitor( mRTree.get() );
287  other.mRTree->intersectsWithQuery( query, visitor );
288  }
289 
290  ~QgsMeshSpatialIndexData() = default;
291 
292  QgsMeshSpatialIndexData &operator=( const QgsMeshSpatialIndexData &rh ) = delete;
293 
294  void initTree( IDataStream *inputStream = nullptr )
295  {
296  // for now only memory manager
297  mStorage.reset( StorageManager::createNewMemoryStorageManager() );
298 
299  // R-Tree parameters
300  const double fillFactor = 0.7;
301  const unsigned int indexCapacity = 10;
302  const unsigned int leafCapacity = 10;
303  const unsigned int dimension = 2;
304  const RTree::RTreeVariant variant = RTree::RV_RSTAR;
305 
306  // create R-tree
307  SpatialIndex::id_type indexId;
308 
309  if ( inputStream && inputStream->hasNext() )
310  mRTree.reset(
311  RTree::createAndBulkLoadNewRTree(
312  RTree::BLM_STR,
313  *inputStream,
314  *mStorage, fillFactor,
315  indexCapacity,
316  leafCapacity,
317  dimension,
318  variant,
319  indexId )
320  );
321  else
322  mRTree.reset(
323  RTree::createNewRTree(
324  *mStorage,
325  fillFactor,
326  indexCapacity,
327  leafCapacity,
328  dimension,
329  variant,
330  indexId )
331  );
332  }
333 
335  std::unique_ptr<SpatialIndex::IStorageManager> mStorage;
336 
338  std::unique_ptr<SpatialIndex::ISpatialIndex> mRTree;
339 
340  mutable QMutex mMutex;
341 };
342 
344 
346 {
347  d = new QgsMeshSpatialIndexData;
348 }
349 
351  : mElementType( elementType )
352 {
353  d = new QgsMeshSpatialIndexData( mesh, feedback, elementType );
354 }
355 
357  : d( other.d )
358 {
359 }
360 
361 QgsMeshSpatialIndex:: ~QgsMeshSpatialIndex() = default; //NOLINT
362 
364 {
365  if ( this != &other )
366  d = other.d;
367  return *this;
368 }
369 
370 QList<int> QgsMeshSpatialIndex::intersects( const QgsRectangle &rect ) const
371 {
372  QList<int> list;
373  QgisMeshVisitor visitor( list );
374 
375  const SpatialIndex::Region r = rectToRegion( rect );
376 
377  const QMutexLocker locker( &d->mMutex );
378  d->mRTree->intersectsWithQuery( r, visitor );
379 
380  return list;
381 }
382 
383 QList<int> QgsMeshSpatialIndex::nearestNeighbor( const QgsPointXY &point, int neighbors ) const
384 {
385  QList<int> list;
386  QgisMeshVisitor visitor( list );
387 
388  double pt[2] = { point.x(), point.y() };
389  const Point p( pt, 2 );
390 
391  const QMutexLocker locker( &d->mMutex );
392  d->mRTree->nearestNeighborQuery( static_cast<uint32_t>( neighbors ), p, visitor );
393 
394  return list;
395 }
396 
398 {
399  return mElementType;
400 }
401 
402 void QgsMeshSpatialIndex::addFace( int faceIndex, const QgsMesh &mesh )
403 {
404  if ( mesh.face( faceIndex ).isEmpty() )
405  return;
406 
407  bool ok = false;
408  const SpatialIndex::Region r( faceToRegion( mesh, faceIndex, ok ) );
409  if ( !ok )
410  return;
411 
412  const QMutexLocker locker( &d.constData()->mMutex );
413 
414  try
415  {
416  d.constData()->mRTree->insertData( 0, nullptr, r, faceIndex );
417  }
418  catch ( Tools::Exception &e )
419  {
420  Q_UNUSED( e )
421  QgsDebugMsg( QStringLiteral( "Tools::Exception caught: " ).arg( e.what().c_str() ) );
422  }
423  catch ( const std::exception &e )
424  {
425  Q_UNUSED( e )
426  QgsDebugMsg( QStringLiteral( "std::exception caught: " ).arg( e.what() ) );
427  }
428  catch ( ... )
429  {
430  QgsDebugMsg( QStringLiteral( "unknown spatial index exception caught" ) );
431  }
432 }
433 
434 void QgsMeshSpatialIndex::removeFace( int faceIndex, const QgsMesh &mesh )
435 {
436  if ( mesh.face( faceIndex ).isEmpty() )
437  return;
438  const QMutexLocker locker( &d.constData()->mMutex );
439  bool ok = false;
440  d.constData()->mRTree->deleteData( faceToRegion( mesh, faceIndex, ok ), faceIndex );
441 }
SpatialIndex
Definition: qgspointlocator.h:83
QgsPointXY::y
double y
Definition: qgspointxy.h:63
qgsrectangle.h
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:48
qgsmeshspatialindex.h
QgsMeshSpatialIndex::elementType
QgsMesh::ElementType elementType() const
Returns the type of mesh elements that are indexed.
Definition: qgsmeshspatialindex.cpp:397
QgsRectangle::yMinimum
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
QgsMesh
Mesh - vertices, edges and faces.
Definition: qgsmeshdataprovider.h:58
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QgsRectangle::xMaximum
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
QgsPoint::y
double y
Definition: qgspoint.h:70
QgsMesh::faceCount
int faceCount() const
Returns number of faces.
Definition: qgsmeshdataprovider.cpp:205
QgsMesh::ElementType
ElementType
Defines type of mesh elements.
Definition: qgsmeshdataprovider.h:65
QgsMesh::edgeCount
int edgeCount() const
Returns number of edge.
Definition: qgsmeshdataprovider.cpp:210
QgsMesh::edge
QgsMeshEdge edge(int index) const
Returns an edge at the index.
Definition: qgsmeshdataprovider.cpp:152
QgsFeedback
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:44
QgsMeshSpatialIndex::nearestNeighbor
QList< int > nearestNeighbor(const QgsPointXY &point, int neighbors) const
Returns nearest neighbors to a point.
Definition: qgsmeshspatialindex.cpp:383
QgsMeshSpatialIndex::intersects
QList< int > intersects(const QgsRectangle &rectangle) const
Returns a list of face ids with a bounding box which intersects the specified rectangle.
Definition: qgsmeshspatialindex.cpp:370
QgsMesh::face
QgsMeshFace face(int index) const
Returns a face at the index.
Definition: qgsmeshdataprovider.cpp:145
QgsRectangle::xMinimum
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
QgsMeshFace
QVector< int > QgsMeshFace
List of vertex indexes.
Definition: qgsmeshdataprovider.h:42
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:58
QgsMeshSpatialIndex::~QgsMeshSpatialIndex
~QgsMeshSpatialIndex()
Destructor finalizes work with spatial index.
QgsMeshSpatialIndex
A spatial index for QgsMeshFace or QgsMeshEdge objects.
Definition: qgsmeshspatialindex.h:49
QgsRectangle::yMaximum
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
QgsMeshEdge
QPair< int, int > QgsMeshEdge
Edge is a straight line seqment between 2 points.
Definition: qgsmeshdataprovider.h:49
QgsMeshSpatialIndex::removeFace
void removeFace(int faceIndex, const QgsMesh &mesh)
Removes a face with faceIndex from the mesh in the spatial index.
Definition: qgsmeshspatialindex.cpp:434
QgsPointXY::x
double x
Definition: qgspointxy.h:62
QgsMesh::vertices
QVector< QgsMeshVertex > vertices
Definition: qgsmeshdataprovider.h:112
qgslogger.h
qgsfeedback.h
QgsMeshSpatialIndex::addFace
void addFace(int faceIndex, const QgsMesh &mesh)
Adds a face with faceIndex from the mesh in the spatial index.
Definition: qgsmeshspatialindex.cpp:402
QgsMeshSpatialIndex::QgsMeshSpatialIndex
QgsMeshSpatialIndex()
Constructor for QgsSpatialIndex.
Definition: qgsmeshspatialindex.cpp:345
QgsPoint::x
double x
Definition: qgspoint.h:69
QgsMeshSpatialIndex::operator=
QgsMeshSpatialIndex & operator=(const QgsMeshSpatialIndex &other)
Implement assignment operator.
Definition: qgsmeshspatialindex.cpp:363