Skip to content

Commit 0b41425

Browse files
committed
Add validation of changed geometries
Whenever geometries are modified, validation will be run while node tool is active and validation errors are displayed in status bar and with markers on canvas
1 parent f004468 commit 0b41425

File tree

2 files changed

+137
-1
lines changed

2 files changed

+137
-1
lines changed

src/app/nodetool/qgsnodetool2.cpp

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
#include "qgscurve.h"
2020
#include "qgscurvepolygon.h"
2121
#include "qgsgeometryutils.h"
22+
#include "qgsgeometryvalidator.h"
2223
#include "qgslogger.h"
2324
#include "qgsmapcanvas.h"
2425
#include "qgsmulticurve.h"
2526
#include "qgspointlocator.h"
2627
#include "qgsproject.h"
2728
#include "qgsrubberband.h"
29+
#include "qgssettings.h"
2830
#include "qgssnappingutils.h"
2931
#include "qgsvectorlayer.h"
3032
#include "qgsvertexmarker.h"
@@ -845,6 +847,9 @@ void QgsNodeTool2::onCachedGeometryChanged( QgsFeatureId fid, const QgsGeometry
845847

846848
// refresh highlighted nodes - their position may have changed
847849
setHighlightedNodes( mSelectedNodes );
850+
851+
// re-run validation for the feature
852+
validateGeometry( layer, fid );
848853
}
849854

850855
void QgsNodeTool2::onCachedGeometryDeleted( QgsFeatureId fid )
@@ -1521,7 +1526,6 @@ void QgsNodeTool2::deleteVertex()
15211526
setHighlightedNodes( nodes_new );
15221527
}
15231528
}
1524-
15251529
}
15261530

15271531
void QgsNodeTool2::setHighlightedNodes( QList<Vertex> listNodes )
@@ -1635,3 +1639,106 @@ void QgsNodeTool2::CircularBand::updateRubberBand( const QgsPoint &mapPoint )
16351639
Q_FOREACH ( const QgsPointV2 &p, points )
16361640
band->addPoint( p );
16371641
}
1642+
1643+
1644+
void QgsNodeTool2::validationErrorFound( QgsGeometry::Error e )
1645+
{
1646+
QgsGeometryValidator *validator = qobject_cast<QgsGeometryValidator *>( sender() );
1647+
if ( !validator )
1648+
return;
1649+
1650+
QHash< QPair<QgsVectorLayer *, QgsFeatureId>, GeometryValidation>::iterator it = mValidations.begin();
1651+
for ( ; it != mValidations.end(); ++it )
1652+
{
1653+
GeometryValidation &validation = *it;
1654+
if ( validation.validator == validator )
1655+
{
1656+
validation.addError( e );
1657+
break;
1658+
}
1659+
}
1660+
}
1661+
1662+
void QgsNodeTool2::validationFinished()
1663+
{
1664+
QgsGeometryValidator *validator = qobject_cast<QgsGeometryValidator *>( sender() );
1665+
if ( !validator )
1666+
return;
1667+
1668+
QHash< QPair<QgsVectorLayer *, QgsFeatureId>, GeometryValidation>::iterator it = mValidations.begin();
1669+
for ( ; it != mValidations.end(); ++it )
1670+
{
1671+
GeometryValidation &validation = *it;
1672+
if ( validation.validator == validator )
1673+
{
1674+
QStatusBar *sb = QgisApp::instance()->statusBar();
1675+
sb->showMessage( tr( "Validation finished (%n error(s) found).", "number of geometry errors", validation.errorMarkers.size() ) );
1676+
}
1677+
}
1678+
}
1679+
1680+
void QgsNodeTool2::GeometryValidation::start( QgsGeometry &geom, QgsNodeTool2 *t, QgsVectorLayer *l )
1681+
{
1682+
tool = t;
1683+
layer = l;
1684+
validator = new QgsGeometryValidator( &geom );
1685+
connect( validator, &QgsGeometryValidator::errorFound, tool, &QgsNodeTool2::validationErrorFound );
1686+
connect( validator, &QThread::finished, tool, &QgsNodeTool2::validationFinished );
1687+
validator->start();
1688+
}
1689+
1690+
void QgsNodeTool2::GeometryValidation::addError( QgsGeometry::Error e )
1691+
{
1692+
if ( !errors.isEmpty() )
1693+
errors += '\n';
1694+
errors += e.what();
1695+
1696+
if ( e.hasWhere() )
1697+
{
1698+
QgsVertexMarker *marker = new QgsVertexMarker( tool->canvas() );
1699+
marker->setCenter( tool->canvas()->mapSettings().layerToMapCoordinates( layer, e.where() ) );
1700+
marker->setIconType( QgsVertexMarker::ICON_X );
1701+
marker->setColor( Qt::green );
1702+
marker->setZValue( marker->zValue() + 1 );
1703+
marker->setPenWidth( 2 );
1704+
marker->setToolTip( e.what() );
1705+
errorMarkers << marker;
1706+
}
1707+
1708+
QStatusBar *sb = QgisApp::instance()->statusBar();
1709+
sb->showMessage( e.what() );
1710+
sb->setToolTip( errors );
1711+
}
1712+
1713+
void QgsNodeTool2::GeometryValidation::cleanup()
1714+
{
1715+
if ( validator )
1716+
{
1717+
validator->stop();
1718+
validator->wait();
1719+
validator->deleteLater();
1720+
validator = nullptr;
1721+
}
1722+
1723+
qDeleteAll( errorMarkers );
1724+
errorMarkers.clear();
1725+
}
1726+
1727+
void QgsNodeTool2::validateGeometry( QgsVectorLayer *layer, QgsFeatureId featureId )
1728+
{
1729+
QgsSettings settings;
1730+
if ( settings.value( QStringLiteral( "qgis/digitizing/validate_geometries" ), 1 ).toInt() == 0 )
1731+
return;
1732+
1733+
QPair<QgsVectorLayer *, QgsFeatureId> id( layer, featureId );
1734+
if ( mValidations.contains( id ) )
1735+
{
1736+
mValidations[id].cleanup();
1737+
mValidations.remove( id );
1738+
}
1739+
1740+
GeometryValidation validation;
1741+
QgsGeometry geom = cachedGeometry( layer, featureId );
1742+
validation.start( geom, this, layer );
1743+
mValidations.insert( id, validation );
1744+
}

