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