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