Skip to content

Commit 4607930

Browse files
committed
Allow resolving errors
1 parent dc2c78f commit 4607930

15 files changed

+199
-17
lines changed

python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ It will be retrieved from the cache or from the underlying layer if unavailable.
3131
If the feature is neither available from the cache nor from the layer it will return false.
3232
%End
3333

34+
3435
virtual void updateFeature( QgsFeature &feature ) = 0;
3536
%Docstring
3637
Updates a feature in this pool.

src/analysis/vector/geometry_checker/qgsfeaturepool.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@ bool QgsFeaturePool::getFeature( QgsFeatureId id, QgsFeature &feature )
6262
return true;
6363
}
6464

65+
QgsFeatureIds QgsFeaturePool::getFeatures( const QgsFeatureRequest &request )
66+
{
67+
QgsFeatureIds fids;
68+
69+
std::unique_ptr<QgsVectorLayerFeatureSource> source = QgsVectorLayerUtils::getFeatureSource( mLayer );
70+
71+
QgsFeatureIterator it = source->getFeatures( request );
72+
QgsFeature feature;
73+
while ( it.nextFeature( feature ) )
74+
{
75+
insertFeature( feature );
76+
fids << feature.id();
77+
}
78+
79+
return fids;
80+
}
81+
6582
QgsFeatureIds QgsFeaturePool::allFeatureIds() const
6683
{
6784
return mFeatureIds;

src/analysis/vector/geometry_checker/qgsfeaturepool.h

+6
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ class ANALYSIS_EXPORT QgsFeaturePool : public QgsFeatureSink SIP_ABSTRACT
4646
*/
4747
bool getFeature( QgsFeatureId id, QgsFeature &feature );
4848

49+
/**
50+
* Warm the cache ...
51+
* TODO write more docs
52+
*/
53+
QgsFeatureIds getFeatures( const QgsFeatureRequest &request ) SIP_SKIP;
54+
4955
/**
5056
* Updates a feature in this pool.
5157
* Implementations will update the feature on the layer or on the data provider.

src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ void QgsGeometryGapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &
9797
}
9898

9999
// Skip gaps above threshold
100-
if ( gapGeom->area() > mGapThresholdMapUnits || gapGeom->area() < mContext->reducedTolerance )
100+
if ( ( mGapThresholdMapUnits > 0 && gapGeom->area() > mGapThresholdMapUnits ) || gapGeom->area() < mContext->reducedTolerance )
101101
{
102102
continue;
103103
}

src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h

+6
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ class ANALYSIS_EXPORT QgsGeometryGapCheck : public QgsGeometryCheck
7878
public:
7979
enum ResolutionMethod { MergeLongestEdge, NoChange };
8080

81+
/**
82+
* The \a configuration accepts a "gapThreshold" key which specifies
83+
* the maximum gap size in squared map units. Any gaps which are larger
84+
* than this area are accepted. If "gapThreshold" is set to 0, the check
85+
* is disabled.
86+
*/
8187
explicit QgsGeometryGapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration );
8288

8389
QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); }

src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp

+22-1
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,32 @@ void QgsGeometryMissingVertexCheck::fixError( const QMap<QString, QgsFeaturePool
7171
{
7272
error->setFixed( method );
7373
}
74+
if ( method == AddMissingVertex )
75+
{
76+
QgsFeaturePool *featurePool = featurePools[ error->layerId() ];
77+
78+
QgsFeature feature;
79+
featurePool->getFeature( error->featureId(), feature );
80+
81+
QgsPointXY pointOnSegment; // Should be equal to location
82+
int vertexIndex;
83+
QgsGeometry geometry = feature.geometry();
84+
geometry.closestSegmentWithContext( error->location(), pointOnSegment, vertexIndex );
85+
geometry.insertVertex( QgsPoint( error->location() ), vertexIndex );
86+
feature.setGeometry( geometry );
87+
88+
featurePool->updateFeature( feature );
89+
// TODO update "changes" structure
90+
91+
error->setFixed( method );
92+
}
7493
}
7594

