@@ -21,10 +21,18 @@ email : matthias@opengis.ch
21
21
#include " qgsanalysis.h"
22
22
#include " qgsgeometrycheckregistry.h"
23
23
#include " qgsgeometrycheckfactory.h"
24
+ #include " qgsvectorlayereditbuffer.h"
25
+ #include " qgsvectorlayerfeaturepool.h"
26
+ #include " qgsfeedback.h"
27
+ #include " qgsreadwritelocker.h"
28
+
29
+ #include < QtConcurrent>
30
+ #include < QFutureWatcher>
24
31
25
32
QgsGeometryValidationService::QgsGeometryValidationService ( QgsProject *project )
26
33
: mProject( project )
27
34
{
35
+ qRegisterMetaType< QList<std::shared_ptr<QgsGeometryCheckError> > >( " QList<std::shared_ptr<QgsGeometryCheckError>>" );
28
36
connect ( project, &QgsProject::layersAdded, this , &QgsGeometryValidationService::onLayersAdded );
29
37
}
30
38
@@ -64,11 +72,21 @@ void QgsGeometryValidationService::onLayersAdded( const QList<QgsMapLayer *> &la
64
72
65
73
void QgsGeometryValidationService::onFeatureAdded ( QgsVectorLayer *layer, QgsFeatureId fid )
66
74
{
75
+ if ( !mLayerCheckStates [layer].topologyChecks .empty () )
76
+ {
77
+ // TODO: Cancel topology checks
78
+ layer->setAllowCommit ( false );
79
+ }
67
80
processFeature ( layer, fid );
68
81
}
69
82
70
83
void QgsGeometryValidationService::onGeometryChanged ( QgsVectorLayer *layer, QgsFeatureId fid, const QgsGeometry &geometry )
71
84
{
85
+ if ( !mLayerCheckStates [layer].topologyChecks .empty () )
86
+ {
87
+ // TODO: Cancel topology checks
88
+ layer->setAllowCommit ( false );
89
+ }
72
90
Q_UNUSED ( geometry )
73
91
74
92
cancelChecks ( layer, fid );
@@ -77,21 +95,32 @@ void QgsGeometryValidationService::onGeometryChanged( QgsVectorLayer *layer, Qgs
77
95
78
96
void QgsGeometryValidationService::onFeatureDeleted ( QgsVectorLayer *layer, QgsFeatureId fid )
79
97
{
98
+ if ( !mLayerCheckStates [layer].topologyChecks .empty () )
99
+ {
100
+ // TODO: Cancel topology checks
101
+ layer->setAllowCommit ( false );
102
+ }
103
+
80
104
cancelChecks ( layer, fid );
81
105
}
82
106
83
107
void QgsGeometryValidationService::onBeforeCommitChanges ( QgsVectorLayer *layer )
84
108
{
85
- if ( !mTopologyChecksOk . value ( layer ) )
109
+ if ( !mLayerCheckStates [layer]. topologyChecks . empty ( ) ) // TODO && topologyChecks not fulfilled
86
110
{
111
+ if ( !layer->allowCommit () )
112
+ {
113
+ emit warning ( tr ( " Can not save yet, we'll need to run some topology checks on your dataset first..." ) );
114
+ }
87
115
triggerTopologyChecks ( layer );
88
116
}
89
117
}
90
118
91
119
void QgsGeometryValidationService::enableLayerChecks ( QgsVectorLayer *layer )
92
120
{
93
121
// TODO: finish all ongoing checks
94
- qDeleteAll ( mSingleFeatureChecks .value ( layer ) );
122
+ qDeleteAll ( mLayerCheckStates [layer].singleFeatureChecks );
123
+ qDeleteAll ( mLayerCheckStates [layer].topologyChecks );
95
124
96
125
// TODO: ownership and lifetime of the context!!
97
126
auto context = new QgsGeometryCheckContext ( 1 , mProject ->crs (), mProject ->transformContext () );
@@ -120,16 +149,23 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer )
120
149
singleGeometryChecks.append ( dynamic_cast <QgsSingleGeometryCheck *>( check ) );
121
150
}
122
151
123
- mSingleFeatureChecks . insert ( layer, singleGeometryChecks ) ;
152
+ mLayerCheckStates [layer]. singleFeatureChecks = singleGeometryChecks;
124
153
125
- #if 0
154
+ // Topology checks
155
+ QList<QgsGeometryCheck *> topologyChecks;
126
156
const QList<QgsGeometryCheckFactory *> topologyCheckFactories = checkRegistry->geometryCheckFactories ( layer, QgsGeometryCheck::SingleLayerTopologyCheck );
127
157
128
- for ( const QString &check : activeChecks )
158
+ for ( QgsGeometryCheckFactory *factory : topologyCheckFactories )
129
159
{
130
- checkRegistry->geometryCheckFactories( layer, QgsGeometryCheck::SingleLayerTopologyCheck );
160
+ const QString checkId = factory->id ();
161
+ if ( activeChecks.contains ( checkId ) )
162
+ {
163
+ const QVariantMap checkConfiguration = layer->geometryOptions ()->checkConfiguration ( checkId );
164
+ topologyChecks.append ( factory->createGeometryCheck ( context, checkConfiguration ) );
165
+ }
131
166
}
132
- #endif
167
+
168
+ mLayerCheckStates [layer].topologyChecks = topologyChecks;
133
169
}
134
170
135
171
void QgsGeometryValidationService::cancelChecks ( QgsVectorLayer *layer, QgsFeatureId fid )
@@ -143,7 +179,7 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea
143
179
144
180
QgsFeature feature = layer->getFeature ( fid );
145
181
146
- const auto &checks = mSingleFeatureChecks . value ( layer ) ;
182
+ const auto &checks = mLayerCheckStates [ layer]. singleFeatureChecks ;
147
183
148
184
// The errors are going to be sent out via a signal. We cannot keep ownership in here (or can we?)
149
185
// nor can we be sure that a single slot is connected to the signal. So make it a shared_ptr.
@@ -161,5 +197,58 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea
161
197
162
198
void QgsGeometryValidationService::triggerTopologyChecks ( QgsVectorLayer *layer )
163
199
{
200
+ QFutureWatcher<void > *futureWatcher = mLayerCheckStates [layer].topologyCheckFutureWatcher ;
201
+ if ( futureWatcher )
202
+ {
203
+ // TODO: kill!!
204
+ delete futureWatcher;
205
+ }
206
+
207
+ QgsFeatureIds checkFeatureIds = layer->editBuffer ()->changedGeometries ().keys ().toSet ();
208
+ checkFeatureIds.unite ( layer->editBuffer ()->addedFeatures ().keys ().toSet () );
209
+
210
+ // TODO: ownership of these objects...
211
+ QgsVectorLayerFeaturePool *featurePool = new QgsVectorLayerFeaturePool ( layer );
212
+ QList<QgsGeometryCheckError *> &allErrors = mLayerCheckStates [layer].topologyCheckErrors ;
213
+ QgsFeedback *feedback = new QgsFeedback ();
214
+ QMap<QString, QgsFeatureIds> layerIds;
215
+ layerIds.insert ( layer->id (), checkFeatureIds );
216
+ QgsGeometryCheck::LayerFeatureIds layerFeatureIds ( layerIds );
217
+
218
+ QMap<QString, QgsFeaturePool *> featurePools;
219
+ featurePools.insert ( layer->id (), featurePool );
220
+
221
+ const QList<QgsGeometryCheck *> checks = mLayerCheckStates [layer].topologyChecks ;
222
+
223
+ QFuture<void > future = QtConcurrent::map ( checks, [featurePools, &allErrors, feedback, layerFeatureIds, layer, this ]( const QgsGeometryCheck * check )
224
+ {
225
+ // Watch out with the layer pointer in here. We are running in a thread, so we do not want to actually use it
226
+ // except for using its address to report the error.
227
+ QList<QgsGeometryCheckError *> errors;
228
+ QStringList messages; // Do we really need these?
229
+
230
+ check->collectErrors ( featurePools, errors, messages, feedback, layerFeatureIds );
231
+ QgsReadWriteLocker errorLocker ( mTopologyCheckLock , QgsReadWriteLocker::Write );
232
+ allErrors.append ( errors );
233
+
234
+ QList<std::shared_ptr<QgsGeometryCheckError> > sharedErrors;
235
+ for ( QgsGeometryCheckError *error : errors )
236
+ {
237
+ sharedErrors.append ( std::shared_ptr<QgsGeometryCheckError>( error ) );
238
+ }
239
+ emit topologyChecksUpdated ( layer, sharedErrors );
240
+ errorLocker.unlock ();
241
+ } );
242
+
243
+ futureWatcher = new QFutureWatcher<void >();
244
+ futureWatcher->setFuture ( future );
245
+
246
+ connect ( futureWatcher, &QFutureWatcherBase::finished, this , [&allErrors, layer, this ]()
247
+ {
248
+ QgsReadWriteLocker errorLocker ( mTopologyCheckLock , QgsReadWriteLocker::Read );
249
+ layer->setAllowCommit ( allErrors.empty () );
250
+ errorLocker.unlock ();
251
+ } );
164
252
253
+ mLayerCheckStates [layer].topologyCheckFutureWatcher = futureWatcher;
165
254
}
0 commit comments