QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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 
106 class QgsMeshSpatialIndexCopyVisitor : public SpatialIndex::IVisitor
107 {
108  public:
109  explicit QgsMeshSpatialIndexCopyVisitor( SpatialIndex::ISpatialIndex *newIndex )
110  : mNewIndex( newIndex ) {}
111 
112  void visitNode( const INode &n ) override
113  { Q_UNUSED( n ) }
114 
115  void visitData( const IData &d ) override
116  {
117  SpatialIndex::IShape *shape = nullptr;
118  d.getShape( &shape );
119  mNewIndex->insertData( 0, nullptr, *shape, d.getIdentifier() );
120  delete shape;
121  }
122 
123  void visitData( std::vector<const IData *> &v ) override
124  { Q_UNUSED( v ) }
125 
126  private:
127  SpatialIndex::ISpatialIndex *mNewIndex = nullptr;
128 };
129 
130 
137 class QgsMeshIteratorDataStream : public IDataStream
138 {
139  public:
141  explicit QgsMeshIteratorDataStream( const QgsMesh &mesh,
142  int featuresCount,
143  std::function<Region( const QgsMesh &mesh, int id )> featureToRegionFunction,
144  QgsFeedback *feedback = nullptr )
145  : mMesh( mesh )
146  , mFeaturesCount( featuresCount )
147  , mFeatureToRegionFunction( featureToRegionFunction )
148  , mFeedback( feedback )
149  {
150  readNextEntry();
151  }
152 
153  ~QgsMeshIteratorDataStream() override
154  {
155  delete mNextData;
156  }
157 
159  IData *getNext() override
160  {
161  if ( mFeedback && mFeedback->isCanceled() )
162  return nullptr;
163 
164  RTree::Data *ret = mNextData;
165  mNextData = nullptr;
166  readNextEntry();
167  return ret;
168  }
169 
171  bool hasNext() override
172  {
173  return nullptr != mNextData;
174  }
175 
177  uint32_t size() override
178  {
179  return static_cast<uint32_t>( mFeaturesCount );
180  }
181 
183  void rewind() override
184  {
185  mIterator = 0;
186  }
187 
188  protected:
189  void readNextEntry()
190  {
191  SpatialIndex::Region r;
192  if ( mIterator < mFeaturesCount )
193  {
194  r = mFeatureToRegionFunction( mMesh, mIterator );
195  mNextData = new RTree::Data(
196  0,
197  nullptr,
198  r,
199  mIterator );
200  ++mIterator;
201  }
202  }
203 
204  private:
205  int mIterator = 0;
206  const QgsMesh &mMesh;
207  int mFeaturesCount = 0;
208  std::function<Region( const QgsMesh &mesh, int id )> mFeatureToRegionFunction;
209  RTree::Data *mNextData = nullptr;
210  QgsFeedback *mFeedback = nullptr;
211 };
212 
219 class QgsMeshSpatialIndexData : public QSharedData
220 {
221  public:
222  QgsMeshSpatialIndexData()
223  {
224  initTree();
225  }
226 
235  explicit QgsMeshSpatialIndexData( const QgsMesh &fi, QgsFeedback *feedback, QgsMesh::ElementType elementType )
236  {
237  switch ( elementType )
238  {
239  case QgsMesh::ElementType::Edge:
240  {
241  QgsMeshIteratorDataStream fids( fi, fi.edgeCount(), edgeToRegion, feedback );
242  initTree( &fids );
243  }
244  break;
246  {
247  QgsMeshIteratorDataStream fids( fi, fi.faceCount(), faceToRegion, feedback );
248  initTree( &fids );
249  }
250  break;
251  default:
252  // vertices are not supported
253  Q_ASSERT( false );
254  break;
255  }
256  }
257 
258  QgsMeshSpatialIndexData( const QgsMeshSpatialIndexData &other )
259  : QSharedData( other )
260  {
261  QMutexLocker locker( &other.mMutex );
262 
263  initTree();
264 
265  // copy R-tree data one by one (is there a faster way??)
266  double low[] = { std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest() };
267  double high[] = { std::numeric_limits<double>::max(), std::numeric_limits<double>::max() };
268  SpatialIndex::Region query( low, high, 2 );
269  QgsMeshSpatialIndexCopyVisitor visitor( mRTree.get() );
270  other.mRTree->intersectsWithQuery( query, visitor );
271  }
272 
273  ~QgsMeshSpatialIndexData() = default;
274 
275  QgsMeshSpatialIndexData &operator=( const QgsMeshSpatialIndexData &rh ) = delete;
276 
277  void initTree( IDataStream *inputStream = nullptr )
278  {
279  // for now only memory manager
280  mStorage.reset( StorageManager::createNewMemoryStorageManager() );
281 
282  // R-Tree parameters
283  double fillFactor = 0.7;
284  unsigned int indexCapacity = 10;
285  unsigned int leafCapacity = 10;
286  unsigned int dimension = 2;
287  RTree::RTreeVariant variant = RTree::RV_RSTAR;
288 
289  // create R-tree
290  SpatialIndex::id_type indexId;
291 
292  if ( inputStream && inputStream->hasNext() )
293  mRTree.reset(
294  RTree::createAndBulkLoadNewRTree(
295  RTree::BLM_STR,
296  *inputStream,
297  *mStorage, fillFactor,
298  indexCapacity,
299  leafCapacity,
300  dimension,
301  variant,
302  indexId )
303  );
304  else
305  mRTree.reset(
306  RTree::createNewRTree(
307  *mStorage,
308  fillFactor,
309  indexCapacity,
310  leafCapacity,
311  dimension,
312  variant,
313  indexId )
314  );
315  }
316 
318  std::unique_ptr<SpatialIndex::IStorageManager> mStorage;
319 
321  std::unique_ptr<SpatialIndex::ISpatialIndex> mRTree;
322 
323  mutable QMutex mMutex;
324 };
325 
327 
329 {
330  d = new QgsMeshSpatialIndexData;
331 }
332 
334  : mElementType( elementType )
335 {
336  d = new QgsMeshSpatialIndexData( mesh, feedback, elementType );
337 }
338 
340  : d( other.d )
341 {
342 }
343 
344 QgsMeshSpatialIndex:: ~QgsMeshSpatialIndex() = default; //NOLINT
345 
347 {
348  if ( this != &other )
349  d = other.d;
350  return *this;
351 }
352 
353 QList<int> QgsMeshSpatialIndex::intersects( const QgsRectangle &rect ) const
354 {
355  QList<int> list;
356  QgisMeshVisitor visitor( list );
357 
358  SpatialIndex::Region r = rectToRegion( rect );
359 
360  QMutexLocker locker( &d->mMutex );
361  d->mRTree->intersectsWithQuery( r, visitor );
362 
363  return list;
364 }
365 
366 QList<int> QgsMeshSpatialIndex::nearestNeighbor( const QgsPointXY &point, int neighbors ) const
367 {
368  QList<int> list;
369  QgisMeshVisitor visitor( list );
370 
371  double pt[2] = { point.x(), point.y() };
372  Point p( pt, 2 );
373 
374  QMutexLocker locker( &d->mMutex );
375  d->mRTree->nearestNeighborQuery( static_cast<uint32_t>( neighbors ), p, visitor );
376 
377  return list;
378 }
379 
381 {
382  return mElementType;
383 }
SpatialIndex
Definition: qgspointlocator.h:81
QgsPointXY::y
double y
Definition: qgspointxy.h:48
Face
struct Face_t Face
qgsrectangle.h
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
qgsmeshspatialindex.h
QgsRectangle::xMaximum
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
QgsMeshSpatialIndex::elementType
QgsMesh::ElementType elementType() const
Returns the type of mesh elements that are indexed.
Definition: qgsmeshspatialindex.cpp:380
QgsMesh
Definition: qgsmeshdataprovider.h:57
QgsRectangle
Definition: qgsrectangle.h:41
QgsPoint::y
double y
Definition: qgspoint.h:59
QgsMesh::faceCount
int faceCount() const
Returns number of faces.
Definition: qgsmeshdataprovider.cpp:124
QgsMesh::ElementType
ElementType
Defines type of mesh elements.
Definition: qgsmeshdataprovider.h:64
QgsMesh::edgeCount
int edgeCount() const
Returns number of edge.
Definition: qgsmeshdataprovider.cpp:129
QgsMesh::edge
QgsMeshEdge edge(int index) const
Returns an edge at the index.
Definition: qgsmeshdataprovider.cpp:91
QgsFeedback
Definition: qgsfeedback.h:43
QgsMeshSpatialIndex::nearestNeighbor
QList< int > nearestNeighbor(const QgsPointXY &point, int neighbors) const
Returns nearest neighbors to a point.
Definition: qgsmeshspatialindex.cpp:366
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:353
QgsMesh::face
QgsMeshFace face(int index) const
Returns a face at the index.
Definition: qgsmeshdataprovider.cpp:84
QgsMeshFace
QVector< int > QgsMeshFace
List of vertex indexes.
Definition: qgsmeshdataprovider.h:41
QgsRectangle::yMaximum
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QgsPointXY
Definition: qgspointxy.h:43
QgsMeshSpatialIndex::~QgsMeshSpatialIndex
~QgsMeshSpatialIndex()
Destructor finalizes work with spatial index.
QgsMeshSpatialIndex
Definition: qgsmeshspatialindex.h:49
QgsMeshEdge
QPair< int, int > QgsMeshEdge
Edge is a straight line seqment between 2 points.
Definition: qgsmeshdataprovider.h:48
QgsPointXY::x
double x
Definition: qgspointxy.h:47
QgsMesh::vertices
QVector< QgsMeshVertex > vertices
Definition: qgsmeshdataprovider.h:105
QgsRectangle::yMinimum
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
qgslogger.h
qgsfeedback.h
QgsMeshSpatialIndex::QgsMeshSpatialIndex
QgsMeshSpatialIndex()
Constructor for QgsSpatialIndex.
Definition: qgsmeshspatialindex.cpp:328
QgsPoint::x
double x
Definition: qgspoint.h:58
QgsRectangle::xMinimum
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
QgsMeshSpatialIndex::operator=
QgsMeshSpatialIndex & operator=(const QgsMeshSpatialIndex &other)
Implement assignment operator.
Definition: qgsmeshspatialindex.cpp:346