7695
QStringList QgsGeometryMissingVertexCheck::resolutionMethods() const
7796
{
78-
static QStringList methods = QStringList() << tr( "No action" );
97+
static QStringList methods = QStringList()
98+
<< tr( "No action" )
99+
<< tr( "Add missing vertex" );
79100
return methods;
80101
}
81102

src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ class ANALYSIS_EXPORT QgsGeometryMissingVertexCheck : public QgsGeometryCheck
3737
public:
3838
enum ResolutionMethod
3939
{
40-
NoChange
40+
NoChange,
41+
AddMissingVertex
4142
};
4243

4344
explicit QgsGeometryMissingVertexCheck( const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration );

src/app/qgisapp.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
939939
mGeometryValidationModel->setCurrentLayer( qobject_cast<QgsVectorLayer *>( layer ) );
940940
} );
941941
mGeometryValidationDock->setGeometryValidationModel( mGeometryValidationModel );
942+
mGeometryValidationDock->setGeometryValidationService( mGeometryValidationService.get() );
942943
addDockWidget( Qt::RightDockWidgetArea, mGeometryValidationDock );
943944
endProfile();
944945

src/app/qgsgeometryvalidationdock.cpp

+62
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ email : matthias@opengis.ch
1515

1616
#include "qgsgeometryvalidationdock.h"
1717
#include "qgsgeometryvalidationmodel.h"
18+
#include "qgsgeometryvalidationservice.h"
1819
#include "qgsmapcanvas.h"
20+
#include "qgsrubberband.h"
21+
#include "qgsvectorlayer.h"
22+
#include "qgsgeometrycheck.h"
23+
#include "qgsgeometrycheckerror.h"
1924

2025
#include <QButtonGroup>
2126

@@ -32,8 +37,23 @@ QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsM
3237
connect( mMapCanvas, &QgsMapCanvas::currentLayerChanged, this, &QgsGeometryValidationDock::updateLayerTransform );
3338
connect( mMapCanvas, &QgsMapCanvas::destinationCrsChanged, this, &QgsGeometryValidationDock::updateLayerTransform );
3439
connect( mMapCanvas, &QgsMapCanvas::transformContextChanged, this, &QgsGeometryValidationDock::updateLayerTransform );
40+
41+
mFeatureRubberband = new QgsRubberBand( mMapCanvas );
42+
mErrorRubberband = new QgsRubberBand( mMapCanvas );
43+
mErrorLocationRubberband = new QgsRubberBand( mMapCanvas );
44+
45+
double scaleFactor = mMapCanvas->fontMetrics().xHeight() * .2;
46+
47+
mFeatureRubberband->setColor( QColor( 250, 180, 180, 100 ) );
48+
mFeatureRubberband->setWidth( scaleFactor );
49+
mErrorRubberband->setColor( QColor( 180, 250, 180, 100 ) );
50+
mErrorRubberband->setWidth( scaleFactor );
51+
mErrorLocationRubberband->setIcon( QgsRubberBand::ICON_X );
52+
mErrorLocationRubberband->setWidth( scaleFactor * 3 );
53+
mErrorLocationRubberband->setColor( QColor( 180, 180, 250, 100 ) );
3554
}
3655

56+
3757
QgsGeometryValidationModel *QgsGeometryValidationDock::geometryValidationModel() const
3858
{
3959
return mGeometryValidationModel;
@@ -89,6 +109,16 @@ void QgsGeometryValidationDock::updateLayerTransform()
89109
mLayerTransform = QgsCoordinateTransform( mMapCanvas->currentLayer()->crs(), mMapCanvas->mapSettings().destinationCrs(), mMapCanvas->mapSettings().transformContext() );
90110
}
91111

