Skip to content

Commit

Permalink
Add validation of changed geometries
Browse files Browse the repository at this point in the history
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
  • Loading branch information
wonder-sk committed Apr 6, 2017
1 parent f004468 commit 0b41425
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 1 deletion.
109 changes: 108 additions & 1 deletion src/app/nodetool/qgsnodetool2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
#include "qgscurve.h"
#include "qgscurvepolygon.h"
#include "qgsgeometryutils.h"
#include "qgsgeometryvalidator.h"
#include "qgslogger.h"
#include "qgsmapcanvas.h"
#include "qgsmulticurve.h"
#include "qgspointlocator.h"
#include "qgsproject.h"
#include "qgsrubberband.h"
#include "qgssettings.h"
#include "qgssnappingutils.h"
#include "qgsvectorlayer.h"
#include "qgsvertexmarker.h"
Expand Down Expand Up @@ -845,6 +847,9 @@ void QgsNodeTool2::onCachedGeometryChanged( QgsFeatureId fid, const QgsGeometry

// refresh highlighted nodes - their position may have changed
setHighlightedNodes( mSelectedNodes );

// re-run validation for the feature
validateGeometry( layer, fid );
}

void QgsNodeTool2::onCachedGeometryDeleted( QgsFeatureId fid )
Expand Down Expand Up @@ -1521,7 +1526,6 @@ void QgsNodeTool2::deleteVertex()
setHighlightedNodes( nodes_new );
}
}

}

void QgsNodeTool2::setHighlightedNodes( QList<Vertex> listNodes )
Expand Down Expand Up @@ -1635,3 +1639,106 @@ void QgsNodeTool2::CircularBand::updateRubberBand( const QgsPoint &mapPoint )
Q_FOREACH ( const QgsPointV2 &p, points )
band->addPoint( p );
}


void QgsNodeTool2::validationErrorFound( QgsGeometry::Error e )
{
QgsGeometryValidator *validator = qobject_cast<QgsGeometryValidator *>( sender() );
if ( !validator )
return;

QHash< QPair<QgsVectorLayer *, QgsFeatureId>, GeometryValidation>::iterator it = mValidations.begin();
for ( ; it != mValidations.end(); ++it )
{
GeometryValidation &validation = *it;
if ( validation.validator == validator )
{
validation.addError( e );
break;
}
}
}

void QgsNodeTool2::validationFinished()
{
QgsGeometryValidator *validator = qobject_cast<QgsGeometryValidator *>( sender() );
if ( !validator )
return;

QHash< QPair<QgsVectorLayer *, QgsFeatureId>, GeometryValidation>::iterator it = mValidations.begin();
for ( ; it != mValidations.end(); ++it )
{
GeometryValidation &validation = *it;
if ( validation.validator == validator )
{
QStatusBar *sb = QgisApp::instance()->statusBar();
sb->showMessage( tr( "Validation finished (%n error(s) found).", "number of geometry errors", validation.errorMarkers.size() ) );
}
}
}

void QgsNodeTool2::GeometryValidation::start( QgsGeometry &geom, QgsNodeTool2 *t, QgsVectorLayer *l )
{
tool = t;
layer = l;
validator = new QgsGeometryValidator( &geom );
connect( validator, &QgsGeometryValidator::errorFound, tool, &QgsNodeTool2::validationErrorFound );
connect( validator, &QThread::finished, tool, &QgsNodeTool2::validationFinished );
validator->start();
}

void QgsNodeTool2::GeometryValidation::addError( QgsGeometry::Error e )
{
if ( !errors.isEmpty() )
errors += '\n';
errors += e.what();

if ( e.hasWhere() )
{
QgsVertexMarker *marker = new QgsVertexMarker( tool->canvas() );
marker->setCenter( tool->canvas()->mapSettings().layerToMapCoordinates( layer, e.where() ) );
marker->setIconType( QgsVertexMarker::ICON_X );
marker->setColor( Qt::green );
marker->setZValue( marker->zValue() + 1 );
marker->setPenWidth( 2 );
marker->setToolTip( e.what() );
errorMarkers << marker;
}

QStatusBar *sb = QgisApp::instance()->statusBar();
sb->showMessage( e.what() );
sb->setToolTip( errors );
}

void QgsNodeTool2::GeometryValidation::cleanup()
{
if ( validator )
{
validator->stop();
validator->wait();
validator->deleteLater();
validator = nullptr;
}

qDeleteAll( errorMarkers );
errorMarkers.clear();
}

void QgsNodeTool2::validateGeometry( QgsVectorLayer *layer, QgsFeatureId featureId )
{
QgsSettings settings;
if ( settings.value( QStringLiteral( "qgis/digitizing/validate_geometries" ), 1 ).toInt() == 0 )
return;

QPair<QgsVectorLayer *, QgsFeatureId> id( layer, featureId );
if ( mValidations.contains( id ) )
{
mValidations[id].cleanup();
mValidations.remove( id );
}

GeometryValidation validation;
QgsGeometry geom = cachedGeometry( layer, featureId );
validation.start( geom, this, layer );
mValidations.insert( id, validation );
}
29 changes: 29 additions & 0 deletions src/app/nodetool/qgsnodetool2.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@

#include "qgis_app.h"
#include "qgsmaptooladvanceddigitizing.h"
#include "qgsgeometry.h"

class QRubberBand;

class QgsGeometryValidator;
class QgsNodeEditor;
class QgsSelectedFeature;
class QgsVertexMarker;
Expand Down Expand Up @@ -88,6 +90,10 @@ class APP_EXPORT QgsNodeTool2 : public QgsMapToolAdvancedDigitizing

void deleteNodeEditorSelection();

void validationErrorFound( QgsGeometry::Error e );

void validationFinished();

private:

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

void cleanupNodeEditor();

//! Run validation on a geometry (in a background thread)
void validateGeometry( QgsVectorLayer *layer, QgsFeatureId featureId );

private:

// members used for temporary highlight of stuff
Expand Down Expand Up @@ -305,6 +314,26 @@ class APP_EXPORT QgsNodeTool2 : public QgsMapToolAdvancedDigitizing
std::unique_ptr<QgsSelectedFeature> mSelectedFeature;
//! Dock widget which allows editing vertices
std::unique_ptr<QgsNodeEditor> mNodeEditor;

// suport for validation of geometries

//! data structure for validation of one geometry of a vector layer
struct GeometryValidation
{
QgsNodeTool2 *tool = nullptr; //!< Pointer to the parent node tool (for connections / canvas)
QgsVectorLayer *layer = nullptr; //!< Pointer to the layer of the validated geometry (for reporojection)
QgsGeometryValidator *validator = nullptr; //!< Object that does validation. Non-null if active
QList<QgsVertexMarker *> errorMarkers; //!< Markers created by validation
QString errors; //!< Full error text from validation

void start( QgsGeometry &geom, QgsNodeTool2 *tool, QgsVectorLayer *l ); //!< Start validation
void addError( QgsGeometry::Error e ); //!< Add another error to the validation
void cleanup(); //!< Delete everything
};

//! data structure to keep validation details
QHash< QPair<QgsVectorLayer *, QgsFeatureId>, GeometryValidation> mValidations;

};


Expand Down

0 comments on commit 0b41425

Please sign in to comment.