src/app/nodetool/qgsnodetool2.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020

2121
#include "qgis_app.h"
2222
#include "qgsmaptooladvanceddigitizing.h"
23+
#include "qgsgeometry.h"
2324

2425
class QRubberBand;
2526

27+
class QgsGeometryValidator;
2628
class QgsNodeEditor;
2729
class QgsSelectedFeature;
2830
class QgsVertexMarker;
@@ -88,6 +90,10 @@ class APP_EXPORT QgsNodeTool2 : public QgsMapToolAdvancedDigitizing
8890

8991
void deleteNodeEditorSelection();
9092

93+
void validationErrorFound( QgsGeometry::Error e );
94+
95+
void validationFinished();
96+
9197
private:
9298

9399
void buildDragBandsForVertices( const QSet<Vertex> &movingVertices, const QgsPoint &dragVertexMapPoint );
@@ -178,6 +184,9 @@ class APP_EXPORT QgsNodeTool2 : public QgsMapToolAdvancedDigitizing
178184

179185
void cleanupNodeEditor();
180186

187+
//! Run validation on a geometry (in a background thread)
188+
void validateGeometry( QgsVectorLayer *layer, QgsFeatureId featureId );
189+
181190
private:
182191

183192
// members used for temporary highlight of stuff
@@ -305,6 +314,26 @@ class APP_EXPORT QgsNodeTool2 : public QgsMapToolAdvancedDigitizing
305314
std::unique_ptr<QgsSelectedFeature> mSelectedFeature;
306315
//! Dock widget which allows editing vertices
307316
std::unique_ptr<QgsNodeEditor> mNodeEditor;
317+
318+
// suport for validation of geometries
319+
320+
//! data structure for validation of one geometry of a vector layer
321+
struct GeometryValidation
322+
{
323+
QgsNodeTool2 *tool = nullptr; //!< Pointer to the parent node tool (for connections / canvas)
324+
QgsVectorLayer *layer = nullptr; //!< Pointer to the layer of the validated geometry (for reporojection)
325+
QgsGeometryValidator *validator = nullptr; //!< Object that does validation. Non-null if active
326+
QList<QgsVertexMarker *> errorMarkers; //!< Markers created by validation
327+
QString errors; //!< Full error text from validation
328+
329+
void start( QgsGeometry &geom, QgsNodeTool2 *tool, QgsVectorLayer *l ); //!< Start validation
330+
void addError( QgsGeometry::Error e ); //!< Add another error to the validation
331+
void cleanup(); //!< Delete everything
332+
};
333+
334+
//! data structure to keep validation details
335+
QHash< QPair<QgsVectorLayer *, QgsFeatureId>, GeometryValidation> mValidations;
336+
308337
};
309338

310339

0 commit comments

Comments
 (0)