Skip to content

Commit 405c55f

Browse files
committed
Create QgsVectorLayerSelectedFeatureSource
...which is a QgsFeatureSource subclass which only considers selected features from a QgsVectorLayer
1 parent f69d1c2 commit 405c55f

File tree

4 files changed

+186
-0
lines changed

4 files changed

+186
-0
lines changed

python/core/qgsvectorlayerfeatureiterator.sip

+36
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ class QgsVectorLayerFeatureSource : QgsAbstractFeatureSource
4646
:rtype: QgsFields
4747
%End
4848

49+
QgsCoordinateReferenceSystem crs() const;
50+
%Docstring
51+
Returns the coordinate reference system for features retrieved from this source.
52+
.. versionadded:: 3.0
53+
:rtype: QgsCoordinateReferenceSystem
54+
%End
55+
4956
protected:
5057

5158

@@ -138,6 +145,35 @@ Setup the simplification of geometries to fetch using the specified simplify met
138145
QgsVectorLayerFeatureIterator( const QgsVectorLayerFeatureIterator &rhs );
139146
};
140147

148+
149+
150+
class QgsVectorLayerSelectedFeatureSource : QgsFeatureSource
151+
{
152+
%Docstring
153+
QgsFeatureSource subclass for the selected features from a QgsVectorLayer.
154+
.. versionadded:: 3.0
155+
%End
156+
157+
%TypeHeaderCode
158+
#include "qgsvectorlayerfeatureiterator.h"
159+
%End
160+
public:
161+
162+
QgsVectorLayerSelectedFeatureSource( QgsVectorLayer *layer );
163+
%Docstring
164+
Constructor for QgsVectorLayerSelectedFeatureSource, for selected features from the specified ``layer``.
165+
The currently selected feature IDs are stored, so change to the layer selection after constructing
166+
the QgsVectorLayerSelectedFeatureSource will not be reflected.
167+
%End
168+
169+
virtual QgsFeatureIterator getFeatures( const QgsFeatureRequest &request = QgsFeatureRequest() ) const;
170+
virtual QgsCoordinateReferenceSystem sourceCrs() const;
171+
virtual QgsFields fields() const;
172+
virtual QgsWkbTypes::Type wkbType() const;
173+
virtual long featureCount() const;
174+
175+
};
176+
141177
/************************************************************************
142178
* This file has been generated automatically from *
143179
* *

src/core/qgsvectorlayerfeatureiterator.cpp

+53
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ QgsFields QgsVectorLayerFeatureSource::fields() const
9898
return mFields;
9999
}
100100

101+
QgsCoordinateReferenceSystem QgsVectorLayerFeatureSource::crs() const
102+
{
103+
return mCrs;
104+
}
105+
101106

102107
QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayerFeatureSource *source, bool ownSource, const QgsFeatureRequest &request )
103108
: QgsAbstractFeatureIteratorFromSource<QgsVectorLayerFeatureSource>( source, ownSource, request )
@@ -1006,3 +1011,51 @@ bool QgsVectorLayerFeatureIterator::prepareOrderBy( const QList<QgsFeatureReques
10061011
return true;
10071012
}
10081013

1014+
1015+
//
1016+
// QgsVectorLayerSelectedFeatureSource
1017+
//
1018+
1019+
QgsVectorLayerSelectedFeatureSource::QgsVectorLayerSelectedFeatureSource( QgsVectorLayer *layer )
1020+
: mSource( layer )
1021+
, mSelectedFeatureIds( layer->selectedFeatureIds() )
1022+
, mWkbType( layer->wkbType() )
1023+
{}
1024+
1025+
QgsFeatureIterator QgsVectorLayerSelectedFeatureSource::getFeatures( const QgsFeatureRequest &request ) const
1026+
{
1027+
QgsFeatureRequest req( request );
1028+
1029+
if ( req.filterFids().isEmpty() && req.filterType() != QgsFeatureRequest::FilterFids )
1030+
{
1031+
req.setFilterFids( mSelectedFeatureIds );
1032+
}
1033+
else if ( !req.filterFids().isEmpty() )
1034+
{
1035+
QgsFeatureIds reqIds = mSelectedFeatureIds;
1036+
reqIds.intersect( req.filterFids() );
1037+
req.setFilterFids( reqIds );
1038+
}
1039+
1040+
return mSource.getFeatures( req );
1041+
}
1042+
1043+
QgsCoordinateReferenceSystem QgsVectorLayerSelectedFeatureSource::sourceCrs() const
1044+
{
1045+
return mSource.crs();
1046+
}
1047+
1048+
QgsFields QgsVectorLayerSelectedFeatureSource::fields() const
1049+
{
1050+
return mSource.fields();
1051+
}
1052+
1053+
QgsWkbTypes::Type QgsVectorLayerSelectedFeatureSource::wkbType() const
1054+
{
1055+
return mWkbType;
1056+
}
1057+
1058+
long QgsVectorLayerSelectedFeatureSource::featureCount() const
1059+
{
1060+
return mSelectedFeatureIds.count();
1061+
}

src/core/qgsvectorlayerfeatureiterator.h

+41
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "qgsfeatureiterator.h"
2121
#include "qgsfields.h"
2222
#include "qgscoordinatereferencesystem.h"
23+
#include "qgsfeaturesource.h"
2324

2425
#include <QSet>
2526
#include <memory>
@@ -67,6 +68,12 @@ class CORE_EXPORT QgsVectorLayerFeatureSource : public QgsAbstractFeatureSource
6768
*/
6869
QgsFields fields() const;
6970

71+
/**
72+
* Returns the coordinate reference system for features retrieved from this source.
73+
* \since QGIS 3.0
74+
*/
75+
QgsCoordinateReferenceSystem crs() const;
76+
7077
protected:
7178

