Skip to content

Commit 5c552dd

Browse files
committed
QgsSpatialIndexKDBush is implicitly shared for fast copies
1 parent 0df1056 commit 5c552dd

6 files changed

+235
-63
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/core/qgsspatialindexkdbush.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
13+
class QgsSpatialIndexKDBush
14+
{
15+
%Docstring
16+
17+
A very fast static spatial index for 2D points based on a flat KD-tree.
18+
19+
Compared to QgsSpatialIndex, this index:
20+
- supports single point features only (no multipoints)
21+
- is static (features cannot be added or removed from the index after construction)
22+
- is much faster!
23+
- supports true "distance based" searches, i.e. return all points within a radius
24+
from a search point
25+
26+
.. seealso:: :py:class:`QgsSpatialIndex`
27+
28+
.. versionadded:: 3.4
29+
%End
30+
31+
%TypeHeaderCode
32+
#include "qgsspatialindexkdbush.h"
33+
%End
34+
public:
35+
36+
explicit QgsSpatialIndexKDBush( QgsFeatureIterator &fi, QgsFeedback *feedback = 0 );
37+
%Docstring
38+
Constructor - creates KDBush index and bulk loads it with features from the iterator.
39+
40+
The optional ``feedback`` object can be used to allow cancelation of bulk feature loading. Ownership
41+
of ``feedback`` is not transferred, and callers must take care that the lifetime of feedback exceeds
42+
that of the spatial index construction.
43+
44+
Any non-single point features encountered during iteration will be ignored and not included in the index.
45+
%End
46+
47+
explicit QgsSpatialIndexKDBush( const QgsFeatureSource &source, QgsFeedback *feedback = 0 );
48+
%Docstring
49+
Constructor - creates KDBush index and bulk loads it with features from the source.
50+
51+
The optional ``feedback`` object can be used to allow cancelation of bulk feature loading. Ownership
52+
of ``feedback`` is not transferred, and callers must take care that the lifetime of feedback exceeds
53+
that of the spatial index construction.
54+
55+
Any non-single point features encountered during iteration will be ignored and not included in the index.
56+
%End
57+
58+
QgsSpatialIndexKDBush( const QgsSpatialIndexKDBush &other );
59+
%Docstring
60+
Copy constructor
61+
%End
62+
63+
64+
~QgsSpatialIndexKDBush();
65+
66+
QList<QgsFeatureId> intersect( const QgsRectangle &rectangle ) const;
67+
%Docstring
68+
Returns a list of features which fall within the specified ``rectangle``.
69+
%End
70+
71+
QList<QgsFeatureId> within( const QgsPointXY &point, double radius ) const;
72+
%Docstring
73+
Returns a list of features which are within the given search ``radius``
74+
of ``point``.
75+
%End
76+
77+
};
78+
79+
/************************************************************************
80+
* This file has been generated automatically from *
81+
* *
82+
* src/core/qgsspatialindexkdbush.h *
83+
* *
84+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
85+
************************************************************************/

src/core/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,7 @@ SET(QGIS_CORE_HDRS
914914
qgssnappingutils.h
915915
qgsspatialindex.h
916916
qgsspatialindexkdbush.h
917+
qgsspatialindexkdbush_p.h
917918
qgsspatialiteutils.h
918919
qgssqlstatement.h
919920
qgssqliteutils.h

src/core/qgsspatialindexkdbush.cpp

+29-61
Original file line numberDiff line numberDiff line change
@@ -16,91 +16,59 @@
1616
***************************************************************************/
1717

1818
#include "qgsspatialindexkdbush.h"
19-
#include "kdbush.hpp"
19+
#include "qgsspatialindexkdbush_p.h"
2020
#include "qgsfeatureiterator.h"
2121
#include "qgsfeedback.h"
2222
#include "qgsfeaturesource.h"
2323

24-
class PointXYKDBush : public kdbush::KDBush< std::pair<double, double>, QgsFeatureId >
24+
QgsSpatialIndexKDBush::QgsSpatialIndexKDBush( QgsFeatureIterator &fi, QgsFeedback *feedback )
25+
: d( new QgsSpatialIndexKDBushPrivate( fi, feedback ) )
2526
{
26-
public:
27-
28-
explicit PointXYKDBush( QgsFeatureIterator &fi, QgsFeedback *feedback = nullptr )
29-
{
30-
fillFromIterator( fi, feedback );
31-
}
32-
33-
explicit PointXYKDBush( const QgsFeatureSource &source, QgsFeedback *feedback )
34-
{
35-
points.reserve( source.featureCount() );
36-
ids.reserve( source.featureCount() );
37-
QgsFeatureIterator it = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ) );
38-
fillFromIterator( it, feedback );
39-
}
40-
41-
void fillFromIterator( QgsFeatureIterator &fi, QgsFeedback *feedback = nullptr )
42-
{
43-
QgsFeatureId size = 0;
44-
45-
QgsFeature f;
46-
while ( fi.nextFeature( f ) )
47-
{
48-
if ( feedback && feedback->isCanceled() )
49-
return;
50-
51-
if ( !f.hasGeometry() )
52-
continue;
53-
54-
if ( QgsWkbTypes::flatType( f.geometry().wkbType() ) == QgsWkbTypes::Point )
55-
{
56-
const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( f.geometry().constGet() );
57-
points.emplace_back( point->x(), point->y() );
58-
}
59-
else
60-
{
61-
// not a point
62-
continue;
63-
}
64-
65-
ids.push_back( f.id() );
66-
size++;
67-
}
68-
69-
sortKD( 0, size - 1, 0 );
70-
}
7127