112+
QgsGeometryValidationService *QgsGeometryValidationDock::geometryValidationService() const
113+
{
114+
return mGeometryValidationService;
115+
}
116+
117+
void QgsGeometryValidationDock::setGeometryValidationService( QgsGeometryValidationService *geometryValidationService )
118+
{
119+
mGeometryValidationService = geometryValidationService;
120+
}
121+
92122
QModelIndex QgsGeometryValidationDock::currentIndex() const
93123
{
94124
return mErrorListView->selectionModel()->currentIndex();
@@ -102,6 +132,38 @@ void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex &curren
102132

103133
mProblemDetailWidget->setVisible( current.isValid() );
104134
mProblemDescriptionLabel->setText( current.data().toString() );
135+
{
136+
QgsGeometryCheckError *error = current.data( QgsGeometryValidationModel::GeometryCheckErrorRole ).value<QgsGeometryCheckError *>();
137+
while ( QPushButton *btn = mResolutionWidget->findChild<QPushButton *>() )
138+
delete btn;
139+
const QStringList resolutionMethods = error->check()->resolutionMethods();
140+
int resolutionIndex = 0;
141+
for ( const QString &resolutionMethod : resolutionMethods )
142+
{
143+
QPushButton *resolveBtn = new QPushButton( resolutionMethod );
144+
connect( resolveBtn, &QPushButton::clicked, this, [resolutionIndex, error, this]()
145+
{
146+
mGeometryValidationService->fixError( error, resolutionIndex );
147+
} );
148+
mResolutionWidget->layout()->addWidget( resolveBtn );
149+
resolutionIndex++;
150+
}
151+
}
152+
153+
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mMapCanvas->currentLayer() );
154+
if ( vlayer )
155+
{
156+
QgsGeometry featureGeometry = current.data( QgsGeometryValidationModel::FeatureGeometryRole ).value<QgsGeometry>();
157+
QgsGeometry errorGeometry = current.data( QgsGeometryValidationModel::ErrorGeometryRole ).value<QgsGeometry>();
158+
QgsPointXY locationGeometry = current.data( QgsGeometryValidationModel::ErrorLocationGeometryRole ).value<QgsPointXY>();
159+
qDebug() << "feature geom : " << featureGeometry.asWkt();
160+
qDebug() << "error geom : " << errorGeometry.asWkt();
161+
qDebug() << "locationgeom : " << QgsGeometry( new QgsPoint( locationGeometry ) ).asWkt();
162+
163+
mFeatureRubberband->setToGeometry( featureGeometry );
164+
mErrorRubberband->setToGeometry( errorGeometry );
165+
mErrorLocationRubberband->setToGeometry( QgsGeometry( new QgsPoint( locationGeometry ) ) );
166+
}
105167

