Skip to content

Commit cdc7d39

Browse files
committedSep 5, 2018
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 57 additions & 72 deletions
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

Lines changed: 100 additions & 13 deletions
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

Lines changed: 1 addition & 1 deletion
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
}

‎src/analysis/vector/geometry_checker/qgsgeometrychecker.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ QgsGeometryChecker::QgsGeometryChecker( const QList<QgsGeometryCheck *> &checks,
3232
{
3333
for ( auto it = mContext->featurePools.constBegin(); it != mContext->featurePools.constEnd(); ++it )
3434
{
35-
if ( it.value()->getLayer() )
35+
if ( it.value()->layer() )
3636
{
37-
it.value()->getLayer()->setReadOnly( true );
37+
it.value()->layer()->setReadOnly( true );
3838
// Enter update mode to defer ogr dataset repacking until the checker has finished
39-
it.value()->getLayer()->dataProvider()->enterUpdateMode();
39+
it.value()->layer()->dataProvider()->enterUpdateMode();
4040
}
4141
}
4242
}
@@ -47,10 +47,10 @@ QgsGeometryChecker::~QgsGeometryChecker()
4747
qDeleteAll( mChecks );
4848
for ( auto it = mContext->featurePools.constBegin(); it != mContext->featurePools.constEnd(); ++it )
4949
{
50-
if ( it.value()->getLayer() )
50+
if ( it.value()->layer() )
5151
{
52-
it.value()->getLayer()->dataProvider()->leaveUpdateMode();
53-
it.value()->getLayer()->setReadOnly( false );
52+
it.value()->layer()->dataProvider()->leaveUpdateMode();
53+
it.value()->layer()->setReadOnly( false );
5454
}
5555
delete it.value();
5656
}
@@ -68,7 +68,7 @@ QFuture<void> QgsGeometryChecker::execute( int *totalSteps )
6868
{
6969
if ( check->checkType() <= QgsGeometryCheck::FeatureCheck )
7070
{
71-
*totalSteps += check->isCompatible( it.value()->getLayer()->geometryType() ) ? it.value()->getFeatureIds().size() : 0;
71+
*totalSteps += check->isCompatible( it.value()->layer()->geometryType() ) ? it.value()->getFeatureIds().size() : 0;
7272
}
7373
else
7474
{
@@ -145,7 +145,7 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, boo
145145
{
146146
const QMap<QgsFeatureId, QList<QgsGeometryCheck::Change>> &layerChanges = it.value();
147147
QgsFeaturePool *featurePool = mContext->featurePools[it.key()];
148-
QgsCoordinateTransform t( featurePool->getLayer()->crs(), mContext->mapCrs, QgsProject::instance() );
148+
QgsCoordinateTransform t( featurePool->layer()->crs(), mContext->mapCrs, QgsProject::instance() );
149149
for ( auto layerChangeIt = layerChanges.constBegin(); layerChangeIt != layerChanges.constEnd(); ++layerChangeIt )
150150
{
151151
bool removed = false;
@@ -184,7 +184,7 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, boo
184184
for ( const QString &layerId : mContext->featurePools.keys() )
185185
{
186186
QgsFeaturePool *featurePool = mContext->featurePools[layerId];
187-
QgsCoordinateTransform t( mContext->mapCrs, featurePool->getLayer()->crs(), QgsProject::instance() );
187+
QgsCoordinateTransform t( mContext->mapCrs, featurePool->layer()->crs(), QgsProject::instance() );
188188
recheckAreaFeatures[layerId] = featurePool->getIntersects( t.transform( recheckArea ) );
189189
}
190190

@@ -269,7 +269,7 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, boo
269269
{
270270
for ( const QString &layerId : changes.keys() )
271271
{
272-
mContext->featurePools[layerId]->getLayer()->triggerRepaint();
272+
mContext->featurePools[layerId]->layer()->triggerRepaint();
273273
}
274274
}
275275

‎src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ namespace QgsGeometryCheckerUtils
4343
{
4444
delete mGeometry;
4545
}
46-
const QgsVectorLayer &LayerFeature::layer() const { return *mFeaturePool->getLayer(); }
46+
const QgsVectorLayer &LayerFeature::layer() const { return *mFeaturePool->layer(); }
4747
double LayerFeature::layerToMapUnits() const { return mFeaturePool->getLayerToMapUnits(); }
4848
const QgsCoordinateTransform &LayerFeature::layerToMapTransform() const { return mFeaturePool->getLayerToMapTransform(); }
4949

@@ -114,7 +114,7 @@ namespace QgsGeometryCheckerUtils
114114
{
115115
break;
116116
}
117-
if ( mParent->mGeometryTypes.contains( mParent->mFeaturePools[*mLayerIt]->getLayer()->geometryType() ) )
117+
if ( mParent->mGeometryTypes.contains( mParent->mFeaturePools[*mLayerIt]->geometryType() ) )
118118
{
119119
mFeatureIt = mParent->mFeatureIds[*mLayerIt].constBegin();
120120
return true;
@@ -178,7 +178,7 @@ namespace QgsGeometryCheckerUtils
178178
for ( const QString &layerId : layerIds )
179179
{
180180
const QgsFeaturePool *featurePool = featurePools[layerId];
181-
if ( geometryTypes.contains( featurePool->getLayer()->geometryType() ) )
181+
if ( geometryTypes.contains( featurePool->layer()->geometryType() ) )
182182
{
183183
mFeatureIds.insert( layerId, featurePool->getIntersects( featurePool->getLayerToMapTransform().transform( extent, QgsCoordinateTransform::ReverseTransform ) ) );
184184
}

‎src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ QString QgsGeometryDuplicateCheckError::duplicatesString( const QMap<QString, Qg
2525
QStringList str;
2626
for ( auto it = duplicates.constBegin(); it != duplicates.constEnd(); ++it )
2727
{
28-
str.append( featurePools[it.key()]->getLayer()->name() + ":" );
28+
str.append( featurePools[it.key()]->layer()->name() + ":" );
2929
QStringList ids;
3030
for ( QgsFeatureId id : it.value() )
3131
{
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/***************************************************************************
2+
qgsvectordataproviderfeaturepool.h
3+
--------------------------------------
4+
Date : 3.9.2018
5+
Copyright : (C) 2018 by Matthias Kuhn
6+
email : matthias@opengis.ch
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 "qgsvectordataproviderfeaturepool.h"
17+
18+
#include "qgsfeaturerequest.h"
19+
20+
template <typename Func>
21+
void runOnMainThread( const Func &func )
22+
{
23+
#if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
24+
// Make sure we only deal with the vector layer on the main thread where it lives.
25+
// Anything else risks a crash.
26+
if ( QThread::currentThread() == qApp->thread() )
27+
func();
28+
else
29+
QMetaObject::invokeMethod( qApp, func, Qt::BlockingQueuedConnection );
30+
#else
31+
func();
32+
#endif
33+
}
34+
35+
QgsVectorDataProviderFeaturePool::QgsVectorDataProviderFeaturePool( QgsVectorLayer *layer, double layerToMapUnits, const QgsCoordinateTransform &layerToMapTransform, bool selectedOnly )
36+
: QgsFeaturePool( layer, layerToMapUnits, layerToMapTransform )
37+
, mSelectedOnly( selectedOnly )
38+
{
39+
// Build spatial index
40+
QgsFeature feature;
41+
QgsFeatureRequest req;
42+
req.setSubsetOfAttributes( QgsAttributeList() );
43+
QgsFeatureIds featureIds;
44+
if ( selectedOnly )
45+
{
46+
featureIds = layer->selectedFeatureIds();
47+
req.setFilterFids( featureIds );
48+
}
49+
50+
QgsFeatureIterator it = layer->getFeatures( req );
51+
while ( it.nextFeature( feature ) )
52+
{
53+
if ( feature.geometry() )
54+
{
55+
insertFeature( feature );
56+
featureIds.insert( feature.id() );
57+
}
58+
else
59+
{
60+
featureIds.remove( feature.id() );
61+
}
62+
}
63+
setFeatureIds( featureIds );
64+
}
65+
66+
void QgsVectorDataProviderFeaturePool::addFeature( QgsFeature &feature )
67+
{
68+
QgsFeatureList features;
69+
features.append( feature );
70+
71+
auto addFeatureSynchronized = [ this, &features ]()
72+
{
73+
QgsVectorLayer *lyr = layer();
74+
if ( lyr )
75+
lyr->dataProvider()->addFeatures( features );
76+
};
77+
78+
runOnMainThread( addFeatureSynchronized );
79+
80+
feature.setId( features.front().id() );
81+
if ( mSelectedOnly )
82+
{
83+
runOnMainThread( [ this, feature ]()
84+
{
85+
QgsVectorLayer *lyr = layer();
86+
if ( lyr )
87+
{
88+
QgsFeatureIds selectedFeatureIds = lyr->selectedFeatureIds();
89+
selectedFeatureIds.insert( feature.id() );
90+
lyr->selectByIds( selectedFeatureIds );
91+
}
92+
} );
93+
}
94+
95+
insertFeature( feature );
96+
}
97+
98+
void QgsVectorDataProviderFeaturePool::updateFeature( QgsFeature &feature )
99+
{
100+
QgsFeature origFeature;
101+
get( feature.id(), origFeature );
102+
103+
QgsGeometryMap geometryMap;
104+
geometryMap.insert( feature.id(), feature.geometry() );
105+
QgsChangedAttributesMap changedAttributesMap;
106+
QgsAttributeMap attribMap;
107+
for ( int i = 0, n = feature.attributes().size(); i < n; ++i )
108+
{
109+
attribMap.insert( i, feature.attributes().at( i ) );
110+
}
111+
changedAttributesMap.insert( feature.id(), attribMap );
112+
113+
removeFeature( origFeature.id() );
114+
runOnMainThread( [this, geometryMap, changedAttributesMap]()
115+
{
116+
QgsVectorLayer *lyr = layer();
117+
if ( lyr )
118+
{
119+
lyr->dataProvider()->changeGeometryValues( geometryMap );
120+
lyr->dataProvider()->changeAttributeValues( changedAttributesMap );
121+
}
122+
} );
123+
124+
insertFeature( feature );
125+
}
126+
127+
void QgsVectorDataProviderFeaturePool::deleteFeature( QgsFeatureId fid )
128+
{
129+
removeFeature( fid );
130+
runOnMainThread( [this, fid]()
131+
{
132+
QgsVectorLayer *lyr = layer();
133+
if ( lyr )
134+
{
135+
lyr->dataProvider()->deleteFeatures( QgsFeatureIds() << fid );
136+
}
137+
} );
138+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/***************************************************************************
2+
qgsvectordataproviderfeaturepool.h
3+
--------------------------------------
4+
Date : 3.9.2018
5+
Copyright : (C) 2018 by Matthias Kuhn
6+
email : matthias@opengis.ch
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+
#ifndef QGSVECTORDATAPROVIDERFEATUREPOOL_H
17+
#define QGSVECTORDATAPROVIDERFEATUREPOOL_H
18+
19+
#include "qgsfeaturepool.h"
20+
#include "qgsvectorlayer.h"
21+
22+
#define SIP_NO_FILE
23+
24+
/**
25+
* \ingroup analysis
26+
* A feature pool based on a vector data provider.
27+
*
28+
* \since QGIS 3.4
29+
*/
30+
class ANALYSIS_EXPORT QgsVectorDataProviderFeaturePool : public QgsFeaturePool
31+
{
32+
public:
33+
QgsVectorDataProviderFeaturePool( QgsVectorLayer *layer, double layerToMapUnits, const QgsCoordinateTransform &layerToMapTransform, bool selectedOnly = false );
34+
35+
void addFeature( QgsFeature &feature );
36+
void updateFeature( QgsFeature &feature );
37+
void deleteFeature( QgsFeatureId fid );
38+
39+
private:
40+
bool mSelectedOnly = false;
41+
};
42+
43+
#endif // QGSVECTORDATAPROVIDERFEATUREPOOL_H

‎src/plugins/geometry_checker/qgsgeometrycheckerfixsummarydialog.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ void QgsGeometryCheckerFixSummaryDialog::addError( QTableWidget *table, QgsGeome
7575

7676
int row = table->rowCount();
7777
table->insertRow( row );
78-
table->setItem( row, 0, new QTableWidgetItem( !error->layerId().isEmpty() ? mChecker->getContext()->featurePools[error->layerId()]->getLayer()->name() : "" ) );
78+
table->setItem( row, 0, new QTableWidgetItem( !error->layerId().isEmpty() ? mChecker->getContext()->featurePools[error->layerId()]->layer()->name() : "" ) );
7979
QTableWidgetItem *idItem = new QTableWidgetItem();
8080
idItem->setData( Qt::EditRole, error->featureId() != FEATUREID_NULL ? QVariant( error->featureId() ) : QVariant() );
8181
table->setItem( row, 1, idItem );

‎src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ QgsGeometryCheckerResultTab::QgsGeometryCheckerResultTab( QgisInterface *iface,
5757

5858
for ( const QString &layerId : mChecker->getContext()->featurePools.keys() )
5959
{
60-
QgsVectorLayer *layer = mChecker->getContext()->featurePools[layerId]->getLayer();
60+
QgsVectorLayer *layer = mChecker->getContext()->featurePools[layerId]->layer();
6161
QTreeWidgetItem *item = new QTreeWidgetItem( ui.treeWidgetMergeAttribute, QStringList() << layer->name() << "" );
6262
QComboBox *attribCombo = new QComboBox();
6363
for ( int i = 0, n = layer->fields().count(); i < n; ++i )
@@ -85,7 +85,7 @@ QgsGeometryCheckerResultTab::QgsGeometryCheckerResultTab( QgisInterface *iface,
8585
bool allLayersEditable = true;
8686
for ( const QgsFeaturePool *featurePool : mChecker->getContext()->featurePools.values() )
8787
{
88-
if ( ( featurePool->getLayer()->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeGeometries ) == 0 )
88+
if ( ( featurePool->layer()->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeGeometries ) == 0 )
8989
{
9090
allLayersEditable = false;
9191
break;
@@ -148,7 +148,7 @@ void QgsGeometryCheckerResultTab::addError( QgsGeometryCheckError *error )
148148
ui.tableWidgetErrors->insertRow( row );
149149
QTableWidgetItem *idItem = new QTableWidgetItem();
150150
idItem->setData( Qt::EditRole, error->featureId() != FEATUREID_NULL ? QVariant( error->featureId() ) : QVariant() );
151-
ui.tableWidgetErrors->setItem( row, 0, new QTableWidgetItem( !error->layerId().isEmpty() ? mChecker->getContext()->featurePools[error->layerId()]->getLayer()->name() : "" ) );
151+
ui.tableWidgetErrors->setItem( row, 0, new QTableWidgetItem( !error->layerId().isEmpty() ? mChecker->getContext()->featurePools[error->layerId()]->layer()->name() : "" ) );
152152
ui.tableWidgetErrors->setItem( row, 1, idItem );
153153
ui.tableWidgetErrors->setItem( row, 2, new QTableWidgetItem( error->description() ) );
154154
ui.tableWidgetErrors->setItem( row, 3, new QTableWidgetItem( posStr ) );
@@ -221,7 +221,7 @@ void QgsGeometryCheckerResultTab::updateError( QgsGeometryCheckError *error, boo
221221
void QgsGeometryCheckerResultTab::exportErrors()
222222
{
223223
QString initialdir;
224-
QDir dir = QFileInfo( mChecker->getContext()->featurePools.first()->getLayer()->dataProvider()->dataSourceUri() ).dir();
224+
QDir dir = QFileInfo( mChecker->getContext()->featurePools.first()->layer()->dataProvider()->dataSourceUri() ).dir();
225225
if ( dir.exists() )
226226
{
227227
initialdir = dir.absolutePath();
@@ -280,7 +280,7 @@ bool QgsGeometryCheckerResultTab::exportErrorsDo( const QString &file )
280280
for ( int row = 0, nRows = ui.tableWidgetErrors->rowCount(); row < nRows; ++row )
281281
{
282282
QgsGeometryCheckError *error = ui.tableWidgetErrors->item( row, 0 )->data( Qt::UserRole ).value<QgsGeometryCheckError *>();
283-
QgsVectorLayer *srcLayer = mChecker->getContext()->featurePools[error->layerId()]->getLayer();
283+
QgsVectorLayer *srcLayer = mChecker->getContext()->featurePools[error->layerId()]->layer();
284284
QgsFeature f( layer->fields() );
285285
f.setAttribute( fieldLayer, srcLayer->name() );
286286
f.setAttribute( fieldFeatureId, error->featureId() );
@@ -454,7 +454,7 @@ void QgsGeometryCheckerResultTab::openAttributeTable()
454454
{
455455
mAttribTableDialogs[layerId]->close();
456456
}
457-
mAttribTableDialogs[layerId] = mIface->showAttributeTable( mChecker->getContext()->featurePools[layerId]->getLayer(), expr.join( QStringLiteral( " or " ) ) );
457+
mAttribTableDialogs[layerId] = mIface->showAttributeTable( mChecker->getContext()->featurePools[layerId]->layer(), expr.join( QStringLiteral( " or " ) ) );
458458
}
459459
}
460460

@@ -528,7 +528,7 @@ void QgsGeometryCheckerResultTab::fixErrors( bool prompt )
528528
}
529529
for ( const QString &layerId : mChecker->getContext()->featurePools.keys() )
530530
{
531-
mChecker->getContext()->featurePools[layerId]->getLayer()->triggerRepaint();
531+
mChecker->getContext()->featurePools[layerId]->layer()->triggerRepaint();
532532
}
533533

534534
if ( mStatistics.itemCount() > 0 )
@@ -618,7 +618,6 @@ void QgsGeometryCheckerResultTab::checkRemovedLayer( const QStringList &ids )
618618
{
619619
if ( ids.contains( layerId ) )
620620
{
621-
mChecker->getContext()->featurePools[layerId]->clearLayer();
622621
if ( isEnabled() )
623622
requiredLayersRemoved = true;
624623
}

‎src/plugins/geometry_checker/qgsgeometrycheckersetuptab.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "qgsgeometrycheckfactory.h"
2222
#include "qgsgeometrycheck.h"
2323
#include "qgsfeaturepool.h"
24+
#include "qgsvectordataproviderfeaturepool.h"
2425

2526
#include "qgsfeatureiterator.h"
2627
#include "qgisinterface.h"
@@ -416,7 +417,7 @@ void QgsGeometryCheckerSetupTab::runChecks()
416417
{
417418
double layerToMapUntis = mIface->mapCanvas()->mapSettings().layerToMapUnits( layer );
418419
QgsCoordinateTransform layerToMapTransform( layer->crs(), QgsProject::instance()->crs(), QgsProject::instance() );
419-
featurePools.insert( layer->id(), new QgsFeaturePool( layer, layerToMapUntis, layerToMapTransform, selectedOnly ) );
420+
featurePools.insert( layer->id(), new QgsVectorDataProviderFeaturePool( layer, layerToMapUntis, layerToMapTransform, selectedOnly ) );
420421
}
421422
// LineLayerIntersection check is enabled, make sure there is also a feature pool for that layer
422423
if ( ui.checkLineLayerIntersection->isChecked() && !featurePools.keys().contains( ui.comboLineLayerIntersection->currentData().toString() ) )
@@ -425,7 +426,7 @@ void QgsGeometryCheckerSetupTab::runChecks()
425426
Q_ASSERT( layer );
426427
double layerToMapUntis = mIface->mapCanvas()->mapSettings().layerToMapUnits( layer );
427428
QgsCoordinateTransform layerToMapTransform( layer->crs(), QgsProject::instance()->crs(), QgsProject::instance() );
428-
featurePools.insert( layer->id(), new QgsFeaturePool( layer, layerToMapUntis, layerToMapTransform, selectedOnly ) );
429+
featurePools.insert( layer->id(), new QgsVectorDataProviderFeaturePool( layer, layerToMapUntis, layerToMapTransform, selectedOnly ) );
429430
}
430431

431432
QgsGeometryCheckerContext *context = new QgsGeometryCheckerContext( ui.spinBoxTolerance->value(), QgsProject::instance()->crs(), featurePools );

‎tests/src/geometry_checker/testqgsgeometrychecks.cpp

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "qgsgeometryselfcontactcheck.h"
3939
#include "qgsgeometryselfintersectioncheck.h"
4040
#include "qgsgeometrysliverpolygoncheck.h"
41+
#include "qgsvectordataproviderfeaturepool.h"
4142
#include "qgsproject.h"
4243

4344
#include "qgsgeometrytypecheck.h"
@@ -478,7 +479,7 @@ void TestQgsGeometryChecks::testFollowBoundariesCheck()
478479
QList<QgsGeometryCheckError *> checkErrors;
479480
QStringList messages;
480481

481-
QgsGeometryFollowBoundariesCheck( context, context->featurePools[layers["follow_ref.shp"]]->getLayer() ).collectErrors( checkErrors, messages );
482+
QgsGeometryFollowBoundariesCheck( context, context->featurePools[layers["follow_ref.shp"]]->layer() ).collectErrors( checkErrors, messages );
482483
listErrors( checkErrors, messages );
483484

484485
QCOMPARE( checkErrors.size(), 2 );
@@ -640,9 +641,9 @@ void TestQgsGeometryChecks::testMultipartCheck()
640641

641642
QVERIFY( searchCheckErrors( checkErrors, layers["point_layer.shp"] ).isEmpty() );
642643
// Easier to ensure that multipart features don't appear as errors than verifying each single-part multi-type feature
643-
QVERIFY( QgsWkbTypes::isSingleType( context->featurePools[layers["point_layer.shp"]]->getLayer()->wkbType() ) );
644-
QVERIFY( QgsWkbTypes::isMultiType( context->featurePools[layers["line_layer.shp"]]->getLayer()->wkbType() ) );
645-
QVERIFY( QgsWkbTypes::isMultiType( context->featurePools[layers["polygon_layer.shp"]]->getLayer()->wkbType() ) );
644+
QVERIFY( QgsWkbTypes::isSingleType( context->featurePools[layers["point_layer.shp"]]->layer()->wkbType() ) );
645+
QVERIFY( QgsWkbTypes::isMultiType( context->featurePools[layers["line_layer.shp"]]->layer()->wkbType() ) );
646+
QVERIFY( QgsWkbTypes::isMultiType( context->featurePools[layers["polygon_layer.shp"]]->layer()->wkbType() ) );
646647
QVERIFY( searchCheckErrors( checkErrors, layers["line_layer.shp"] ).size() > 0 );
647648
QVERIFY( searchCheckErrors( checkErrors, layers["polygon_layer.shp"] ).size() > 0 );
648649
QVERIFY( searchCheckErrors( checkErrors, layers["line_layer.shp"], 0 ).isEmpty() );
@@ -858,7 +859,7 @@ void TestQgsGeometryChecks::testSelfIntersectionCheck()
858859
// Test fixes
859860
QgsFeature f;
860861

861-
int nextId = context->featurePools[errs1[0]->layerId()]->getLayer()->featureCount();
862+
int nextId = context->featurePools[errs1[0]->layerId()]->layer()->featureCount();
862863
QVERIFY( fixCheckError( errs1[0],
863864
QgsGeometrySelfIntersectionCheck::ToSingleObjects, QgsGeometryCheckError::StatusFixed,
864865
{
@@ -885,7 +886,7 @@ void TestQgsGeometryChecks::testSelfIntersectionCheck()
885886
QCOMPARE( f.geometry().constGet()->vertexCount( 0 ), 4 );
886887
QCOMPARE( f.geometry().constGet()->vertexCount( 1 ), 5 );
887888

888-
nextId = context->featurePools[errs3[0]->layerId()]->getLayer()->featureCount();
889+
nextId = context->featurePools[errs3[0]->layerId()]->layer()->featureCount();
889890
QVERIFY( fixCheckError( errs3[0],
890891
QgsGeometrySelfIntersectionCheck::ToSingleObjects, QgsGeometryCheckError::StatusFixed,
891892
{
@@ -975,7 +976,7 @@ QgsFeaturePool *TestQgsGeometryChecks::createFeaturePool( QgsVectorLayer *layer,
975976
{
976977
double layerToMapUntis = layerToMapUnits( layer, mapCrs );
977978
QgsCoordinateTransform layerToMapTransform = QgsCoordinateTransform( layer->crs(), mapCrs, QgsProject::instance() );
978-
return new QgsFeaturePool( layer, layerToMapUntis, layerToMapTransform, selectedOnly );
979+
return new QgsVectorDataProviderFeaturePool( layer, layerToMapUntis, layerToMapTransform, selectedOnly );
979980
}
980981

981982
QgsGeometryCheckerContext *TestQgsGeometryChecks::createTestContext( QTemporaryDir &tempDir, QMap<QString, QString> &layers, const QgsCoordinateReferenceSystem &mapCrs, double prec ) const
@@ -1008,8 +1009,8 @@ void TestQgsGeometryChecks::cleanupTestContext( QgsGeometryCheckerContext *ctx )
10081009
{
10091010
for ( const QgsFeaturePool *pool : ctx->featurePools )
10101011
{
1011-
pool->getLayer()->dataProvider()->leaveUpdateMode();
1012-
delete pool->getLayer();
1012+
pool->layer()->dataProvider()->leaveUpdateMode();
1013+
delete pool->layer();
10131014
}
10141015
qDeleteAll( ctx->featurePools );
10151016
delete ctx;

0 commit comments

Comments
 (0)
Please sign in to comment.