7279
QgsAbstractFeatureSource *mProviderFeatureSource = nullptr;
@@ -253,4 +260,38 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera
253260
bool checkGeometryValidity( const QgsFeature &feature );
254261
};
255262

263+
264+
265+
/**
266+
* \class QgsVectorLayerSelectedFeatureSource
267+
* \ingroup core
268+
* QgsFeatureSource subclass for the selected features from a QgsVectorLayer.
269+
* \since QGIS 3.0
270+
*/
271+
class CORE_EXPORT QgsVectorLayerSelectedFeatureSource : public QgsFeatureSource
272+
{
273+
public:
274+
275+
/**
276+
* Constructor for QgsVectorLayerSelectedFeatureSource, for selected features from the specified \a layer.
277+
* The currently selected feature IDs are stored, so change to the layer selection after constructing
278+
* the QgsVectorLayerSelectedFeatureSource will not be reflected.
279+
*/
280+
QgsVectorLayerSelectedFeatureSource( QgsVectorLayer *layer );
281+
282+
virtual QgsFeatureIterator getFeatures( const QgsFeatureRequest &request = QgsFeatureRequest() ) const override;
283+
virtual QgsCoordinateReferenceSystem sourceCrs() const override;
284+
virtual QgsFields fields() const override;
285+
virtual QgsWkbTypes::Type wkbType() const override;
286+
virtual long featureCount() const override;
287+
288+
private:
289+
290+
// ideally this wouldn't be mutable, but QgsVectorLayerFeatureSource has non-const getFeatures()
291+
mutable QgsVectorLayerFeatureSource mSource;
292+
QgsFeatureIds mSelectedFeatureIds;
293+
QgsWkbTypes::Type mWkbType = QgsWkbTypes::Unknown;
294+
295+
};
296+
256297
#endif // QGSVECTORLAYERFEATUREITERATOR_H

tests/src/python/test_qgsvectorlayer.py

+56
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
QgsSingleCategoryDiagramRenderer,
5353
QgsDiagramLayerSettings,
5454
QgsTextFormat,
55+
QgsVectorLayerSelectedFeatureSource,
5556
NULL)
5657
from qgis.testing import start_app, unittest
5758
from featuresourcetestbase import FeatureSourceTestCase
@@ -2298,6 +2299,61 @@ def testClone(self):
22982299
# compare xml documents
22992300
self.assertEqual(layer_doc.toString(), clone_doc.toString())
23002301

2302+
def testQgsVectorLayerSelectedFeatureSource(self):
2303+
"""
2304+
test QgsVectorLayerSelectedFeatureSource
2305+
"""
2306+
2307+
layer = QgsVectorLayer("Point?crs=epsg:3111&field=fldtxt:string&field=fldint:integer",
2308+
"addfeat", "memory")
2309+
pr = layer.dataProvider()
2310+
f1 = QgsFeature(1)
2311+
f1.setAttributes(["test", 123])
2312+
f1.setGeometry(QgsGeometry.fromPoint(QgsPointXY(100, 200)))
2313+
f2 = QgsFeature(2)
2314+
f2.setAttributes(["test2", 457])
2315+
f2.setGeometry(QgsGeometry.fromPoint(QgsPointXY(200, 200)))
2316+
f3 = QgsFeature(3)
2317+
f3.setAttributes(["test2", 888])
2318+
f3.setGeometry(QgsGeometry.fromPoint(QgsPointXY(300, 200)))
2319+
f4 = QgsFeature(4)
2320+
f4.setAttributes(["test3", -1])
2321+
f4.setGeometry(QgsGeometry.fromPoint(QgsPointXY(400, 300)))
2322+
f5 = QgsFeature(5)
2323+
f5.setAttributes(["test4", 0])
2324+
f5.setGeometry(QgsGeometry.fromPoint(QgsPointXY(0, 0)))
2325+
self.assertTrue(pr.addFeatures([f1, f2, f3, f4, f5]))
2326+
self.assertEqual(layer.featureCount(), 5)
2327+
2328+
source = QgsVectorLayerSelectedFeatureSource(layer)
2329+
self.assertEqual(source.sourceCrs().authid(), 'EPSG:3111')
2330+
self.assertEqual(source.wkbType(), QgsWkbTypes.Point)
2331+
self.assertEqual(source.fields(), layer.fields())
2332+
2333+
# no selection
2334+
self.assertEqual(source.featureCount(), 0)
2335+
it = source.getFeatures()
2336+
f = QgsFeature()
2337+
self.assertFalse(it.nextFeature(f))
2338+
2339+
# with selection
2340+
layer.selectByIds([f1.id(), f3.id(), f5.id()])
2341+
source = QgsVectorLayerSelectedFeatureSource(layer)
2342+
self.assertEqual(source.featureCount(), 3)
2343+
ids = set([f.id() for f in source.getFeatures()])
2344+
self.assertEqual(ids, {f1.id(), f3.id(), f5.id()})
2345+
2346+
# test that source has stored snapshot of selected features
2347+
layer.selectByIds([f2.id(), f4.id()])
2348+
self.assertEqual(source.featureCount(), 3)
2349+
ids = set([f.id() for f in source.getFeatures()])
2350+
self.assertEqual(ids, {f1.id(), f3.id(), f5.id()})
2351+
2352+
# test that source is not dependent on layer
2353+
del layer
2354+
ids = set([f.id() for f in source.getFeatures()])
2355+
self.assertEqual(ids, {f1.id(), f3.id(), f5.id()})
2356+
23012357

23022358
# TODO:
23032359
# - fetch rect: feat with changed geometry: 1. in rect, 2. out of rect

0 commit comments

Comments
 (0)