Skip to content

Commit cdc7d39

Browse files
committed
Refactor QgsFeaturePool
QgsFeaturePool is now an abstract baseclass, with a new inherited class QgsVectorDataProviderFeaturePool. This allows creating other subclasses, most notably a QgsVectorLayerFeaturePool subclass, that is able to also work on uncommitted features. Critical calls to methods which are not threadsafe have been protected by executing them on the main thread.
1 parent 4b411e0 commit cdc7d39

13 files changed

+376
-120
lines changed

src/analysis/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ SET(QGIS_ANALYSIS_SRCS
132132
network/qgsgraphanalyzer.cpp
133133

134134
vector/geometry_checker/qgsfeaturepool.cpp
135+
vector/geometry_checker/qgsvectordataproviderfeaturepool.cpp
135136
vector/geometry_checker/qgsgeometrychecker.cpp
136137
vector/geometry_checker/qgsgeometryanglecheck.cpp
137138
vector/geometry_checker/qgsgeometryareacheck.cpp
@@ -232,6 +233,7 @@ SET(QGIS_ANALYSIS_HDRS
232233
vector/qgszonalstatistics.h
233234
vector/geometry_checker/qgsgeometrycheckerutils.h
234235
vector/geometry_checker/qgsfeaturepool.h
236+
vector/geometry_checker/qgsvectordataproviderfeaturepool.h
235237

236238
interpolation/qgsinterpolator.h
237239
interpolation/qgsgridfilewriter.h

src/analysis/vector/geometry_checker/qgsfeaturepool.cpp

+57-72
Original file line numberDiff line numberDiff line change
@@ -20,44 +20,24 @@
2020
#include "qgsgeometry.h"
2121
#include "qgsvectorlayer.h"
2222
#include "qgsvectordataprovider.h"
23+
#include "qgsvectorlayerutils.h"
2324

2425
#include <QMutexLocker>
2526

26-
QgsFeaturePool::QgsFeaturePool( QgsVectorLayer *layer, double layerToMapUnits, const QgsCoordinateTransform &layerToMapTransform, bool selectedOnly )
27+
QgsFeaturePool::QgsFeaturePool( QgsVectorLayer *layer, double layerToMapUnits, const QgsCoordinateTransform &layerToMapTransform )
2728
: mFeatureCache( CACHE_SIZE )
2829
, mLayer( layer )
2930
, mLayerToMapUnits( layerToMapUnits )
3031
, mLayerToMapTransform( layerToMapTransform )
31-
, mSelectedOnly( selectedOnly )
32+
, mLayerId( layer->id() )
33+
, mGeometryType( layer->geometryType() )
3234
{
33-
// Build spatial index
34-
QgsFeature feature;
35-
QgsFeatureRequest req;
36-
req.setSubsetOfAttributes( QgsAttributeList() );
37-
if ( selectedOnly )
38-
{
39-
mFeatureIds = layer->selectedFeatureIds();
40-
req.setFilterFids( mFeatureIds );
41-
}
4235

43-
QgsFeatureIterator it = layer->getFeatures( req );
44-
while ( it.nextFeature( feature ) )
45-
{
46-
if ( feature.geometry() )
47-
{
48-
mIndex.insertFeature( feature );
49-
mFeatureIds.insert( feature.id() );
50-
}
51-
else
52-
{
53-
mFeatureIds.remove( feature.id() );
54-
}
55-
}
5636
}
5737

5838
bool QgsFeaturePool::get( QgsFeatureId id, QgsFeature &feature )
5939
{
60-
QMutexLocker lock( &mLayerMutex );
40+
mCacheLock.lockForRead();
6141
QgsFeature *cachedFeature = mFeatureCache.object( id );
6242
if ( cachedFeature )
6343
{
@@ -66,78 +46,83 @@ bool QgsFeaturePool::get( QgsFeatureId id, QgsFeature &feature )
6646
}
6747
else
6848
{
49+
std::unique_ptr<QgsVectorLayerFeatureSource> source = QgsVectorLayerUtils::getFeatureSource( mLayer );
50+
6951
// Feature not in cache, retrieve from layer
7052
// TODO: avoid always querying all attributes (attribute values are needed when merging by attribute)
71-
if ( !mLayer->getFeatures( QgsFeatureRequest( id ) ).nextFeature( feature ) )
53+
if ( !source->getFeatures( QgsFeatureRequest( id ) ).nextFeature( feature ) )
7254
{
7355
return false;
7456
}
57+
mCacheLock.unlock();
58+
mCacheLock.lockForWrite();
7559
mFeatureCache.insert( id, new QgsFeature( feature ) );
7660
}
61+
mCacheLock.unlock();
7762
return true;
7863
}
7964

80-
void QgsFeaturePool::addFeature( QgsFeature &feature )
65+
QgsFeatureIds QgsFeaturePool::getFeatureIds() const
8166
{
82-
QgsFeatureList features;
83-
features.append( feature );
84-
mLayerMutex.lock();
85-
mLayer->dataProvider()->addFeatures( features );
86-
feature.setId( features.front().id() );
87-
if ( mSelectedOnly )
88-
{
89-
QgsFeatureIds selectedFeatureIds = mLayer->selectedFeatureIds();
90-
selectedFeatureIds.insert( feature.id() );
91-
mLayer->selectByIds( selectedFeatureIds );
92-
}
93-
mLayerMutex.unlock();
94-
mIndexMutex.lock();
95-
mIndex.insertFeature( feature );
96-
mIndexMutex.unlock();
67+
return mFeatureIds;
9768
}
9869

99-
void QgsFeaturePool::updateFeature( QgsFeature &feature )
70+
QgsFeatureIds QgsFeaturePool::getIntersects( const QgsRectangle &rect ) const
10071
{
101-
QgsFeature origFeature;
102-
get( feature.id(), origFeature );
72+
mIndexLock.lockForRead();
73+
QgsFeatureIds ids = QgsFeatureIds::fromList( mIndex.intersects( rect ) );
74+
mIndexLock.unlock();
75+
return ids;
76+
}
10377

104-
QgsGeometryMap geometryMap;
105-
geometryMap.insert( feature.id(), feature.geometry() );
106-
QgsChangedAttributesMap changedAttributesMap;
107-
QgsAttributeMap attribMap;
108-
for ( int i = 0, n = feature.attributes().size(); i < n; ++i )
109-
{
110-
attribMap.insert( i, feature.attributes().at( i ) );
111-
}
112-
changedAttributesMap.insert( feature.id(), attribMap );
113-
mLayerMutex.lock();
114-
mFeatureCache.remove( feature.id() ); // Remove to force reload on next get()
115-
mLayer->dataProvider()->changeGeometryValues( geometryMap );
116-
mLayer->dataProvider()->changeAttributeValues( changedAttributesMap );
117-
mLayerMutex.unlock();
118-
mIndexMutex.lock();
119-
mIndex.deleteFeature( origFeature );
78+
QgsVectorLayer *QgsFeaturePool::layer() const
79+
{
80+
Q_ASSERT( QThread::currentThread() == qApp->thread() );
81+
82+
return mLayer.data();
83+
}
84+
85+
void QgsFeaturePool::insertFeature( const QgsFeature &feature )
86+
{
87+
mCacheLock.lockForWrite();
88+
mFeatureCache.insert( feature.id(), new QgsFeature( feature ) );
89+
mIndex.insertFeature( feature );
90+
mCacheLock.unlock();
91+
}
92+
93+
void QgsFeaturePool::changeFeature( const QgsFeature &feature )
94+
{
95+
mCacheLock.lockForWrite();
96+
mFeatureCache.remove( feature.id() );
97+
mFeatureCache.insert( feature.id(), new QgsFeature( feature ) );
98+
mIndex.deleteFeature( feature );
12099
mIndex.insertFeature( feature );
121-
mIndexMutex.unlock();
100+
mCacheLock.unlock();
122101
}
123102

124-
void QgsFeaturePool::deleteFeature( QgsFeatureId fid )
103+
void QgsFeaturePool::removeFeature( const QgsFeatureId featureId )
125104
{
126105
QgsFeature origFeature;
127-
if ( get( fid, origFeature ) )
106+
mCacheLock.lockForWrite();
107+
if ( get( featureId, origFeature ) )
128108
{
129-
mIndexMutex.lock();
130109
mIndex.deleteFeature( origFeature );
131-
mIndexMutex.unlock();
132110
}
133-
mLayerMutex.lock();
134111
mFeatureCache.remove( origFeature.id() );
135-
mLayer->dataProvider()->deleteFeatures( QgsFeatureIds() << fid );
136-
mLayerMutex.unlock();
112+
mCacheLock.unlock();
137113
}
138114

139-
QgsFeatureIds QgsFeaturePool::getIntersects( const QgsRectangle &rect ) const
115+
void QgsFeaturePool::setFeatureIds( const QgsFeatureIds &ids )
116+
{
117+
mFeatureIds = ids;
118+
}
119+
120+
QgsWkbTypes::GeometryType QgsFeaturePool::geometryType() const
121+
{
122+
return mGeometryType;
123+
}
124+
125+
QString QgsFeaturePool::layerId() const
140126
{
141-
QMutexLocker lock( &mIndexMutex );
142-
return QgsFeatureIds::fromList( mIndex.intersects( rect ) );
127+
return mLayerId;
143128
}

src/analysis/vector/geometry_checker/qgsfeaturepool.h

+100-13
Original file line numberDiff line numberDiff line change
@@ -24,39 +24,126 @@
2424
#include "qgis_analysis.h"
2525
#include "qgsfeature.h"
2626
#include "qgsspatialindex.h"
27+
#include "qgsvectorlayer.h"
2728

2829
class QgsVectorLayer;
2930

31+
/**
32+
* \ingroup analysis
33+
* A feature pool is based on a vector layer and caches features.
34+
*/
3035
class ANALYSIS_EXPORT QgsFeaturePool
3136
{
3237

3338
public:
34-
QgsFeaturePool( QgsVectorLayer *layer, double layerToMapUnits, const QgsCoordinateTransform &layerToMapTransform, bool selectedOnly = false );
39+
QgsFeaturePool( QgsVectorLayer *layer, double layerToMapUnits, const QgsCoordinateTransform &layerToMapTransform );
40+
virtual ~QgsFeaturePool() = default;
41+
42+
/**
43+
* Retrieve the feature with the specified \a id into \a feature.
44+
* It will be retrieved from the cache or from the underlying layer if unavailable.
45+
* If the feature is neither available from the cache nor from the layer it will return false.
46+
*/
3547
bool get( QgsFeatureId id, QgsFeature &feature );
36-
void addFeature( QgsFeature &feature );
37-
void updateFeature( QgsFeature &feature );
38-
void deleteFeature( QgsFeatureId fid );
48+
49+
/**
50+
* Adds a feature to this pool.
51+
* Implementations will add the feature to the layer or to the data provider.
52+
*/
53+
virtual void addFeature( QgsFeature &feature ) = 0;
54+
55+
/**
56+
* Updates a feature in this pool.
57+
* Implementations will update the feature on the layer or on the data provider.
58+
*/
59+
virtual void updateFeature( QgsFeature &feature ) = 0;
60+
61+
/**
62+
* Removes a feature from this pool.
63+
* Implementations will remove the feature from the layer or from the data provider.
64+
*/
65+
virtual void deleteFeature( QgsFeatureId fid ) = 0;
66+
67+
/**
68+
* Returns the complete set of feature ids in this pool.
69+
* Note that this concerns the features governed by this pool, which are not necessarily all cached.
70+
*/
71+
QgsFeatureIds getFeatureIds() const;
72+
73+
/**
74+
* Get all feature ids in the bounding box \a rect. It will use a spatial index to
75+
* determine the ids.
76+
*/
3977
QgsFeatureIds getIntersects( const QgsRectangle &rect ) const;
40-
QgsVectorLayer *getLayer() const { return mLayer; }
41-
const QgsFeatureIds &getFeatureIds() const { return mFeatureIds; }
78+
79+
/**
80+
* The factor of layer units to map units.
81+
* TODO: should this be removed and determined on runtime by checks that need it?
82+
*/
4283
double getLayerToMapUnits() const { return mLayerToMapUnits; }
84+
85+
/**
86+
* A coordinate transform from layer to map CRS.
87+
* TODO: should this be removed and determined on runtime by checks that need it?
88+
*/
4389
const QgsCoordinateTransform &getLayerToMapTransform() const { return mLayerToMapTransform; }
4490

45-
void clearLayer() { mLayer = nullptr; }
91+
/**
92+
* Get a pointer to the underlying layer.
93+
* May return a ``nullptr`` if the layer has been deleted.
94+
* This must only be called from the main thread.
95+
*/
96+
QgsVectorLayer *layer() const;
4697

47-
private:
98+
/**
99+
* The layer id of the layer.
100+
*/
101+
QString layerId() const;
48102

49-
static const int CACHE_SIZE = 1000;
103+
/**
104+
* The geometry type of this layer.
105+
*/
106+
QgsWkbTypes::GeometryType geometryType() const;
107+
108+
protected:
109+
110+
/**
111+
* Inserts a feature into the cache and the spatial index.
112+
* To be used by implementations of ``addFeature``.
113+
*/
114+
void insertFeature( const QgsFeature &feature );
115+
116+
/**
117+
* Changes a feature in the cache and the spatial index.
118+
* To be used by implementations of ``updateFeature``.
119+
*/
120+
void changeFeature( const QgsFeature &feature );
50121

122+
/**
123+
* Removes a feature from the cache and the spatial index.
124+
* To be used by implementations of ``deleteFeature``.
125+
*/
126+
void removeFeature( const QgsFeatureId featureId );
127+
128+
/**
129+
* Set all the feature ids governed by this feature pool.
130+
* Should be called by subclasses constructor and whenever
131+
* they insert a new feature.
132+
*/
133+
void setFeatureIds( const QgsFeatureIds &ids );
134+
135+
private:
136+
static const int CACHE_SIZE = 1000;
51137
QCache<QgsFeatureId, QgsFeature> mFeatureCache;
52-
QgsVectorLayer *mLayer = nullptr;
138+
QPointer<QgsVectorLayer> mLayer;
139+
QReadWriteLock mCacheLock;
53140
QgsFeatureIds mFeatureIds;
54-
QMutex mLayerMutex;
55-
mutable QMutex mIndexMutex;
141+
mutable QReadWriteLock mIndexLock;
56142
QgsSpatialIndex mIndex;
57143
double mLayerToMapUnits = 1.0;
58144
QgsCoordinateTransform mLayerToMapTransform;
59-
bool mSelectedOnly = false;
145+
QString mLayerId;
146+
QgsWkbTypes::GeometryType mGeometryType;
60147
};
61148

62149
#endif // QGS_FEATUREPOOL_H

src/analysis/vector/geometry_checker/qgsgeometrycheck.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ QMap<QString, QgsFeatureIds> QgsGeometryCheck::allLayerFeatureIds() const
149149
QMap<QString, QgsFeatureIds> featureIds;
150150
for ( QgsFeaturePool *pool : mContext->featurePools )
151151
{
152-
featureIds.insert( pool->getLayer()->id(), pool->getFeatureIds() );
152+
featureIds.insert( pool->layerId(), pool->getFeatureIds() );
153153
}
154154
return featureIds;
155155
}

0 commit comments

Comments
 (0)