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