106168
switch ( mLastZoomToAction )
107169
{

src/app/qgsgeometryvalidationdock.h

+9
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ email : matthias@opengis.ch
2222

2323
class QgsMapCanvas;
2424
class QgsGeometryValidationModel;
25+
class QgsGeometryValidationService;
26+
class QgsRubberBand;
2527

2628
/**
2729
* @brief The QgsGeometryValidationDock class
@@ -36,6 +38,9 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal
3638
QgsGeometryValidationModel *geometryValidationModel() const;
3739
void setGeometryValidationModel( QgsGeometryValidationModel *geometryValidationModel );
3840

41+
QgsGeometryValidationService *geometryValidationService() const;
42+
void setGeometryValidationService( QgsGeometryValidationService *geometryValidationService );
43+
3944
private slots:
4045
void onCurrentErrorChanged( const QModelIndex &current, const QModelIndex &previous );
4146
void gotoNextError();
@@ -52,10 +57,14 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal
5257
};
5358
ZoomToAction mLastZoomToAction = ZoomToFeature;
5459
QgsGeometryValidationModel *mGeometryValidationModel = nullptr;
60+
QgsGeometryValidationService *mGeometryValidationService = nullptr;
5561
QButtonGroup *mZoomToButtonGroup = nullptr;
5662
QgsMapCanvas *mMapCanvas = nullptr;
5763
QgsCoordinateTransform mLayerTransform;
5864
QModelIndex currentIndex() const;
65+
QgsRubberBand *mFeatureRubberband = nullptr;
66+
QgsRubberBand *mErrorRubberband = nullptr;
67+
QgsRubberBand *mErrorLocationRubberband = nullptr;
5968
};
6069

6170
#endif // QGSGEOMETRYVALIDATIONPANEL_H

src/app/qgsgeometryvalidationmodel.cpp

+22
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,28 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role )
7474
{
7575
return topologyError->affectedAreaBBox();
7676
}
77+
78+
case ErrorGeometryRole:
79+
{
80+
return topologyError->geometry();
81+
}
82+
83+
case FeatureGeometryRole:
84+
{
85+
const QgsFeatureId fid = topologyError->featureId();
86+
const QgsFeature feature = mCurrentLayer->getFeature( fid ); // TODO: this should be cached!
87+
return feature.geometry();
88+
}
89+
90+
case ErrorLocationGeometryRole:
91+
{
92+
return topologyError->location();
93+
}
94+
95+
case GeometryCheckErrorRole:
96+
{
97+
return QVariant::fromValue<QgsGeometryCheckError *>( topologyError.get() );
98+
}
7799
}
78100
}
79101
else

src/app/qgsgeometryvalidationmodel.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ class QgsGeometryValidationModel : public QAbstractItemModel
1616
enum Roles
1717
{
1818
FeatureExtentRole = Qt::UserRole,
19-
ProblemExtentRole
19+
ProblemExtentRole,
20+
ErrorGeometryRole,
21+
FeatureGeometryRole,
22+
ErrorLocationGeometryRole,
23+
GeometryCheckErrorRole
2024
};
2125

2226
QgsGeometryValidationModel( QgsGeometryValidationService *geometryValidationService, QObject *parent = nullptr );

src/app/qgsgeometryvalidationservice.cpp

+29-7
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ bool QgsGeometryValidationService::validationActive( QgsVectorLayer *layer, QgsF
4040
return false;
4141
}
4242

43+
void QgsGeometryValidationService::fixError( const QgsGeometryCheckError *error, int method )
44+
{
45+
QgsGeometryCheck::Changes changes;
46+
QgsGeometryCheckError *nonconsterr = const_cast<QgsGeometryCheckError *>( error );
47+
error->check()->fixError( mFeaturePools, nonconsterr, method, QMap<QString, int>(), changes );
48+
}
49+
4350
void QgsGeometryValidationService::onLayersAdded( const QList<QgsMapLayer *> &layers )
4451
{
4552
for ( QgsMapLayer *layer : layers )
@@ -122,7 +129,7 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer )
122129
qDeleteAll( mLayerCheckStates[layer].topologyChecks );
123130

124131
// TODO: ownership and lifetime of the context!!
125-
auto context = new QgsGeometryCheckContext( 1, mProject->crs(), mProject->transformContext() );
132+
auto context = new QgsGeometryCheckContext( 8, mProject->crs(), mProject->transformContext() );
126133
QList<QgsGeometryCheck *> layerChecks;
127134

128135
QgsGeometryCheckRegistry *checkRegistry = QgsAnalysis::instance()->geometryCheckRegistry();
@@ -216,18 +223,33 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer
216223
mLayerCheckStates[layer].topologyCheckFeedbacks.clear();
217224
}
218225

219-
QgsFeatureIds checkFeatureIds = layer->editBuffer()->changedGeometries().keys().toSet();
220-
checkFeatureIds.unite( layer->editBuffer()->addedFeatures().keys().toSet() );
226+
QgsFeatureIds affectedFeatureIds = layer->editBuffer()->changedGeometries().keys().toSet();
227+
affectedFeatureIds.unite( layer->editBuffer()->addedFeatures().keys().toSet() );
221228

222229
// TODO: ownership of these objects...
223230
QgsVectorLayerFeaturePool *featurePool = new QgsVectorLayerFeaturePool( layer );
224231
QList<QgsGeometryCheckError *> &allErrors = mLayerCheckStates[layer].topologyCheckErrors;
225232
QMap<QString, QgsFeatureIds> layerIds;
233+
234+
QgsFeatureRequest request = QgsFeatureRequest( affectedFeatureIds ).setSubsetOfAttributes( QgsAttributeList() );
235+
QgsFeatureIterator it = layer->getFeatures( request );
236+
QgsFeature feature;
237+
QgsRectangle area;
238+
while ( it.nextFeature( feature ) )
239+
{
240+
area.combineExtentWith( feature.geometry().boundingBox() );
241+
}
242+
243+
QgsFeatureRequest areaRequest = QgsFeatureRequest().setFilterRect( area );
244+
QgsFeatureIds checkFeatureIds = featurePool->getFeatures( areaRequest );
245+
226246
layerIds.insert( layer->id(), checkFeatureIds );
227247
QgsGeometryCheck::LayerFeatureIds layerFeatureIds( layerIds );
228248

229-
QMap<QString, QgsFeaturePool *> featurePools;
230-
featurePools.insert( layer->id(), featurePool );
249+
if ( !mFeaturePools.contains( layer->id() ) )
250+
{
251+
mFeaturePools.insert( layer->id(), featurePool );
252+
}
231253

232254
const QList<QgsGeometryCheck *> checks = mLayerCheckStates[layer].topologyChecks;
233255

@@ -237,15 +259,15 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer
237259

238260
mLayerCheckStates[layer].topologyCheckFeedbacks = feedbacks.values();
239261

240-
QFuture<void> future = QtConcurrent::map( checks, [featurePools, &allErrors, layerFeatureIds, layer, feedbacks, this]( const QgsGeometryCheck * check )
262+
QFuture<void> future = QtConcurrent::map( checks, [&allErrors, layerFeatureIds, layer, feedbacks, this]( const QgsGeometryCheck * check )
241263
{
242264
// Watch out with the layer pointer in here. We are running in a thread, so we do not want to actually use it
243265
// except for using its address to report the error.
244266
QList<QgsGeometryCheckError *> errors;
245267
QStringList messages; // Do we really need these?
246268
QgsFeedback *feedback = feedbacks.value( check );
247269

248-
check->collectErrors( featurePools, errors, messages, feedback, layerFeatureIds );
270+
check->collectErrors( mFeaturePools, errors, messages, feedback, layerFeatureIds );
249271
QgsReadWriteLocker errorLocker( mTopologyCheckLock, QgsReadWriteLocker::Write );
250272
allErrors.append( errors );
251273

src/app/qgsgeometryvalidationservice.h

+4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class QgsSingleGeometryCheck;
3131
class QgsSingleGeometryCheckError;
3232
class QgsGeometryCheckError;
3333
class QgsFeedback;
34+
class QgsFeaturePool;
3435

3536
/**
3637
* This service connects to all layers in a project and triggers validation
@@ -64,6 +65,8 @@ class QgsGeometryValidationService : public QObject
6465
*/
6566
bool validationActive( QgsVectorLayer *layer, QgsFeatureId feature ) const;
6667

68+
void fixError( const QgsGeometryCheckError *error, int method );
69+
6770
signals:
6871
void geometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid );
6972
void geometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList<std::shared_ptr<QgsSingleGeometryCheckError>> &errors );
@@ -101,6 +104,7 @@ class QgsGeometryValidationService : public QObject
101104

102105
QReadWriteLock mTopologyCheckLock;
103106
QHash<QgsVectorLayer *, VectorCheckState> mLayerCheckStates;
107+
QMap<QString, QgsFeaturePool *> mFeaturePools;
104108
};
105109

106110
#endif // QGSGEOMETRYVALIDATIONSERVICE_H

0 commit comments

Comments
 (0)