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