72-
};
73-
74-
//
75-
// QgsSpatialIndexKDBush
76-
//
28+
}
7729

78-
QgsSpatialIndexKDBush::QgsSpatialIndexKDBush( QgsFeatureIterator &fi, QgsFeedback *feedback )
79-
: mIndex( qgis::make_unique < PointXYKDBush >( fi, feedback ) )
30+
QgsSpatialIndexKDBush::QgsSpatialIndexKDBush( const QgsFeatureSource &source, QgsFeedback *feedback )
31+
: d( new QgsSpatialIndexKDBushPrivate( source, feedback ) )
8032
{
33+
}
8134

35+
QgsSpatialIndexKDBush::QgsSpatialIndexKDBush( const QgsSpatialIndexKDBush &other )
36+
{
37+
d = other.d;
38+
d->ref.ref();
8239
}
8340

84-
QgsSpatialIndexKDBush::QgsSpatialIndexKDBush( const QgsFeatureSource &source, QgsFeedback *feedback )
85-
: mIndex( qgis::make_unique < PointXYKDBush >( source, feedback ) )
41+
QgsSpatialIndexKDBush &QgsSpatialIndexKDBush::operator=( const QgsSpatialIndexKDBush &other )
8642
{
43+
if ( !d->ref.deref() )
44+
{
45+
delete d;
46+
}
47+
48+
d = other.d;
49+
d->ref.ref();
50+
return *this;
8751
}
8852

89-
QgsSpatialIndexKDBush::~QgsSpatialIndexKDBush() = default;
53+
QgsSpatialIndexKDBush::~QgsSpatialIndexKDBush()
54+
{
55+
if ( !d->ref.deref() )
56+
delete d;
57+
}
9058

9159
QList<QgsFeatureId> QgsSpatialIndexKDBush::within( const QgsPointXY &point, double radius ) const
9260
{
9361
QList<QgsFeatureId> result;
94-
mIndex->within( point.x(), point.y(), radius, [&result]( const QgsFeatureId id ) { result << id; } );
62+
d->index->within( point.x(), point.y(), radius, [&result]( const QgsFeatureId id ) { result << id; } );
9563
return result;
9664
}
9765

