QGIS API Documentation 4.3.0-Master (9ff14a2eeba)
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
96 void visitNode( const INode &n ) override { Q_UNUSED( n ) }
97
98 void visitData( const IData &d ) override { mList.append( static_cast<int>( d.getIdentifier() ) ); }
99
100 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
101
102 private:
103 QList<int> &mList;
104};
105
112class QgsMeshSpatialIndexCopyVisitor : public SpatialIndex::IVisitor
113{
114 public:
115 explicit QgsMeshSpatialIndexCopyVisitor( SpatialIndex::ISpatialIndex *newIndex )
116 : mNewIndex( newIndex )
117 {}
118
119 void visitNode( const INode &n ) override { Q_UNUSED( n ) }
120
121 void visitData( const IData &d ) override
122 {
123 SpatialIndex::IShape *shape = nullptr;
124 d.getShape( &shape );
125 mNewIndex->insertData( 0, nullptr, *shape, d.getIdentifier() );
126 delete shape;
127 }
128
129 void visitData( std::vector<const IData *> &v ) override { 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, int featuresCount, std::function<Region( const QgsMesh &mesh, int id, bool &ok )> featureToRegionFunction, QgsFeedback *feedback = nullptr )
147 : mMesh( mesh )
148 , mFeaturesCount( featuresCount )
149 , mFeatureToRegionFunction( std::move( featureToRegionFunction ) )
150 , mFeedback( feedback )
151 {
152 readNextEntry();
153 }
154
155 ~QgsMeshIteratorDataStream() override {}
156
158 IData *getNext() override
159 {
160 if ( mFeedback && mFeedback->isCanceled() )
161 return nullptr;
162
163 RTree::Data *ret = mNextData.release();
164 readNextEntry();
165 return ret;
166 }
167
169 bool hasNext() override { return nullptr != mNextData.get(); }
170
172 uint32_t size() override { return static_cast<uint32_t>( mFeaturesCount ); }
173
175 void rewind() override { mIterator = 0; }
176
177 protected:
178 void readNextEntry()
179 {
180 SpatialIndex::Region r;
181 while ( mIterator < mFeaturesCount )
182 {
183 bool ok = false;
184 r = mFeatureToRegionFunction( mMesh, mIterator, ok );
185 if ( ok )
186 {
187 mNextData = std::make_unique<RTree::Data>( 0, nullptr, r, mIterator );
188 ++mIterator;
189 return;
190 }
191 else
192 {
193 ++mIterator;
194 continue;
195 }
196 }
197 }
198
199 private:
200 int mIterator = 0;
201 const QgsMesh &mMesh;
202 int mFeaturesCount = 0;
203 std::function<Region( const QgsMesh &mesh, int id, bool &ok )> mFeatureToRegionFunction;
204 std::unique_ptr<RTree::Data> mNextData;
205 QgsFeedback *mFeedback = nullptr;
206};
207
214class QgsMeshSpatialIndexData : public QSharedData
215{
216 public:
217 QgsMeshSpatialIndexData() { initTree(); }
218
227 explicit QgsMeshSpatialIndexData( const QgsMesh &fi, QgsFeedback *feedback, QgsMesh::ElementType elementType )
228 {
229 switch ( elementType )
230 {
232 {
233 QgsMeshIteratorDataStream fids( fi, fi.edgeCount(), edgeToRegion, feedback );
234 initTree( &fids );
235 }
236 break;
238 {
239 QgsMeshIteratorDataStream fids( fi, fi.faceCount(), faceToRegion, feedback );
240 initTree( &fids );
241 }
242 break;
243 default:
244 // vertices are not supported
245 Q_ASSERT( false );
246 break;
247 }
248 }
249
250 QgsMeshSpatialIndexData( const QgsMeshSpatialIndexData &other )
251 : QSharedData( other )
252 {
253 const QMutexLocker locker( &other.mMutex );
254
255 initTree();
256
257 // copy R-tree data one by one (is there a faster way??)
258 double low[] = { std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest() };
259 double high[] = { std::numeric_limits<double>::max(), std::numeric_limits<double>::max() };
260 const SpatialIndex::Region query( low, high, 2 );
261 QgsMeshSpatialIndexCopyVisitor visitor( mRTree.get() );
262 other.mRTree->intersectsWithQuery( query, visitor );
263 }
264
265 ~QgsMeshSpatialIndexData() = default;
266
267 QgsMeshSpatialIndexData &operator=( const QgsMeshSpatialIndexData &rh ) = delete;
268
269 void initTree( IDataStream *inputStream = nullptr )
270 {
271 // for now only memory manager
272 mStorage.reset( StorageManager::createNewMemoryStorageManager() );
273
274 // R-Tree parameters
275 const double fillFactor = 0.7;
276 const unsigned int indexCapacity = 10;
277 const unsigned int leafCapacity = 10;
278 const unsigned int dimension = 2;
279 const RTree::RTreeVariant variant = RTree::RV_RSTAR;
280
281 // create R-tree
282 SpatialIndex::id_type indexId;
283
284 if ( inputStream && inputStream->hasNext() )
285 mRTree.reset( RTree::createAndBulkLoadNewRTree( RTree::BLM_STR, *inputStream, *mStorage, fillFactor, indexCapacity, leafCapacity, dimension, variant, indexId ) );
286 else
287 mRTree.reset( RTree::createNewRTree( *mStorage, fillFactor, indexCapacity, leafCapacity, dimension, variant, indexId ) );
288 }
289
291 std::unique_ptr<SpatialIndex::IStorageManager> mStorage;
292
294 std::unique_ptr<SpatialIndex::ISpatialIndex> mRTree;
295
296 mutable QMutex mMutex;
297};
298
300
302{
303 d = new QgsMeshSpatialIndexData;
304}
305
307 : mElementType( elementType )
308{
309 d = new QgsMeshSpatialIndexData( mesh, feedback, elementType );
310}
311
313 : mElementType( other.mElementType )
314 , d( other.d )
315{}
316
318 : mElementType( other.mElementType )
319 , d( std::move( other.d ) )
320{}
321
323
325{
326 if ( this != &other )
327 {
328 mElementType = other.mElementType;
329 d = other.d;
330 }
331 return *this;
332}
333
335{
336 if ( this != &other )
337 {
338 mElementType = std::move( other.mElementType );
339 d = std::move( other.d );
340 }
341 return *this;
342}
343
344QList<int> QgsMeshSpatialIndex::intersects( const QgsRectangle &rect ) const
345{
346 QList<int> list;
347 if ( rect.isNull() )
348 return list;
349
350 QgisMeshVisitor visitor( list );
351
352 const SpatialIndex::Region r = QgsSpatialIndexUtils::rectangleToRegion( rect );
353
354 const QMutexLocker locker( &d->mMutex );
355 d->mRTree->intersectsWithQuery( r, visitor );
356
357 return list;
358}
359
360QList<int> QgsMeshSpatialIndex::nearestNeighbor( const QgsPointXY &point, int neighbors ) const
361{
362 QList<int> list;
363 QgisMeshVisitor visitor( list );
364
365 double pt[2] = { point.x(), point.y() };
366 const Point p( pt, 2 );
367
368 const QMutexLocker locker( &d->mMutex );
369 d->mRTree->nearestNeighborQuery( static_cast<uint32_t>( neighbors ), p, visitor );
370
371 return list;
372}
373
375{
376 return mElementType;
377}
378
379void QgsMeshSpatialIndex::addFace( int faceIndex, const QgsMesh &mesh )
380{
381 if ( mesh.face( faceIndex ).isEmpty() )
382 return;
383
384 bool ok = false;
385 const SpatialIndex::Region r( faceToRegion( mesh, faceIndex, ok ) );
386 if ( !ok )
387 return;
388
389 const QMutexLocker locker( &d.constData()->mMutex );
390
391 try
392 {
393 d.constData()->mRTree->insertData( 0, nullptr, r, faceIndex );
394 }
395 catch ( Tools::Exception &e )
396 {
397 Q_UNUSED( e )
398 QgsDebugError( u"Tools::Exception caught: "_s.arg( e.what().c_str() ) );
399 }
400 catch ( const std::exception &e )
401 {
402 Q_UNUSED( e )
403 QgsDebugError( u"std::exception caught: "_s.arg( e.what() ) );
404 }
405 catch ( ... )
406 {
407 QgsDebugError( u"unknown spatial index exception caught"_s );
408 }
409}
410
411void QgsMeshSpatialIndex::removeFace( int faceIndex, const QgsMesh &mesh )
412{
413 if ( mesh.face( faceIndex ).isEmpty() )
414 return;
415 const QMutexLocker locker( &d.constData()->mMutex );
416 bool ok = false;
417 d.constData()->mRTree->deleteData( faceToRegion( mesh, faceIndex, ok ), faceIndex );
418}
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:71
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.