QGIS API Documentation  3.20.0-Odense (decaadbb31)
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 )
31 {
32  const QgsMeshFace face = mesh.face( id );
33  const QVector<QgsMeshVertex> &vertices = mesh.vertices;
34  Q_ASSERT( face.size() > 0 );
35  double xMinimum = vertices[face[0]].x();
36  double yMinimum = vertices[face[0]].y();
37  double xMaximum = vertices[face[0]].x();
38  double yMaximum = vertices[face[0]].y();
39 
40  for ( int i = 1; i < face.size(); ++i )
41  {
42  xMinimum = std::min( vertices[face[i]].x(), xMinimum );
43  yMinimum = std::min( vertices[face[i]].y(), yMinimum );
44  xMaximum = std::max( vertices[face[i]].x(), xMaximum );
45  yMaximum = std::max( vertices[face[i]].y(), yMaximum );
46  }
47 
48  double pt1[2] = { xMinimum, yMinimum };
49  double pt2[2] = { xMaximum, yMaximum };
50  return SpatialIndex::Region( pt1, pt2, 2 );
51 }
52 
53 static Region edgeToRegion( const QgsMesh &mesh, int id )
54 {
55  const QgsMeshEdge edge = mesh.edge( id );
56  const QgsMeshVertex firstVertex = mesh.vertices[edge.first];
57  const QgsMeshVertex secondVertex = mesh.vertices[edge.second];
58  double xMinimum = std::min( firstVertex.x(), secondVertex.x() );
59  double yMinimum = std::min( firstVertex.y(), secondVertex.y() );
60  double xMaximum = std::max( firstVertex.x(), secondVertex.x() );
61  double yMaximum = std::max( firstVertex.y(), secondVertex.y() );
62  double pt1[2] = { xMinimum, yMinimum };
63  double pt2[2] = { xMaximum, yMaximum };
64  return SpatialIndex::Region( pt1, pt2, 2 );
65 }
66 
67 static Region rectToRegion( const QgsRectangle &rect )
68 {
69  double pt1[2] = { rect.xMinimum(), rect.yMinimum() };
70  double pt2[2] = { rect.xMaximum(), rect.yMaximum() };
71  return SpatialIndex::Region( pt1, pt2, 2 );
72 }
73 
80 class QgisMeshVisitor : public SpatialIndex::IVisitor
81 {
82  public:
83  explicit QgisMeshVisitor( QList<int> &list )
84  : mList( list ) {}
85 
86  void visitNode( const INode &n ) override
87  { Q_UNUSED( n ) }
88 
89  void visitData( const IData &d ) override
90  {
91  mList.append( static_cast<int>( d.getIdentifier() ) );
92  }
93 
94  void visitData( std::vector<const IData *> &v ) override
95  { Q_UNUSED( v ) }
96 
97  private:
98  QList<int> &mList;
99 };
100 
107 class QgsMeshSpatialIndexCopyVisitor : public SpatialIndex::IVisitor
108 {
109  public:
110  explicit QgsMeshSpatialIndexCopyVisitor( SpatialIndex::ISpatialIndex *newIndex )
111  : mNewIndex( newIndex ) {}
112 
113  void visitNode( const INode &n ) override
114  { Q_UNUSED( n ) }
115 
116  void visitData( const IData &d ) override
117  {
118  SpatialIndex::IShape *shape = nullptr;
119  d.getShape( &shape );
120  mNewIndex->insertData( 0, nullptr, *shape, d.getIdentifier() );
121  delete shape;
122  }
123 
124  void visitData( std::vector<const IData *> &v ) override
125  { Q_UNUSED( v ) }
126 
127  private:
128  SpatialIndex::ISpatialIndex *mNewIndex = nullptr;
129 };
130 
131 
138 class QgsMeshIteratorDataStream : public IDataStream
139 {
140  public:
142  explicit QgsMeshIteratorDataStream( const QgsMesh &mesh,
143  int featuresCount,
144  std::function<Region( const QgsMesh &mesh, int id )> featureToRegionFunction,
145  QgsFeedback *feedback = nullptr )
146  : mMesh( mesh )
147  , mFeaturesCount( featuresCount )
148  , mFeatureToRegionFunction( featureToRegionFunction )
149  , mFeedback( feedback )
150  {
151  readNextEntry();
152  }
153 
154  ~QgsMeshIteratorDataStream() override
155  {
156  delete mNextData;
157  }
158 
160  IData *getNext() override
161  {
162  if ( mFeedback && mFeedback->isCanceled() )
163  return nullptr;
164 
165  RTree::Data *ret = mNextData;
166  mNextData = nullptr;
167  readNextEntry();
168  return ret;
169  }
170 
172  bool hasNext() override
173  {
174  return nullptr != mNextData;
175  }
176 
178  uint32_t size() override
179  {
180  return static_cast<uint32_t>( mFeaturesCount );
181  }
182 
184  void rewind() override
185  {
186  mIterator = 0;
187  }
188 
189  protected:
190  void readNextEntry()
191  {
192  SpatialIndex::Region r;
193  if ( mIterator < mFeaturesCount )
194  {
195  r = mFeatureToRegionFunction( mMesh, mIterator );
196  mNextData = new RTree::Data(
197  0,
198  nullptr,
199  r,
200  mIterator );
201  ++mIterator;
202  }
203  }
204 
205  private:
206  int mIterator = 0;
207  const QgsMesh &mMesh;
208  int mFeaturesCount = 0;
209  std::function<Region( const QgsMesh &mesh, int id )> mFeatureToRegionFunction;
210  RTree::Data *mNextData = nullptr;
211  QgsFeedback *mFeedback = nullptr;
212 };
213 
220 class QgsMeshSpatialIndexData : public QSharedData
221 {
222  public:
223  QgsMeshSpatialIndexData()
224  {
225  initTree();
226  }
227 
236  explicit QgsMeshSpatialIndexData( const QgsMesh &fi, QgsFeedback *feedback, QgsMesh::ElementType elementType )
237  {
238  switch ( elementType )
239  {
240  case QgsMesh::ElementType::Edge:
241  {
242  QgsMeshIteratorDataStream fids( fi, fi.edgeCount(), edgeToRegion, feedback );
243  initTree( &fids );
244  }
245  break;
246  case QgsMesh::ElementType::Face:
247  {
248  QgsMeshIteratorDataStream fids( fi, fi.faceCount(), faceToRegion, feedback );
249  initTree( &fids );
250  }
251  break;
252  default:
253  // vertices are not supported
254  Q_ASSERT( false );
255  break;
256  }
257  }
258 
259  QgsMeshSpatialIndexData( const QgsMeshSpatialIndexData &other )
260  : QSharedData( other )
261  {
262  QMutexLocker locker( &other.mMutex );
263 
264  initTree();
265 
266  // copy R-tree data one by one (is there a faster way??)
267  double low[] = { std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest() };
268  double high[] = { std::numeric_limits<double>::max(), std::numeric_limits<double>::max() };
269  SpatialIndex::Region query( low, high, 2 );
270  QgsMeshSpatialIndexCopyVisitor visitor( mRTree.get() );
271  other.mRTree->intersectsWithQuery( query, visitor );
272  }
273 
274  ~QgsMeshSpatialIndexData() = default;
275 
276  QgsMeshSpatialIndexData &operator=( const QgsMeshSpatialIndexData &rh ) = delete;
277 
278  void initTree( IDataStream *inputStream = nullptr )
279  {
280  // for now only memory manager
281  mStorage.reset( StorageManager::createNewMemoryStorageManager() );
282 
283  // R-Tree parameters
284  double fillFactor = 0.7;
285  unsigned int indexCapacity = 10;
286  unsigned int leafCapacity = 10;
287  unsigned int dimension = 2;
288  RTree::RTreeVariant variant = RTree::RV_RSTAR;
289 
290  // create R-tree
291  SpatialIndex::id_type indexId;
292 
293  if ( inputStream && inputStream->hasNext() )
294  mRTree.reset(
295  RTree::createAndBulkLoadNewRTree(
296  RTree::BLM_STR,
297  *inputStream,
298  *mStorage, fillFactor,
299  indexCapacity,
300  leafCapacity,
301  dimension,
302  variant,
303  indexId )
304  );
305  else
306  mRTree.reset(
307  RTree::createNewRTree(
308  *mStorage,
309  fillFactor,
310  indexCapacity,
311  leafCapacity,
312  dimension,
313  variant,
314  indexId )
315  );
316  }
317 
319  std::unique_ptr<SpatialIndex::IStorageManager> mStorage;
320 
322  std::unique_ptr<SpatialIndex::ISpatialIndex> mRTree;
323 
324  mutable QMutex mMutex;
325 };
326 
328 
330 {
331  d = new QgsMeshSpatialIndexData;
332 }
333 
335  : mElementType( elementType )
336 {
337  d = new QgsMeshSpatialIndexData( mesh, feedback, elementType );
338 }
339 
341  : d( other.d )
342 {
343 }
344 
345 QgsMeshSpatialIndex:: ~QgsMeshSpatialIndex() = default; //NOLINT
346 
348 {
349  if ( this != &other )
350  d = other.d;
351  return *this;
352 }
353 
354 QList<int> QgsMeshSpatialIndex::intersects( const QgsRectangle &rect ) const
355 {
356  QList<int> list;
357  QgisMeshVisitor visitor( list );
358 
359  SpatialIndex::Region r = rectToRegion( rect );
360 
361  QMutexLocker locker( &d->mMutex );
362  d->mRTree->intersectsWithQuery( r, visitor );
363 
364  return list;
365 }
366 
367 QList<int> QgsMeshSpatialIndex::nearestNeighbor( const QgsPointXY &point, int neighbors ) const
368 {
369  QList<int> list;
370  QgisMeshVisitor visitor( list );
371 
372  double pt[2] = { point.x(), point.y() };
373  Point p( pt, 2 );
374 
375  QMutexLocker locker( &d->mMutex );
376  d->mRTree->nearestNeighborQuery( static_cast<uint32_t>( neighbors ), p, visitor );
377 
378  return list;
379 }
380 
382 {
383  return mElementType;
384 }
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
A spatial index for QgsMeshFace or QgsMeshEdge objects.
QList< int > intersects(const QgsRectangle &rectangle) const
Returns a list of face ids with a bounding box which intersects the specified rectangle.
QgsMesh::ElementType elementType() const
Returns the type of mesh elements that are indexed.
QgsMeshSpatialIndex()
Constructor for QgsSpatialIndex.
QgsMeshSpatialIndex & operator=(const QgsMeshSpatialIndex &other)
Implement assignment operator.
QList< int > nearestNeighbor(const QgsPointXY &point, int neighbors) const
Returns nearest neighbors to a point.
~QgsMeshSpatialIndex()
Destructor finalizes work with spatial index.
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
Q_GADGET double x
Definition: qgspoint.h:52
double y
Definition: qgspoint.h:53
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
QVector< int > QgsMeshFace
List of vertex indexes.
QPair< int, int > QgsMeshEdge
Edge is a straight line seqment between 2 points.
Mesh - vertices, edges and faces.
QVector< QgsMeshVertex > vertices
QgsMeshFace face(int index) const
Returns a face at the index.
int faceCount() const
Returns number of faces.
ElementType
Defines type of mesh elements.
QgsMeshEdge edge(int index) const
Returns an edge at the index.
int edgeCount() const
Returns number of edge.