9866
QList<QgsFeatureId> QgsSpatialIndexKDBush::intersect( const QgsRectangle &rectangle ) const
9967
{
10068
QList<QgsFeatureId> result;
101-
mIndex->range( rectangle.xMinimum(),
102-
rectangle.yMinimum(),
103-
rectangle.xMaximum(),
69+
d->index->range( rectangle.xMinimum(),
70+
rectangle.yMinimum(),
71+
rectangle.xMaximum(),
10472
rectangle.yMaximum(), [&result]( const QgsFeatureId id ) { result << id; } );
10573
return result;
10674
}

src/core/qgsspatialindexkdbush.h

+11-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class QgsPointXY;
2222
class QgsFeatureIterator;
2323
class QgsFeedback;
2424
class QgsFeatureSource;
25-
class PointXYKDBush;
25+
class QgsSpatialIndexKDBushPrivate;
2626
class QgsRectangle;
2727

2828
#include "qgis_core.h"
@@ -43,6 +43,8 @@ class QgsRectangle;
4343
* - supports true "distance based" searches, i.e. return all points within a radius
4444
* from a search point
4545
*
46+
* QgsSpatialIndexKDBush objects are implicitly shared and can be inexpensively copied.
47+
*
4648
* \see QgsSpatialIndex, which is an general, mutable index for geometry bounding boxes.
4749
* \since QGIS 3.4
4850
*/
@@ -72,6 +74,12 @@ class CORE_EXPORT QgsSpatialIndexKDBush
7274
*/
7375
explicit QgsSpatialIndexKDBush( const QgsFeatureSource &source, QgsFeedback *feedback = nullptr );
7476

77+
//! Copy constructor
78+
QgsSpatialIndexKDBush( const QgsSpatialIndexKDBush &other );
79+
80+
//! Assignment operator
81+
QgsSpatialIndexKDBush &operator=( const QgsSpatialIndexKDBush &other );
82+
7583
~QgsSpatialIndexKDBush();
7684

7785
/**
@@ -87,7 +95,8 @@ class CORE_EXPORT QgsSpatialIndexKDBush
8795

8896
private:
8997

90-
std::unique_ptr< PointXYKDBush > mIndex;
98+
//! Implicitly shared data pointer
99+
QgsSpatialIndexKDBushPrivate *d = nullptr;
91100
};
92101

93102
#endif // QGSSPATIALINDEXKDBUSH_H

src/core/qgsspatialindexkdbush_p.cpp

Whitespace-only changes.

src/core/qgsspatialindexkdbush_p.h

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/***************************************************************************
2+
qgsspatialindexkdbush_p.h
3+
-----------------
4+
begin : July 2018
5+
copyright : (C) 2018 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#ifndef QGSSPATIALINDEXKDBUSH_PRIVATE_H
19+
#define QGSSPATIALINDEXKDBUSH_PRIVATE_H
20+
21+
/// @cond PRIVATE
22+
23+
//
24+
// W A R N I N G
25+
// -------------
26+
//
27+
// This file is not part of the QGIS API. It exists purely as an
28+
// implementation detail. This header file may change from version to
29+
// version without notice, or even be removed.
30+
//
31+
32+
#include "qgsfeature.h"
33+
34+
#include "qgsfeatureiterator.h"
35+
#include "qgsfeedback.h"
36+
#include "qgsfeaturesource.h"
37+
#include <memory>
38+
#include <QList>
39+
#include "kdbush.hpp"
40+
41+
class PointXYKDBush : public kdbush::KDBush< std::pair<double, double>, QgsFeatureId >
42+
{
43+
public:
44+
45+
explicit PointXYKDBush( QgsFeatureIterator &fi, QgsFeedback *feedback = nullptr )
46+
{
47+
fillFromIterator( fi, feedback );
48+
}
49+
50+
explicit PointXYKDBush( const QgsFeatureSource &source, QgsFeedback *feedback )
51+
{
52+
points.reserve( source.featureCount() );
53+
ids.reserve( source.featureCount() );
54+
QgsFeatureIterator it = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ) );
55+
fillFromIterator( it, feedback );
56+
}
57+
58+
void fillFromIterator( QgsFeatureIterator &fi, QgsFeedback *feedback = nullptr )
59+
{
60+
QgsFeatureId size = 0;
61+
62+
QgsFeature f;
63+
while ( fi.nextFeature( f ) )
64+
{
65+
if ( feedback && feedback->isCanceled() )
66+
return;
67+
68+
if ( !f.hasGeometry() )
69+
continue;
70+
71+
if ( QgsWkbTypes::flatType( f.geometry().wkbType() ) == QgsWkbTypes::Point )
72+
{
73+
const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( f.geometry().constGet() );
74+
points.emplace_back( point->x(), point->y() );
75+
}
76+
else
77+
{
78+
// not a point
79+
continue;
80+
}
81+
82+
ids.push_back( f.id() );
83+
size++;
84+
}
85+
86+
sortKD( 0, size - 1, 0 );
87+
}
88+
89+
};
90+
91+
class QgsSpatialIndexKDBushPrivate
92+
{
93+
public:
94+
95+
explicit QgsSpatialIndexKDBushPrivate( QgsFeatureIterator &fi, QgsFeedback *feedback = nullptr )
96+
: index( qgis::make_unique < PointXYKDBush >( fi, feedback ) )
97+
{}
98+
99+
explicit QgsSpatialIndexKDBushPrivate( const QgsFeatureSource &source, QgsFeedback *feedback = nullptr )
100+
: index( qgis::make_unique < PointXYKDBush >( source, feedback ) )
101+
{}
102+
103+
QAtomicInt ref = 1;
104+
std::unique_ptr< PointXYKDBush > index;
105+
};
106+
107+
/// @endcond
108+
109+
#endif // QGSSPATIALINDEXKDBUSH_PRIVATE_H

0 commit comments

Comments
 (0)