From f5486ee51670e56a9d259969268cb471d8c1e1ba Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Thu, 6 Sep 2018 17:30:56 +0200 Subject: [PATCH 001/126] Add single geometry check --- src/app/CMakeLists.txt | 7 ++ src/app/qgisapp.cpp | 12 ++- src/app/qgisapp.h | 17 ++-- src/app/qgsgeometryvalidationdock.cpp | 33 ++++++++ src/app/qgsgeometryvalidationdock.h | 41 +++++++++ src/app/qgsgeometryvalidationmodel.cpp | 67 +++++++++++++++ src/app/qgsgeometryvalidationmodel.h | 30 +++++++ src/app/qgsgeometryvalidationservice.cpp | 102 +++++++++++++++++++++++ src/app/qgsgeometryvalidationservice.h | 71 ++++++++++++++++ src/core/qgsgeometrycheckqueue.cpp | 6 ++ src/core/qgsgeometrycheckqueue.h | 15 ++++ src/ui/qgsgeometryvalidationdockbase.ui | 83 ++++++++++++++++++ 12 files changed, 471 insertions(+), 13 deletions(-) create mode 100644 src/app/qgsgeometryvalidationdock.cpp create mode 100644 src/app/qgsgeometryvalidationdock.h create mode 100644 src/app/qgsgeometryvalidationmodel.cpp create mode 100644 src/app/qgsgeometryvalidationmodel.h create mode 100644 src/app/qgsgeometryvalidationservice.cpp create mode 100644 src/app/qgsgeometryvalidationservice.h create mode 100644 src/core/qgsgeometrycheckqueue.cpp create mode 100644 src/core/qgsgeometrycheckqueue.h create mode 100644 src/ui/qgsgeometryvalidationdockbase.ui diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 65fd720726e..1ccea8f5211 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -48,6 +48,9 @@ SET(QGIS_APP_SRCS qgsdisplayangle.cpp qgsfieldcalculator.cpp qgsfirstrundialog.cpp + qgsgeometryvalidationservice.cpp + qgsgeometryvalidationdock.cpp + qgsgeometryvalidationmodel.cpp qgssourcefieldsproperties.cpp qgsattributesformproperties.cpp qgsidentifyresultsdialog.cpp @@ -276,6 +279,9 @@ SET (QGIS_APP_MOC_HDRS qgsattributesformproperties.h qgsformannotationdialog.h qgsguivectorlayertools.h + qgsgeometryvalidationservice.h + qgsgeometryvalidationdock.h + qgsgeometryvalidationmodel.h qgshtmlannotationdialog.h qgsidentifyresultsdialog.h qgslabelengineconfigdialog.h @@ -692,6 +698,7 @@ INCLUDE_DIRECTORIES(SYSTEM INCLUDE_DIRECTORIES( ../analysis/processing ../analysis/raster + ../analysis/vector/geometry_checker ../core ../core/annotations ../core/auth diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 3eeb0fcffdd..323a0c49cb6 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -79,6 +79,7 @@ #include "qgsziputils.h" #include "qgsbrowsermodel.h" #include "qgsvectorlayerjoinbuffer.h" +#include "qgsgeometryvalidationservice.h" #ifdef HAVE_3D #include "qgsabstract3drenderer.h" @@ -406,6 +407,8 @@ Q_GUI_EXPORT extern int qt_defaultDpiX(); #include "qgsmaptoolrotatelabel.h" #include "qgsmaptoolchangelabelproperties.h" #include "qgsmaptoolreverseline.h" +#include "qgsgeometryvalidationmodel.h" +#include "qgsgeometryvalidationdock.h" #include "vertextool/qgsvertextool.h" @@ -778,7 +781,6 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh // what type of project to auto-open mProjOpen = settings.value( QStringLiteral( "qgis/projOpenAtLaunch" ), 0 ).toInt(); - startProfile( QStringLiteral( "Welcome page" ) ); mWelcomePage = new QgsWelcomePage( skipVersionCheck ); connect( mWelcomePage, &QgsWelcomePage::projectRemoved, this, [ this ]( int row ) @@ -919,6 +921,14 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh functionProfile( &QgisApp::initNativeProcessing, this, QStringLiteral( "Initialize native processing" ) ); functionProfile( &QgisApp::initLayouts, this, QStringLiteral( "Initialize layouts support" ) ); + startProfile( QStringLiteral( "Geometry validation" ) ); + mGeometryValidationService = qgis::make_unique( QgsProject::instance() ); + mGeometryValidationDock = new QgsGeometryValidationDock( tr( "Geometry Validation" ) ); + mGeometryValidationModel = new QgsGeometryValidationModel( mGeometryValidationService.get(), mGeometryValidationDock ); + mGeometryValidationDock->setGeometryValidationModel( mGeometryValidationModel ); + addDockWidget( Qt::RightDockWidgetArea, mGeometryValidationDock ); + endProfile(); + QgsApplication::annotationRegistry()->addAnnotationType( QgsAnnotationMetadata( QStringLiteral( "FormAnnotationItem" ), &QgsFormAnnotation::create ) ); connect( QgsProject::instance()->annotationManager(), &QgsAnnotationManager::annotationAdded, this, &QgisApp::annotationCreated ); diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index 438c472f44a..38584202aed 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -101,6 +101,9 @@ class QgsVectorLayerTools; class QgsWelcomePage; class QgsOptionsWidgetFactory; class QgsStatusBar; +class QgsGeometryValidationService; +class QgsGeometryValidationDock; +class QgsGeometryValidationModel; class QgsUserProfileManagerWidgetFactory; class Qgs3DMapCanvasDockWidget; @@ -114,6 +117,7 @@ class QgsAdvancedDigitizingDockWidget; class QgsGpsInformationWidget; class QgsStatisticalSummaryDockWidget; class QgsMapCanvasTracer; + class QgsDecorationItem; class QgsMessageLogViewer; class QgsMessageBar; @@ -690,13 +694,6 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow //! Returns pointer to the identify map tool - used by identify tool in 3D view QgsMapToolIdentifyAction *identifyMapTool() const { return mMapTools.mIdentify; } - /** - * Take screenshots for user documentation - * @param saveDirectory path were the screenshots will be saved - * @param categories an int as a flag value of QgsAppScreenShots::Categories - */ - void takeAppScreenShots( const QString &saveDirectory, const int categories = 0 ); - public slots: //! save current vector layer void saveAsFile( QgsMapLayer *layer = nullptr, bool onlySelected = false ); @@ -1970,9 +1967,6 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow //! Populates project "load from" / "save to" menu based on project storages (when the menu is about to be shown) void populateProjectStorageMenu( QMenu *menu, bool saving ); - //! Create the option dialog - QgsOptions *createOptionsDialog( QWidget *parent = nullptr ); - QgisAppStyleSheet *mStyleSheetBuilder = nullptr; // actions for menus and toolbars ----------------- @@ -2145,6 +2139,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow //! interface to QgisApp for plugins QgisAppInterface *mQgisInterface = nullptr; + friend class QgisAppInterface; QSplashScreen *mSplash = nullptr; //! list of recently opened/saved project files @@ -2311,8 +2306,6 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow bool mBlockActiveLayerChanged = false; friend class TestQgisAppPython; - friend class QgisAppInterface; - friend class QgsAppScreenShots; }; #ifdef ANDROID diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp new file mode 100644 index 00000000000..12461adec27 --- /dev/null +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -0,0 +1,33 @@ +/*************************************************************************** + qgsgeometryvalidationdock.cpp + -------------------------------------- +Date : 7.9.2018 +Copyright : (C) 2018 by Matthias Kuhn +email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsgeometryvalidationdock.h" +#include "qgsgeometryvalidationmodel.h" + +QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QWidget *parent, Qt::WindowFlags flags ) + : QgsDockWidget( title, parent, flags ) +{ + setupUi( this ); +} + +QgsGeometryValidationModel *QgsGeometryValidationDock::geometryValidationModel() const +{ + return mGeometryValidationModel; +} + +void QgsGeometryValidationDock::setGeometryValidationModel( QgsGeometryValidationModel *geometryValidationModel ) +{ + mGeometryValidationModel = geometryValidationModel; +} diff --git a/src/app/qgsgeometryvalidationdock.h b/src/app/qgsgeometryvalidationdock.h new file mode 100644 index 00000000000..a8d7b855195 --- /dev/null +++ b/src/app/qgsgeometryvalidationdock.h @@ -0,0 +1,41 @@ +/*************************************************************************** + qgsgeometryvalidationdock.h + -------------------------------------- +Date : 7.9.2018 +Copyright : (C) 2018 by Matthias Kuhn +email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSGEOMETRYVALIDATIONPANEL_H +#define QGSGEOMETRYVALIDATIONPANEL_H + +#include "ui_qgsgeometryvalidationdockbase.h" +#include "qgsdockwidget.h" + +class QgsGeometryValidationModel; + +/** + * @brief The QgsGeometryValidationDock class + */ +class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryValidationDockBase +{ + Q_OBJECT + + public: + QgsGeometryValidationDock( const QString &title, QWidget *parent = nullptr, Qt::WindowFlags flags = nullptr ); + + QgsGeometryValidationModel *geometryValidationModel() const; + void setGeometryValidationModel( QgsGeometryValidationModel *geometryValidationModel ); + + private: + QgsGeometryValidationModel *mGeometryValidationModel = nullptr; +}; + +#endif // QGSGEOMETRYVALIDATIONPANEL_H diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp new file mode 100644 index 00000000000..57693690af8 --- /dev/null +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -0,0 +1,67 @@ +#include "qgsgeometryvalidationmodel.h" + +#include "qgsvectorlayer.h" + +QgsGeometryValidationModel::QgsGeometryValidationModel( QgsGeometryValidationService *geometryValidationService, QObject *parent ) + : QAbstractItemModel( parent ) + , mGeometryValidationService( geometryValidationService ) +{ + +} + +QModelIndex QgsGeometryValidationModel::index( int row, int column, const QModelIndex &parent ) const +{ + Q_UNUSED( parent ) + return createIndex( row, column ); +} + +QModelIndex QgsGeometryValidationModel::parent( const QModelIndex &child ) const +{ + Q_UNUSED( child ) + return QModelIndex(); +} + +int QgsGeometryValidationModel::rowCount( const QModelIndex &parent ) const +{ + Q_UNUSED( parent ) + return mGeometryValidationService->featureErrors( mCurrentLayer ).size(); +} + +int QgsGeometryValidationModel::columnCount( const QModelIndex &parent ) const +{ + Q_UNUSED( parent ) + return 1; +} + +QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) const +{ + switch ( role ) + { + case Qt::DisplayRole: + QgsGeometryValidationService::FeatureError error = mGeometryValidationService->featureError( mCurrentLayer, index.row() ); + QgsFeature feature = mCurrentLayer->getFeature( error.featureId ); + mExpressionContext.setFeature( feature ); + QString featureTitle = mDisplayExpression.evaluate( &mExpressionContext ).toString(); + return QStringLiteral( "%1: %2" ).arg( featureTitle, error.error.what() ); + } + + return QVariant(); +} + +QgsVectorLayer *QgsGeometryValidationModel::currentLayer() const +{ + return mCurrentLayer; +} + +void QgsGeometryValidationModel::setCurrentLayer( QgsVectorLayer *currentLayer ) +{ + if ( mCurrentLayer == currentLayer ) + return; + + beginResetModel(); + mCurrentLayer = currentLayer; + mDisplayExpression = mCurrentLayer->displayExpression(); + mExpressionContext = QgsExpressionContext( QgsExpressionContextUtils::globalProjectLayerScopes( mCurrentLayer ) ); + mDisplayExpression.prepare( &mExpressionContext ); + endResetModel(); +} diff --git a/src/app/qgsgeometryvalidationmodel.h b/src/app/qgsgeometryvalidationmodel.h new file mode 100644 index 00000000000..948c06176da --- /dev/null +++ b/src/app/qgsgeometryvalidationmodel.h @@ -0,0 +1,30 @@ +#ifndef QGSGEOMETRYVALIDATIONMODEL_H +#define QGSGEOMETRYVALIDATIONMODEL_H + +#include +#include "qgsgeometryvalidationservice.h" +#include "qgsexpression.h" +#include "qgsexpressioncontext.h" + +class QgsGeometryValidationModel : public QAbstractItemModel +{ + Q_OBJECT + + public: + QgsGeometryValidationModel( QgsGeometryValidationService *geometryValidationService, QObject *parent = nullptr ); + QModelIndex index( int row, int column, const QModelIndex &parent ) const override; + QModelIndex parent( const QModelIndex &child ) const override; + int rowCount( const QModelIndex &parent ) const override; + int columnCount( const QModelIndex &parent ) const override; + QVariant data( const QModelIndex &index, int role ) const override; + QgsVectorLayer *currentLayer() const; + void setCurrentLayer( QgsVectorLayer *currentLayer ); + + private: + QgsGeometryValidationService *mGeometryValidationService = nullptr; + QgsVectorLayer *mCurrentLayer = nullptr; + mutable QgsExpression mDisplayExpression; + mutable QgsExpressionContext mExpressionContext; +}; + +#endif // QGSGEOMETRYVALIDATIONMODEL_H diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp new file mode 100644 index 00000000000..8302625f7b2 --- /dev/null +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -0,0 +1,102 @@ +/*************************************************************************** + qgsgeometryvalidationservice.cpp + -------------------------------------- +Date : 7.9.2018 +Copyright : (C) 2018 by Matthias Kuhn +email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + + +#include "qgsgeometryvalidationservice.h" +#include "qgsproject.h" +#include "qgsvectorlayer.h" + +// TODO: Replace with registry +#include "qgsisvalidgeometrycheck.h" + +QgsGeometryValidationService::QgsGeometryValidationService( QgsProject *project ) +{ + connect( project, &QgsProject::layersAdded, this, &QgsGeometryValidationService::onLayersAdded ); + + mIsValidGeometryCheck = new QgsIsValidGeometryCheck(); +} + +QgsGeometryValidationService::~QgsGeometryValidationService() +{ + delete mIsValidGeometryCheck; +} + +void QgsGeometryValidationService::onLayersAdded( const QList &layers ) +{ + for ( QgsMapLayer *layer : layers ) + { + QgsVectorLayer *vectorLayer = qobject_cast( layer ); + if ( vectorLayer ) + { + connect( vectorLayer, &QgsVectorLayer::featureAdded, this, [this, vectorLayer]( QgsFeatureId fid ) + { + onFeatureAdded( vectorLayer, fid ); + } ); + connect( vectorLayer, &QgsVectorLayer::geometryChanged, this, [this, vectorLayer]( QgsFeatureId fid, const QgsGeometry & geometry ) + { + onGeometryChanged( vectorLayer, fid, geometry ); + } ); + connect( vectorLayer, &QgsVectorLayer::featureDeleted, this, [this, vectorLayer]( QgsFeatureId fid ) + { + onFeatureDeleted( vectorLayer, fid ); + } ); + } + } +} + +void QgsGeometryValidationService::onFeatureAdded( QgsVectorLayer *layer, QgsFeatureId fid ) +{ + emit geometryCheckStarted( layer, fid ); + + QgsFeature feature = layer->getFeature( fid ); + const auto errors = mIsValidGeometryCheck->collectErrors( feature ); + for ( const auto &error : errors ) + { + qDebug() << error.what(); + } + + emit geometryCheckCompleted( layer, fid ); +} + +void QgsGeometryValidationService::onGeometryChanged( QgsVectorLayer *layer, QgsFeatureId fid, const QgsGeometry &geometry ) +{ + emit geometryCheckStarted( layer, fid ); + + QgsFeature feature = layer->getFeature( fid ); + const auto errors = mIsValidGeometryCheck->collectErrors( feature ); + for ( const auto &error : errors ) + { + qDebug() << error.what(); + } + + emit geometryCheckCompleted( layer, fid ); +} + +void QgsGeometryValidationService::onFeatureDeleted( QgsVectorLayer *layer, QgsFeatureId fid ) +{ + // TODO: cleanup any ongoing validation threads. + + emit geometryCheckCompleted( layer, fid ); +} + +QgsGeometryValidationService::FeatureErrors QgsGeometryValidationService::featureErrors( QgsVectorLayer *layer ) const +{ + return mFeatureErrors.value( layer ); +} + +QgsGeometryValidationService::FeatureError QgsGeometryValidationService::featureError( QgsVectorLayer *layer, int errorIndex ) +{ + return mFeatureErrors.value( layer ).value( errorIndex ); +} diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h new file mode 100644 index 00000000000..76b4454ee41 --- /dev/null +++ b/src/app/qgsgeometryvalidationservice.h @@ -0,0 +1,71 @@ +/*************************************************************************** + qgsgeometryvalidationservice.h + -------------------------------------- +Date : 7.9.2018 +Copyright : (C) 2018 by Matthias Kuhn +email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSGEOMETRYVALIDATIONSERVICE_H +#define QGSGEOMETRYVALIDATIONSERVICE_H + +#include +#include + +#include "qgsfeature.h" + +class QgsProject; +class QgsMapLayer; +class QgsVectorLayer; +// TODO: Should be retrieved from registry!! +class QgsIsValidGeometryCheck; + +class QgsGeometryValidationService : public QObject +{ + Q_OBJECT + + public: + struct FeatureError + { + QgsFeatureId featureId; + QgsGeometry::Error error; + }; + + typedef QList FeatureErrors; + + QgsGeometryValidationService( QgsProject *project ); + ~QgsGeometryValidationService(); + + FeatureErrors featureErrors( QgsVectorLayer *layer ) const; + + FeatureError featureError( QgsVectorLayer *layer, int errorIndex ); + + signals: + void geometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid ); + void geometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid ); + + private slots: + void onLayersAdded( const QList &layers ); + void onFeatureAdded( QgsVectorLayer *layer, QgsFeatureId fid ); + void onGeometryChanged( QgsVectorLayer *layer, QgsFeatureId fid, const QgsGeometry &geometry ); + void onFeatureDeleted( QgsVectorLayer *layer, QgsFeatureId fid ); + + private: + void cancelChecks( QgsVectorLayer *layer, QgsFeatureId fid ); + + QgsIsValidGeometryCheck *mIsValidGeometryCheck; + + QgsProject *mProject; + + QMap mActiveChecks; + QMap mFeatureErrors; +}; + +#endif // QGSGEOMETRYVALIDATIONSERVICE_H diff --git a/src/core/qgsgeometrycheckqueue.cpp b/src/core/qgsgeometrycheckqueue.cpp new file mode 100644 index 00000000000..e9b9573f80f --- /dev/null +++ b/src/core/qgsgeometrycheckqueue.cpp @@ -0,0 +1,6 @@ +#include "qgsgeometrycheckqueue.h" + +QgsGeometryCheckQueue::QgsGeometryCheckQueue() +{ + +} diff --git a/src/core/qgsgeometrycheckqueue.h b/src/core/qgsgeometrycheckqueue.h new file mode 100644 index 00000000000..0f5fc0da47b --- /dev/null +++ b/src/core/qgsgeometrycheckqueue.h @@ -0,0 +1,15 @@ +#ifndef QGSGEOMETRYCHECKQUEUE_H +#define QGSGEOMETRYCHECKQUEUE_H + +#include + +class QgsGeometryCheckQueue +{ + public: + QgsGeometryCheckQueue(); + + private: + +}; + +#endif // QGSGEOMETRYCHECKQUEUE_H diff --git a/src/ui/qgsgeometryvalidationdockbase.ui b/src/ui/qgsgeometryvalidationdockbase.ui new file mode 100644 index 00000000000..a66e55d1867 --- /dev/null +++ b/src/ui/qgsgeometryvalidationdockbase.ui @@ -0,0 +1,83 @@ + + + QgsGeometryValidationDockBase + + + + 0 + 0 + 607 + 973 + + + + Geometry Validation + + + + + + + + + + + + + + ... + + + + :/images/themes/default/mIconTopologicalEditing.svg:/images/themes/default/mIconTopologicalEditing.svg + + + + + + + + + + + + QgsDockWidget + QDockWidget +
qgsdockwidget.h
+ 1 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
From 56d299ec7049c40f9d111fb5e0aab513fd35b151 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 11 Sep 2018 18:39:49 +0200 Subject: [PATCH 002/126] YAY, it is working --- src/app/qgisapp.cpp | 4 + src/app/qgsgeometryvalidationdock.cpp | 1 + src/app/qgsgeometryvalidationmodel.cpp | 124 +++++++++++++++++++++-- src/app/qgsgeometryvalidationmodel.h | 25 +++++ src/app/qgsgeometryvalidationservice.cpp | 44 ++++---- src/app/qgsgeometryvalidationservice.h | 17 ++-- 6 files changed, 175 insertions(+), 40 deletions(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 323a0c49cb6..9eb2a405576 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -925,6 +925,10 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh mGeometryValidationService = qgis::make_unique( QgsProject::instance() ); mGeometryValidationDock = new QgsGeometryValidationDock( tr( "Geometry Validation" ) ); mGeometryValidationModel = new QgsGeometryValidationModel( mGeometryValidationService.get(), mGeometryValidationDock ); + connect( this, &QgisApp::activeLayerChanged, mGeometryValidationModel, [this]( QgsMapLayer * layer ) + { + mGeometryValidationModel->setCurrentLayer( qobject_cast( layer ) ); + } ); mGeometryValidationDock->setGeometryValidationModel( mGeometryValidationModel ); addDockWidget( Qt::RightDockWidgetArea, mGeometryValidationDock ); endProfile(); diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 12461adec27..9e8b443bc24 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -30,4 +30,5 @@ QgsGeometryValidationModel *QgsGeometryValidationDock::geometryValidationModel() void QgsGeometryValidationDock::setGeometryValidationModel( QgsGeometryValidationModel *geometryValidationModel ) { mGeometryValidationModel = geometryValidationModel; + mErrorListView->setModel( mGeometryValidationModel ); } diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index 57693690af8..f440e983edb 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -2,11 +2,14 @@ #include "qgsvectorlayer.h" +#include + QgsGeometryValidationModel::QgsGeometryValidationModel( QgsGeometryValidationService *geometryValidationService, QObject *parent ) : QAbstractItemModel( parent ) , mGeometryValidationService( geometryValidationService ) { - + connect( mGeometryValidationService, &QgsGeometryValidationService::geometryCheckCompleted, this, &QgsGeometryValidationModel::onGeometryCheckCompleted ); + connect( mGeometryValidationService, &QgsGeometryValidationService::geometryCheckStarted, this, &QgsGeometryValidationModel::onGeometryCheckStarted ); } QModelIndex QgsGeometryValidationModel::index( int row, int column, const QModelIndex &parent ) const @@ -24,7 +27,7 @@ QModelIndex QgsGeometryValidationModel::parent( const QModelIndex &child ) const int QgsGeometryValidationModel::rowCount( const QModelIndex &parent ) const { Q_UNUSED( parent ) - return mGeometryValidationService->featureErrors( mCurrentLayer ).size(); + return mErrorStorage.value( mCurrentLayer ).size(); } int QgsGeometryValidationModel::columnCount( const QModelIndex &parent ) const @@ -35,14 +38,37 @@ int QgsGeometryValidationModel::columnCount( const QModelIndex &parent ) const QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) const { + const auto &layerErrors = mErrorStorage.value( mCurrentLayer ); + + const auto &featureItem = layerErrors.at( index.row() ); + switch ( role ) { case Qt::DisplayRole: - QgsGeometryValidationService::FeatureError error = mGeometryValidationService->featureError( mCurrentLayer, index.row() ); - QgsFeature feature = mCurrentLayer->getFeature( error.featureId ); + { + QgsFeature feature = mCurrentLayer->getFeature( featureItem.fid ); // TODO: this should be cached! mExpressionContext.setFeature( feature ); QString featureTitle = mDisplayExpression.evaluate( &mExpressionContext ).toString(); - return QStringLiteral( "%1: %2" ).arg( featureTitle, error.error.what() ); + if ( featureTitle.isEmpty() ) + featureTitle = featureItem.fid; + + if ( featureItem.errors.count() > 1 ) + return tr( "%1: %n Errors", "", featureItem.errors.count() ).arg( featureTitle ); + else if ( featureItem.errors.count() == 1 ) + return tr( "%1: %2" ).arg( featureTitle, featureItem.errors.at( 0 ).what() ); +#if 0 + else + return tr( "%1: No Errors" ).arg( featureTitle ); +#endif + } + + case Qt::DecorationRole: + { + if ( mGeometryValidationService->validationActive( mCurrentLayer, featureItem.fid ) ) + return QgsApplication::getThemeIcon( "/mActionTracing.svg" ); + else + return QVariant(); + } } return QVariant(); @@ -60,8 +86,90 @@ void QgsGeometryValidationModel::setCurrentLayer( QgsVectorLayer *currentLayer ) beginResetModel(); mCurrentLayer = currentLayer; - mDisplayExpression = mCurrentLayer->displayExpression(); - mExpressionContext = QgsExpressionContext( QgsExpressionContextUtils::globalProjectLayerScopes( mCurrentLayer ) ); - mDisplayExpression.prepare( &mExpressionContext ); + if ( mCurrentLayer ) + { + mDisplayExpression = mCurrentLayer ? mCurrentLayer->displayExpression() : QString(); + mExpressionContext = QgsExpressionContext( QgsExpressionContextUtils::globalProjectLayerScopes( mCurrentLayer ) ); + mDisplayExpression.prepare( &mExpressionContext ); + } + else + { + mDisplayExpression = QString(); + mExpressionContext = QgsExpressionContext(); + } endResetModel(); } + +void QgsGeometryValidationModel::onGeometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList &errors ) +{ + auto &layerErrors = mErrorStorage[layer]; + + int featureIdx = errorsForFeature( layer, fid ); + + // The last check for this feature finished: remove + if ( featureIdx > -1 && errors.empty() && !mGeometryValidationService->validationActive( layer, fid ) ) + { + if ( mCurrentLayer == layer ) + beginRemoveRows( QModelIndex(), featureIdx, featureIdx ); + + layerErrors.removeAt( featureIdx ); + + if ( mCurrentLayer == layer ) + endRemoveRows(); + } + else if ( !errors.empty() ) + { + // a new or updated feature + if ( featureIdx == -1 ) + { + featureIdx = layerErrors.count(); + if ( mCurrentLayer == layer ) + beginInsertRows( QModelIndex(), featureIdx, featureIdx ); + + layerErrors << FeatureErrors( fid ); + + if ( mCurrentLayer == layer ) + endInsertRows(); + } + + auto &featureItem = layerErrors[featureIdx]; + featureItem.errors.append( errors ); + if ( mCurrentLayer == layer ) + { + QModelIndex modelIndex = index( featureIdx, 0, QModelIndex() ); + emit dataChanged( modelIndex, modelIndex ); + } + } +} + +void QgsGeometryValidationModel::onGeometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid ) +{ + auto &layerErrors = mErrorStorage[layer]; + int featureIdx = errorsForFeature( layer, fid ); + if ( featureIdx != -1 ) + { + auto &featureItem = layerErrors[featureIdx]; + + featureItem.errors.clear(); + + if ( mCurrentLayer == layer ) + { + QModelIndex modelIndex = index( featureIdx, 0, QModelIndex() ); + emit dataChanged( modelIndex, modelIndex ); + } + } +} + +int QgsGeometryValidationModel::errorsForFeature( QgsVectorLayer *layer, QgsFeatureId fid ) +{ + const auto &layerErrors = mErrorStorage[layer]; + int idx = 0; + + for ( const auto &feature : layerErrors ) + { + if ( feature.fid == fid ) + return idx; + idx++; + } + return -1; +} diff --git a/src/app/qgsgeometryvalidationmodel.h b/src/app/qgsgeometryvalidationmodel.h index 948c06176da..7fd579bf1a4 100644 --- a/src/app/qgsgeometryvalidationmodel.h +++ b/src/app/qgsgeometryvalidationmodel.h @@ -12,19 +12,44 @@ class QgsGeometryValidationModel : public QAbstractItemModel public: QgsGeometryValidationModel( QgsGeometryValidationService *geometryValidationService, QObject *parent = nullptr ); + QModelIndex index( int row, int column, const QModelIndex &parent ) const override; QModelIndex parent( const QModelIndex &child ) const override; int rowCount( const QModelIndex &parent ) const override; int columnCount( const QModelIndex &parent ) const override; QVariant data( const QModelIndex &index, int role ) const override; + QgsVectorLayer *currentLayer() const; + + public slots: void setCurrentLayer( QgsVectorLayer *currentLayer ); + private slots: + void onGeometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList &errors ); + void onGeometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid ); + private: + struct FeatureErrors + { + FeatureErrors() + {} + + FeatureErrors( QgsFeatureId fid ) + : fid( fid ) + {} + + QgsFeatureId fid; // TODO INITIALIZE PROPERLY + QList errors; + }; + + int errorsForFeature( QgsVectorLayer *layer, QgsFeatureId fid ); + QgsGeometryValidationService *mGeometryValidationService = nullptr; QgsVectorLayer *mCurrentLayer = nullptr; mutable QgsExpression mDisplayExpression; mutable QgsExpressionContext mExpressionContext; + + QMap > mErrorStorage; }; #endif // QGSGEOMETRYVALIDATIONMODEL_H diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 8302625f7b2..0babb6b6f72 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -33,6 +33,11 @@ QgsGeometryValidationService::~QgsGeometryValidationService() delete mIsValidGeometryCheck; } +bool QgsGeometryValidationService::validationActive( QgsVectorLayer *layer, QgsFeatureId feature ) const +{ + return false; +} + void QgsGeometryValidationService::onLayersAdded( const QList &layers ) { for ( QgsMapLayer *layer : layers ) @@ -58,45 +63,32 @@ void QgsGeometryValidationService::onLayersAdded( const QList &la void QgsGeometryValidationService::onFeatureAdded( QgsVectorLayer *layer, QgsFeatureId fid ) { - emit geometryCheckStarted( layer, fid ); - - QgsFeature feature = layer->getFeature( fid ); - const auto errors = mIsValidGeometryCheck->collectErrors( feature ); - for ( const auto &error : errors ) - { - qDebug() << error.what(); - } - - emit geometryCheckCompleted( layer, fid ); + processFeature( layer, fid ); } void QgsGeometryValidationService::onGeometryChanged( QgsVectorLayer *layer, QgsFeatureId fid, const QgsGeometry &geometry ) { - emit geometryCheckStarted( layer, fid ); + Q_UNUSED( geometry ) - QgsFeature feature = layer->getFeature( fid ); - const auto errors = mIsValidGeometryCheck->collectErrors( feature ); - for ( const auto &error : errors ) - { - qDebug() << error.what(); - } - - emit geometryCheckCompleted( layer, fid ); + cancelChecks( layer, fid ); + processFeature( layer, fid ); } void QgsGeometryValidationService::onFeatureDeleted( QgsVectorLayer *layer, QgsFeatureId fid ) { - // TODO: cleanup any ongoing validation threads. - - emit geometryCheckCompleted( layer, fid ); + cancelChecks( layer, fid ); } -QgsGeometryValidationService::FeatureErrors QgsGeometryValidationService::featureErrors( QgsVectorLayer *layer ) const +void QgsGeometryValidationService::cancelChecks( QgsVectorLayer *layer, QgsFeatureId fid ) { - return mFeatureErrors.value( layer ); + } -QgsGeometryValidationService::FeatureError QgsGeometryValidationService::featureError( QgsVectorLayer *layer, int errorIndex ) +void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFeatureId fid ) { - return mFeatureErrors.value( layer ).value( errorIndex ); + emit geometryCheckStarted( layer, fid ); + + QgsFeature feature = layer->getFeature( fid ); + const auto errors = mIsValidGeometryCheck->collectErrors( feature ); + emit geometryCheckCompleted( layer, fid, errors ); } diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index 76b4454ee41..01a67f3b847 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -34,7 +34,13 @@ class QgsGeometryValidationService : public QObject public: struct FeatureError { - QgsFeatureId featureId; + FeatureError() + {} + FeatureError( QgsFeatureId fid, QgsGeometry::Error error ) + : featureId( fid ) + , error( error ) + {} + QgsFeatureId featureId = std::numeric_limits::min(); QgsGeometry::Error error; }; @@ -43,13 +49,11 @@ class QgsGeometryValidationService : public QObject QgsGeometryValidationService( QgsProject *project ); ~QgsGeometryValidationService(); - FeatureErrors featureErrors( QgsVectorLayer *layer ) const; - - FeatureError featureError( QgsVectorLayer *layer, int errorIndex ); + bool validationActive( QgsVectorLayer *layer, QgsFeatureId feature ) const; signals: void geometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid ); - void geometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid ); + void geometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList &errors ); private slots: void onLayersAdded( const QList &layers ); @@ -60,12 +64,13 @@ class QgsGeometryValidationService : public QObject private: void cancelChecks( QgsVectorLayer *layer, QgsFeatureId fid ); + void processFeature( QgsVectorLayer *layer, QgsFeatureId fid ); + QgsIsValidGeometryCheck *mIsValidGeometryCheck; QgsProject *mProject; QMap mActiveChecks; - QMap mFeatureErrors; }; #endif // QGSGEOMETRYVALIDATIONSERVICE_H From 71dbd5a5fe320896ebbebf86f371f6e2712784a9 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 12 Sep 2018 09:52:12 +0200 Subject: [PATCH 003/126] docstrings --- src/app/qgsgeometryvalidationservice.cpp | 3 --- src/app/qgsgeometryvalidationservice.h | 9 +++++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 0babb6b6f72..14ff3f16a92 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -24,13 +24,10 @@ email : matthias@opengis.ch QgsGeometryValidationService::QgsGeometryValidationService( QgsProject *project ) { connect( project, &QgsProject::layersAdded, this, &QgsGeometryValidationService::onLayersAdded ); - - mIsValidGeometryCheck = new QgsIsValidGeometryCheck(); } QgsGeometryValidationService::~QgsGeometryValidationService() { - delete mIsValidGeometryCheck; } bool QgsGeometryValidationService::validationActive( QgsVectorLayer *layer, QgsFeatureId feature ) const diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index 01a67f3b847..5c6812cdd21 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -27,6 +27,11 @@ class QgsVectorLayer; // TODO: Should be retrieved from registry!! class QgsIsValidGeometryCheck; + +/** + * This service connects to all layers in a project and triggers validation + * of features whenever they are edited. + */ class QgsGeometryValidationService : public QObject { Q_OBJECT @@ -49,6 +54,10 @@ class QgsGeometryValidationService : public QObject QgsGeometryValidationService( QgsProject *project ); ~QgsGeometryValidationService(); + /** + * Returns if a validation is active for the specified \a feature on + * \a layer. + */ bool validationActive( QgsVectorLayer *layer, QgsFeatureId feature ) const; signals: From 5e27163705cd9fd3ce4ffcd3adb729b002e043dd Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 12 Sep 2018 11:15:39 +0200 Subject: [PATCH 004/126] Save geometry configuration to layer --- src/core/qgsgeometryoptions.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/core/qgsgeometryoptions.h b/src/core/qgsgeometryoptions.h index b33c7a6eb66..406e8d0cf5b 100644 --- a/src/core/qgsgeometryoptions.h +++ b/src/core/qgsgeometryoptions.h @@ -85,6 +85,34 @@ class CORE_EXPORT QgsGeometryOptions */ void apply( QgsGeometry &geometry ) const; + /** + * A list of activated geometry checks. + * + * \since QGIS 3.4 + */ + QStringList geometryChecks() const; + + /** + * A list of activated geometry checks. + * + * \since QGIS 3.4 + */ + void setGeometryChecks( const QStringList &geometryChecks ); + + /** + * Access the configuration for the check \a checkId. + * + * \since QGIS 3.4 + */ + QVariantMap checkConfiguration( const QString &checkId ) const; + + /** + * Set the configuration for the check \a checkId. + * + * \since QGIS 3.4 + */ + void setCheckConfiguration( const QString &checkId, const QVariantMap &checkConfiguration ); + /** * Write the geometry options to the \a node. * @@ -116,6 +144,9 @@ class CORE_EXPORT QgsGeometryOptions * \since QGIS 3.4 */ double mGeometryPrecision = 0.0; + + QStringList mGeometryChecks; + QVariantMap mCheckConfiguration; }; #endif // QGSGEOMETRYOPTIONS_H From 1b0bd51434a0e8b94f7bc50f3c6c7d4ae58b4b69 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Fri, 14 Sep 2018 10:46:42 +0200 Subject: [PATCH 005/126] Registry --- .../auto_generated/qgsgeometryoptions.sip.in | 29 +++++++++++++++++++ .../qgsgeometrycheckfactory.cpp | 18 ++++++++++++ src/app/qgsgeometryvalidationmodel.cpp | 1 + src/app/qgsgeometryvalidationservice.cpp | 1 + 4 files changed, 49 insertions(+) diff --git a/python/core/auto_generated/qgsgeometryoptions.sip.in b/python/core/auto_generated/qgsgeometryoptions.sip.in index f003e963177..ae6e0da4a1f 100644 --- a/python/core/auto_generated/qgsgeometryoptions.sip.in +++ b/python/core/auto_generated/qgsgeometryoptions.sip.in @@ -9,6 +9,7 @@ + class QgsGeometryOptions { %Docstring @@ -72,6 +73,34 @@ Determines if at least one fix is enabled. %Docstring Apply any fixes configured on this class to ``geometry``. +.. versionadded:: 3.4 +%End + + QStringList geometryChecks() const; +%Docstring +A list of activated geometry checks. + +.. versionadded:: 3.4 +%End + + void setGeometryChecks( const QStringList &geometryChecks ); +%Docstring +A list of activated geometry checks. + +.. versionadded:: 3.4 +%End + + QVariantMap checkConfiguration( const QString &checkId ) const; +%Docstring +Access the configuration for the check ``checkId``. + +.. versionadded:: 3.4 +%End + + void setCheckConfiguration( const QString &checkId, const QVariantMap &checkConfiguration ); +%Docstring +Set the configuration for the check ``checkId``. + .. versionadded:: 3.4 %End diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp index e69de29bb2d..dafbe209640 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp @@ -0,0 +1,18 @@ +/*************************************************************************** + qgsgeometrycheckfactory.cpp + -------------------------------------- + Date : September 2018 + Copyright : (C) 2018 Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsgeometrycheckfactory.h" + +#include "qgis.h" diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index f440e983edb..a6a73473e33 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -62,6 +62,7 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) #endif } + case Qt::DecorationRole: { if ( mGeometryValidationService->validationActive( mCurrentLayer, featureItem.fid ) ) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 14ff3f16a92..12e9ccc773e 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -24,6 +24,7 @@ email : matthias@opengis.ch QgsGeometryValidationService::QgsGeometryValidationService( QgsProject *project ) { connect( project, &QgsProject::layersAdded, this, &QgsGeometryValidationService::onLayersAdded ); + mIsValidGeometryCheck = new QgsIsValidGeometryCheck(); } QgsGeometryValidationService::~QgsGeometryValidationService() From cde294ebb83eeea0102fe37dab95095717caff38 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Fri, 14 Sep 2018 20:07:21 +0200 Subject: [PATCH 006/126] UI improvements --- .../qgsgeometrycheckfactory.cpp | 5 ++++ .../qgsgeometrycheckfactory.h | 2 ++ .../qgsgeometrycheckregistry.h | 2 ++ src/app/CMakeLists.txt | 1 + src/app/qgisapp.cpp | 5 ++++ src/app/qgsvectorlayerproperties.cpp | 10 +++---- src/ui/qgsvectorlayerpropertiesbase.ui | 30 ++++++++++++++++--- 7 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp index dafbe209640..042483a3412 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp @@ -16,3 +16,8 @@ #include "qgsgeometrycheckfactory.h" #include "qgis.h" + +QgsGeometryCheck::CheckType QgsGeometryCheckFactory::flags() const +{ + return QgsGeometryCheck::FeatureNodeCheck; +} diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h index 5b000aeea1c..0214f8d2132 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h @@ -16,6 +16,8 @@ #ifndef QGSGEOMETRYCHECKFACTORY_H #define QGSGEOMETRYCHECKFACTORY_H +#define SIP_NO_FILE + #include #include #include diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h index d9809ff9566..6c5ee1ae7af 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h @@ -16,6 +16,8 @@ #ifndef QGSGEOMETRYCHECKREGISTRY_H #define QGSGEOMETRYCHECKREGISTRY_H +#define SIP_NO_FILE + #include #include #include diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 1ccea8f5211..998926be14e 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -634,6 +634,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/src/app/dwg ${CMAKE_SOURCE_DIR}/src/app/mesh ${CMAKE_SOURCE_DIR}/src/app/locator + ${CMAKE_SOURCE_DIR}/src/analysis ${CMAKE_SOURCE_DIR}/src/analysis/raster ${CMAKE_SOURCE_DIR}/src/core ${CMAKE_SOURCE_DIR}/src/core/annotations diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 9eb2a405576..61f63f8eca5 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -81,6 +81,9 @@ #include "qgsvectorlayerjoinbuffer.h" #include "qgsgeometryvalidationservice.h" +#include "qgsanalysis.h" +#include "qgsgeometrycheckregistry.h" + #ifdef HAVE_3D #include "qgsabstract3drenderer.h" #include "qgs3danimationsettings.h" @@ -922,6 +925,8 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh functionProfile( &QgisApp::initLayouts, this, QStringLiteral( "Initialize layouts support" ) ); startProfile( QStringLiteral( "Geometry validation" ) ); + QgsAnalysis::instance()->geometryCheckRegistry()->initialize(); + mGeometryValidationService = qgis::make_unique( QgsProject::instance() ); mGeometryValidationDock = new QgsGeometryValidationDock( tr( "Geometry Validation" ) ); mGeometryValidationModel = new QgsGeometryValidationModel( mGeometryValidationService.get(), mGeometryValidationDock ); diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index 5382fe64fac..e3e84739201 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -400,17 +400,17 @@ QgsVectorLayerProperties::QgsVectorLayerProperties( if ( mLayer->isSpatial() ) { mRemoveDuplicateNodesCheckbox->setEnabled( true ); - mGeometryPrecisionSpinBox->setEnabled( true ); + mGeometryPrecisionLineEdit->setEnabled( true ); mRemoveDuplicateNodesCheckbox->setChecked( mLayer->geometryOptions()->removeDuplicateNodes() ); - mGeometryPrecisionSpinBox->setValue( mLayer->geometryOptions()->geometryPrecision() ); + mGeometryPrecisionLineEdit->setText( QString::number( mLayer->geometryOptions()->geometryPrecision() ) ); - mGeometryPrecisionSpinBox->setSuffix( QStringLiteral( " [%1]" ).arg( QgsUnitTypes::toAbbreviatedString( mLayer->crs().mapUnits() ) ) ); + mPrecisionUnitsLabel->setText( QStringLiteral( "[%1]" ).arg( QgsUnitTypes::toAbbreviatedString( mLayer->crs().mapUnits() ) ) ); } else { mRemoveDuplicateNodesCheckbox->setEnabled( false ); - mGeometryPrecisionSpinBox->setEnabled( false ); + mGeometryPrecisionLineEdit->setEnabled( false ); mGeometryAutoFixesGroupBox->setEnabled( false ); } @@ -753,7 +753,7 @@ void QgsVectorLayerProperties::apply() #endif mLayer->geometryOptions()->setRemoveDuplicateNodes( mRemoveDuplicateNodesCheckbox->isChecked() ); - mLayer->geometryOptions()->setGeometryPrecision( mGeometryPrecisionSpinBox->value() ); + mLayer->geometryOptions()->setGeometryPrecision( mGeometryPrecisionLineEdit->text().toDouble() ); mLayer->triggerRepaint(); // notify the project we've made a change diff --git a/src/ui/qgsvectorlayerpropertiesbase.ui b/src/ui/qgsvectorlayerpropertiesbase.ui index c1cc06101ae..d87ab828508 100644 --- a/src/ui/qgsvectorlayerpropertiesbase.ui +++ b/src/ui/qgsvectorlayerpropertiesbase.ui @@ -721,8 +721,8 @@ border-radius: 2px; 0 0 - 100 - 30 + 653 + 806 @@ -907,8 +907,8 @@ border-radius: 2px; 0 0 - 104 - 102 + 653 + 806 @@ -2410,6 +2410,28 @@ border-radius: 2px; + + + + Geometry validation + + + + + + Qt::Vertical + + + + 20 + 352 + + + + + + + From 5d7c33eb1f5387e3c07b767f7820172cccd1a2e4 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 17 Sep 2018 09:59:02 +0200 Subject: [PATCH 007/126] Move to unified single geometry check --- .../vector/geometry_checker/qgssinglegeometrycheck.cpp | 5 +++++ src/app/qgsgeometryvalidationservice.cpp | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp index 6f511d893f6..b81da010bcf 100644 --- a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp +++ b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp @@ -100,3 +100,8 @@ bool QgsGeometryCheckErrorSingle::handleChanges( const QgsGeometryCheck::Changes return mError->handleChanges( changes.value( layerId() ).value( featureId() ) ); } + +void QgsSingleGeometryCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap &ids ) const +{ + +} diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 12e9ccc773e..a3220368cee 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -87,6 +87,7 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea emit geometryCheckStarted( layer, fid ); QgsFeature feature = layer->getFeature( fid ); - const auto errors = mIsValidGeometryCheck->collectErrors( feature ); + // TODO: this is a bit hardcore + const auto errors = mIsValidGeometryCheck->processGeometry( QgsGeometryCheckerUtils::LayerFeature( nullptr, feature ) ); emit geometryCheckCompleted( layer, fid, errors ); } From 2f5c7a3ae04903bb1c7a44db3ac00a238a8b37f5 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Fri, 14 Sep 2018 12:11:23 +0200 Subject: [PATCH 008/126] New configuration page for geometry validitiy --- src/ui/qgsvectorlayerpropertiesbase.ui | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/ui/qgsvectorlayerpropertiesbase.ui b/src/ui/qgsvectorlayerpropertiesbase.ui index d87ab828508..2ec6894ed62 100644 --- a/src/ui/qgsvectorlayerpropertiesbase.ui +++ b/src/ui/qgsvectorlayerpropertiesbase.ui @@ -2410,28 +2410,6 @@ border-radius: 2px; - - - - Geometry validation - - - - - - Qt::Vertical - - - - 20 - 352 - - - - - - - From d7597f9307061f9b323099f8d684cddd485cb6ec Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 17 Sep 2018 13:01:30 +0200 Subject: [PATCH 009/126] single geometry xyz --- .../vector/geometry_checker/qgssinglegeometrycheck.cpp | 7 ++++++- src/app/qgsgeometryvalidationservice.cpp | 7 ++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp index b81da010bcf..a49911c1ea4 100644 --- a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp +++ b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp @@ -103,5 +103,10 @@ bool QgsGeometryCheckErrorSingle::handleChanges( const QgsGeometryCheck::Changes void QgsSingleGeometryCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap &ids ) const { - + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext ); + for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) + { + errors.append( processGeometry( layerFeature.geometry() ) ); + } } diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index a3220368cee..f76d9ce8dd6 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -24,7 +24,8 @@ email : matthias@opengis.ch QgsGeometryValidationService::QgsGeometryValidationService( QgsProject *project ) { connect( project, &QgsProject::layersAdded, this, &QgsGeometryValidationService::onLayersAdded ); - mIsValidGeometryCheck = new QgsIsValidGeometryCheck(); + // TODO: should not provide a nullptr context + mIsValidGeometryCheck = new QgsIsValidGeometryCheck( nullptr ); } QgsGeometryValidationService::~QgsGeometryValidationService() @@ -88,6 +89,6 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea QgsFeature feature = layer->getFeature( fid ); // TODO: this is a bit hardcore - const auto errors = mIsValidGeometryCheck->processGeometry( QgsGeometryCheckerUtils::LayerFeature( nullptr, feature ) ); - emit geometryCheckCompleted( layer, fid, errors ); + const auto errors = mIsValidGeometryCheck->processGeometry( feature.geometry() ); + // emit geometryCheckCompleted( layer, fid, errors ); } From ceec9105f478f5cea243e07df19d694756912e12 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 18 Sep 2018 11:21:10 +0200 Subject: [PATCH 010/126] Add python bindings for geometry checker --- src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h | 2 -- src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h index 0214f8d2132..5b000aeea1c 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h @@ -16,8 +16,6 @@ #ifndef QGSGEOMETRYCHECKFACTORY_H #define QGSGEOMETRYCHECKFACTORY_H -#define SIP_NO_FILE - #include #include #include diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h index 6c5ee1ae7af..d9809ff9566 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h @@ -16,8 +16,6 @@ #ifndef QGSGEOMETRYCHECKREGISTRY_H #define QGSGEOMETRYCHECKREGISTRY_H -#define SIP_NO_FILE - #include #include #include From e0697fb43692ea17833dc183cc0cc74a9a829843 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 18 Sep 2018 11:21:23 +0200 Subject: [PATCH 011/126] Fix build warnings --- .../geometry_checker/qgsgeometryisvalidcheck.cpp | 2 ++ .../vector/geometry_checker/qgssinglegeometrycheck.cpp | 10 ---------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.cpp index ee5acbebd6d..79401bbf190 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.cpp @@ -30,6 +30,8 @@ QList QgsGeometryIsValidCheck::compatibleGeometryType QList QgsGeometryIsValidCheck::processGeometry( const QgsGeometry &geometry ) const { + Q_UNUSED( configuration ) + QVector errors; QgsGeometry::ValidationMethod method = QgsGeometry::ValidatorQgisInternal; diff --git a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp index a49911c1ea4..6f511d893f6 100644 --- a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp +++ b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp @@ -100,13 +100,3 @@ bool QgsGeometryCheckErrorSingle::handleChanges( const QgsGeometryCheck::Changes return mError->handleChanges( changes.value( layerId() ).value( featureId() ) ); } - -void QgsSingleGeometryCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap &ids ) const -{ - QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; - QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext ); - for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) - { - errors.append( processGeometry( layerFeature.geometry() ) ); - } -} From dd608bbeb94f93932e34113c78a63fec7eb2359a Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 18 Sep 2018 15:41:11 +0200 Subject: [PATCH 012/126] More python bindings --- src/app/qgsgeometryvalidationservice.cpp | 2 +- src/app/qgsmaplayerstylecategoriesmodel.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index f76d9ce8dd6..3c9e1b29ebf 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -89,6 +89,6 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea QgsFeature feature = layer->getFeature( fid ); // TODO: this is a bit hardcore - const auto errors = mIsValidGeometryCheck->processGeometry( feature.geometry() ); + const auto errors = mIsValidGeometryCheck->processGeometry( feature.geometry(), QVariantMap() ); // emit geometryCheckCompleted( layer, fid, errors ); } diff --git a/src/app/qgsmaplayerstylecategoriesmodel.cpp b/src/app/qgsmaplayerstylecategoriesmodel.cpp index b0e8149f20a..cef26f68eea 100644 --- a/src/app/qgsmaplayerstylecategoriesmodel.cpp +++ b/src/app/qgsmaplayerstylecategoriesmodel.cpp @@ -230,6 +230,7 @@ QVariant QgsMapLayerStyleCategoriesModel::data( const QModelIndex &index, int ro return QVariant(); } break; + } return QVariant(); } From 6f261f706793b69e61ab072beb18153c0624e8fe Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 19 Sep 2018 13:51:42 +0200 Subject: [PATCH 013/126] Flags --- src/ui/qgsvectorlayerpropertiesbase.ui | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/ui/qgsvectorlayerpropertiesbase.ui b/src/ui/qgsvectorlayerpropertiesbase.ui index 2ec6894ed62..879f5a82bd4 100644 --- a/src/ui/qgsvectorlayerpropertiesbase.ui +++ b/src/ui/qgsvectorlayerpropertiesbase.ui @@ -721,8 +721,8 @@ border-radius: 2px; 0 0 - 653 - 806 + 100 + 30 @@ -907,8 +907,8 @@ border-radius: 2px; 0 0 - 653 - 806 + 104 + 102 @@ -2410,6 +2410,13 @@ border-radius: 2px; + + + + Geometry validation + + + From e55bb7a3b6eda57379b1695f4ff750513eaf85e1 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 19 Sep 2018 16:33:50 +0200 Subject: [PATCH 014/126] Fix build --- src/core/qgsgeometryoptions.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/core/qgsgeometryoptions.cpp b/src/core/qgsgeometryoptions.cpp index 718a27035a1..53752116fa9 100644 --- a/src/core/qgsgeometryoptions.cpp +++ b/src/core/qgsgeometryoptions.cpp @@ -53,6 +53,26 @@ void QgsGeometryOptions::apply( QgsGeometry &geometry ) const geometry.removeDuplicateNodes(); } +QStringList QgsGeometryOptions::geometryChecks() const +{ + return mGeometryChecks; +} + +void QgsGeometryOptions::setGeometryChecks( const QStringList &geometryChecks ) +{ + mGeometryChecks = geometryChecks; +} + +QVariantMap QgsGeometryOptions::checkConfiguration( const QString &checkId ) const +{ + return mCheckConfiguration.value( checkId ).toMap(); +} + +void QgsGeometryOptions::setCheckConfiguration( const QString &checkId, const QVariantMap &checkConfiguration ) +{ + mCheckConfiguration[checkId] = checkConfiguration; +} + void QgsGeometryOptions::writeXml( QDomNode &node ) const { QDomElement geometryOptionsElement = node.ownerDocument().createElement( QStringLiteral( "geometryOptions" ) ); From e8a7ab4303c6de0342ff4df7ad98c1b2316d6f58 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 19 Sep 2018 18:14:06 +0200 Subject: [PATCH 015/126] Registry updates --- .../vector/geometry_checker/qgsgeometrycheckfactory.cpp | 9 +++++++-- src/app/qgsvectorlayerproperties.cpp | 6 ++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp index 042483a3412..475754603f2 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp @@ -17,7 +17,12 @@ #include "qgis.h" -QgsGeometryCheck::CheckType QgsGeometryCheckFactory::flags() const +bool QgsGeometryCheckFactory::isCompatible( QgsVectorLayer *layer ) const { - return QgsGeometryCheck::FeatureNodeCheck; + return true; +} + +QgsGeometryCheck::Flags QgsGeometryCheckFactory::flags() const +{ + return nullptr; } diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index e3e84739201..f2e6b54e9c8 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -64,6 +64,9 @@ #include "qgsvectorlayersavestyledialog.h" #include "qgsvectorlayerloadstyledialog.h" #include "qgsmessagebar.h" +#include "qgsgeometrycheckregistry.h" +#include "qgsgeometrycheck.h" +#include "qgsanalysis.h" #include "layertree/qgslayertreelayer.h" #include "qgslayertree.h" @@ -755,6 +758,9 @@ void QgsVectorLayerProperties::apply() mLayer->geometryOptions()->setRemoveDuplicateNodes( mRemoveDuplicateNodesCheckbox->isChecked() ); mLayer->geometryOptions()->setGeometryPrecision( mGeometryPrecisionLineEdit->text().toDouble() ); + // update symbology + emit refreshLegend( mLayer->id() ); + mLayer->triggerRepaint(); // notify the project we've made a change QgsProject::instance()->setDirty( true ); From 0b20ae620333e7d773c636908b50edc77b271a8b Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Thu, 20 Sep 2018 11:41:38 -0400 Subject: [PATCH 016/126] activate/deactivate geometry checks in vector layer properties --- .../qgsgeometrycheckfactory.cpp | 3 ++- src/app/qgsvectorlayerproperties.cpp | 22 +++++++++++++++++++ src/app/qgsvectorlayerproperties.h | 2 ++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp index 475754603f2..b1dbb4ffdce 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp @@ -19,10 +19,11 @@ bool QgsGeometryCheckFactory::isCompatible( QgsVectorLayer *layer ) const { + Q_UNUSED( layer ); return true; } QgsGeometryCheck::Flags QgsGeometryCheckFactory::flags() const { - return nullptr; + return QgsGeometryCheck::Flags( QgsGeometryCheck::SingleGeometryCheck | QgsGeometryCheck::AvailableInValidation ); } diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index f2e6b54e9c8..16d251e2e9e 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -61,6 +61,7 @@ #include "qgslabelinggui.h" #include "qgssymbollayer.h" #include "qgsgeometryoptions.h" +#include "qgsgeometrycheckfactory.h" #include "qgsvectorlayersavestyledialog.h" #include "qgsvectorlayerloadstyledialog.h" #include "qgsmessagebar.h" @@ -409,6 +410,18 @@ QgsVectorLayerProperties::QgsVectorLayerProperties( mGeometryPrecisionLineEdit->setText( QString::number( mLayer->geometryOptions()->geometryPrecision() ) ); mPrecisionUnitsLabel->setText( QStringLiteral( "[%1]" ).arg( QgsUnitTypes::toAbbreviatedString( mLayer->crs().mapUnits() ) ) ); + + QLayout *layout = new QVBoxLayout(); + const QList factories = QgsAnalysis::instance()->geometryCheckRegistry()->geometryCheckFactories( mLayer, QgsGeometryCheck::Flag::SingleGeometryCheck ); + const QStringList activeChecks = mLayer->geometryOptions()->geometryChecks(); + for ( const QgsGeometryCheckFactory *factory : factories ) + { + QCheckBox *cb = new QCheckBox( factory->name() ); + cb->setChecked( activeChecks.contains( factory->id() ) ); + mGeometryCheckFactoriesGroupBoxes.insert( cb, factory->id() ); + layout->addWidget( cb ); + } + mGeometryValidationGroupBox->setLayout( layout ); } else { @@ -758,6 +771,15 @@ void QgsVectorLayerProperties::apply() mLayer->geometryOptions()->setRemoveDuplicateNodes( mRemoveDuplicateNodesCheckbox->isChecked() ); mLayer->geometryOptions()->setGeometryPrecision( mGeometryPrecisionLineEdit->text().toDouble() ); + QStringList activeChecks; + QHash::const_iterator it; + for ( it = mGeometryCheckFactoriesGroupBoxes.constBegin(); it != mGeometryCheckFactoriesGroupBoxes.constEnd(); ++it ) + { + if ( it.key()->isChecked() ) + activeChecks << it.value(); + } + mLayer->geometryOptions()->setGeometryChecks( activeChecks ); + // update symbology emit refreshLegend( mLayer->id() ); diff --git a/src/app/qgsvectorlayerproperties.h b/src/app/qgsvectorlayerproperties.h index 8e548a50a7c..8fe4a9433fe 100644 --- a/src/app/qgsvectorlayerproperties.h +++ b/src/app/qgsvectorlayerproperties.h @@ -245,6 +245,8 @@ class APP_EXPORT QgsVectorLayerProperties : public QgsOptionsDialogBase, private QgsVectorLayer3DRendererWidget *mVector3DWidget = nullptr; + QHash mGeometryCheckFactoriesGroupBoxes; + private slots: void openPanel( QgsPanelWidget *panel ); From 1df0a3ea70555f359dab4b543fc43e85c2d7c02d Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Thu, 20 Sep 2018 13:27:23 -0400 Subject: [PATCH 017/126] add flags and isCompatible to QgsGeometryCheck --- .../qgsgeometrycheckfactory.cpp | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp index b1dbb4ffdce..e69de29bb2d 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.cpp @@ -1,29 +0,0 @@ -/*************************************************************************** - qgsgeometrycheckfactory.cpp - -------------------------------------- - Date : September 2018 - Copyright : (C) 2018 Matthias Kuhn - Email : matthias@opengis.ch - *************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgsgeometrycheckfactory.h" - -#include "qgis.h" - -bool QgsGeometryCheckFactory::isCompatible( QgsVectorLayer *layer ) const -{ - Q_UNUSED( layer ); - return true; -} - -QgsGeometryCheck::Flags QgsGeometryCheckFactory::flags() const -{ - return QgsGeometryCheck::Flags( QgsGeometryCheck::SingleGeometryCheck | QgsGeometryCheck::AvailableInValidation ); -} From 7593d51687cb2344b225cd4fb9f7c95046fffb1e Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Fri, 21 Sep 2018 09:51:10 -0400 Subject: [PATCH 018/126] add feature pool as argument to collect/fix error methods --- python/core/auto_generated/qgsgeometryoptions.sip.in | 1 - 1 file changed, 1 deletion(-) diff --git a/python/core/auto_generated/qgsgeometryoptions.sip.in b/python/core/auto_generated/qgsgeometryoptions.sip.in index ae6e0da4a1f..5ad7bd76120 100644 --- a/python/core/auto_generated/qgsgeometryoptions.sip.in +++ b/python/core/auto_generated/qgsgeometryoptions.sip.in @@ -9,7 +9,6 @@ - class QgsGeometryOptions { %Docstring From b0810b180db1bf52cdb1d87554b3b047ae0910fb Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Fri, 21 Sep 2018 11:20:56 -0400 Subject: [PATCH 019/126] QgsGeometryCheckError class requires context in constructor (not inherited classes yet) --- .../vector/geometry_checker/qgsgeometrycheckerror.sip.in | 2 ++ .../vector/geometry_checker/qgsgeometryanglecheck.cpp | 2 +- .../vector/geometry_checker/qgsgeometryareacheck.cpp | 4 ++-- .../vector/geometry_checker/qgsgeometrychecker.cpp | 6 +++--- .../vector/geometry_checker/qgsgeometrycheckerror.h | 3 +++ .../geometry_checker/qgsgeometrycontainedcheck.cpp | 2 +- .../vector/geometry_checker/qgsgeometrycontainedcheck.h | 2 +- .../vector/geometry_checker/qgsgeometrydanglecheck.cpp | 6 +++--- .../qgsgeometrydegeneratepolygoncheck.cpp | 4 ++-- .../geometry_checker/qgsgeometryduplicatecheck.cpp | 2 +- .../geometry_checker/qgsgeometryduplicatenodescheck.cpp | 4 ++-- .../qgsgeometryfollowboundariescheck.cpp | 6 +++--- .../vector/geometry_checker/qgsgeometryholecheck.cpp | 4 ++-- .../qgsgeometrylineintersectioncheck.cpp | 4 ++-- .../qgsgeometrylinelayerintersectioncheck.cpp | 6 +++--- .../qgsgeometrypointcoveredbylinecheck.cpp | 4 ++-- .../geometry_checker/qgsgeometrypointinpolygoncheck.cpp | 4 ++-- .../geometry_checker/qgsgeometrysegmentlengthcheck.cpp | 4 ++-- .../vector/geometry_checker/qgssinglegeometrycheck.cpp | 9 +++++++-- 19 files changed, 44 insertions(+), 34 deletions(-) diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in index e6b44f0eb99..c7d5929e678 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in @@ -33,6 +33,7 @@ the Free Software Foundation; either version 2 of the License, or * enum ValueType { ValueLength, ValueArea, ValueOther }; QgsGeometryCheckError( const QgsGeometryCheck *check, + const QgsGeometryCheckContext *context, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, const QgsPointXY &errorLocation, QgsVertexId vidx = QgsVertexId(), @@ -43,6 +44,7 @@ the Free Software Foundation; either version 2 of the License, or * const QgsGeometryCheck *check() const; + const QgsGeometryCheckContext *context() const; const QString &layerId() const; QgsFeatureId featureId() const; QgsGeometry geometry() const; diff --git a/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.cpp index de4f9c25dd1..a0cb8d1721c 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.cpp @@ -63,7 +63,7 @@ void QgsGeometryAngleCheck::collectErrors( const QMap double angle = std::acos( v21 * v23 ) / M_PI * 180.0; if ( angle < mMinAngle ) { - errors.append( new QgsGeometryCheckError( this, layerFeature, p2, QgsVertexId( iPart, iRing, iVert ), angle ) ); + errors.append( new QgsGeometryCheckError( this, context, layerFeature, p2, QgsVertexId( iPart, iRing, iVert ), angle ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometryareacheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryareacheck.cpp index dc4450efbee..754e8a0996f 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryareacheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryareacheck.cpp @@ -20,7 +20,7 @@ #include "qgsfeaturepool.h" #include "qgsgeometrycheckerror.h" -void QgsGeometryAreaCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryAreaCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); @@ -35,7 +35,7 @@ void QgsGeometryAreaCheck::collectErrors( const QMap const QgsAbstractGeometry *part = QgsGeometryCheckerUtils::getGeomPart( geom, iPart ); if ( checkThreshold( layerToMapUnits, part, value ) ) { - errors.append( new QgsGeometryCheckError( this, layerFeature, part->centroid(), QgsVertexId( iPart ), value * layerToMapUnits * layerToMapUnits, QgsGeometryCheckError::ValueArea ) ); + errors.append( new QgsGeometryCheckError( this, context, layerFeature, part->centroid(), QgsVertexId( iPart ), value * layerToMapUnits * layerToMapUnits, QgsGeometryCheckError::ValueArea ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrychecker.cpp b/src/analysis/vector/geometry_checker/qgsgeometrychecker.cpp index ca00a82ddd6..2378bb833e6 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrychecker.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrychecker.cpp @@ -200,14 +200,14 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, boo { if ( !recheckAreaFeatures.isEmpty() ) { - check->collectErrors( mFeaturePools, recheckErrors, mMessages, nullptr, recheckAreaFeatures ); + check->collectErrors( mFeaturePools, mContext, recheckErrors, mMessages, nullptr, recheckAreaFeatures ); } } else { if ( !recheckFeatures.isEmpty() ) { - check->collectErrors( mFeaturePools, recheckErrors, mMessages, nullptr, recheckFeatures ); + check->collectErrors( mFeaturePools, mContext, recheckErrors, mMessages, nullptr, recheckFeatures ); } } } @@ -285,7 +285,7 @@ void QgsGeometryChecker::runCheck( const QMap &featur // Run checks QList errors; QStringList messages; - check->collectErrors( featurePools, errors, messages, &mFeedback ); + check->collectErrors( featurePools, mContext, errors, messages, &mFeedback ); mErrorListMutex.lock(); mCheckErrors.append( errors ); mMessages.append( messages ); diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h index 9a06138877f..8a692f3d384 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h @@ -33,6 +33,7 @@ class ANALYSIS_EXPORT QgsGeometryCheckError enum ValueType { ValueLength, ValueArea, ValueOther }; QgsGeometryCheckError( const QgsGeometryCheck *check, + const QgsGeometryCheckContext *context, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, const QgsPointXY &errorLocation, QgsVertexId vidx = QgsVertexId(), @@ -44,6 +45,7 @@ class ANALYSIS_EXPORT QgsGeometryCheckError const QgsGeometryCheckError &operator=( const QgsGeometryCheckError & ) = delete; const QgsGeometryCheck *check() const { return mCheck; } + const QgsGeometryCheckContext *context() const {return mContext;} const QString &layerId() const { return mLayerId; } QgsFeatureId featureId() const { return mFeatureId; } // In map units @@ -99,6 +101,7 @@ class ANALYSIS_EXPORT QgsGeometryCheckError ValueType valueType = ValueOther ); const QgsGeometryCheck *mCheck = nullptr; + const QgsGeometryCheckContext *mContext = nullptr; QString mLayerId; QgsFeatureId mFeatureId; QgsGeometry mGeometry; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.cpp index 651449ac42a..6e084dc179c 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.cpp @@ -20,7 +20,7 @@ #include "qgsvectorlayer.h" -void QgsGeometryContainedCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryContainedCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext ); diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h index e0f162b5797..fddf11281a7 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h @@ -30,7 +30,7 @@ class ANALYSIS_EXPORT QgsGeometryContainedCheckError : public QgsGeometryCheckEr const QgsPointXY &errorLocation, const QgsGeometryCheckerUtils::LayerFeature &containingFeature ) - : QgsGeometryCheckError( check, layerFeature, errorLocation, QgsVertexId(), containingFeature.id(), ValueOther ) + : QgsGeometryCheckError( check, check->context(), layerFeature, errorLocation, QgsVertexId(), containingFeature.id(), ValueOther ) , mContainingFeature( qMakePair( containingFeature.layer()->id(), containingFeature.feature().id() ) ) { } const QPair &containingFeature() const { return mContainingFeature; } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.cpp index 96c075c3c6e..f0ec8cb0865 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.cpp @@ -19,7 +19,7 @@ #include "qgsvectorlayer.h" #include "qgsgeometrycheckerror.h" -void QgsGeometryDangleCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryDangleCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); @@ -84,11 +84,11 @@ void QgsGeometryDangleCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryDegeneratePolygonCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -35,7 +35,7 @@ void QgsGeometryDegeneratePolygonCheck::collectErrors( const QMapvertexAt( vidx ), vidx ) ); + errors.append( new QgsGeometryCheckError( this, context, layerFeature, geom->vertexAt( vidx ), vidx ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.cpp index cda0e9dd0d2..2f40f195188 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.cpp @@ -38,7 +38,7 @@ QString QgsGeometryDuplicateCheckError::duplicatesString( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryDuplicateCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true ); diff --git a/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp index 16cb45f80ab..f4ed50b46b0 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp @@ -19,7 +19,7 @@ #include "qgsfeaturepool.h" #include "qgsgeometrycheckerror.h" -void QgsGeometryDuplicateNodesCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryDuplicateNodesCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -41,7 +41,7 @@ void QgsGeometryDuplicateNodesCheck::collectErrors( const QMapvertexAt( QgsVertexId( iPart, iRing, jVert ) ); if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) < mContext->tolerance ) { - errors.append( new QgsGeometryCheckError( this, layerFeature, pj, QgsVertexId( iPart, iRing, jVert ) ) ); + errors.append( new QgsGeometryCheckError( this, context, layerFeature, pj, QgsVertexId( iPart, iRing, jVert ) ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp index b4b877c3589..c07f696ce5f 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp @@ -36,7 +36,7 @@ QgsGeometryFollowBoundariesCheck::~QgsGeometryFollowBoundariesCheck() delete mIndex; } -void QgsGeometryFollowBoundariesCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryFollowBoundariesCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -70,7 +70,7 @@ void QgsGeometryFollowBoundariesCheck::collectErrors( const QMapcentroid() ) ) ); + errors.append( new QgsGeometryCheckError( this, context, layerFeature, QgsPointXY( geom->centroid() ) ) ); } else { @@ -83,7 +83,7 @@ void QgsGeometryFollowBoundariesCheck::collectErrors( const QMapbuffer( -mContext->tolerance, 0 ) ); if ( !( geomEngine->contains( reducedRefGeom.constGet() ) || geomEngine->disjoint( reducedRefGeom.constGet() ) ) ) { - errors.append( new QgsGeometryCheckError( this, layerFeature, QgsPointXY( geom->centroid() ) ) ); + errors.append( new QgsGeometryCheckError( this, context, layerFeature, QgsPointXY( geom->centroid() ) ) ); break; } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometryholecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryholecheck.cpp index bccf84db0f6..bc70f6613fd 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryholecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryholecheck.cpp @@ -20,7 +20,7 @@ #include "qgsfeaturepool.h" #include "qgsgeometrycheckerror.h" -void QgsGeometryHoleCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryHoleCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -41,7 +41,7 @@ void QgsGeometryHoleCheck::collectErrors( const QMap { QgsPoint pos = poly->interiorRing( iRing - 1 )->centroid(); - errors.append( new QgsGeometryCheckError( this, layerFeature, pos, QgsVertexId( iPart, iRing ) ) ); + errors.append( new QgsGeometryCheckError( this, context, layerFeature, pos, QgsVertexId( iPart, iRing ) ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.cpp index ec613dd67fa..1bb00e3d4ea 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.cpp @@ -19,7 +19,7 @@ #include "qgsvectorlayer.h" #include "qgsgeometrycheckerror.h" -void QgsGeometryLineIntersectionCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryLineIntersectionCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -67,7 +67,7 @@ void QgsGeometryLineIntersectionCheck::collectErrors( const QMap intersections = QgsGeometryCheckerUtils::lineIntersections( line, testLine, mContext->tolerance ); for ( const QgsPoint &inter : intersections ) { - errors.append( new QgsGeometryCheckError( this, layerFeatureA, inter, QgsVertexId( iPart ), layerFeatureB.id() ) ); + errors.append( new QgsGeometryCheckError( this, context, layerFeatureA, inter, QgsVertexId( iPart ), layerFeatureB.id() ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.cpp index fe964a99a68..10f7d607e08 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.cpp @@ -20,7 +20,7 @@ #include "qgsfeaturepool.h" #include "qgsgeometrycheckerror.h" -void QgsGeometryLineLayerIntersectionCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryLineLayerIntersectionCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -52,7 +52,7 @@ void QgsGeometryLineLayerIntersectionCheck::collectErrors( const QMap intersections = QgsGeometryCheckerUtils::lineIntersections( line, testLine, mContext->tolerance ); for ( const QgsPoint &inter : intersections ) { - errors.append( new QgsGeometryCheckError( this, layerFeature, inter, QgsVertexId( iPart ), checkFeature.id() ) ); + errors.append( new QgsGeometryCheckError( this, context, layerFeature, inter, QgsVertexId( iPart ), checkFeature.id() ) ); } } else if ( const QgsPolygon *polygon = dynamic_cast( part ) ) @@ -63,7 +63,7 @@ void QgsGeometryLineLayerIntersectionCheck::collectErrors( const QMap intersections = QgsGeometryCheckerUtils::lineIntersections( line, ring, mContext->tolerance ); for ( const QgsPoint &inter : intersections ) { - errors.append( new QgsGeometryCheckError( this, layerFeature, inter, QgsVertexId( iPart ), checkFeature.id() ) ); + errors.append( new QgsGeometryCheckError( this, context, layerFeature, inter, QgsVertexId( iPart ), checkFeature.id() ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.cpp index fe472438db7..0a1d3efb85a 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.cpp @@ -19,7 +19,7 @@ #include "qgsfeaturepool.h" #include "qgsgeometrycheckerror.h" -void QgsGeometryPointCoveredByLineCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryPointCoveredByLineCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -66,7 +66,7 @@ void QgsGeometryPointCoveredByLineCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryPointInPolygonCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true ); @@ -58,7 +58,7 @@ void QgsGeometryPointInPolygonCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometrySegmentLengthCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -50,7 +50,7 @@ void QgsGeometrySegmentLengthCheck::collectErrors( const QMap mContext->tolerance ) { QgsPointXY pos( 0.5 * ( pi.x() + pj.x() ), 0.5 * ( pi.y() + pj.y() ) ); - errors.append( new QgsGeometryCheckError( this, layerFeature, pos, QgsVertexId( iPart, iRing, iVert ), dist * layerToMapUnits, QgsGeometryCheckError::ValueLength ) ); + errors.append( new QgsGeometryCheckError( this, context, layerFeature, pos, QgsVertexId( iPart, iRing, iVert ), dist * layerToMapUnits, QgsGeometryCheckError::ValueLength ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp index 6f511d893f6..847684bded6 100644 --- a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp +++ b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp @@ -81,8 +81,13 @@ QgsVertexId QgsSingleGeometryCheckError::vertexId() const return mVertexId; } -QgsGeometryCheckErrorSingle::QgsGeometryCheckErrorSingle( QgsSingleGeometryCheckError *error, const QgsGeometryCheckerUtils::LayerFeature &layerFeature ) - : QgsGeometryCheckError( error->check(), layerFeature, QgsPointXY( error->errorLocation().constGet()->centroid() ), error->vertexId() ) // TODO: should send geometry to QgsGeometryCheckError +QgsGeometryCheckErrorSingle::QgsGeometryCheckErrorSingle( QgsSingleGeometryCheckError *error, + const QgsGeometryCheckerUtils::LayerFeature &layerFeature ) + : QgsGeometryCheckError( error->check(), + error->check()->context(), + layerFeature, + QgsPointXY( error->errorLocation().constGet()->centroid() ), + error->vertexId() ) // TODO: should send geometry to QgsGeometryCheckError , mError( error ) { From 7df2c6305e6b9b85424d29cfa77a0c4d9c19dd01 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 24 Sep 2018 13:49:22 +0200 Subject: [PATCH 020/126] Add additional check factories for Gap, Sliver and Overlap checks --- .../vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp index 8f7a8311797..15504f6e337 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp @@ -27,5 +27,5 @@ bool QgsGeometrySliverPolygonCheck::checkThreshold( double layerToMapUnits, cons { return false; } - return value > mThresholdMapUnits; // the sliver threshold is actually a map unit independent number, just abusing QgsGeometryAreaCheck::mThresholdMapUnits to store it + return value > mCheckConfiguration.value( QStringLiteral( "thresholdMapUnits" ) ).toDouble(); // the sliver threshold is actually a map unit independent number, just abusing QgsGeometryAreaCheck::mThresholdMapUnits to store it } From 3817df69b2085a64427d5b7f2d883e45087fc707 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 24 Sep 2018 09:18:09 -0400 Subject: [PATCH 021/126] Revert "QgsGeometryCheckError class requires context in constructor (not inherited classes yet)" This reverts commit 73c89aee8c2f5ca6d2ce5a621b1ffad9f08bba02. --- .../vector/geometry_checker/qgsgeometrycheckerror.sip.in | 2 -- .../geometry_checker/qgsgeometrycheckerutils.sip.in | 2 +- .../vector/geometry_checker/qgsgeometryanglecheck.cpp | 2 +- .../vector/geometry_checker/qgsgeometryareacheck.cpp | 4 ++-- .../vector/geometry_checker/qgsgeometrychecker.cpp | 6 +++--- .../vector/geometry_checker/qgsgeometrycheckerror.h | 3 --- .../vector/geometry_checker/qgsgeometrycheckerutils.cpp | 6 +++--- .../vector/geometry_checker/qgsgeometrycheckerutils.h | 8 ++++---- .../geometry_checker/qgsgeometrycontainedcheck.cpp | 2 +- .../vector/geometry_checker/qgsgeometrycontainedcheck.h | 2 +- .../vector/geometry_checker/qgsgeometrydanglecheck.cpp | 6 +++--- .../qgsgeometrydegeneratepolygoncheck.cpp | 4 ++-- .../geometry_checker/qgsgeometryduplicatecheck.cpp | 2 +- .../geometry_checker/qgsgeometryduplicatenodescheck.cpp | 4 ++-- .../qgsgeometryfollowboundariescheck.cpp | 6 +++--- .../vector/geometry_checker/qgsgeometryholecheck.cpp | 4 ++-- .../qgsgeometrylineintersectioncheck.cpp | 4 ++-- .../qgsgeometrylinelayerintersectioncheck.cpp | 6 +++--- .../qgsgeometrypointcoveredbylinecheck.cpp | 4 ++-- .../geometry_checker/qgsgeometrypointinpolygoncheck.cpp | 4 ++-- .../geometry_checker/qgsgeometrysegmentlengthcheck.cpp | 4 ++-- .../vector/geometry_checker/qgssinglegeometrycheck.cpp | 9 ++------- 22 files changed, 42 insertions(+), 52 deletions(-) diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in index c7d5929e678..e6b44f0eb99 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in @@ -33,7 +33,6 @@ the Free Software Foundation; either version 2 of the License, or * enum ValueType { ValueLength, ValueArea, ValueOther }; QgsGeometryCheckError( const QgsGeometryCheck *check, - const QgsGeometryCheckContext *context, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, const QgsPointXY &errorLocation, QgsVertexId vidx = QgsVertexId(), @@ -44,7 +43,6 @@ the Free Software Foundation; either version 2 of the License, or * const QgsGeometryCheck *check() const; - const QgsGeometryCheckContext *context() const; const QString &layerId() const; QgsFeatureId featureId() const; QgsGeometry geometry() const; diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in index 44f2ea3b4fe..1bfa8b74ab3 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in @@ -35,7 +35,7 @@ the Free Software Foundation; either version 2 of the License, or * %End public: - LayerFeature( const QgsFeaturePool *pool, const QgsFeature &feature, const QgsGeometryCheckContext *context, bool useMapCrs ); + LayerFeature( const QgsFeaturePool *pool, const QgsFeature &feature, QgsGeometryCheckContext *context, bool useMapCrs ); %Docstring Create a new layer/feature combination. The layer is defined by ``pool``, ``feature`` needs to be from this layer. diff --git a/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.cpp index a0cb8d1721c..de4f9c25dd1 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.cpp @@ -63,7 +63,7 @@ void QgsGeometryAngleCheck::collectErrors( const QMap double angle = std::acos( v21 * v23 ) / M_PI * 180.0; if ( angle < mMinAngle ) { - errors.append( new QgsGeometryCheckError( this, context, layerFeature, p2, QgsVertexId( iPart, iRing, iVert ), angle ) ); + errors.append( new QgsGeometryCheckError( this, layerFeature, p2, QgsVertexId( iPart, iRing, iVert ), angle ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometryareacheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryareacheck.cpp index 754e8a0996f..dc4450efbee 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryareacheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryareacheck.cpp @@ -20,7 +20,7 @@ #include "qgsfeaturepool.h" #include "qgsgeometrycheckerror.h" -void QgsGeometryAreaCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryAreaCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); @@ -35,7 +35,7 @@ void QgsGeometryAreaCheck::collectErrors( const QMap const QgsAbstractGeometry *part = QgsGeometryCheckerUtils::getGeomPart( geom, iPart ); if ( checkThreshold( layerToMapUnits, part, value ) ) { - errors.append( new QgsGeometryCheckError( this, context, layerFeature, part->centroid(), QgsVertexId( iPart ), value * layerToMapUnits * layerToMapUnits, QgsGeometryCheckError::ValueArea ) ); + errors.append( new QgsGeometryCheckError( this, layerFeature, part->centroid(), QgsVertexId( iPart ), value * layerToMapUnits * layerToMapUnits, QgsGeometryCheckError::ValueArea ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrychecker.cpp b/src/analysis/vector/geometry_checker/qgsgeometrychecker.cpp index 2378bb833e6..ca00a82ddd6 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrychecker.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrychecker.cpp @@ -200,14 +200,14 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, boo { if ( !recheckAreaFeatures.isEmpty() ) { - check->collectErrors( mFeaturePools, mContext, recheckErrors, mMessages, nullptr, recheckAreaFeatures ); + check->collectErrors( mFeaturePools, recheckErrors, mMessages, nullptr, recheckAreaFeatures ); } } else { if ( !recheckFeatures.isEmpty() ) { - check->collectErrors( mFeaturePools, mContext, recheckErrors, mMessages, nullptr, recheckFeatures ); + check->collectErrors( mFeaturePools, recheckErrors, mMessages, nullptr, recheckFeatures ); } } } @@ -285,7 +285,7 @@ void QgsGeometryChecker::runCheck( const QMap &featur // Run checks QList errors; QStringList messages; - check->collectErrors( featurePools, mContext, errors, messages, &mFeedback ); + check->collectErrors( featurePools, errors, messages, &mFeedback ); mErrorListMutex.lock(); mCheckErrors.append( errors ); mMessages.append( messages ); diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h index 8a692f3d384..9a06138877f 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h @@ -33,7 +33,6 @@ class ANALYSIS_EXPORT QgsGeometryCheckError enum ValueType { ValueLength, ValueArea, ValueOther }; QgsGeometryCheckError( const QgsGeometryCheck *check, - const QgsGeometryCheckContext *context, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, const QgsPointXY &errorLocation, QgsVertexId vidx = QgsVertexId(), @@ -45,7 +44,6 @@ class ANALYSIS_EXPORT QgsGeometryCheckError const QgsGeometryCheckError &operator=( const QgsGeometryCheckError & ) = delete; const QgsGeometryCheck *check() const { return mCheck; } - const QgsGeometryCheckContext *context() const {return mContext;} const QString &layerId() const { return mLayerId; } QgsFeatureId featureId() const { return mFeatureId; } // In map units @@ -101,7 +99,6 @@ class ANALYSIS_EXPORT QgsGeometryCheckError ValueType valueType = ValueOther ); const QgsGeometryCheck *mCheck = nullptr; - const QgsGeometryCheckContext *mContext = nullptr; QString mLayerId; QgsFeatureId mFeatureId; QgsGeometry mGeometry; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp index 2bd4589ae05..bf05adb0f18 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp @@ -31,7 +31,7 @@ QgsGeometryCheckerUtils::LayerFeature::LayerFeature( const QgsFeaturePool *pool, const QgsFeature &feature, - const QgsGeometryCheckContext *context, + QgsGeometryCheckContext *context, bool useMapCrs ) : mFeaturePool( pool ) , mFeature( feature ) @@ -211,7 +211,7 @@ QgsGeometryCheckerUtils::LayerFeatures::LayerFeatures( const QMap &featureIds, const QList &geometryTypes, QgsFeedback *feedback, - const QgsGeometryCheckContext *context, + QgsGeometryCheckContext *context, bool useMapCrs ) : mFeaturePools( featurePools ) , mFeatureIds( featureIds ) @@ -225,7 +225,7 @@ QgsGeometryCheckerUtils::LayerFeatures::LayerFeatures( const QMap &featurePools, const QList &layerIds, const QgsRectangle &extent, const QList &geometryTypes, - const QgsGeometryCheckContext *context ) + QgsGeometryCheckContext *context ) : mFeaturePools( featurePools ) , mLayerIds( layerIds ) , mExtent( extent ) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h index 9d9165c8a61..cb6b1dfc041 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h @@ -41,7 +41,7 @@ class ANALYSIS_EXPORT QgsGeometryCheckerUtils * If \a useMapCrs is True, geometries will be reprojected to the mapCrs defined * in \a context. */ - LayerFeature( const QgsFeaturePool *pool, const QgsFeature &feature, const QgsGeometryCheckContext *context, bool useMapCrs ); + LayerFeature( const QgsFeaturePool *pool, const QgsFeature &feature, QgsGeometryCheckContext *context, bool useMapCrs ); /** * Returns the feature. @@ -89,13 +89,13 @@ class ANALYSIS_EXPORT QgsGeometryCheckerUtils const QMap &featureIds, const QList &geometryTypes, QgsFeedback *feedback, - const QgsGeometryCheckContext *context, + QgsGeometryCheckContext *context, bool useMapCrs = false ); LayerFeatures( const QMap &featurePools, const QList &layerIds, const QgsRectangle &extent, const QList &geometryTypes, - const QgsGeometryCheckContext *context ); + QgsGeometryCheckContext *context ); class iterator { @@ -133,7 +133,7 @@ class ANALYSIS_EXPORT QgsGeometryCheckerUtils QgsRectangle mExtent; QList mGeometryTypes; QgsFeedback *mFeedback = nullptr; - const QgsGeometryCheckContext *mContext = nullptr; + QgsGeometryCheckContext *mContext = nullptr; bool mUseMapCrs = true; }; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.cpp index 6e084dc179c..651449ac42a 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.cpp @@ -20,7 +20,7 @@ #include "qgsvectorlayer.h" -void QgsGeometryContainedCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryContainedCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext ); diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h index fddf11281a7..e0f162b5797 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h @@ -30,7 +30,7 @@ class ANALYSIS_EXPORT QgsGeometryContainedCheckError : public QgsGeometryCheckEr const QgsPointXY &errorLocation, const QgsGeometryCheckerUtils::LayerFeature &containingFeature ) - : QgsGeometryCheckError( check, check->context(), layerFeature, errorLocation, QgsVertexId(), containingFeature.id(), ValueOther ) + : QgsGeometryCheckError( check, layerFeature, errorLocation, QgsVertexId(), containingFeature.id(), ValueOther ) , mContainingFeature( qMakePair( containingFeature.layer()->id(), containingFeature.feature().id() ) ) { } const QPair &containingFeature() const { return mContainingFeature; } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.cpp index f0ec8cb0865..96c075c3c6e 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.cpp @@ -19,7 +19,7 @@ #include "qgsvectorlayer.h" #include "qgsgeometrycheckerror.h" -void QgsGeometryDangleCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryDangleCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); @@ -84,11 +84,11 @@ void QgsGeometryDangleCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryDegeneratePolygonCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -35,7 +35,7 @@ void QgsGeometryDegeneratePolygonCheck::collectErrors( const QMapvertexAt( vidx ), vidx ) ); + errors.append( new QgsGeometryCheckError( this, layerFeature, geom->vertexAt( vidx ), vidx ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.cpp index 2f40f195188..cda0e9dd0d2 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.cpp @@ -38,7 +38,7 @@ QString QgsGeometryDuplicateCheckError::duplicatesString( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryDuplicateCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true ); diff --git a/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp index f4ed50b46b0..16cb45f80ab 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp @@ -19,7 +19,7 @@ #include "qgsfeaturepool.h" #include "qgsgeometrycheckerror.h" -void QgsGeometryDuplicateNodesCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryDuplicateNodesCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -41,7 +41,7 @@ void QgsGeometryDuplicateNodesCheck::collectErrors( const QMapvertexAt( QgsVertexId( iPart, iRing, jVert ) ); if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) < mContext->tolerance ) { - errors.append( new QgsGeometryCheckError( this, context, layerFeature, pj, QgsVertexId( iPart, iRing, jVert ) ) ); + errors.append( new QgsGeometryCheckError( this, layerFeature, pj, QgsVertexId( iPart, iRing, jVert ) ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp index c07f696ce5f..b4b877c3589 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp @@ -36,7 +36,7 @@ QgsGeometryFollowBoundariesCheck::~QgsGeometryFollowBoundariesCheck() delete mIndex; } -void QgsGeometryFollowBoundariesCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryFollowBoundariesCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -70,7 +70,7 @@ void QgsGeometryFollowBoundariesCheck::collectErrors( const QMapcentroid() ) ) ); + errors.append( new QgsGeometryCheckError( this, layerFeature, QgsPointXY( geom->centroid() ) ) ); } else { @@ -83,7 +83,7 @@ void QgsGeometryFollowBoundariesCheck::collectErrors( const QMapbuffer( -mContext->tolerance, 0 ) ); if ( !( geomEngine->contains( reducedRefGeom.constGet() ) || geomEngine->disjoint( reducedRefGeom.constGet() ) ) ) { - errors.append( new QgsGeometryCheckError( this, context, layerFeature, QgsPointXY( geom->centroid() ) ) ); + errors.append( new QgsGeometryCheckError( this, layerFeature, QgsPointXY( geom->centroid() ) ) ); break; } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometryholecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryholecheck.cpp index bc70f6613fd..bccf84db0f6 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryholecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryholecheck.cpp @@ -20,7 +20,7 @@ #include "qgsfeaturepool.h" #include "qgsgeometrycheckerror.h" -void QgsGeometryHoleCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryHoleCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -41,7 +41,7 @@ void QgsGeometryHoleCheck::collectErrors( const QMap { QgsPoint pos = poly->interiorRing( iRing - 1 )->centroid(); - errors.append( new QgsGeometryCheckError( this, context, layerFeature, pos, QgsVertexId( iPart, iRing ) ) ); + errors.append( new QgsGeometryCheckError( this, layerFeature, pos, QgsVertexId( iPart, iRing ) ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.cpp index 1bb00e3d4ea..ec613dd67fa 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.cpp @@ -19,7 +19,7 @@ #include "qgsvectorlayer.h" #include "qgsgeometrycheckerror.h" -void QgsGeometryLineIntersectionCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryLineIntersectionCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -67,7 +67,7 @@ void QgsGeometryLineIntersectionCheck::collectErrors( const QMap intersections = QgsGeometryCheckerUtils::lineIntersections( line, testLine, mContext->tolerance ); for ( const QgsPoint &inter : intersections ) { - errors.append( new QgsGeometryCheckError( this, context, layerFeatureA, inter, QgsVertexId( iPart ), layerFeatureB.id() ) ); + errors.append( new QgsGeometryCheckError( this, layerFeatureA, inter, QgsVertexId( iPart ), layerFeatureB.id() ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.cpp index 10f7d607e08..fe964a99a68 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.cpp @@ -20,7 +20,7 @@ #include "qgsfeaturepool.h" #include "qgsgeometrycheckerror.h" -void QgsGeometryLineLayerIntersectionCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryLineLayerIntersectionCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -52,7 +52,7 @@ void QgsGeometryLineLayerIntersectionCheck::collectErrors( const QMap intersections = QgsGeometryCheckerUtils::lineIntersections( line, testLine, mContext->tolerance ); for ( const QgsPoint &inter : intersections ) { - errors.append( new QgsGeometryCheckError( this, context, layerFeature, inter, QgsVertexId( iPart ), checkFeature.id() ) ); + errors.append( new QgsGeometryCheckError( this, layerFeature, inter, QgsVertexId( iPart ), checkFeature.id() ) ); } } else if ( const QgsPolygon *polygon = dynamic_cast( part ) ) @@ -63,7 +63,7 @@ void QgsGeometryLineLayerIntersectionCheck::collectErrors( const QMap intersections = QgsGeometryCheckerUtils::lineIntersections( line, ring, mContext->tolerance ); for ( const QgsPoint &inter : intersections ) { - errors.append( new QgsGeometryCheckError( this, context, layerFeature, inter, QgsVertexId( iPart ), checkFeature.id() ) ); + errors.append( new QgsGeometryCheckError( this, layerFeature, inter, QgsVertexId( iPart ), checkFeature.id() ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.cpp index 0a1d3efb85a..fe472438db7 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.cpp @@ -19,7 +19,7 @@ #include "qgsfeaturepool.h" #include "qgsgeometrycheckerror.h" -void QgsGeometryPointCoveredByLineCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryPointCoveredByLineCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -66,7 +66,7 @@ void QgsGeometryPointCoveredByLineCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometryPointInPolygonCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { QMap featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true ); @@ -58,7 +58,7 @@ void QgsGeometryPointInPolygonCheck::collectErrors( const QMap &featurePools, const QgsGeometryCheckContext *context, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const +void QgsGeometrySegmentLengthCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) @@ -50,7 +50,7 @@ void QgsGeometrySegmentLengthCheck::collectErrors( const QMap mContext->tolerance ) { QgsPointXY pos( 0.5 * ( pi.x() + pj.x() ), 0.5 * ( pi.y() + pj.y() ) ); - errors.append( new QgsGeometryCheckError( this, context, layerFeature, pos, QgsVertexId( iPart, iRing, iVert ), dist * layerToMapUnits, QgsGeometryCheckError::ValueLength ) ); + errors.append( new QgsGeometryCheckError( this, layerFeature, pos, QgsVertexId( iPart, iRing, iVert ), dist * layerToMapUnits, QgsGeometryCheckError::ValueLength ) ); } } } diff --git a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp index 847684bded6..6f511d893f6 100644 --- a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp +++ b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.cpp @@ -81,13 +81,8 @@ QgsVertexId QgsSingleGeometryCheckError::vertexId() const return mVertexId; } -QgsGeometryCheckErrorSingle::QgsGeometryCheckErrorSingle( QgsSingleGeometryCheckError *error, - const QgsGeometryCheckerUtils::LayerFeature &layerFeature ) - : QgsGeometryCheckError( error->check(), - error->check()->context(), - layerFeature, - QgsPointXY( error->errorLocation().constGet()->centroid() ), - error->vertexId() ) // TODO: should send geometry to QgsGeometryCheckError +QgsGeometryCheckErrorSingle::QgsGeometryCheckErrorSingle( QgsSingleGeometryCheckError *error, const QgsGeometryCheckerUtils::LayerFeature &layerFeature ) + : QgsGeometryCheckError( error->check(), layerFeature, QgsPointXY( error->errorLocation().constGet()->centroid() ), error->vertexId() ) // TODO: should send geometry to QgsGeometryCheckError , mError( error ) { From 16481d203a255f15a6d18bab50a730a15daf9d0a Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 24 Sep 2018 11:28:14 -0400 Subject: [PATCH 022/126] add configuration map to constructors of all tests --- .../vector/geometry_checker/qgsgeometryisvalidcheck.cpp | 2 -- .../vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp | 2 +- src/app/qgsgeometryvalidationservice.cpp | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.cpp index 79401bbf190..ee5acbebd6d 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.cpp @@ -30,8 +30,6 @@ QList QgsGeometryIsValidCheck::compatibleGeometryType QList QgsGeometryIsValidCheck::processGeometry( const QgsGeometry &geometry ) const { - Q_UNUSED( configuration ) - QVector errors; QgsGeometry::ValidationMethod method = QgsGeometry::ValidatorQgisInternal; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp index 15504f6e337..39108bcda4e 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp @@ -27,5 +27,5 @@ bool QgsGeometrySliverPolygonCheck::checkThreshold( double layerToMapUnits, cons { return false; } - return value > mCheckConfiguration.value( QStringLiteral( "thresholdMapUnits" ) ).toDouble(); // the sliver threshold is actually a map unit independent number, just abusing QgsGeometryAreaCheck::mThresholdMapUnits to store it + return value > mThreshold; // the sliver threshold is actually a map unit independent number, just abusing QgsGeometryAreaCheck::mThresholdMapUnits to store it } diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 3c9e1b29ebf..73376088da0 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -25,7 +25,7 @@ QgsGeometryValidationService::QgsGeometryValidationService( QgsProject *project { connect( project, &QgsProject::layersAdded, this, &QgsGeometryValidationService::onLayersAdded ); // TODO: should not provide a nullptr context - mIsValidGeometryCheck = new QgsIsValidGeometryCheck( nullptr ); + mIsValidGeometryCheck = new QgsIsValidGeometryCheck( nullptr, QVariantMap() ); } QgsGeometryValidationService::~QgsGeometryValidationService() @@ -89,6 +89,6 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea QgsFeature feature = layer->getFeature( fid ); // TODO: this is a bit hardcore - const auto errors = mIsValidGeometryCheck->processGeometry( feature.geometry(), QVariantMap() ); + const auto errors = mIsValidGeometryCheck->processGeometry( feature.geometry() ); // emit geometryCheckCompleted( layer, fid, errors ); } From a6dc7198decfe2dbb5931e3657407b161e68ca4c Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 24 Sep 2018 11:42:36 -0400 Subject: [PATCH 023/126] rename errorDescription -> description, errorName -> id --- src/app/qgsvectorlayerproperties.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index 16d251e2e9e..ce2e15f2d1b 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -416,7 +416,7 @@ QgsVectorLayerProperties::QgsVectorLayerProperties( const QStringList activeChecks = mLayer->geometryOptions()->geometryChecks(); for ( const QgsGeometryCheckFactory *factory : factories ) { - QCheckBox *cb = new QCheckBox( factory->name() ); + QCheckBox *cb = new QCheckBox( factory->description() ); cb->setChecked( activeChecks.contains( factory->id() ) ); mGeometryCheckFactoriesGroupBoxes.insert( cb, factory->id() ); layout->addWidget( cb ); From 2b8401c251ba078ddae7963cc108d89edf8e1be3 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 24 Sep 2018 12:49:14 -0400 Subject: [PATCH 024/126] rename variables to refleft that they are in map units --- .../vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp index 39108bcda4e..8f7a8311797 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrysliverpolygoncheck.cpp @@ -27,5 +27,5 @@ bool QgsGeometrySliverPolygonCheck::checkThreshold( double layerToMapUnits, cons { return false; } - return value > mThreshold; // the sliver threshold is actually a map unit independent number, just abusing QgsGeometryAreaCheck::mThresholdMapUnits to store it + return value > mThresholdMapUnits; // the sliver threshold is actually a map unit independent number, just abusing QgsGeometryAreaCheck::mThresholdMapUnits to store it } From 550de156c54eb7055813120ceba33c52c6034168 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 24 Sep 2018 14:51:53 -0400 Subject: [PATCH 025/126] make context const --- .../geometry_checker/qgsgeometrycheckerutils.sip.in | 2 +- .../vector/geometry_checker/qgsgeometrycheckerutils.cpp | 6 +++--- .../vector/geometry_checker/qgsgeometrycheckerutils.h | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in index 1bfa8b74ab3..44f2ea3b4fe 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in @@ -35,7 +35,7 @@ the Free Software Foundation; either version 2 of the License, or * %End public: - LayerFeature( const QgsFeaturePool *pool, const QgsFeature &feature, QgsGeometryCheckContext *context, bool useMapCrs ); + LayerFeature( const QgsFeaturePool *pool, const QgsFeature &feature, const QgsGeometryCheckContext *context, bool useMapCrs ); %Docstring Create a new layer/feature combination. The layer is defined by ``pool``, ``feature`` needs to be from this layer. diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp index bf05adb0f18..2bd4589ae05 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp @@ -31,7 +31,7 @@ QgsGeometryCheckerUtils::LayerFeature::LayerFeature( const QgsFeaturePool *pool, const QgsFeature &feature, - QgsGeometryCheckContext *context, + const QgsGeometryCheckContext *context, bool useMapCrs ) : mFeaturePool( pool ) , mFeature( feature ) @@ -211,7 +211,7 @@ QgsGeometryCheckerUtils::LayerFeatures::LayerFeatures( const QMap &featureIds, const QList &geometryTypes, QgsFeedback *feedback, - QgsGeometryCheckContext *context, + const QgsGeometryCheckContext *context, bool useMapCrs ) : mFeaturePools( featurePools ) , mFeatureIds( featureIds ) @@ -225,7 +225,7 @@ QgsGeometryCheckerUtils::LayerFeatures::LayerFeatures( const QMap &featurePools, const QList &layerIds, const QgsRectangle &extent, const QList &geometryTypes, - QgsGeometryCheckContext *context ) + const QgsGeometryCheckContext *context ) : mFeaturePools( featurePools ) , mLayerIds( layerIds ) , mExtent( extent ) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h index cb6b1dfc041..9d9165c8a61 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h @@ -41,7 +41,7 @@ class ANALYSIS_EXPORT QgsGeometryCheckerUtils * If \a useMapCrs is True, geometries will be reprojected to the mapCrs defined * in \a context. */ - LayerFeature( const QgsFeaturePool *pool, const QgsFeature &feature, QgsGeometryCheckContext *context, bool useMapCrs ); + LayerFeature( const QgsFeaturePool *pool, const QgsFeature &feature, const QgsGeometryCheckContext *context, bool useMapCrs ); /** * Returns the feature. @@ -89,13 +89,13 @@ class ANALYSIS_EXPORT QgsGeometryCheckerUtils const QMap &featureIds, const QList &geometryTypes, QgsFeedback *feedback, - QgsGeometryCheckContext *context, + const QgsGeometryCheckContext *context, bool useMapCrs = false ); LayerFeatures( const QMap &featurePools, const QList &layerIds, const QgsRectangle &extent, const QList &geometryTypes, - QgsGeometryCheckContext *context ); + const QgsGeometryCheckContext *context ); class iterator { @@ -133,7 +133,7 @@ class ANALYSIS_EXPORT QgsGeometryCheckerUtils QgsRectangle mExtent; QList mGeometryTypes; QgsFeedback *mFeedback = nullptr; - QgsGeometryCheckContext *mContext = nullptr; + const QgsGeometryCheckContext *mContext = nullptr; bool mUseMapCrs = true; }; From abda86562219a94f1381bb37a565c6e19b9ca129 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 26 Sep 2018 14:33:17 +0200 Subject: [PATCH 026/126] Save / restore geometry check configuration --- src/core/qgsgeometryoptions.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/core/qgsgeometryoptions.cpp b/src/core/qgsgeometryoptions.cpp index 53752116fa9..e838e0c1087 100644 --- a/src/core/qgsgeometryoptions.cpp +++ b/src/core/qgsgeometryoptions.cpp @@ -75,11 +75,19 @@ void QgsGeometryOptions::setCheckConfiguration( const QString &checkId, const QV void QgsGeometryOptions::writeXml( QDomNode &node ) const { - QDomElement geometryOptionsElement = node.ownerDocument().createElement( QStringLiteral( "geometryOptions" ) ); + QDomDocument doc = node.ownerDocument(); + QDomElement geometryOptionsElement = doc.createElement( QStringLiteral( "geometryOptions" ) ); node.appendChild( geometryOptionsElement ); geometryOptionsElement.setAttribute( QStringLiteral( "removeDuplicateNodes" ), mRemoveDuplicateNodes ? 1 : 0 ); geometryOptionsElement.setAttribute( QStringLiteral( "geometryPrecision" ), mGeometryPrecision ); + + QDomElement activeCheckListElement = QgsXmlUtils::writeVariant( mGeometryChecks, doc ); + activeCheckListElement.setTagName( QStringLiteral( "activeChecks" ) ); + geometryOptionsElement.appendChild( activeCheckListElement ); + QDomElement checkConfigurationElement = QgsXmlUtils::writeVariant( mCheckConfiguration, doc ); + checkConfigurationElement.setTagName( QStringLiteral( "checkConfiguration" ) ); + geometryOptionsElement.appendChild( checkConfigurationElement ); } void QgsGeometryOptions::readXml( const QDomNode &node ) @@ -87,4 +95,12 @@ void QgsGeometryOptions::readXml( const QDomNode &node ) QDomElement geometryOptionsElement = node.toElement(); setGeometryPrecision( geometryOptionsElement.attribute( QStringLiteral( "geometryPrecision" ), QStringLiteral( "0.0" ) ).toDouble() ); setRemoveDuplicateNodes( geometryOptionsElement.attribute( QStringLiteral( "removeDuplicateNodes" ), QStringLiteral( "0" ) ).toInt() == 1 ); + + QDomElement activeChecksElem = node.namedItem( QStringLiteral( "activeChecks" ) ).toElement(); + const QVariant activeChecks = QgsXmlUtils::readVariant( activeChecksElem ); + setGeometryChecks( activeChecks.toStringList() ); + + QDomElement checkConfigurationElem = node.namedItem( QStringLiteral( "checkConfiguration" ) ).toElement(); + const QVariant checkConfiguration = QgsXmlUtils::readVariant( checkConfigurationElem ); + mCheckConfiguration = checkConfiguration.toMap(); } From dae3134b2371e9c002fc505916890cdaf232e563 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 26 Sep 2018 23:02:29 +0200 Subject: [PATCH 027/126] Put check registry into use --- src/app/qgsgeometryvalidationmodel.cpp | 5 +- src/app/qgsgeometryvalidationmodel.h | 4 +- src/app/qgsgeometryvalidationservice.cpp | 77 +++++++++++++++++++++--- src/app/qgsgeometryvalidationservice.h | 18 +++--- 4 files changed, 82 insertions(+), 22 deletions(-) diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index a6a73473e33..f09706519ab 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -1,6 +1,7 @@ #include "qgsgeometryvalidationmodel.h" #include "qgsvectorlayer.h" +#include "qgssinglegeometrycheck.h" #include @@ -55,7 +56,7 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) if ( featureItem.errors.count() > 1 ) return tr( "%1: %n Errors", "", featureItem.errors.count() ).arg( featureTitle ); else if ( featureItem.errors.count() == 1 ) - return tr( "%1: %2" ).arg( featureTitle, featureItem.errors.at( 0 ).what() ); + return tr( "%1: %2" ).arg( featureTitle, featureItem.errors.at( 0 )->description() ); #if 0 else return tr( "%1: No Errors" ).arg( featureTitle ); @@ -101,7 +102,7 @@ void QgsGeometryValidationModel::setCurrentLayer( QgsVectorLayer *currentLayer ) endResetModel(); } -void QgsGeometryValidationModel::onGeometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList &errors ) +void QgsGeometryValidationModel::onGeometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList> &errors ) { auto &layerErrors = mErrorStorage[layer]; diff --git a/src/app/qgsgeometryvalidationmodel.h b/src/app/qgsgeometryvalidationmodel.h index 7fd579bf1a4..00b4f24f8cf 100644 --- a/src/app/qgsgeometryvalidationmodel.h +++ b/src/app/qgsgeometryvalidationmodel.h @@ -25,7 +25,7 @@ class QgsGeometryValidationModel : public QAbstractItemModel void setCurrentLayer( QgsVectorLayer *currentLayer ); private slots: - void onGeometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList &errors ); + void onGeometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList > &errors ); void onGeometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid ); private: @@ -39,7 +39,7 @@ class QgsGeometryValidationModel : public QAbstractItemModel {} QgsFeatureId fid; // TODO INITIALIZE PROPERLY - QList errors; + QList> errors; }; int errorsForFeature( QgsVectorLayer *layer, QgsFeatureId fid ); diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 73376088da0..d6a37c30196 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -17,19 +17,15 @@ email : matthias@opengis.ch #include "qgsgeometryvalidationservice.h" #include "qgsproject.h" #include "qgsvectorlayer.h" - -// TODO: Replace with registry -#include "qgsisvalidgeometrycheck.h" +#include "qgsgeometryoptions.h" +#include "qgsanalysis.h" +#include "qgsgeometrycheckregistry.h" +#include "qgsgeometrycheckfactory.h" QgsGeometryValidationService::QgsGeometryValidationService( QgsProject *project ) + : mProject( project ) { connect( project, &QgsProject::layersAdded, this, &QgsGeometryValidationService::onLayersAdded ); - // TODO: should not provide a nullptr context - mIsValidGeometryCheck = new QgsIsValidGeometryCheck( nullptr, QVariantMap() ); -} - -QgsGeometryValidationService::~QgsGeometryValidationService() -{ } bool QgsGeometryValidationService::validationActive( QgsVectorLayer *layer, QgsFeatureId feature ) const @@ -56,6 +52,8 @@ void QgsGeometryValidationService::onLayersAdded( const QList &la { onFeatureDeleted( vectorLayer, fid ); } ); + + enableLayerChecks( vectorLayer ); } } } @@ -78,6 +76,50 @@ void QgsGeometryValidationService::onFeatureDeleted( QgsVectorLayer *layer, QgsF cancelChecks( layer, fid ); } +void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) +{ + // TODO: finish all ongoing checks + qDeleteAll( mSingleFeatureChecks.value( layer ) ); + + // TODO: ownership and lifetime of the context!! + auto context = new QgsGeometryCheckContext( 1, mProject->crs(), mProject->transformContext() ); + QList layerChecks; + + QgsGeometryCheckRegistry *checkRegistry = QgsAnalysis::instance()->geometryCheckRegistry(); + + const QStringList activeChecks = layer->geometryOptions()->geometryChecks(); + + const QList singleCheckFactories = checkRegistry->geometryCheckFactories( layer, QgsGeometryCheck::SingleGeometryCheck ); + + for ( QgsGeometryCheckFactory *factory : singleCheckFactories ) + { + const QString checkId = factory->id(); + if ( activeChecks.contains( checkId ) ) + { + const QVariantMap checkConfiguration = layer->geometryOptions()->checkConfiguration( checkId ); + layerChecks.append( factory->createGeometryCheck( context, checkConfiguration ) ); + } + } + + QList singleGeometryChecks; + for ( QgsGeometryCheck *check : qgis::as_const( layerChecks ) ) + { + Q_ASSERT( dynamic_cast( check ) ); + singleGeometryChecks.append( dynamic_cast( check ) ); + } + + mSingleFeatureChecks.insert( layer, singleGeometryChecks ); + +#if 0 + const QList topologyCheckFactories = checkRegistry->geometryCheckFactories( layer, QgsGeometryCheck::SingleLayerTopologyCheck ); + + for ( const QString &check : activeChecks ) + { + checkRegistry->geometryCheckFactories( layer, QgsGeometryCheck::SingleLayerTopologyCheck ); + } +#endif +} + void QgsGeometryValidationService::cancelChecks( QgsVectorLayer *layer, QgsFeatureId fid ) { @@ -88,7 +130,22 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea emit geometryCheckStarted( layer, fid ); QgsFeature feature = layer->getFeature( fid ); + + const auto &checks = mSingleFeatureChecks.value( layer ); + + // The errors are going to be sent out via a signal. We cannot keep ownership in here (or can we?) + // nor can we be sure that a single slot is connected to the signal. So make it a shared_ptr. + QList> allErrors; + for ( QgsSingleGeometryCheck *check : checks ) + { + const auto errors = check->processGeometry( feature.geometry() ); + + for ( auto error : errors ) + allErrors.append( std::shared_ptr( error ) ); + } + + emit geometryCheckCompleted( layer, fid, allErrors ); // TODO: this is a bit hardcore - const auto errors = mIsValidGeometryCheck->processGeometry( feature.geometry() ); + // const auto errors = mIsValidGeometryCheck->processGeometry( feature.geometry() ); // emit geometryCheckCompleted( layer, fid, errors ); } diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index 5c6812cdd21..b85342d60a8 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -24,8 +24,10 @@ email : matthias@opengis.ch class QgsProject; class QgsMapLayer; class QgsVectorLayer; -// TODO: Should be retrieved from registry!! -class QgsIsValidGeometryCheck; +class QgsGeometryCheck; +class QgsSingleGeometryCheck; +class QgsSingleGeometryCheckError; + /** @@ -52,7 +54,7 @@ class QgsGeometryValidationService : public QObject typedef QList FeatureErrors; QgsGeometryValidationService( QgsProject *project ); - ~QgsGeometryValidationService(); + ~QgsGeometryValidationService() = default; /** * Returns if a validation is active for the specified \a feature on @@ -62,7 +64,7 @@ class QgsGeometryValidationService : public QObject signals: void geometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid ); - void geometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList &errors ); + void geometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList> &errors ); private slots: void onLayersAdded( const QList &layers ); @@ -71,15 +73,15 @@ class QgsGeometryValidationService : public QObject void onFeatureDeleted( QgsVectorLayer *layer, QgsFeatureId fid ); private: + void enableLayerChecks( QgsVectorLayer *layer ); + void cancelChecks( QgsVectorLayer *layer, QgsFeatureId fid ); void processFeature( QgsVectorLayer *layer, QgsFeatureId fid ); - QgsIsValidGeometryCheck *mIsValidGeometryCheck; + QgsProject *mProject = nullptr; - QgsProject *mProject; - - QMap mActiveChecks; + QHash > mSingleFeatureChecks; }; #endif // QGSGEOMETRYVALIDATIONSERVICE_H From d85c0a7e928a142aea83cf01e65f73263b539891 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Thu, 27 Sep 2018 12:15:16 +0200 Subject: [PATCH 028/126] Only allow formatted numbers in precision config --- src/app/qgsvectorlayerproperties.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index ce2e15f2d1b..52179be965b 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -405,6 +405,7 @@ QgsVectorLayerProperties::QgsVectorLayerProperties( { mRemoveDuplicateNodesCheckbox->setEnabled( true ); mGeometryPrecisionLineEdit->setEnabled( true ); + mGeometryPrecisionLineEdit->setValidator( new QDoubleValidator( mGeometryPrecisionLineEdit ) ); mRemoveDuplicateNodesCheckbox->setChecked( mLayer->geometryOptions()->removeDuplicateNodes() ); mGeometryPrecisionLineEdit->setText( QString::number( mLayer->geometryOptions()->geometryPrecision() ) ); From 88ef92155dae451ea77d4485917a447664aad109 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Thu, 27 Sep 2018 12:41:03 +0200 Subject: [PATCH 029/126] Add topology checks to configuration interface --- src/app/qgsvectorlayerproperties.cpp | 22 ++++++-- src/ui/qgsvectorlayerpropertiesbase.ui | 76 ++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 5 deletions(-) diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index 52179be965b..cb6f488558a 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -412,17 +412,29 @@ QgsVectorLayerProperties::QgsVectorLayerProperties( mPrecisionUnitsLabel->setText( QStringLiteral( "[%1]" ).arg( QgsUnitTypes::toAbbreviatedString( mLayer->crs().mapUnits() ) ) ); - QLayout *layout = new QVBoxLayout(); - const QList factories = QgsAnalysis::instance()->geometryCheckRegistry()->geometryCheckFactories( mLayer, QgsGeometryCheck::Flag::SingleGeometryCheck ); + QLayout *geometryCheckLayout = new QVBoxLayout(); + const QList geometryCheckFactories = QgsAnalysis::instance()->geometryCheckRegistry()->geometryCheckFactories( mLayer, QgsGeometryCheck::Flag::SingleGeometryCheck ); const QStringList activeChecks = mLayer->geometryOptions()->geometryChecks(); - for ( const QgsGeometryCheckFactory *factory : factories ) + for ( const QgsGeometryCheckFactory *factory : geometryCheckFactories ) { QCheckBox *cb = new QCheckBox( factory->description() ); cb->setChecked( activeChecks.contains( factory->id() ) ); mGeometryCheckFactoriesGroupBoxes.insert( cb, factory->id() ); - layout->addWidget( cb ); + geometryCheckLayout->addWidget( cb ); } - mGeometryValidationGroupBox->setLayout( layout ); + mGeometryValidationGroupBox->setLayout( geometryCheckLayout ); + + QLayout *topologyCheckLayout = new QVBoxLayout(); + const QList topologyCheckFactories = QgsAnalysis::instance()->geometryCheckRegistry()->geometryCheckFactories( mLayer, QgsGeometryCheck::Flag::SingleLayerTopologyCheck ); + + for ( const QgsGeometryCheckFactory *factory : topologyCheckFactories ) + { + QCheckBox *cb = new QCheckBox( factory->description() ); + cb->setChecked( activeChecks.contains( factory->id() ) ); + mGeometryCheckFactoriesGroupBoxes.insert( cb, factory->id() ); + topologyCheckLayout->addWidget( cb ); + } + mTopologyChecksGroupBox->setLayout( topologyCheckLayout ); } else { diff --git a/src/ui/qgsvectorlayerpropertiesbase.ui b/src/ui/qgsvectorlayerpropertiesbase.ui index 879f5a82bd4..44c46960058 100644 --- a/src/ui/qgsvectorlayerpropertiesbase.ui +++ b/src/ui/qgsvectorlayerpropertiesbase.ui @@ -2415,6 +2415,82 @@ border-radius: 2px; Geometry validation + + + + 0 + 0 + 651 + 804 + + + + + + + Automatic Fixes + + + + + + <html><head/><body><p>The geometry precision defines the maximum precision to of geometry coordinates that should be stored on this layer. A snap to grid algorithm will be applied on every geometry entering this layer, resulting in coordinates being rounded to multiples of this value. The operation is applied in this layer's coordinate reference system.</p></body></html> + + + Geometry precision + + + + + + + Remove duplicate nodes + + + + + + + + + [Units] + + + + + + + Qt::ImhFormattedNumbersOnly + + + + + + [No precision restriction] + + + + + + + + + + + + Geometry checks + + + + + + + Topology checks + + + + + From ea878153b54a758ea9ebf872c02f3b7b06f2854d Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Thu, 27 Sep 2018 12:41:32 +0200 Subject: [PATCH 030/126] Topology check integration in validation service --- src/app/qgsgeometryvalidationservice.cpp | 20 +++++++++++++++++--- src/app/qgsgeometryvalidationservice.h | 4 ++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index d6a37c30196..3a8250e9d78 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -52,6 +52,10 @@ void QgsGeometryValidationService::onLayersAdded( const QList &la { onFeatureDeleted( vectorLayer, fid ); } ); + connect( vectorLayer, &QgsVectorLayer::beforeCommitChanges, this, [this, vectorLayer]() + { + onBeforeCommitChanges( vectorLayer ); + } ); enableLayerChecks( vectorLayer ); } @@ -76,6 +80,14 @@ void QgsGeometryValidationService::onFeatureDeleted( QgsVectorLayer *layer, QgsF cancelChecks( layer, fid ); } +void QgsGeometryValidationService::onBeforeCommitChanges( QgsVectorLayer *layer ) +{ + if ( !mTopologyChecksOk.value( layer ) ) + { + triggerTopologyChecks( layer ); + } +} + void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) { // TODO: finish all ongoing checks @@ -145,7 +157,9 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea } emit geometryCheckCompleted( layer, fid, allErrors ); - // TODO: this is a bit hardcore - // const auto errors = mIsValidGeometryCheck->processGeometry( feature.geometry() ); - // emit geometryCheckCompleted( layer, fid, errors ); +} + +void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer ) +{ + } diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index b85342d60a8..5b58e3ba28b 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -71,6 +71,7 @@ class QgsGeometryValidationService : public QObject void onFeatureAdded( QgsVectorLayer *layer, QgsFeatureId fid ); void onGeometryChanged( QgsVectorLayer *layer, QgsFeatureId fid, const QgsGeometry &geometry ); void onFeatureDeleted( QgsVectorLayer *layer, QgsFeatureId fid ); + void onBeforeCommitChanges( QgsVectorLayer *layer ); private: void enableLayerChecks( QgsVectorLayer *layer ); @@ -79,9 +80,12 @@ class QgsGeometryValidationService : public QObject void processFeature( QgsVectorLayer *layer, QgsFeatureId fid ); + void triggerTopologyChecks( QgsVectorLayer *layer ); + QgsProject *mProject = nullptr; QHash > mSingleFeatureChecks; + QHash mTopologyChecksOk; }; #endif // QGSGEOMETRYVALIDATIONSERVICE_H From 821a4ba32fb6d78735ec5320478de3dbbc30eac1 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Thu, 27 Sep 2018 14:30:11 +0200 Subject: [PATCH 031/126] Fix template based geometry check factory --- src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h | 6 ++++++ .../vector/geometry_checker/qgsgeometryoverlapcheck.h | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h index ee22d7b27e9..35e23e018e4 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h @@ -97,6 +97,12 @@ class ANALYSIS_EXPORT QgsGeometryGapCheck : public QgsGeometryCheck static QgsGeometryCheck::CheckType factoryCheckType() SIP_SKIP; ///@endcond private + static QString factoryDescription() { return tr( "Gap" ); } + static QString factoryId() { return QStringLiteral( "QgsGeometryGapCheck" ); } + static QgsGeometryCheck::Flags factoryFlags() {return QgsGeometryCheck::SingleLayerTopologyCheck;} + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PolygonGeometry};} + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + enum ResolutionMethod { MergeLongestEdge, NoChange }; private: diff --git a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h index 904cc8a80c6..4078aa952bd 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h @@ -92,6 +92,13 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheck : public QgsGeometryCheck static QgsGeometryCheck::CheckType factoryCheckType() SIP_SKIP; ///@endcond private + static QString factoryDescription() { return tr( "Overlap" ); } + static QString factoryId() { return QStringLiteral( "QgsGeometryOverlapCheck" ); } + static QgsGeometryCheck::Flags factoryFlags() {return QgsGeometryCheck::SingleLayerTopologyCheck;} + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PolygonGeometry};} + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + + enum ResolutionMethod { Subtract, NoChange }; private: From 91d54bcb25832e0a5062cab541ed0908d30bde27 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Thu, 27 Sep 2018 14:57:30 +0200 Subject: [PATCH 032/126] Code formatting --- .../vector/geometry_checker/qgsgeometryoverlapcheck.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h index 4078aa952bd..c5b8d3de785 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h @@ -98,6 +98,17 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheck : public QgsGeometryCheck static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PolygonGeometry};} static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + QString description() const override; + QString id() const override; + QgsGeometryCheck::Flags flags() const override; + +///@cond private + static QString factoryDescription() SIP_SKIP; + static QString factoryId() SIP_SKIP; + static QgsGeometryCheck::Flags factoryFlags() SIP_SKIP; + static QList factoryCompatibleGeometryTypes() SIP_SKIP; + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP; +///@endcond private enum ResolutionMethod { Subtract, NoChange }; From d60727f9b506f09d506f07b7215d01f718b7f55f Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Fri, 28 Sep 2018 10:54:09 +0200 Subject: [PATCH 033/126] Report topology errors --- src/app/qgisapp.cpp | 4 + src/app/qgsgeometryvalidationmodel.cpp | 93 ++++++++++++++------ src/app/qgsgeometryvalidationmodel.h | 2 + src/app/qgsgeometryvalidationservice.cpp | 105 +++++++++++++++++++++-- src/app/qgsgeometryvalidationservice.h | 19 +++- 5 files changed, 188 insertions(+), 35 deletions(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 61f63f8eca5..c855d15e10f 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -928,6 +928,10 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh QgsAnalysis::instance()->geometryCheckRegistry()->initialize(); mGeometryValidationService = qgis::make_unique( QgsProject::instance() ); + connect( mGeometryValidationService.get(), &QgsGeometryValidationService::warning, this, [this]( const QString & message ) + { + mInfoBar->pushWarning( tr( "Geometry Validation" ), message ); + } ); mGeometryValidationDock = new QgsGeometryValidationDock( tr( "Geometry Validation" ) ); mGeometryValidationModel = new QgsGeometryValidationModel( mGeometryValidationService.get(), mGeometryValidationDock ); connect( this, &QgisApp::activeLayerChanged, mGeometryValidationModel, [this]( QgsMapLayer * layer ) diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index f09706519ab..88f8ccefb47 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -11,6 +11,7 @@ QgsGeometryValidationModel::QgsGeometryValidationModel( QgsGeometryValidationSer { connect( mGeometryValidationService, &QgsGeometryValidationService::geometryCheckCompleted, this, &QgsGeometryValidationModel::onGeometryCheckCompleted ); connect( mGeometryValidationService, &QgsGeometryValidationService::geometryCheckStarted, this, &QgsGeometryValidationModel::onGeometryCheckStarted ); + connect( mGeometryValidationService, &QgsGeometryValidationService::topologyChecksUpdated, this, &QgsGeometryValidationModel::onTopologyChecksUpdated, Qt::QueuedConnection ); } QModelIndex QgsGeometryValidationModel::index( int row, int column, const QModelIndex &parent ) const @@ -28,7 +29,7 @@ QModelIndex QgsGeometryValidationModel::parent( const QModelIndex &child ) const int QgsGeometryValidationModel::rowCount( const QModelIndex &parent ) const { Q_UNUSED( parent ) - return mErrorStorage.value( mCurrentLayer ).size(); + return mErrorStorage.value( mCurrentLayer ).size() + mTopologyErrorStorage.value( mCurrentLayer ).size(); } int QgsGeometryValidationModel::columnCount( const QModelIndex &parent ) const @@ -41,38 +42,64 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) { const auto &layerErrors = mErrorStorage.value( mCurrentLayer ); - const auto &featureItem = layerErrors.at( index.row() ); - - switch ( role ) + if ( index.row() >= layerErrors.size() ) { - case Qt::DisplayRole: - { - QgsFeature feature = mCurrentLayer->getFeature( featureItem.fid ); // TODO: this should be cached! - mExpressionContext.setFeature( feature ); - QString featureTitle = mDisplayExpression.evaluate( &mExpressionContext ).toString(); - if ( featureTitle.isEmpty() ) - featureTitle = featureItem.fid; + // Topology error + const auto &topologyErrors = mTopologyErrorStorage.value( mCurrentLayer ); + auto topologyError = topologyErrors.at( index.row() - layerErrors.size() ); - if ( featureItem.errors.count() > 1 ) - return tr( "%1: %n Errors", "", featureItem.errors.count() ).arg( featureTitle ); - else if ( featureItem.errors.count() == 1 ) - return tr( "%1: %2" ).arg( featureTitle, featureItem.errors.at( 0 )->description() ); -#if 0 - else - return tr( "%1: No Errors" ).arg( featureTitle ); -#endif + switch ( role ) + { + case Qt::DisplayRole: + { + const QgsFeatureId fid = topologyError->featureId(); + const QgsFeature feature = mCurrentLayer->getFeature( fid ); // TODO: this should be cached! + mExpressionContext.setFeature( feature ); + QString featureTitle = mDisplayExpression.evaluate( &mExpressionContext ).toString(); + if ( featureTitle.isEmpty() ) + featureTitle = fid; + + return tr( "%1: %2" ).arg( featureTitle, topologyError->description() ); + } } + } + else + { + // Geometry error + const auto &featureItem = layerErrors.at( index.row() ); - - case Qt::DecorationRole: + switch ( role ) { - if ( mGeometryValidationService->validationActive( mCurrentLayer, featureItem.fid ) ) - return QgsApplication::getThemeIcon( "/mActionTracing.svg" ); - else + case Qt::DisplayRole: + { + QgsFeature feature = mCurrentLayer->getFeature( featureItem.fid ); // TODO: this should be cached! + mExpressionContext.setFeature( feature ); + QString featureTitle = mDisplayExpression.evaluate( &mExpressionContext ).toString(); + if ( featureTitle.isEmpty() ) + featureTitle = featureItem.fid; + + if ( featureItem.errors.count() > 1 ) + return tr( "%1: %n Errors", "", featureItem.errors.count() ).arg( featureTitle ); + else if ( featureItem.errors.count() == 1 ) + return tr( "%1: %2" ).arg( featureTitle, featureItem.errors.at( 0 )->description() ); + else + return QVariant(); + } + + case Qt::DecorationRole: + { + if ( mGeometryValidationService->validationActive( mCurrentLayer, featureItem.fid ) ) + return QgsApplication::getThemeIcon( "/mActionTracing.svg" ); + else + return QVariant(); + } + + default: return QVariant(); } } + return QVariant(); } @@ -162,6 +189,24 @@ void QgsGeometryValidationModel::onGeometryCheckStarted( QgsVectorLayer *layer, } } +void QgsGeometryValidationModel::onTopologyChecksUpdated( QgsVectorLayer *layer, const QList > &errors ) +{ + auto &topologyLayerErrors = mTopologyErrorStorage[layer]; + + if ( layer == currentLayer() ) + { + const int oldRowCount = rowCount( QModelIndex() ); + beginInsertRows( QModelIndex(), oldRowCount, oldRowCount + errors.size() ); + } + + topologyLayerErrors.append( errors ); + + if ( layer == currentLayer() ) + { + endInsertRows(); + } +} + int QgsGeometryValidationModel::errorsForFeature( QgsVectorLayer *layer, QgsFeatureId fid ) { const auto &layerErrors = mErrorStorage[layer]; diff --git a/src/app/qgsgeometryvalidationmodel.h b/src/app/qgsgeometryvalidationmodel.h index 00b4f24f8cf..4acdb8882e4 100644 --- a/src/app/qgsgeometryvalidationmodel.h +++ b/src/app/qgsgeometryvalidationmodel.h @@ -27,6 +27,7 @@ class QgsGeometryValidationModel : public QAbstractItemModel private slots: void onGeometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList > &errors ); void onGeometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid ); + void onTopologyChecksUpdated( QgsVectorLayer *layer, const QList > &errors ); private: struct FeatureErrors @@ -50,6 +51,7 @@ class QgsGeometryValidationModel : public QAbstractItemModel mutable QgsExpressionContext mExpressionContext; QMap > mErrorStorage; + QMap > > mTopologyErrorStorage; }; #endif // QGSGEOMETRYVALIDATIONMODEL_H diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 3a8250e9d78..df0d773c167 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -21,10 +21,18 @@ email : matthias@opengis.ch #include "qgsanalysis.h" #include "qgsgeometrycheckregistry.h" #include "qgsgeometrycheckfactory.h" +#include "qgsvectorlayereditbuffer.h" +#include "qgsvectorlayerfeaturepool.h" +#include "qgsfeedback.h" +#include "qgsreadwritelocker.h" + +#include +#include QgsGeometryValidationService::QgsGeometryValidationService( QgsProject *project ) : mProject( project ) { + qRegisterMetaType< QList > >( "QList>" ); connect( project, &QgsProject::layersAdded, this, &QgsGeometryValidationService::onLayersAdded ); } @@ -64,11 +72,21 @@ void QgsGeometryValidationService::onLayersAdded( const QList &la void QgsGeometryValidationService::onFeatureAdded( QgsVectorLayer *layer, QgsFeatureId fid ) { + if ( !mLayerCheckStates[layer].topologyChecks.empty() ) + { + // TODO: Cancel topology checks + layer->setAllowCommit( false ); + } processFeature( layer, fid ); } void QgsGeometryValidationService::onGeometryChanged( QgsVectorLayer *layer, QgsFeatureId fid, const QgsGeometry &geometry ) { + if ( !mLayerCheckStates[layer].topologyChecks.empty() ) + { + // TODO: Cancel topology checks + layer->setAllowCommit( false ); + } Q_UNUSED( geometry ) cancelChecks( layer, fid ); @@ -77,13 +95,23 @@ void QgsGeometryValidationService::onGeometryChanged( QgsVectorLayer *layer, Qgs void QgsGeometryValidationService::onFeatureDeleted( QgsVectorLayer *layer, QgsFeatureId fid ) { + if ( !mLayerCheckStates[layer].topologyChecks.empty() ) + { + // TODO: Cancel topology checks + layer->setAllowCommit( false ); + } + cancelChecks( layer, fid ); } void QgsGeometryValidationService::onBeforeCommitChanges( QgsVectorLayer *layer ) { - if ( !mTopologyChecksOk.value( layer ) ) + if ( !mLayerCheckStates[layer].topologyChecks.empty() ) // TODO && topologyChecks not fulfilled { + if ( !layer->allowCommit() ) + { + emit warning( tr( "Can not save yet, we'll need to run some topology checks on your dataset first..." ) ); + } triggerTopologyChecks( layer ); } } @@ -91,7 +119,8 @@ void QgsGeometryValidationService::onBeforeCommitChanges( QgsVectorLayer *layer void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) { // TODO: finish all ongoing checks - qDeleteAll( mSingleFeatureChecks.value( layer ) ); + qDeleteAll( mLayerCheckStates[layer].singleFeatureChecks ); + qDeleteAll( mLayerCheckStates[layer].topologyChecks ); // TODO: ownership and lifetime of the context!! auto context = new QgsGeometryCheckContext( 1, mProject->crs(), mProject->transformContext() ); @@ -120,16 +149,23 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) singleGeometryChecks.append( dynamic_cast( check ) ); } - mSingleFeatureChecks.insert( layer, singleGeometryChecks ); + mLayerCheckStates[layer].singleFeatureChecks = singleGeometryChecks; -#if 0 + // Topology checks + QList topologyChecks; const QList topologyCheckFactories = checkRegistry->geometryCheckFactories( layer, QgsGeometryCheck::SingleLayerTopologyCheck ); - for ( const QString &check : activeChecks ) + for ( QgsGeometryCheckFactory *factory : topologyCheckFactories ) { - checkRegistry->geometryCheckFactories( layer, QgsGeometryCheck::SingleLayerTopologyCheck ); + const QString checkId = factory->id(); + if ( activeChecks.contains( checkId ) ) + { + const QVariantMap checkConfiguration = layer->geometryOptions()->checkConfiguration( checkId ); + topologyChecks.append( factory->createGeometryCheck( context, checkConfiguration ) ); + } } -#endif + + mLayerCheckStates[layer].topologyChecks = topologyChecks; } void QgsGeometryValidationService::cancelChecks( QgsVectorLayer *layer, QgsFeatureId fid ) @@ -143,7 +179,7 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea QgsFeature feature = layer->getFeature( fid ); - const auto &checks = mSingleFeatureChecks.value( layer ); + const auto &checks = mLayerCheckStates[layer].singleFeatureChecks; // The errors are going to be sent out via a signal. We cannot keep ownership in here (or can we?) // 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 void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer ) { + QFutureWatcher *futureWatcher = mLayerCheckStates[layer].topologyCheckFutureWatcher; + if ( futureWatcher ) + { + // TODO: kill!! + delete futureWatcher; + } + QgsFeatureIds checkFeatureIds = layer->editBuffer()->changedGeometries().keys().toSet(); + checkFeatureIds.unite( layer->editBuffer()->addedFeatures().keys().toSet() ); + + // TODO: ownership of these objects... + QgsVectorLayerFeaturePool *featurePool = new QgsVectorLayerFeaturePool( layer ); + QList &allErrors = mLayerCheckStates[layer].topologyCheckErrors; + QgsFeedback *feedback = new QgsFeedback(); + QMap layerIds; + layerIds.insert( layer->id(), checkFeatureIds ); + QgsGeometryCheck::LayerFeatureIds layerFeatureIds( layerIds ); + + QMap featurePools; + featurePools.insert( layer->id(), featurePool ); + + const QList checks = mLayerCheckStates[layer].topologyChecks; + + QFuture future = QtConcurrent::map( checks, [featurePools, &allErrors, feedback, layerFeatureIds, layer, this]( const QgsGeometryCheck * check ) + { + // Watch out with the layer pointer in here. We are running in a thread, so we do not want to actually use it + // except for using its address to report the error. + QList errors; + QStringList messages; // Do we really need these? + + check->collectErrors( featurePools, errors, messages, feedback, layerFeatureIds ); + QgsReadWriteLocker errorLocker( mTopologyCheckLock, QgsReadWriteLocker::Write ); + allErrors.append( errors ); + + QList > sharedErrors; + for ( QgsGeometryCheckError *error : errors ) + { + sharedErrors.append( std::shared_ptr( error ) ); + } + emit topologyChecksUpdated( layer, sharedErrors ); + errorLocker.unlock(); + } ); + + futureWatcher = new QFutureWatcher(); + futureWatcher->setFuture( future ); + + connect( futureWatcher, &QFutureWatcherBase::finished, this, [&allErrors, layer, this]() + { + QgsReadWriteLocker errorLocker( mTopologyCheckLock, QgsReadWriteLocker::Read ); + layer->setAllowCommit( allErrors.empty() ); + errorLocker.unlock(); + } ); + + mLayerCheckStates[layer].topologyCheckFutureWatcher = futureWatcher; } diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index 5b58e3ba28b..2f550c055b3 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -18,6 +18,8 @@ email : matthias@opengis.ch #include #include +#include +#include #include "qgsfeature.h" @@ -27,7 +29,7 @@ class QgsVectorLayer; class QgsGeometryCheck; class QgsSingleGeometryCheck; class QgsSingleGeometryCheckError; - +class QgsGeometryCheckError; /** @@ -65,6 +67,9 @@ class QgsGeometryValidationService : public QObject signals: void geometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid ); void geometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList> &errors ); + void topologyChecksUpdated( QgsVectorLayer *layer, const QList > &errors ); + + void warning( const QString &message ); private slots: void onLayersAdded( const QList &layers ); @@ -84,8 +89,16 @@ class QgsGeometryValidationService : public QObject QgsProject *mProject = nullptr; - QHash > mSingleFeatureChecks; - QHash mTopologyChecksOk; + struct VectorCheckState + { + QList< QgsSingleGeometryCheck * > singleFeatureChecks; + QList< QgsGeometryCheck * > topologyChecks; + QFutureWatcher *topologyCheckFutureWatcher = nullptr; + QList topologyCheckErrors; + }; + + QReadWriteLock mTopologyCheckLock; + QHash mLayerCheckStates; }; #endif // QGSGEOMETRYVALIDATIONSERVICE_H From 8142d5f39e62310e18884b700340fa0e0d1856c2 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Fri, 28 Sep 2018 11:26:52 +0200 Subject: [PATCH 034/126] Move metatype registration to single init code --- src/app/qgsgeometryvalidationservice.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index df0d773c167..4ad8ea75441 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -32,7 +32,6 @@ email : matthias@opengis.ch QgsGeometryValidationService::QgsGeometryValidationService( QgsProject *project ) : mProject( project ) { - qRegisterMetaType< QList > >( "QList>" ); connect( project, &QgsProject::layersAdded, this, &QgsGeometryValidationService::onLayersAdded ); } From c6159bfc52475170c7779fdda28696e1a33be2f0 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Fri, 28 Sep 2018 14:59:03 +0200 Subject: [PATCH 035/126] Improving the UI --- src/app/qgsgeometryvalidationdock.cpp | 53 +++++++++++++++++++++++++ src/app/qgsgeometryvalidationdock.h | 14 +++++++ src/app/qgsgeometryvalidationmodel.h | 10 ++++- src/ui/qgsgeometryvalidationdockbase.ui | 51 ++++++++++++++++++++++-- 4 files changed, 124 insertions(+), 4 deletions(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 9e8b443bc24..68f83a14bbe 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -16,10 +16,19 @@ email : matthias@opengis.ch #include "qgsgeometryvalidationdock.h" #include "qgsgeometryvalidationmodel.h" +#include + QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QWidget *parent, Qt::WindowFlags flags ) : QgsDockWidget( title, parent, flags ) { setupUi( this ); + + connect( mNextButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::gotoNextError ); + connect( mPreviousButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::gotoPreviousError ); + connect( mZoomToProblemButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::zoomToProblem ); + connect( mZoomToFeatureButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::zoomToFeature ); + + onCurrentErrorChanged( QModelIndex(), QModelIndex() ); } QgsGeometryValidationModel *QgsGeometryValidationDock::geometryValidationModel() const @@ -31,4 +40,48 @@ void QgsGeometryValidationDock::setGeometryValidationModel( QgsGeometryValidatio { mGeometryValidationModel = geometryValidationModel; mErrorListView->setModel( mGeometryValidationModel ); + + connect( mErrorListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsGeometryValidationDock::onCurrentErrorChanged ); +} + +void QgsGeometryValidationDock::gotoNextError() +{ + QItemSelectionModel *selectionModel = mErrorListView->selectionModel(); + selectionModel->setCurrentIndex( mGeometryValidationModel->index( selectionModel->currentIndex().row() + 1, 0, QModelIndex() ), QItemSelectionModel::ClearAndSelect ); +} + +void QgsGeometryValidationDock::gotoPreviousError() +{ + QItemSelectionModel *selectionModel = mErrorListView->selectionModel(); + selectionModel->setCurrentIndex( mGeometryValidationModel->index( selectionModel->currentIndex().row() - 1, 0, QModelIndex() ), QItemSelectionModel::ClearAndSelect ); +} + +void QgsGeometryValidationDock::zoomToProblem() +{ + mLastZoomToAction = ZoomToProblem; +} + +void QgsGeometryValidationDock::zoomToFeature() +{ + mLastZoomToAction = ZoomToFeature; + // mErrorListView->currentIndex().data( ) +} + +void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤t, const QModelIndex &previous ) +{ + mNextButton->setEnabled( current.isValid() && current.row() < mGeometryValidationModel->rowCount() - 1 ); + mPreviousButton->setEnabled( current.isValid() && current.row() > 0 ); + + mProblemDetailWidget->setVisible( current.isValid() ); + mProblemDescriptionLabel->setText( current.data().toString() ); + + switch ( mLastZoomToAction ) + { + case ZoomToProblem: + zoomToProblem(); + break; + + case ZoomToFeature: + zoomToFeature(); + } } diff --git a/src/app/qgsgeometryvalidationdock.h b/src/app/qgsgeometryvalidationdock.h index a8d7b855195..eb08236d736 100644 --- a/src/app/qgsgeometryvalidationdock.h +++ b/src/app/qgsgeometryvalidationdock.h @@ -34,8 +34,22 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal QgsGeometryValidationModel *geometryValidationModel() const; void setGeometryValidationModel( QgsGeometryValidationModel *geometryValidationModel ); + private slots: + void onCurrentErrorChanged( const QModelIndex ¤t, const QModelIndex &previous ); + void gotoNextError(); + void gotoPreviousError(); + void zoomToProblem(); + void zoomToFeature(); + private: + enum ZoomToAction + { + ZoomToFeature, + ZoomToProblem + }; + ZoomToAction mLastZoomToAction = ZoomToFeature; QgsGeometryValidationModel *mGeometryValidationModel = nullptr; + QButtonGroup *mZoomToButtonGroup = nullptr; }; #endif // QGSGEOMETRYVALIDATIONPANEL_H diff --git a/src/app/qgsgeometryvalidationmodel.h b/src/app/qgsgeometryvalidationmodel.h index 4acdb8882e4..0182208093f 100644 --- a/src/app/qgsgeometryvalidationmodel.h +++ b/src/app/qgsgeometryvalidationmodel.h @@ -2,6 +2,7 @@ #define QGSGEOMETRYVALIDATIONMODEL_H #include + #include "qgsgeometryvalidationservice.h" #include "qgsexpression.h" #include "qgsexpressioncontext.h" @@ -11,11 +12,18 @@ class QgsGeometryValidationModel : public QAbstractItemModel Q_OBJECT public: + + enum Roles + { + FeatureExtentRole = Qt::UserRole, + ProblemExtentRole + }; + QgsGeometryValidationModel( QgsGeometryValidationService *geometryValidationService, QObject *parent = nullptr ); QModelIndex index( int row, int column, const QModelIndex &parent ) const override; QModelIndex parent( const QModelIndex &child ) const override; - int rowCount( const QModelIndex &parent ) const override; + int rowCount( const QModelIndex &parent = QModelIndex() ) const override; int columnCount( const QModelIndex &parent ) const override; QVariant data( const QModelIndex &index, int role ) const override; diff --git a/src/ui/qgsgeometryvalidationdockbase.ui b/src/ui/qgsgeometryvalidationdockbase.ui index a66e55d1867..1d14210d9c9 100644 --- a/src/ui/qgsgeometryvalidationdockbase.ui +++ b/src/ui/qgsgeometryvalidationdockbase.ui @@ -15,6 +15,9 @@ + + + @@ -22,7 +25,34 @@ - + + + + + + + Zoom To Problem + + + + + + + Zoom To Feature + + + + + + + Detailed Desctiption + + + + + + + ... @@ -33,8 +63,23 @@ - - + + + + + + Previous + + + + + + + Next + + + + From 38b2f71d7addb0dd3825aab81664205dc66d5e86 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Fri, 28 Sep 2018 15:16:23 +0200 Subject: [PATCH 036/126] Fix rebase issues --- .../geometry_checker/qgsgeometrygapcheck.h | 10 ++------ .../qgsgeometrymissingvertexcheck.cpp | 4 +--- .../qgsgeometryoverlapcheck.h | 23 +++---------------- 3 files changed, 6 insertions(+), 31 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h index 35e23e018e4..ceb81ce147e 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h @@ -76,6 +76,8 @@ class ANALYSIS_EXPORT QgsGeometryGapCheckError : public QgsGeometryCheckError class ANALYSIS_EXPORT QgsGeometryGapCheck : public QgsGeometryCheck { public: + enum ResolutionMethod { MergeLongestEdge, NoChange }; + explicit QgsGeometryGapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ); QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } @@ -97,14 +99,6 @@ class ANALYSIS_EXPORT QgsGeometryGapCheck : public QgsGeometryCheck static QgsGeometryCheck::CheckType factoryCheckType() SIP_SKIP; ///@endcond private - static QString factoryDescription() { return tr( "Gap" ); } - static QString factoryId() { return QStringLiteral( "QgsGeometryGapCheck" ); } - static QgsGeometryCheck::Flags factoryFlags() {return QgsGeometryCheck::SingleLayerTopologyCheck;} - static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PolygonGeometry};} - static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } - - enum ResolutionMethod { MergeLongestEdge, NoChange }; - private: bool mergeWithNeighbor( const QMap &featurePools, QgsGeometryGapCheckError *err, Changes &changes, QString &errMsg ) const; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp index cbfa064c71a..788b12c237f 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp @@ -27,9 +27,7 @@ QgsGeometryMissingVertexCheck::QgsGeometryMissingVertexCheck( const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration ) : QgsGeometryCheck( context, geometryCheckConfiguration ) -{ - -} +{} void QgsGeometryMissingVertexCheck::collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { diff --git a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h index c5b8d3de785..5f18557b81a 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h @@ -72,6 +72,9 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheckError : public QgsGeometryCheckErro class ANALYSIS_EXPORT QgsGeometryOverlapCheck : public QgsGeometryCheck { public: + + enum ResolutionMethod { Subtract, NoChange }; + QgsGeometryOverlapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ); QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; @@ -92,26 +95,6 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheck : public QgsGeometryCheck static QgsGeometryCheck::CheckType factoryCheckType() SIP_SKIP; ///@endcond private - static QString factoryDescription() { return tr( "Overlap" ); } - static QString factoryId() { return QStringLiteral( "QgsGeometryOverlapCheck" ); } - static QgsGeometryCheck::Flags factoryFlags() {return QgsGeometryCheck::SingleLayerTopologyCheck;} - static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PolygonGeometry};} - static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } - - QString description() const override; - QString id() const override; - QgsGeometryCheck::Flags flags() const override; - -///@cond private - static QString factoryDescription() SIP_SKIP; - static QString factoryId() SIP_SKIP; - static QgsGeometryCheck::Flags factoryFlags() SIP_SKIP; - static QList factoryCompatibleGeometryTypes() SIP_SKIP; - static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP; -///@endcond private - - enum ResolutionMethod { Subtract, NoChange }; - private: const double mOverlapThresholdMapUnits; From 49883826c2ea1f8081d6387a58601b34ae1e1b52 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Fri, 28 Sep 2018 18:59:35 +0200 Subject: [PATCH 037/126] Fix rebase problem --- src/ui/qgsvectorlayerpropertiesbase.ui | 88 +------------------------- 1 file changed, 3 insertions(+), 85 deletions(-) diff --git a/src/ui/qgsvectorlayerpropertiesbase.ui b/src/ui/qgsvectorlayerpropertiesbase.ui index 44c46960058..43bf49889a6 100644 --- a/src/ui/qgsvectorlayerpropertiesbase.ui +++ b/src/ui/qgsvectorlayerpropertiesbase.ui @@ -1961,8 +1961,8 @@ border-radius: 2px; 0 0 - 340 - 630 + 653 + 806 @@ -2410,89 +2410,6 @@ border-radius: 2px; - - - - Geometry validation - - - - - 0 - 0 - 651 - 804 - - - - - - - Automatic Fixes - - - - - - <html><head/><body><p>The geometry precision defines the maximum precision to of geometry coordinates that should be stored on this layer. A snap to grid algorithm will be applied on every geometry entering this layer, resulting in coordinates being rounded to multiples of this value. The operation is applied in this layer's coordinate reference system.</p></body></html> - - - Geometry precision - - - - - - - Remove duplicate nodes - - - - - - - - - [Units] - - - - - - - Qt::ImhFormattedNumbersOnly - - - - - - [No precision restriction] - - - - - - - - - - - - Geometry checks - - - - - - - Topology checks - - - - - - - @@ -2712,6 +2629,7 @@ border-radius: 2px; + From dc2c78f328fdcea02daa54194f3afc4635aed966 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Fri, 28 Sep 2018 18:59:47 +0200 Subject: [PATCH 038/126] Implement zoom to feature --- src/app/qgisapp.cpp | 2 +- src/app/qgsgeometryvalidationdock.cpp | 37 +++++++++++++++++++++--- src/app/qgsgeometryvalidationdock.h | 8 ++++- src/app/qgsgeometryvalidationmodel.cpp | 34 ++++++++++++++++++++-- src/app/qgsgeometryvalidationmodel.h | 1 + src/app/qgsgeometryvalidationservice.cpp | 37 ++++++++++++++++++++---- src/app/qgsgeometryvalidationservice.h | 6 ++-- 7 files changed, 109 insertions(+), 16 deletions(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index c855d15e10f..67e29156314 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -932,7 +932,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh { mInfoBar->pushWarning( tr( "Geometry Validation" ), message ); } ); - mGeometryValidationDock = new QgsGeometryValidationDock( tr( "Geometry Validation" ) ); + mGeometryValidationDock = new QgsGeometryValidationDock( tr( "Geometry Validation" ), mMapCanvas ); mGeometryValidationModel = new QgsGeometryValidationModel( mGeometryValidationService.get(), mGeometryValidationDock ); connect( this, &QgisApp::activeLayerChanged, mGeometryValidationModel, [this]( QgsMapLayer * layer ) { diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 68f83a14bbe..6b2554dac1b 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -15,11 +15,13 @@ email : matthias@opengis.ch #include "qgsgeometryvalidationdock.h" #include "qgsgeometryvalidationmodel.h" +#include "qgsmapcanvas.h" #include -QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QWidget *parent, Qt::WindowFlags flags ) +QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsMapCanvas *mapCanvas, QWidget *parent, Qt::WindowFlags flags ) : QgsDockWidget( title, parent, flags ) + , mMapCanvas( mapCanvas ) { setupUi( this ); @@ -27,8 +29,9 @@ QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QWid connect( mPreviousButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::gotoPreviousError ); connect( mZoomToProblemButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::zoomToProblem ); connect( mZoomToFeatureButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::zoomToFeature ); - - onCurrentErrorChanged( QModelIndex(), QModelIndex() ); + connect( mMapCanvas, &QgsMapCanvas::currentLayerChanged, this, &QgsGeometryValidationDock::updateLayerTransform ); + connect( mMapCanvas, &QgsMapCanvas::destinationCrsChanged, this, &QgsGeometryValidationDock::updateLayerTransform ); + connect( mMapCanvas, &QgsMapCanvas::transformContextChanged, this, &QgsGeometryValidationDock::updateLayerTransform ); } QgsGeometryValidationModel *QgsGeometryValidationDock::geometryValidationModel() const @@ -59,16 +62,41 @@ void QgsGeometryValidationDock::gotoPreviousError() void QgsGeometryValidationDock::zoomToProblem() { mLastZoomToAction = ZoomToProblem; + if ( !currentIndex().isValid() ) + return; + + QgsRectangle problemExtent = currentIndex().data( QgsGeometryValidationModel::ProblemExtentRole ).value(); + QgsRectangle mapExtent = mLayerTransform.transform( problemExtent ); + mMapCanvas->zoomToFeatureExtent( mapExtent ); } void QgsGeometryValidationDock::zoomToFeature() { mLastZoomToAction = ZoomToFeature; - // mErrorListView->currentIndex().data( ) + if ( !currentIndex().isValid() ) + return; + + QgsRectangle featureExtent = currentIndex().data( QgsGeometryValidationModel::FeatureExtentRole ).value(); + QgsRectangle mapExtent = mLayerTransform.transform( featureExtent ); + mMapCanvas->zoomToFeatureExtent( mapExtent ); +} + +void QgsGeometryValidationDock::updateLayerTransform() +{ + if ( !mMapCanvas->currentLayer() ) + return; + + mLayerTransform = QgsCoordinateTransform( mMapCanvas->currentLayer()->crs(), mMapCanvas->mapSettings().destinationCrs(), mMapCanvas->mapSettings().transformContext() ); +} + +QModelIndex QgsGeometryValidationDock::currentIndex() const +{ + return mErrorListView->selectionModel()->currentIndex(); } void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤t, const QModelIndex &previous ) { + Q_UNUSED( previous ) mNextButton->setEnabled( current.isValid() && current.row() < mGeometryValidationModel->rowCount() - 1 ); mPreviousButton->setEnabled( current.isValid() && current.row() > 0 ); @@ -83,5 +111,6 @@ void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤ case ZoomToFeature: zoomToFeature(); + break; } } diff --git a/src/app/qgsgeometryvalidationdock.h b/src/app/qgsgeometryvalidationdock.h index eb08236d736..0a3993e4552 100644 --- a/src/app/qgsgeometryvalidationdock.h +++ b/src/app/qgsgeometryvalidationdock.h @@ -18,7 +18,9 @@ email : matthias@opengis.ch #include "ui_qgsgeometryvalidationdockbase.h" #include "qgsdockwidget.h" +#include "qgscoordinatetransform.h" +class QgsMapCanvas; class QgsGeometryValidationModel; /** @@ -29,7 +31,7 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal Q_OBJECT public: - QgsGeometryValidationDock( const QString &title, QWidget *parent = nullptr, Qt::WindowFlags flags = nullptr ); + QgsGeometryValidationDock( const QString &title, QgsMapCanvas *mapCanvas, QWidget *parent = nullptr, Qt::WindowFlags flags = nullptr ); QgsGeometryValidationModel *geometryValidationModel() const; void setGeometryValidationModel( QgsGeometryValidationModel *geometryValidationModel ); @@ -40,6 +42,7 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal void gotoPreviousError(); void zoomToProblem(); void zoomToFeature(); + void updateLayerTransform(); private: enum ZoomToAction @@ -50,6 +53,9 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal ZoomToAction mLastZoomToAction = ZoomToFeature; QgsGeometryValidationModel *mGeometryValidationModel = nullptr; QButtonGroup *mZoomToButtonGroup = nullptr; + QgsMapCanvas *mMapCanvas = nullptr; + QgsCoordinateTransform mLayerTransform; + QModelIndex currentIndex() const; }; #endif // QGSGEOMETRYVALIDATIONPANEL_H diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index 88f8ccefb47..d112f0deb61 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -11,7 +11,8 @@ QgsGeometryValidationModel::QgsGeometryValidationModel( QgsGeometryValidationSer { connect( mGeometryValidationService, &QgsGeometryValidationService::geometryCheckCompleted, this, &QgsGeometryValidationModel::onGeometryCheckCompleted ); connect( mGeometryValidationService, &QgsGeometryValidationService::geometryCheckStarted, this, &QgsGeometryValidationModel::onGeometryCheckStarted ); - connect( mGeometryValidationService, &QgsGeometryValidationService::topologyChecksUpdated, this, &QgsGeometryValidationModel::onTopologyChecksUpdated, Qt::QueuedConnection ); + connect( mGeometryValidationService, &QgsGeometryValidationService::topologyChecksUpdated, this, &QgsGeometryValidationModel::onTopologyChecksUpdated ); + connect( mGeometryValidationService, &QgsGeometryValidationService::topologyChecksCleared, this, &QgsGeometryValidationModel::onTopologyChecksCleared ); } QModelIndex QgsGeometryValidationModel::index( int row, int column, const QModelIndex &parent ) const @@ -61,6 +62,18 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) return tr( "%1: %2" ).arg( featureTitle, topologyError->description() ); } + + case FeatureExtentRole: + { + const QgsFeatureId fid = topologyError->featureId(); + const QgsFeature feature = mCurrentLayer->getFeature( fid ); // TODO: this should be cached! + return feature.geometry().boundingBox(); + } + + case ProblemExtentRole: + { + return topologyError->affectedAreaBBox(); + } } } else @@ -195,7 +208,7 @@ void QgsGeometryValidationModel::onTopologyChecksUpdated( QgsVectorLayer *layer, if ( layer == currentLayer() ) { - const int oldRowCount = rowCount( QModelIndex() ); + const int oldRowCount = rowCount(); beginInsertRows( QModelIndex(), oldRowCount, oldRowCount + errors.size() ); } @@ -207,6 +220,23 @@ void QgsGeometryValidationModel::onTopologyChecksUpdated( QgsVectorLayer *layer, } } +void QgsGeometryValidationModel::onTopologyChecksCleared( QgsVectorLayer *layer ) +{ + auto &topologyLayerErrors = mTopologyErrorStorage[layer]; + if ( topologyLayerErrors.empty() ) + return; + + if ( layer == currentLayer() ) + { + beginRemoveRows( QModelIndex(), mErrorStorage.size(), rowCount() - 1 ); + } + topologyLayerErrors.clear(); + if ( layer == currentLayer() ) + { + endRemoveRows(); + } +} + int QgsGeometryValidationModel::errorsForFeature( QgsVectorLayer *layer, QgsFeatureId fid ) { const auto &layerErrors = mErrorStorage[layer]; diff --git a/src/app/qgsgeometryvalidationmodel.h b/src/app/qgsgeometryvalidationmodel.h index 0182208093f..08aa9bdc058 100644 --- a/src/app/qgsgeometryvalidationmodel.h +++ b/src/app/qgsgeometryvalidationmodel.h @@ -36,6 +36,7 @@ class QgsGeometryValidationModel : public QAbstractItemModel void onGeometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList > &errors ); void onGeometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid ); void onTopologyChecksUpdated( QgsVectorLayer *layer, const QList > &errors ); + void onTopologyChecksCleared( QgsVectorLayer *layer ); private: struct FeatureErrors diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 4ad8ea75441..251ae6626c4 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -196,11 +196,24 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer ) { + emit topologyChecksCleared( layer ); + QFutureWatcher *futureWatcher = mLayerCheckStates[layer].topologyCheckFutureWatcher; if ( futureWatcher ) { - // TODO: kill!! - delete futureWatcher; + // Make sure no more checks are started first + futureWatcher->cancel(); + + // Tell all checks to stop asap + const auto feedbacks = mLayerCheckStates[layer].topologyCheckFeedbacks; + for ( QgsFeedback *feedback : feedbacks ) + { + if ( feedback ) + feedback->cancel(); + } + + // The future watcher will take care of deleting + mLayerCheckStates[layer].topologyCheckFeedbacks.clear(); } QgsFeatureIds checkFeatureIds = layer->editBuffer()->changedGeometries().keys().toSet(); @@ -209,7 +222,6 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer // TODO: ownership of these objects... QgsVectorLayerFeaturePool *featurePool = new QgsVectorLayerFeaturePool( layer ); QList &allErrors = mLayerCheckStates[layer].topologyCheckErrors; - QgsFeedback *feedback = new QgsFeedback(); QMap layerIds; layerIds.insert( layer->id(), checkFeatureIds ); QgsGeometryCheck::LayerFeatureIds layerFeatureIds( layerIds ); @@ -219,12 +231,19 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer const QList checks = mLayerCheckStates[layer].topologyChecks; - QFuture future = QtConcurrent::map( checks, [featurePools, &allErrors, feedback, layerFeatureIds, layer, this]( const QgsGeometryCheck * check ) + QMap feedbacks; + for ( QgsGeometryCheck *check : checks ) + feedbacks.insert( check, new QgsFeedback() ); + + mLayerCheckStates[layer].topologyCheckFeedbacks = feedbacks.values(); + + QFuture future = QtConcurrent::map( checks, [featurePools, &allErrors, layerFeatureIds, layer, feedbacks, this]( const QgsGeometryCheck * check ) { // Watch out with the layer pointer in here. We are running in a thread, so we do not want to actually use it // except for using its address to report the error. QList errors; QStringList messages; // Do we really need these? + QgsFeedback *feedback = feedbacks.value( check ); check->collectErrors( featurePools, errors, messages, feedback, layerFeatureIds ); QgsReadWriteLocker errorLocker( mTopologyCheckLock, QgsReadWriteLocker::Write ); @@ -235,18 +254,24 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer { sharedErrors.append( std::shared_ptr( error ) ); } - emit topologyChecksUpdated( layer, sharedErrors ); + if ( !feedback->isCanceled() ) + emit topologyChecksUpdated( layer, sharedErrors ); + errorLocker.unlock(); } ); futureWatcher = new QFutureWatcher(); futureWatcher->setFuture( future ); - connect( futureWatcher, &QFutureWatcherBase::finished, this, [&allErrors, layer, this]() + connect( futureWatcher, &QFutureWatcherBase::finished, this, [&allErrors, layer, feedbacks, futureWatcher, this]() { QgsReadWriteLocker errorLocker( mTopologyCheckLock, QgsReadWriteLocker::Read ); layer->setAllowCommit( allErrors.empty() ); errorLocker.unlock(); + qDeleteAll( feedbacks.values() ); + futureWatcher->deleteLater(); + if ( mLayerCheckStates[layer].topologyCheckFutureWatcher == futureWatcher ) + mLayerCheckStates[layer].topologyCheckFutureWatcher = nullptr; } ); mLayerCheckStates[layer].topologyCheckFutureWatcher = futureWatcher; diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index 2f550c055b3..5f9bdfd14c5 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -30,7 +30,7 @@ class QgsGeometryCheck; class QgsSingleGeometryCheck; class QgsSingleGeometryCheckError; class QgsGeometryCheckError; - +class QgsFeedback; /** * This service connects to all layers in a project and triggers validation @@ -68,6 +68,7 @@ class QgsGeometryValidationService : public QObject void geometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid ); void geometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList> &errors ); void topologyChecksUpdated( QgsVectorLayer *layer, const QList > &errors ); + void topologyChecksCleared( QgsVectorLayer *layer ); void warning( const QString &message ); @@ -92,8 +93,9 @@ class QgsGeometryValidationService : public QObject struct VectorCheckState { QList< QgsSingleGeometryCheck * > singleFeatureChecks; - QList< QgsGeometryCheck * > topologyChecks; + QList< QgsGeometryCheck *> topologyChecks; QFutureWatcher *topologyCheckFutureWatcher = nullptr; + QList topologyCheckFeedbacks; // will be deleted when topologyCheckFutureWatcher is delteed QList topologyCheckErrors; }; From 4607930ece8a4ca496b4c33a25146cf980cb6758 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Sat, 29 Sep 2018 15:17:34 +0200 Subject: [PATCH 039/126] Allow resolving errors --- .../geometry_checker/qgsfeaturepool.sip.in | 1 + .../geometry_checker/qgsfeaturepool.cpp | 17 +++++ .../vector/geometry_checker/qgsfeaturepool.h | 6 ++ .../geometry_checker/qgsgeometrygapcheck.cpp | 2 +- .../geometry_checker/qgsgeometrygapcheck.h | 6 ++ .../qgsgeometrymissingvertexcheck.cpp | 23 ++++++- .../qgsgeometrymissingvertexcheck.h | 3 +- src/app/qgisapp.cpp | 1 + src/app/qgsgeometryvalidationdock.cpp | 62 +++++++++++++++++++ src/app/qgsgeometryvalidationdock.h | 9 +++ src/app/qgsgeometryvalidationmodel.cpp | 22 +++++++ src/app/qgsgeometryvalidationmodel.h | 6 +- src/app/qgsgeometryvalidationservice.cpp | 36 ++++++++--- src/app/qgsgeometryvalidationservice.h | 4 ++ src/ui/qgsgeometryvalidationdockbase.ui | 20 +++--- 15 files changed, 200 insertions(+), 18 deletions(-) diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in index bee154195dc..3f4eb0313c7 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in @@ -31,6 +31,7 @@ It will be retrieved from the cache or from the underlying layer if unavailable. If the feature is neither available from the cache nor from the layer it will return false. %End + virtual void updateFeature( QgsFeature &feature ) = 0; %Docstring Updates a feature in this pool. diff --git a/src/analysis/vector/geometry_checker/qgsfeaturepool.cpp b/src/analysis/vector/geometry_checker/qgsfeaturepool.cpp index 1cb50573ead..b96bc5f4612 100644 --- a/src/analysis/vector/geometry_checker/qgsfeaturepool.cpp +++ b/src/analysis/vector/geometry_checker/qgsfeaturepool.cpp @@ -62,6 +62,23 @@ bool QgsFeaturePool::getFeature( QgsFeatureId id, QgsFeature &feature ) return true; } +QgsFeatureIds QgsFeaturePool::getFeatures( const QgsFeatureRequest &request ) +{ + QgsFeatureIds fids; + + std::unique_ptr source = QgsVectorLayerUtils::getFeatureSource( mLayer ); + + QgsFeatureIterator it = source->getFeatures( request ); + QgsFeature feature; + while ( it.nextFeature( feature ) ) + { + insertFeature( feature ); + fids << feature.id(); + } + + return fids; +} + QgsFeatureIds QgsFeaturePool::allFeatureIds() const { return mFeatureIds; diff --git a/src/analysis/vector/geometry_checker/qgsfeaturepool.h b/src/analysis/vector/geometry_checker/qgsfeaturepool.h index 8ddcb1c48f8..09573a01f5a 100644 --- a/src/analysis/vector/geometry_checker/qgsfeaturepool.h +++ b/src/analysis/vector/geometry_checker/qgsfeaturepool.h @@ -46,6 +46,12 @@ class ANALYSIS_EXPORT QgsFeaturePool : public QgsFeatureSink SIP_ABSTRACT */ bool getFeature( QgsFeatureId id, QgsFeature &feature ); + /** + * Warm the cache ... + * TODO write more docs + */ + QgsFeatureIds getFeatures( const QgsFeatureRequest &request ) SIP_SKIP; + /** * Updates a feature in this pool. * Implementations will update the feature on the layer or on the data provider. diff --git a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp index 1fa79a0368a..f439c0bd630 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp @@ -97,7 +97,7 @@ void QgsGeometryGapCheck::collectErrors( const QMap & } // Skip gaps above threshold - if ( gapGeom->area() > mGapThresholdMapUnits || gapGeom->area() < mContext->reducedTolerance ) + if ( ( mGapThresholdMapUnits > 0 && gapGeom->area() > mGapThresholdMapUnits ) || gapGeom->area() < mContext->reducedTolerance ) { continue; } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h index ceb81ce147e..32dc0b5e707 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h @@ -78,6 +78,12 @@ class ANALYSIS_EXPORT QgsGeometryGapCheck : public QgsGeometryCheck public: enum ResolutionMethod { MergeLongestEdge, NoChange }; + /** + * The \a configuration accepts a "gapThreshold" key which specifies + * the maximum gap size in squared map units. Any gaps which are larger + * than this area are accepted. If "gapThreshold" is set to 0, the check + * is disabled. + */ explicit QgsGeometryGapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ); QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp index 788b12c237f..7c747e5e84a 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp @@ -71,11 +71,32 @@ void QgsGeometryMissingVertexCheck::fixError( const QMapsetFixed( method ); } + if ( method == AddMissingVertex ) + { + QgsFeaturePool *featurePool = featurePools[ error->layerId() ]; + + QgsFeature feature; + featurePool->getFeature( error->featureId(), feature ); + + QgsPointXY pointOnSegment; // Should be equal to location + int vertexIndex; + QgsGeometry geometry = feature.geometry(); + geometry.closestSegmentWithContext( error->location(), pointOnSegment, vertexIndex ); + geometry.insertVertex( QgsPoint( error->location() ), vertexIndex ); + feature.setGeometry( geometry ); + + featurePool->updateFeature( feature ); + // TODO update "changes" structure + + error->setFixed( method ); + } } QStringList QgsGeometryMissingVertexCheck::resolutionMethods() const { - static QStringList methods = QStringList() << tr( "No action" ); + static QStringList methods = QStringList() + << tr( "No action" ) + << tr( "Add missing vertex" ); return methods; } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h index 05c4aca5717..f0ad1999e70 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h @@ -37,7 +37,8 @@ class ANALYSIS_EXPORT QgsGeometryMissingVertexCheck : public QgsGeometryCheck public: enum ResolutionMethod { - NoChange + NoChange, + AddMissingVertex }; explicit QgsGeometryMissingVertexCheck( const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration ); diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 67e29156314..fda2a85ae60 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -939,6 +939,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh mGeometryValidationModel->setCurrentLayer( qobject_cast( layer ) ); } ); mGeometryValidationDock->setGeometryValidationModel( mGeometryValidationModel ); + mGeometryValidationDock->setGeometryValidationService( mGeometryValidationService.get() ); addDockWidget( Qt::RightDockWidgetArea, mGeometryValidationDock ); endProfile(); diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 6b2554dac1b..c2aacc9ba14 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -15,7 +15,12 @@ email : matthias@opengis.ch #include "qgsgeometryvalidationdock.h" #include "qgsgeometryvalidationmodel.h" +#include "qgsgeometryvalidationservice.h" #include "qgsmapcanvas.h" +#include "qgsrubberband.h" +#include "qgsvectorlayer.h" +#include "qgsgeometrycheck.h" +#include "qgsgeometrycheckerror.h" #include @@ -32,8 +37,23 @@ QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsM connect( mMapCanvas, &QgsMapCanvas::currentLayerChanged, this, &QgsGeometryValidationDock::updateLayerTransform ); connect( mMapCanvas, &QgsMapCanvas::destinationCrsChanged, this, &QgsGeometryValidationDock::updateLayerTransform ); connect( mMapCanvas, &QgsMapCanvas::transformContextChanged, this, &QgsGeometryValidationDock::updateLayerTransform ); + + mFeatureRubberband = new QgsRubberBand( mMapCanvas ); + mErrorRubberband = new QgsRubberBand( mMapCanvas ); + mErrorLocationRubberband = new QgsRubberBand( mMapCanvas ); + + double scaleFactor = mMapCanvas->fontMetrics().xHeight() * .2; + + mFeatureRubberband->setColor( QColor( 250, 180, 180, 100 ) ); + mFeatureRubberband->setWidth( scaleFactor ); + mErrorRubberband->setColor( QColor( 180, 250, 180, 100 ) ); + mErrorRubberband->setWidth( scaleFactor ); + mErrorLocationRubberband->setIcon( QgsRubberBand::ICON_X ); + mErrorLocationRubberband->setWidth( scaleFactor * 3 ); + mErrorLocationRubberband->setColor( QColor( 180, 180, 250, 100 ) ); } + QgsGeometryValidationModel *QgsGeometryValidationDock::geometryValidationModel() const { return mGeometryValidationModel; @@ -89,6 +109,16 @@ void QgsGeometryValidationDock::updateLayerTransform() mLayerTransform = QgsCoordinateTransform( mMapCanvas->currentLayer()->crs(), mMapCanvas->mapSettings().destinationCrs(), mMapCanvas->mapSettings().transformContext() ); } +QgsGeometryValidationService *QgsGeometryValidationDock::geometryValidationService() const +{ + return mGeometryValidationService; +} + +void QgsGeometryValidationDock::setGeometryValidationService( QgsGeometryValidationService *geometryValidationService ) +{ + mGeometryValidationService = geometryValidationService; +} + QModelIndex QgsGeometryValidationDock::currentIndex() const { return mErrorListView->selectionModel()->currentIndex(); @@ -102,6 +132,38 @@ void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤ mProblemDetailWidget->setVisible( current.isValid() ); mProblemDescriptionLabel->setText( current.data().toString() ); + { + QgsGeometryCheckError *error = current.data( QgsGeometryValidationModel::GeometryCheckErrorRole ).value(); + while ( QPushButton *btn = mResolutionWidget->findChild() ) + delete btn; + const QStringList resolutionMethods = error->check()->resolutionMethods(); + int resolutionIndex = 0; + for ( const QString &resolutionMethod : resolutionMethods ) + { + QPushButton *resolveBtn = new QPushButton( resolutionMethod ); + connect( resolveBtn, &QPushButton::clicked, this, [resolutionIndex, error, this]() + { + mGeometryValidationService->fixError( error, resolutionIndex ); + } ); + mResolutionWidget->layout()->addWidget( resolveBtn ); + resolutionIndex++; + } + } + + QgsVectorLayer *vlayer = qobject_cast( mMapCanvas->currentLayer() ); + if ( vlayer ) + { + QgsGeometry featureGeometry = current.data( QgsGeometryValidationModel::FeatureGeometryRole ).value(); + QgsGeometry errorGeometry = current.data( QgsGeometryValidationModel::ErrorGeometryRole ).value(); + QgsPointXY locationGeometry = current.data( QgsGeometryValidationModel::ErrorLocationGeometryRole ).value(); + qDebug() << "feature geom : " << featureGeometry.asWkt(); + qDebug() << "error geom : " << errorGeometry.asWkt(); + qDebug() << "locationgeom : " << QgsGeometry( new QgsPoint( locationGeometry ) ).asWkt(); + + mFeatureRubberband->setToGeometry( featureGeometry ); + mErrorRubberband->setToGeometry( errorGeometry ); + mErrorLocationRubberband->setToGeometry( QgsGeometry( new QgsPoint( locationGeometry ) ) ); + } switch ( mLastZoomToAction ) { diff --git a/src/app/qgsgeometryvalidationdock.h b/src/app/qgsgeometryvalidationdock.h index 0a3993e4552..53041249f18 100644 --- a/src/app/qgsgeometryvalidationdock.h +++ b/src/app/qgsgeometryvalidationdock.h @@ -22,6 +22,8 @@ email : matthias@opengis.ch class QgsMapCanvas; class QgsGeometryValidationModel; +class QgsGeometryValidationService; +class QgsRubberBand; /** * @brief The QgsGeometryValidationDock class @@ -36,6 +38,9 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal QgsGeometryValidationModel *geometryValidationModel() const; void setGeometryValidationModel( QgsGeometryValidationModel *geometryValidationModel ); + QgsGeometryValidationService *geometryValidationService() const; + void setGeometryValidationService( QgsGeometryValidationService *geometryValidationService ); + private slots: void onCurrentErrorChanged( const QModelIndex ¤t, const QModelIndex &previous ); void gotoNextError(); @@ -52,10 +57,14 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal }; ZoomToAction mLastZoomToAction = ZoomToFeature; QgsGeometryValidationModel *mGeometryValidationModel = nullptr; + QgsGeometryValidationService *mGeometryValidationService = nullptr; QButtonGroup *mZoomToButtonGroup = nullptr; QgsMapCanvas *mMapCanvas = nullptr; QgsCoordinateTransform mLayerTransform; QModelIndex currentIndex() const; + QgsRubberBand *mFeatureRubberband = nullptr; + QgsRubberBand *mErrorRubberband = nullptr; + QgsRubberBand *mErrorLocationRubberband = nullptr; }; #endif // QGSGEOMETRYVALIDATIONPANEL_H diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index d112f0deb61..fca46fd720c 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -74,6 +74,28 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) { return topologyError->affectedAreaBBox(); } + + case ErrorGeometryRole: + { + return topologyError->geometry(); + } + + case FeatureGeometryRole: + { + const QgsFeatureId fid = topologyError->featureId(); + const QgsFeature feature = mCurrentLayer->getFeature( fid ); // TODO: this should be cached! + return feature.geometry(); + } + + case ErrorLocationGeometryRole: + { + return topologyError->location(); + } + + case GeometryCheckErrorRole: + { + return QVariant::fromValue( topologyError.get() ); + } } } else diff --git a/src/app/qgsgeometryvalidationmodel.h b/src/app/qgsgeometryvalidationmodel.h index 08aa9bdc058..1f345a0efdc 100644 --- a/src/app/qgsgeometryvalidationmodel.h +++ b/src/app/qgsgeometryvalidationmodel.h @@ -16,7 +16,11 @@ class QgsGeometryValidationModel : public QAbstractItemModel enum Roles { FeatureExtentRole = Qt::UserRole, - ProblemExtentRole + ProblemExtentRole, + ErrorGeometryRole, + FeatureGeometryRole, + ErrorLocationGeometryRole, + GeometryCheckErrorRole }; QgsGeometryValidationModel( QgsGeometryValidationService *geometryValidationService, QObject *parent = nullptr ); diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 251ae6626c4..41583fe0599 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -40,6 +40,13 @@ bool QgsGeometryValidationService::validationActive( QgsVectorLayer *layer, QgsF return false; } +void QgsGeometryValidationService::fixError( const QgsGeometryCheckError *error, int method ) +{ + QgsGeometryCheck::Changes changes; + QgsGeometryCheckError *nonconsterr = const_cast( error ); + error->check()->fixError( mFeaturePools, nonconsterr, method, QMap(), changes ); +} + void QgsGeometryValidationService::onLayersAdded( const QList &layers ) { for ( QgsMapLayer *layer : layers ) @@ -122,7 +129,7 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) qDeleteAll( mLayerCheckStates[layer].topologyChecks ); // TODO: ownership and lifetime of the context!! - auto context = new QgsGeometryCheckContext( 1, mProject->crs(), mProject->transformContext() ); + auto context = new QgsGeometryCheckContext( 8, mProject->crs(), mProject->transformContext() ); QList layerChecks; QgsGeometryCheckRegistry *checkRegistry = QgsAnalysis::instance()->geometryCheckRegistry(); @@ -216,18 +223,33 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer mLayerCheckStates[layer].topologyCheckFeedbacks.clear(); } - QgsFeatureIds checkFeatureIds = layer->editBuffer()->changedGeometries().keys().toSet(); - checkFeatureIds.unite( layer->editBuffer()->addedFeatures().keys().toSet() ); + QgsFeatureIds affectedFeatureIds = layer->editBuffer()->changedGeometries().keys().toSet(); + affectedFeatureIds.unite( layer->editBuffer()->addedFeatures().keys().toSet() ); // TODO: ownership of these objects... QgsVectorLayerFeaturePool *featurePool = new QgsVectorLayerFeaturePool( layer ); QList &allErrors = mLayerCheckStates[layer].topologyCheckErrors; QMap layerIds; + + QgsFeatureRequest request = QgsFeatureRequest( affectedFeatureIds ).setSubsetOfAttributes( QgsAttributeList() ); + QgsFeatureIterator it = layer->getFeatures( request ); + QgsFeature feature; + QgsRectangle area; + while ( it.nextFeature( feature ) ) + { + area.combineExtentWith( feature.geometry().boundingBox() ); + } + + QgsFeatureRequest areaRequest = QgsFeatureRequest().setFilterRect( area ); + QgsFeatureIds checkFeatureIds = featurePool->getFeatures( areaRequest ); + layerIds.insert( layer->id(), checkFeatureIds ); QgsGeometryCheck::LayerFeatureIds layerFeatureIds( layerIds ); - QMap featurePools; - featurePools.insert( layer->id(), featurePool ); + if ( !mFeaturePools.contains( layer->id() ) ) + { + mFeaturePools.insert( layer->id(), featurePool ); + } const QList checks = mLayerCheckStates[layer].topologyChecks; @@ -237,7 +259,7 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer mLayerCheckStates[layer].topologyCheckFeedbacks = feedbacks.values(); - QFuture future = QtConcurrent::map( checks, [featurePools, &allErrors, layerFeatureIds, layer, feedbacks, this]( const QgsGeometryCheck * check ) + QFuture future = QtConcurrent::map( checks, [&allErrors, layerFeatureIds, layer, feedbacks, this]( const QgsGeometryCheck * check ) { // Watch out with the layer pointer in here. We are running in a thread, so we do not want to actually use it // except for using its address to report the error. @@ -245,7 +267,7 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer QStringList messages; // Do we really need these? QgsFeedback *feedback = feedbacks.value( check ); - check->collectErrors( featurePools, errors, messages, feedback, layerFeatureIds ); + check->collectErrors( mFeaturePools, errors, messages, feedback, layerFeatureIds ); QgsReadWriteLocker errorLocker( mTopologyCheckLock, QgsReadWriteLocker::Write ); allErrors.append( errors ); diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index 5f9bdfd14c5..1143a541eb4 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -31,6 +31,7 @@ class QgsSingleGeometryCheck; class QgsSingleGeometryCheckError; class QgsGeometryCheckError; class QgsFeedback; +class QgsFeaturePool; /** * This service connects to all layers in a project and triggers validation @@ -64,6 +65,8 @@ class QgsGeometryValidationService : public QObject */ bool validationActive( QgsVectorLayer *layer, QgsFeatureId feature ) const; + void fixError( const QgsGeometryCheckError *error, int method ); + signals: void geometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid ); void geometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList> &errors ); @@ -101,6 +104,7 @@ class QgsGeometryValidationService : public QObject QReadWriteLock mTopologyCheckLock; QHash mLayerCheckStates; + QMap mFeaturePools; }; #endif // QGSGEOMETRYVALIDATIONSERVICE_H diff --git a/src/ui/qgsgeometryvalidationdockbase.ui b/src/ui/qgsgeometryvalidationdockbase.ui index 1d14210d9c9..87bc0981e71 100644 --- a/src/ui/qgsgeometryvalidationdockbase.ui +++ b/src/ui/qgsgeometryvalidationdockbase.ui @@ -28,13 +28,6 @@ - - - - Zoom To Problem - - - @@ -42,6 +35,13 @@ + + + + Zoom To Problem + + + @@ -49,6 +49,11 @@ + + + + + @@ -123,6 +128,7 @@ + From d8c4473c8a31fb16bebd88104759a0a9667753b0 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Sun, 30 Sep 2018 09:37:59 +0200 Subject: [PATCH 040/126] Make QgsRubberBand fillColor a Q_PROPERTY --- src/gui/qgsrubberband.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/qgsrubberband.cpp b/src/gui/qgsrubberband.cpp index c8dc91ce659..f8ed9b0870d 100644 --- a/src/gui/qgsrubberband.cpp +++ b/src/gui/qgsrubberband.cpp @@ -51,6 +51,9 @@ void QgsRubberBand::setColor( const QColor &color ) void QgsRubberBand::setFillColor( const QColor &color ) { + if ( mBrush.color() == color ) + return; + mBrush.setColor( color ); } From cf0b3e9e7d2560621e15aff15acd9225fbd82e78 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Sun, 30 Sep 2018 10:20:17 +0200 Subject: [PATCH 041/126] More UX --- .../geometry_checker/qgsgeometrygapcheck.cpp | 1 - src/app/qgsgeometryvalidationdock.cpp | 57 +++++++++++++------ src/app/qgsgeometryvalidationdock.h | 4 ++ src/app/qgsgeometryvalidationmodel.cpp | 17 ++++-- 4 files changed, 55 insertions(+), 24 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp index f439c0bd630..a5bdca73632 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp @@ -124,7 +124,6 @@ void QgsGeometryGapCheck::collectErrors( const QMap & // Add error double area = gapGeom->area(); errors.append( new QgsGeometryGapCheckError( this, QString(), QgsGeometry( gapGeom.release() ), neighboringIds, area, gapAreaBBox ) ); - } } diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index c2aacc9ba14..c68b9b28624 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -42,15 +42,18 @@ QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsM mErrorRubberband = new QgsRubberBand( mMapCanvas ); mErrorLocationRubberband = new QgsRubberBand( mMapCanvas ); - double scaleFactor = mMapCanvas->fontMetrics().xHeight() * .2; + double scaleFactor = mMapCanvas->fontMetrics().xHeight() * .4; - mFeatureRubberband->setColor( QColor( 250, 180, 180, 100 ) ); mFeatureRubberband->setWidth( scaleFactor ); + mFeatureRubberband->setStrokeColor( QColor( 100, 255, 100, 100 ) ); + mErrorRubberband->setColor( QColor( 180, 250, 180, 100 ) ); mErrorRubberband->setWidth( scaleFactor ); + mErrorLocationRubberband->setIcon( QgsRubberBand::ICON_X ); - mErrorLocationRubberband->setWidth( scaleFactor * 3 ); - mErrorLocationRubberband->setColor( QColor( 180, 180, 250, 100 ) ); + mErrorLocationRubberband->setWidth( scaleFactor ); + mErrorLocationRubberband->setIconSize( scaleFactor * 5 ); + mErrorLocationRubberband->setColor( QColor( 50, 255, 50, 255 ) ); } @@ -150,20 +153,7 @@ void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤ } } - QgsVectorLayer *vlayer = qobject_cast( mMapCanvas->currentLayer() ); - if ( vlayer ) - { - QgsGeometry featureGeometry = current.data( QgsGeometryValidationModel::FeatureGeometryRole ).value(); - QgsGeometry errorGeometry = current.data( QgsGeometryValidationModel::ErrorGeometryRole ).value(); - QgsPointXY locationGeometry = current.data( QgsGeometryValidationModel::ErrorLocationGeometryRole ).value(); - qDebug() << "feature geom : " << featureGeometry.asWkt(); - qDebug() << "error geom : " << errorGeometry.asWkt(); - qDebug() << "locationgeom : " << QgsGeometry( new QgsPoint( locationGeometry ) ).asWkt(); - - mFeatureRubberband->setToGeometry( featureGeometry ); - mErrorRubberband->setToGeometry( errorGeometry ); - mErrorLocationRubberband->setToGeometry( QgsGeometry( new QgsPoint( locationGeometry ) ) ); - } + showHighlight( current ); switch ( mLastZoomToAction ) { @@ -176,3 +166,34 @@ void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤ break; } } + +void QgsGeometryValidationDock::showHighlight( const QModelIndex ¤t ) +{ + QgsVectorLayer *vlayer = qobject_cast( mMapCanvas->currentLayer() ); + if ( vlayer ) + { + QgsGeometry featureGeometry = current.data( QgsGeometryValidationModel::FeatureGeometryRole ).value(); + QgsGeometry errorGeometry = current.data( QgsGeometryValidationModel::ErrorGeometryRole ).value(); + QgsPointXY locationGeometry = current.data( QgsGeometryValidationModel::ErrorLocationGeometryRole ).value(); + + mFeatureRubberband->setToGeometry( featureGeometry ); + + + QPropertyAnimation *animation = new QPropertyAnimation( mFeatureRubberband, "fillColor" ); + animation->setEasingCurve( QEasingCurve::OutQuad ); + connect( animation, &QPropertyAnimation::finished, animation, &QPropertyAnimation::deleteLater ); + connect( animation, &QPropertyAnimation::valueChanged, this, [this] + { + mFeatureRubberband->update(); + } ); + + animation->setDuration( 2000 ); + animation->setStartValue( QColor( 100, 255, 100, 255 ) ); + animation->setEndValue( QColor( 100, 255, 100, 0 ) ); + + animation->start(); + + // mErrorRubberband->setToGeometry( errorGeometry ); + mErrorLocationRubberband->setToGeometry( QgsGeometry( new QgsPoint( locationGeometry ) ) ); + } +} diff --git a/src/app/qgsgeometryvalidationdock.h b/src/app/qgsgeometryvalidationdock.h index 53041249f18..a52b6101488 100644 --- a/src/app/qgsgeometryvalidationdock.h +++ b/src/app/qgsgeometryvalidationdock.h @@ -50,11 +50,15 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal void updateLayerTransform(); private: + enum ZoomToAction { ZoomToFeature, ZoomToProblem }; + + void showHighlight( const QModelIndex ¤t ); + ZoomToAction mLastZoomToAction = ZoomToFeature; QgsGeometryValidationModel *mGeometryValidationModel = nullptr; QgsGeometryValidationService *mGeometryValidationService = nullptr; diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index fca46fd720c..dbcf2fcaeff 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -56,11 +56,17 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) const QgsFeatureId fid = topologyError->featureId(); const QgsFeature feature = mCurrentLayer->getFeature( fid ); // TODO: this should be cached! mExpressionContext.setFeature( feature ); - QString featureTitle = mDisplayExpression.evaluate( &mExpressionContext ).toString(); - if ( featureTitle.isEmpty() ) - featureTitle = fid; + const QVariant featureTitle = mDisplayExpression.evaluate( &mExpressionContext ); + + if ( featureTitle.isNull() ) + { + return topologyError->description(); + } + else + { + return tr( "%1: %2" ).arg( featureTitle.toString(), topologyError->description() ); + } - return tr( "%1: %2" ).arg( featureTitle, topologyError->description() ); } case FeatureExtentRole: @@ -77,7 +83,8 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) case ErrorGeometryRole: { - return topologyError->geometry(); + // TODO: save as QgsGeometry already in the error + return QgsGeometry( topologyError->geometry()->clone() ); } case FeatureGeometryRole: From 81cfc36c1f07da144842e9a72e0b42ada11a0791 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Sun, 30 Sep 2018 16:09:58 +0200 Subject: [PATCH 042/126] Make QgsGeometryCheck::CheckType available in factory --- .../vector/geometry_checker/qgsgeometryduplicatecheck.h | 6 ++++++ src/app/qgisapp.cpp | 1 - src/app/qgsgeometryvalidationservice.cpp | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h b/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h index 767a7e74249..031340af50e 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h @@ -70,6 +70,12 @@ class ANALYSIS_EXPORT QgsGeometryDuplicateCheck : public QgsGeometryCheck static QString factoryId(); static QgsGeometryCheck::CheckType factoryCheckType(); + static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; } + static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } + static QString factoryDescription() { return tr( "Duplicate" ); } + static QString factoryId(); + static QgsGeometryCheck::CheckType factoryCheckType(); + enum ResolutionMethod { NoChange, RemoveDuplicates }; }; diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index fda2a85ae60..e3f99a7ef66 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -925,7 +925,6 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh functionProfile( &QgisApp::initLayouts, this, QStringLiteral( "Initialize layouts support" ) ); startProfile( QStringLiteral( "Geometry validation" ) ); - QgsAnalysis::instance()->geometryCheckRegistry()->initialize(); mGeometryValidationService = qgis::make_unique( QgsProject::instance() ); connect( mGeometryValidationService.get(), &QgsGeometryValidationService::warning, this, [this]( const QString & message ) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 41583fe0599..7620678ad29 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -136,7 +136,7 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) const QStringList activeChecks = layer->geometryOptions()->geometryChecks(); - const QList singleCheckFactories = checkRegistry->geometryCheckFactories( layer, QgsGeometryCheck::SingleGeometryCheck ); + const QList singleCheckFactories = checkRegistry->geometryCheckFactories( layer, QgsGeometryCheck::FeatureNodeCheck, QgsGeometryCheck::AvailableInValidation ); for ( QgsGeometryCheckFactory *factory : singleCheckFactories ) { @@ -159,7 +159,7 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) // Topology checks QList topologyChecks; - const QList topologyCheckFactories = checkRegistry->geometryCheckFactories( layer, QgsGeometryCheck::SingleLayerTopologyCheck ); + const QList topologyCheckFactories = checkRegistry->geometryCheckFactories( layer, QgsGeometryCheck::LayerCheck, QgsGeometryCheck::AvailableInValidation ); for ( QgsGeometryCheckFactory *factory : topologyCheckFactories ) { From 7b3221fd4e5d032f77074fee7cc28fb06d2972ea Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Sun, 30 Sep 2018 16:26:48 +0200 Subject: [PATCH 043/126] Fix rebase issue --- src/ui/qgsvectorlayerpropertiesbase.ui | 161 ++++++++++++++----------- 1 file changed, 92 insertions(+), 69 deletions(-) diff --git a/src/ui/qgsvectorlayerpropertiesbase.ui b/src/ui/qgsvectorlayerpropertiesbase.ui index 43bf49889a6..f26d5f6ab28 100644 --- a/src/ui/qgsvectorlayerpropertiesbase.ui +++ b/src/ui/qgsvectorlayerpropertiesbase.ui @@ -23,8 +23,8 @@ :/images/icons/qgis-icon-16x16.png:/images/icons/qgis-icon-16x16.png - - + + Qt::Horizontal @@ -357,7 +357,7 @@ - 1 + 18 @@ -433,7 +433,7 @@ 0 0 - 289 + 325 389 @@ -1961,8 +1961,8 @@ border-radius: 2px; 0 0 - 653 - 806 + 340 + 630 @@ -2358,56 +2358,86 @@ border-radius: 2px; 0 - - - Automatic Fixes + + + true - - - - - <html><head/><body><p>The geometry precision defines the maximum precision to of geometry coordinates that should be stored on this layer. A snap to grid algorithm will be applied on every geometry entering this layer, resulting in coordinates being rounded to multiples of this value. The operation is applied in this layer's coordinate reference system.</p></body></html> - - - Geometry precision - - - - - - - Remove duplicate nodes - - - - - - - - - <html><head/><body><p>The geometry precision defines the maximum precision to of geometry coordinates that should be stored on this layer. A snap to grid algorithm will be applied on every geometry entering this layer, resulting in coordinates being rounded to multiples of this value. The operation is applied in this layer's coordinate reference system.</p></body></html> - - - [Disabled] - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + + + + 0 + 0 + 651 + 804 + + + + + + + Automatic Fixes + + + + + + <html><head/><body><p>The geometry precision defines the maximum precision to of geometry coordinates that should be stored on this layer. A snap to grid algorithm will be applied on every geometry entering this layer, resulting in coordinates being rounded to multiples of this value. The operation is applied in this layer's coordinate reference system.</p></body></html> + + + Geometry precision + + + + + + + Remove duplicate nodes + + + + + + + + + [Units] + + + + + + + Qt::ImhFormattedNumbersOnly + + + + + + [No precision restriction] + + + + + + + + + + + + Geometry checks + + + + + + + Topology checks + + + + + @@ -2418,7 +2448,7 @@ border-radius: 2px; - + @@ -2461,18 +2491,18 @@ border-radius: 2px; - - QgsScrollArea - QScrollArea -
qgsscrollarea.h
- 1 -
QgsCollapsibleGroupBox QGroupBox
qgscollapsiblegroupbox.h
1
+ + QgsScrollArea + QScrollArea +
qgsscrollarea.h
+ 1 +
QgsProjectionSelectionWidget QWidget @@ -2529,11 +2559,6 @@ border-radius: 2px;
qgsvectorlayerlegendwidget.h
1
- - QgsDoubleSpinBox - QDoubleSpinBox -
qgsdoublespinbox.h
-
mSearchLineEdit @@ -2596,7 +2621,6 @@ border-radius: 2px; mLayerLegendUrlLineEdit mLayerLegendUrlFormatComboBox mRemoveDuplicateNodesCheckbox - mGeometryPrecisionSpinBox @@ -2629,7 +2653,6 @@ border-radius: 2px; - From 0ef292322ac1668667694560cff6031f4f5c263b Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Sun, 30 Sep 2018 16:26:59 +0200 Subject: [PATCH 044/126] New API for check filtering --- src/app/qgsvectorlayerproperties.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index cb6f488558a..f4db288ede6 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -413,7 +413,7 @@ QgsVectorLayerProperties::QgsVectorLayerProperties( mPrecisionUnitsLabel->setText( QStringLiteral( "[%1]" ).arg( QgsUnitTypes::toAbbreviatedString( mLayer->crs().mapUnits() ) ) ); QLayout *geometryCheckLayout = new QVBoxLayout(); - const QList geometryCheckFactories = QgsAnalysis::instance()->geometryCheckRegistry()->geometryCheckFactories( mLayer, QgsGeometryCheck::Flag::SingleGeometryCheck ); + const QList geometryCheckFactories = QgsAnalysis::instance()->geometryCheckRegistry()->geometryCheckFactories( mLayer, QgsGeometryCheck::FeatureNodeCheck, QgsGeometryCheck::Flag::AvailableInValidation ); const QStringList activeChecks = mLayer->geometryOptions()->geometryChecks(); for ( const QgsGeometryCheckFactory *factory : geometryCheckFactories ) { @@ -425,7 +425,7 @@ QgsVectorLayerProperties::QgsVectorLayerProperties( mGeometryValidationGroupBox->setLayout( geometryCheckLayout ); QLayout *topologyCheckLayout = new QVBoxLayout(); - const QList topologyCheckFactories = QgsAnalysis::instance()->geometryCheckRegistry()->geometryCheckFactories( mLayer, QgsGeometryCheck::Flag::SingleLayerTopologyCheck ); + const QList topologyCheckFactories = QgsAnalysis::instance()->geometryCheckRegistry()->geometryCheckFactories( mLayer, QgsGeometryCheck::LayerCheck, QgsGeometryCheck::Flag::AvailableInValidation ); for ( const QgsGeometryCheckFactory *factory : topologyCheckFactories ) { From 4d374464f2de1b1ddb7c37441e916a3db779072d Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Sun, 30 Sep 2018 16:51:17 +0200 Subject: [PATCH 045/126] Make use of implicitly shared geometry --- src/app/qgsgeometryvalidationmodel.cpp | 3 +-- src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index dbcf2fcaeff..bc3b519f771 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -83,8 +83,7 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) case ErrorGeometryRole: { - // TODO: save as QgsGeometry already in the error - return QgsGeometry( topologyError->geometry()->clone() ); + return topologyError->geometry(); } case FeatureGeometryRole: diff --git a/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp b/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp index 330ca4a70b5..4eac99b4733 100644 --- a/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp +++ b/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp @@ -43,6 +43,7 @@ #include "qgssettings.h" #include "qgsscrollarea.h" #include "qgsgeometrycheckerror.h" +#include "qgsgeometry.h" QString QgsGeometryCheckerResultTab::sSettingsGroup = QStringLiteral( "/geometry_checker/default_fix_methods/" ); From 2abb13f0bbd56423703d6c870d1dbad07b276d7a Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Sun, 30 Sep 2018 16:53:50 +0200 Subject: [PATCH 046/126] More detach avoidance --- src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp b/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp index 4eac99b4733..330ca4a70b5 100644 --- a/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp +++ b/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp @@ -43,7 +43,6 @@ #include "qgssettings.h" #include "qgsscrollarea.h" #include "qgsgeometrycheckerror.h" -#include "qgsgeometry.h" QString QgsGeometryCheckerResultTab::sSettingsGroup = QStringLiteral( "/geometry_checker/default_fix_methods/" ); From 91cd1ed608ef42364faefa1fab4dc23a244793ba Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 09:06:45 +0200 Subject: [PATCH 047/126] Do not show standard message bar error with geometry validation errors --- src/app/qgisapp.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index e3f99a7ef66..d651c98429a 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -6965,11 +6965,18 @@ void QgisApp::labelingFontNotFound( QgsVectorLayer *vlayer, const QString &fontf void QgisApp::commitError( QgsVectorLayer *vlayer ) { + const QStringList commitErrors = vlayer->commitErrors(); + if ( !vlayer->allowCommit() && commitErrors.empty() ) + { + QgsMessageLog::logMessage( tr( "Could not save changes. Geometry validation failed." ) ); + return; + } + QgsMessageViewer *mv = new QgsMessageViewer(); mv->setWindowTitle( tr( "Commit Errors" ) ); mv->setMessageAsPlainText( tr( "Could not commit changes to layer %1" ).arg( vlayer->name() ) + "\n\n" - + tr( "Errors: %1\n" ).arg( vlayer->commitErrors().join( QStringLiteral( "\n " ) ) ) + + tr( "Errors: %1\n" ).arg( commitErrors.join( QStringLiteral( "\n " ) ) ) ); QToolButton *showMore = new QToolButton(); From ed274428c2c0cc1ec97112cc1244aaaef818c2f6 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 09:07:14 +0200 Subject: [PATCH 048/126] Disable message bar feedback from geometryvalidator --- src/gui/qgsmaptoolcapture.cpp | 19 +------------------ src/gui/qgsmaptoolcapture.h | 5 +---- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/gui/qgsmaptoolcapture.cpp b/src/gui/qgsmaptoolcapture.cpp index 562022e8d60..a7ac7cbc242 100644 --- a/src/gui/qgsmaptoolcapture.cpp +++ b/src/gui/qgsmaptoolcapture.cpp @@ -81,16 +81,6 @@ void QgsMapToolCapture::deactivate() QgsMapToolAdvancedDigitizing::deactivate(); } -void QgsMapToolCapture::validationFinished() -{ - emit messageDiscarded(); - QString msgFinished = tr( "Validation finished" ); - if ( !mValidationWarnings.isEmpty() ) - { - emit messageEmitted( mValidationWarnings.join( QStringLiteral( "\n" ) ).append( "\n" ).append( msgFinished ), Qgis::Warning ); - } -} - void QgsMapToolCapture::currentLayerChanged( QgsMapLayer *layer ) { if ( !mCaptureModeFromLayer ) @@ -688,7 +678,6 @@ void QgsMapToolCapture::validateGeometry() mValidator = nullptr; } - mValidationWarnings.clear(); mGeomErrors.clear(); while ( !mGeomErrorMarkers.isEmpty() ) { @@ -726,20 +715,17 @@ void QgsMapToolCapture::validateGeometry() method = QgsGeometry::ValidatorGeos; mValidator = new QgsGeometryValidator( geom, nullptr, method ); connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError ); - connect( mValidator, &QThread::finished, this, &QgsMapToolCapture::validationFinished ); mValidator->start(); QgsDebugMsgLevel( "Validation started", 4 ); } -void QgsMapToolCapture::addError( QgsGeometry::Error e ) +void QgsMapToolCapture::addError( const QgsGeometry::Error &e ) { mGeomErrors << e; QgsVectorLayer *vlayer = qobject_cast( mCanvas->currentLayer() ); if ( !vlayer ) return; - mValidationWarnings << e.what(); - if ( e.hasWhere() ) { QgsVertexMarker *vm = new QgsVertexMarker( mCanvas ); @@ -751,9 +737,6 @@ void QgsMapToolCapture::addError( QgsGeometry::Error e ) vm->setZValue( vm->zValue() + 1 ); mGeomErrorMarkers << vm; } - - emit messageDiscarded(); - emit messageEmitted( mValidationWarnings.join( QStringLiteral( "\n" ) ), Qgis::Warning ); } int QgsMapToolCapture::size() diff --git a/src/gui/qgsmaptoolcapture.h b/src/gui/qgsmaptoolcapture.h index 9cdc11124c3..9df9a460706 100644 --- a/src/gui/qgsmaptoolcapture.h +++ b/src/gui/qgsmaptoolcapture.h @@ -107,11 +107,9 @@ class GUI_EXPORT QgsMapToolCapture : public QgsMapToolAdvancedDigitizing void clean() override; private slots: - void validationFinished(); - void addError( QgsGeometry::Error ); + void addError( const QgsGeometry::Error &error ); void currentLayerChanged( QgsMapLayer *layer ); - protected: /** @@ -264,7 +262,6 @@ class GUI_EXPORT QgsMapToolCapture : public QgsMapToolAdvancedDigitizing QList mSnappingMatches; void validateGeometry(); - QStringList mValidationWarnings; QgsGeometryValidator *mValidator = nullptr; QList< QgsGeometry::Error > mGeomErrors; QList< QgsVertexMarker * > mGeomErrorMarkers; From baf1e52f7fd467c1d87738d736e73de637ff9e58 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 10:37:53 +0200 Subject: [PATCH 049/126] Better error reporting from is valid check --- .../qgsgeometryisvalidcheck.cpp | 18 +++++++++++++++++- .../geometry_checker/qgsgeometryisvalidcheck.h | 11 +++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.cpp index ee5acbebd6d..b4357461a68 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.cpp @@ -49,7 +49,11 @@ QList QgsGeometryIsValidCheck::processGeometry( c QList result; for ( const auto &error : qgis::as_const( errors ) ) { - result << new QgsSingleGeometryCheckError( this, geometry, QgsGeometry( qgis::make_unique( error.where() ) ) ); + QgsGeometry errorGeometry; + if ( error.hasWhere() ) + errorGeometry = QgsGeometry( qgis::make_unique( error.where() ) ); + + result << new QgsGeometryIsValidCheckError( this, geometry, errorGeometry, error.what() ); } return result; } @@ -89,3 +93,15 @@ QgsGeometryCheck::CheckType QgsGeometryIsValidCheck::factoryCheckType() return QgsGeometryCheck::FeatureNodeCheck; } ///@endcond + +QgsGeometryIsValidCheckError::QgsGeometryIsValidCheckError( const QgsSingleGeometryCheck *check, const QgsGeometry &geometry, const QgsGeometry &errorLocation, const QString &errorDescription ) + : QgsSingleGeometryCheckError( check, geometry, errorLocation ) + , mDescription( errorDescription ) +{ + +} + +QString QgsGeometryIsValidCheckError::description() const +{ + return mDescription; +} diff --git a/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.h b/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.h index 2d0fd9dcdf9..bbfc10d87a4 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryisvalidcheck.h @@ -20,6 +20,17 @@ email : matthias@opengis.ch #include "qgssinglegeometrycheck.h" +class ANALYSIS_EXPORT QgsGeometryIsValidCheckError : public QgsSingleGeometryCheckError +{ + public: + QgsGeometryIsValidCheckError( const QgsSingleGeometryCheck *check, const QgsGeometry &geometry, const QgsGeometry &errorLocation, const QString &errorDescription ); + + QString description() const override; + + private: + QString mDescription; +}; + /** * Checks if geometries are valid using the backend configured in the QGIS settings. * This does not offer any fixes but makes sure that all geometries are valid. From 32c71b8a53556e5eee52de9c9f132653e76f9157 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 10:38:14 +0200 Subject: [PATCH 050/126] Better UX in geometry validation panel --- src/app/qgsgeometryvalidationdock.cpp | 19 ++++++++++++-- src/app/qgsgeometryvalidationdock.h | 1 + src/app/qgsgeometryvalidationmodel.cpp | 36 ++++++++++++++++++++++++++ src/app/qgsgeometryvalidationmodel.h | 3 ++- src/app/qgsgeometryvalidationservice.h | 4 +-- 5 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index c68b9b28624..c0ce97f6d74 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -37,6 +37,7 @@ QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsM connect( mMapCanvas, &QgsMapCanvas::currentLayerChanged, this, &QgsGeometryValidationDock::updateLayerTransform ); connect( mMapCanvas, &QgsMapCanvas::destinationCrsChanged, this, &QgsGeometryValidationDock::updateLayerTransform ); connect( mMapCanvas, &QgsMapCanvas::transformContextChanged, this, &QgsGeometryValidationDock::updateLayerTransform ); + connect( mTopologyChecksPendingButton, &QToolButton::clicked, this, &QgsGeometryValidationDock::triggerTopologyChecks ); mFeatureRubberband = new QgsRubberBand( mMapCanvas ); mErrorRubberband = new QgsRubberBand( mMapCanvas ); @@ -54,6 +55,8 @@ QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsM mErrorLocationRubberband->setWidth( scaleFactor ); mErrorLocationRubberband->setIconSize( scaleFactor * 5 ); mErrorLocationRubberband->setColor( QColor( 50, 255, 50, 255 ) ); + + mProblemDetailWidget->setVisible( false ); } @@ -104,6 +107,13 @@ void QgsGeometryValidationDock::zoomToFeature() mMapCanvas->zoomToFeatureExtent( mapExtent ); } +void QgsGeometryValidationDock::triggerTopologyChecks() +{ + QgsVectorLayer *layer = qobject_cast( mMapCanvas->currentLayer() ); + if ( layer ) + mGeometryValidationService->triggerTopologyChecks( layer ); +} + void QgsGeometryValidationDock::updateLayerTransform() { if ( !mMapCanvas->currentLayer() ) @@ -130,13 +140,18 @@ QModelIndex QgsGeometryValidationDock::currentIndex() const void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤t, const QModelIndex &previous ) { Q_UNUSED( previous ) + + mProblemDetailWidget->setVisible( current.isValid() ); + mNextButton->setEnabled( current.isValid() && current.row() < mGeometryValidationModel->rowCount() - 1 ); mPreviousButton->setEnabled( current.isValid() && current.row() > 0 ); mProblemDetailWidget->setVisible( current.isValid() ); - mProblemDescriptionLabel->setText( current.data().toString() ); + mProblemDescriptionLabel->setText( current.data( QgsGeometryValidationModel::DetailsRole ).toString() ); + + QgsGeometryCheckError *error = current.data( QgsGeometryValidationModel::GeometryCheckErrorRole ).value(); + if ( error ) { - QgsGeometryCheckError *error = current.data( QgsGeometryValidationModel::GeometryCheckErrorRole ).value(); while ( QPushButton *btn = mResolutionWidget->findChild() ) delete btn; const QStringList resolutionMethods = error->check()->resolutionMethods(); diff --git a/src/app/qgsgeometryvalidationdock.h b/src/app/qgsgeometryvalidationdock.h index a52b6101488..8da0c0f9aac 100644 --- a/src/app/qgsgeometryvalidationdock.h +++ b/src/app/qgsgeometryvalidationdock.h @@ -47,6 +47,7 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal void gotoPreviousError(); void zoomToProblem(); void zoomToFeature(); + void triggerTopologyChecks(); void updateLayerTransform(); private: diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index bc3b519f771..8ba224bb2ac 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -52,6 +52,8 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) switch ( role ) { case Qt::DisplayRole: + FALLTHROUGH; + case DetailsRole: { const QgsFeatureId fid = topologyError->featureId(); const QgsFeature feature = mCurrentLayer->getFeature( fid ); // TODO: this should be cached! @@ -135,6 +137,40 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) return QVariant(); } + case GeometryCheckErrorRole: + { + // Not (yet?) used + break; + } + + case FeatureExtentRole: + { + return mCurrentLayer->getFeature( featureItem.fid ).geometry().boundingBox(); + } + + case ErrorLocationGeometryRole: + { + QgsSingleGeometryCheckError *error = featureItem.errors.first().get(); + return error->errorLocation(); + } + + case ProblemExtentRole: + { + QgsSingleGeometryCheckError *error = featureItem.errors.first().get(); + return error->errorLocation().boundingBox(); + } + + case DetailsRole: + { + QStringList details; + for ( const std::shared_ptr &error : qgis::as_const( featureItem.errors ) ) + { + details << error->description(); + } + + return details.join( '\n' ); + } + default: return QVariant(); } diff --git a/src/app/qgsgeometryvalidationmodel.h b/src/app/qgsgeometryvalidationmodel.h index 1f345a0efdc..96fb136e18b 100644 --- a/src/app/qgsgeometryvalidationmodel.h +++ b/src/app/qgsgeometryvalidationmodel.h @@ -20,7 +20,8 @@ class QgsGeometryValidationModel : public QAbstractItemModel ErrorGeometryRole, FeatureGeometryRole, ErrorLocationGeometryRole, - GeometryCheckErrorRole + GeometryCheckErrorRole, + DetailsRole }; QgsGeometryValidationModel( QgsGeometryValidationService *geometryValidationService, QObject *parent = nullptr ); diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index 1143a541eb4..49afd048f1b 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -67,6 +67,8 @@ class QgsGeometryValidationService : public QObject void fixError( const QgsGeometryCheckError *error, int method ); + void triggerTopologyChecks( QgsVectorLayer *layer ); + signals: void geometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid ); void geometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList> &errors ); @@ -89,8 +91,6 @@ class QgsGeometryValidationService : public QObject void processFeature( QgsVectorLayer *layer, QgsFeatureId fid ); - void triggerTopologyChecks( QgsVectorLayer *layer ); - QgsProject *mProject = nullptr; struct VectorCheckState From 859ac16f3dcf0b60d4dde10c712249f0cf5f9f33 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 12:33:37 +0200 Subject: [PATCH 051/126] Make maxOverlapArea parameter of overlap check optional --- .../vector/geometry_checker/qgsgeometryoverlapcheck.cpp | 2 +- .../vector/geometry_checker/qgsgeometryoverlapcheck.h | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp index 0b8984ce06b..1c99944cc86 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp @@ -65,7 +65,7 @@ void QgsGeometryOverlapCheck::collectErrors( const QMaparea(); - if ( area > mContext->reducedTolerance && area < mOverlapThresholdMapUnits ) + if ( area > mContext->reducedTolerance && ( area < mOverlapThresholdMapUnits || mOverlapThresholdMapUnits == 0.0 ) ) { errors.append( new QgsGeometryOverlapCheckError( this, layerFeatureA, QgsGeometry( interPart->clone() ), interPart->centroid(), area, layerFeatureB ) ); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h index 5f18557b81a..252e54aef63 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h @@ -75,6 +75,14 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheck : public QgsGeometryCheck enum ResolutionMethod { Subtract, NoChange }; + /** + * Checks for overlapping polygons. + * + * In \a configuration a maxOverlapArea parameter can be passed. In case this parameter is set + * to something else than 0.0, the error will only be reported if the overlapping area is smaller + * than maxOverlapArea. + * Overlapping areas smaller than the reducedTolerance parameter of the \a context are ignored. + */ QgsGeometryOverlapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ); QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; From f82970d79a4b3204d7058184064816ef6e8ef2ff Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 13:10:40 +0200 Subject: [PATCH 052/126] Send signals when geometry options change --- .../auto_generated/qgsgeometryoptions.sip.in | 35 ++++++++++++++++- src/core/CMakeLists.txt | 2 +- src/core/qgsgeometryoptions.cpp | 4 ++ src/core/qgsgeometryoptions.h | 38 ++++++++++++++++++- 4 files changed, 76 insertions(+), 3 deletions(-) diff --git a/python/core/auto_generated/qgsgeometryoptions.sip.in b/python/core/auto_generated/qgsgeometryoptions.sip.in index 5ad7bd76120..ef46b02c303 100644 --- a/python/core/auto_generated/qgsgeometryoptions.sip.in +++ b/python/core/auto_generated/qgsgeometryoptions.sip.in @@ -9,7 +9,8 @@ -class QgsGeometryOptions + +class QgsGeometryOptions : QObject { %Docstring @@ -114,6 +115,38 @@ Write the geometry options to the ``node``. %Docstring Read the geometry options from ``node``. +.. versionadded:: 3.4 +%End + + signals: + + void checkConfigurationChanged(); +%Docstring +Access the configuration for the check ``checkId``. + +.. versionadded:: 3.4 +%End + + void geometryChecksChanged(); +%Docstring +A list of activated geometry checks. + +.. versionadded:: 3.4 +%End + + void removeDuplicateNodesChanged(); +%Docstring +Automatically remove duplicate nodes on all geometries which are edited on this layer. + +.. versionadded:: 3.4 +%End + + void geometryPrecisionChanged(); +%Docstring +The precision in which geometries on this layer should be saved. +Geometries which are edited on this layer will be rounded to multiples of this value (snap to grid). +Set to 0.0 to disable. + .. versionadded:: 3.4 %End diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 711cf49d273..287f412806a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -606,6 +606,7 @@ SET(QGIS_CORE_MOC_HDRS qgsfiledownloader.h qgsfeaturefiltermodel.h qgsfeaturefiltermodel_p.h + qgsgeometryoptions.h qgsgeometryvalidator.h qgsgml.h qgsgmlschema.h @@ -869,7 +870,6 @@ SET(QGIS_CORE_HDRS qgsfields.h qgsfileutils.h qgsfontutils.h - qgsgeometryoptions.h qgsgeometrysimplifier.h qgshistogram.h qgshstoreutils.h diff --git a/src/core/qgsgeometryoptions.cpp b/src/core/qgsgeometryoptions.cpp index e838e0c1087..861b1314a17 100644 --- a/src/core/qgsgeometryoptions.cpp +++ b/src/core/qgsgeometryoptions.cpp @@ -27,6 +27,7 @@ bool QgsGeometryOptions::removeDuplicateNodes() const void QgsGeometryOptions::setRemoveDuplicateNodes( bool value ) { mRemoveDuplicateNodes = value; + emit removeDuplicateNodesChanged(); } double QgsGeometryOptions::geometryPrecision() const @@ -37,6 +38,7 @@ double QgsGeometryOptions::geometryPrecision() const void QgsGeometryOptions::setGeometryPrecision( double value ) { mGeometryPrecision = value; + emit geometryPrecisionChanged(); } bool QgsGeometryOptions::isActive() const @@ -61,6 +63,7 @@ QStringList QgsGeometryOptions::geometryChecks() const void QgsGeometryOptions::setGeometryChecks( const QStringList &geometryChecks ) { mGeometryChecks = geometryChecks; + emit geometryChecksChanged(); } QVariantMap QgsGeometryOptions::checkConfiguration( const QString &checkId ) const @@ -71,6 +74,7 @@ QVariantMap QgsGeometryOptions::checkConfiguration( const QString &checkId ) con void QgsGeometryOptions::setCheckConfiguration( const QString &checkId, const QVariantMap &checkConfiguration ) { mCheckConfiguration[checkId] = checkConfiguration; + emit checkConfigurationChanged(); } void QgsGeometryOptions::writeXml( QDomNode &node ) const diff --git a/src/core/qgsgeometryoptions.h b/src/core/qgsgeometryoptions.h index 406e8d0cf5b..7d6eabaf80d 100644 --- a/src/core/qgsgeometryoptions.h +++ b/src/core/qgsgeometryoptions.h @@ -22,6 +22,8 @@ #include "qgis_sip.h" #include "qgsgeometry.h" +#include + /** * \ingroup core * @@ -30,8 +32,10 @@ * * \since QGIS 3.4 */ -class CORE_EXPORT QgsGeometryOptions +class CORE_EXPORT QgsGeometryOptions : public QObject { + Q_OBJECT + public: /** @@ -127,6 +131,38 @@ class CORE_EXPORT QgsGeometryOptions */ void readXml( const QDomNode &node ); + signals: + + /** + * Access the configuration for the check \a checkId. + * + * \since QGIS 3.4 + */ + void checkConfigurationChanged(); + + /** + * A list of activated geometry checks. + * + * \since QGIS 3.4 + */ + void geometryChecksChanged(); + + /** + * Automatically remove duplicate nodes on all geometries which are edited on this layer. + * + * \since QGIS 3.4 + */ + void removeDuplicateNodesChanged(); + + /** + * The precision in which geometries on this layer should be saved. + * Geometries which are edited on this layer will be rounded to multiples of this value (snap to grid). + * Set to 0.0 to disable. + * + * \since QGIS 3.4 + */ + void geometryPrecisionChanged(); + private: /** From e300387b521ca7486b532bdabe42aeecf64daa75 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 13:22:59 +0200 Subject: [PATCH 053/126] Use qgsgeometry_cast rather than dynamic_cast --- .../vector/geometry_checker/qgsgeometrycheckerutils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp index 2bd4589ae05..6d18485de09 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.cpp @@ -302,12 +302,12 @@ QList QgsGeometryCheckerUtils::polygonRings( const QgsPol void QgsGeometryCheckerUtils::filter1DTypes( QgsAbstractGeometry *geom ) { - if ( dynamic_cast( geom ) ) + if ( qgsgeometry_cast( geom ) ) { QgsGeometryCollection *geomCollection = static_cast( geom ); for ( int nParts = geom->partCount(), iPart = nParts - 1; iPart >= 0; --iPart ) { - if ( !dynamic_cast( geomCollection->geometryN( iPart ) ) ) + if ( !qgsgeometry_cast( geomCollection->geometryN( iPart ) ) ) { geomCollection->removeGeometry( iPart ); } From 42c3f4549c4bd119ccf048efb4d5f4fec2ef8110 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 13:23:32 +0200 Subject: [PATCH 054/126] Abort geometry checks if feedback is cancelled --- .../geometry_checker/qgsgeometrygapcheck.cpp | 7 +++++++ .../qgsgeometrymissingvertexcheck.cpp | 14 +++++++++++--- .../qgsgeometrymissingvertexcheck.h | 2 +- .../geometry_checker/qgsgeometryoverlapcheck.cpp | 15 ++++++++++----- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp index a5bdca73632..d80965c4f39 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp @@ -44,6 +44,13 @@ void QgsGeometryGapCheck::collectErrors( const QMap & for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { geomList.append( layerFeature.geometry().constGet()->clone() ); + + if ( feedback->isCanceled() ) + { + qDeleteAll( geomList ); + geomList.clear(); + break; + } } if ( geomList.isEmpty() ) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp index 7c747e5e84a..bd180725573 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp @@ -43,11 +43,16 @@ void QgsGeometryMissingVertexCheck::collectErrors( const QMapisCanceled() ) + { + break; + } + const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); if ( QgsCurvePolygon *polygon = qgsgeometry_cast( geom ) ) { - processPolygon( polygon, featurePool, errors, layerFeature ); + processPolygon( polygon, featurePool, errors, layerFeature, feedback ); } else if ( QgsGeometryCollection *collection = qgsgeometry_cast( geom ) ) { @@ -56,7 +61,7 @@ void QgsGeometryMissingVertexCheck::collectErrors( const QMap( collection->geometryN( i ) ) ) { - processPolygon( polygon, featurePool, errors, layerFeature ); + processPolygon( polygon, featurePool, errors, layerFeature, feedback ); } } } @@ -105,7 +110,7 @@ QString QgsGeometryMissingVertexCheck::description() const return factoryDescription(); } -void QgsGeometryMissingVertexCheck::processPolygon( const QgsCurvePolygon *polygon, QgsFeaturePool *featurePool, QList &errors, const QgsGeometryCheckerUtils::LayerFeature &layerFeature ) const +void QgsGeometryMissingVertexCheck::processPolygon( const QgsCurvePolygon *polygon, QgsFeaturePool *featurePool, QList &errors, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, QgsFeedback *feedback ) const { const QgsFeature ¤tFeature = layerFeature.feature(); std::unique_ptr boundaries = qgis::make_unique(); @@ -133,6 +138,9 @@ void QgsGeometryMissingVertexCheck::processPolygon( const QgsCurvePolygon *polyg if ( featurePool->getFeature( fid, compareFeature ) ) { + if ( feedback->isCanceled() ) + break; + QgsVertexIterator vertexIterator = compareFeature.geometry().vertices(); while ( vertexIterator.hasNext() ) { diff --git a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h index f0ad1999e70..9b020ce3226 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h @@ -62,7 +62,7 @@ class ANALYSIS_EXPORT QgsGeometryMissingVertexCheck : public QgsGeometryCheck ///@endcond private: - void processPolygon( const QgsCurvePolygon *polygon, QgsFeaturePool *featurePool, QList &errors, const QgsGeometryCheckerUtils::LayerFeature &layerFeature ) const; + void processPolygon( const QgsCurvePolygon *polygon, QgsFeaturePool *featurePool, QList &errors, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, QgsFeedback *feedback ) const; }; diff --git a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp index 1c99944cc86..999b12ffedf 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp @@ -18,7 +18,7 @@ #include "qgsgeometryoverlapcheck.h" #include "qgsfeaturepool.h" #include "qgsvectorlayer.h" - +#include "qgsfeedback.h" QgsGeometryOverlapCheck::QgsGeometryOverlapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ) : QgsGeometryCheck( context, configuration ) @@ -35,6 +35,9 @@ void QgsGeometryOverlapCheck::collectErrors( const QMap layerIds = featureIds.keys(); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureA : layerFeaturesA ) { + if ( feedback->isCanceled() ) + break; + // Ensure each pair of layers only gets compared once: remove the current layer from the layerIds, but add it to the layerList for layerFeaturesB layerIds.removeOne( layerFeatureA.layer()->id() ); @@ -49,6 +52,9 @@ void QgsGeometryOverlapCheck::collectErrors( const QMap() << layerFeatureA.layer()->id() << layerIds, bboxA, compatibleGeometryTypes(), mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureB : layerFeaturesB ) { + if ( feedback->isCanceled() ) + break; + // > : only report overlaps within same layer once if ( layerFeatureA.layer()->id() == layerFeatureB.layer()->id() && layerFeatureB.feature().id() >= layerFeatureA.feature().id() ) { @@ -57,13 +63,13 @@ void QgsGeometryOverlapCheck::collectErrors( const QMapoverlaps( layerFeatureB.geometry().constGet(), &errMsg ) ) { - QgsAbstractGeometry *interGeom = geomEngineA->intersection( layerFeatureB.geometry().constGet() ); + std::unique_ptr interGeom( geomEngineA->intersection( layerFeatureB.geometry().constGet() ) ); if ( interGeom && !interGeom->isEmpty() ) { - QgsGeometryCheckerUtils::filter1DTypes( interGeom ); + QgsGeometryCheckerUtils::filter1DTypes( interGeom.get() ); for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart ) { - QgsAbstractGeometry *interPart = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart ); + QgsAbstractGeometry *interPart = QgsGeometryCheckerUtils::getGeomPart( interGeom.get(), iPart ); double area = interPart->area(); if ( area > mContext->reducedTolerance && ( area < mOverlapThresholdMapUnits || mOverlapThresholdMapUnits == 0.0 ) ) { @@ -75,7 +81,6 @@ void QgsGeometryOverlapCheck::collectErrors( const QMap Date: Mon, 1 Oct 2018 14:59:30 +0200 Subject: [PATCH 055/126] Add test for overlap check with no area size restriction --- .../testqgsgeometrychecks.cpp | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/src/geometry_checker/testqgsgeometrychecks.cpp b/tests/src/geometry_checker/testqgsgeometrychecks.cpp index 59d5e7177dd..f5683ddbbf9 100644 --- a/tests/src/geometry_checker/testqgsgeometrychecks.cpp +++ b/tests/src/geometry_checker/testqgsgeometrychecks.cpp @@ -765,6 +765,38 @@ void TestQgsGeometryChecks::testOverlapCheck() cleanupTestContext( testContext ); } +void TestQgsGeometryChecks::testOverlapCheckNoMaxArea() +{ + QTemporaryDir dir; + QMap layers; + layers.insert( QStringLiteral("point_layer.shp"), QString() ); + layers.insert( QStringLiteral("line_layer.shp"), QString() ); + layers.insert( QStringLiteral("polygon_layer.shp"), QString() ); + + auto testContext = createTestContext( dir, layers ); + + // Test detection + QList checkErrors; + QStringList messages; + + QVariantMap configuration; + configuration.insert( "maxOverlapArea", 0.0 ); + + QgsGeometryOverlapCheck check( testContext.first, configuration ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); + listErrors( checkErrors, messages ); + + QList errs1; + QCOMPARE( checkErrors.size(), 4 ); + QVERIFY( searchCheckErrors( checkErrors, layers["point_layer.shp"] ).isEmpty() ); + QVERIFY( searchCheckErrors( checkErrors, layers["line_layer.shp"] ).isEmpty() ); + errs1 = searchCheckErrors( checkErrors, layers["polygon_layer.shp"], 10 ); + QCOMPARE( errs1.size(), 2 ); + errs1 = searchCheckErrors( checkErrors, layers["polygon_layer.shp"], 9 ); + QCOMPARE( errs1.size(), 2 ); +} + void TestQgsGeometryChecks::testPointCoveredByLineCheck() { QTemporaryDir dir; From 6a9403348741321453b656392ae9ba09cf3c2ee0 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 15:00:16 +0200 Subject: [PATCH 056/126] Make feedback a required parameter for geometry checks --- .../geometry_checker/qgsgeometrycheck.sip.in | 2 +- .../geometry_checker/qgsgeometryanglecheck.h | 2 +- .../geometry_checker/qgsgeometryareacheck.h | 2 +- .../geometry_checker/qgsgeometrycheck.h | 2 +- .../qgsgeometrycontainedcheck.h | 2 +- .../geometry_checker/qgsgeometrydanglecheck.h | 2 +- .../qgsgeometrydegeneratepolygoncheck.h | 2 +- .../qgsgeometryduplicatecheck.h | 2 +- .../qgsgeometryduplicatenodescheck.h | 2 +- .../qgsgeometryfollowboundariescheck.h | 2 +- .../geometry_checker/qgsgeometrygapcheck.h | 2 +- .../geometry_checker/qgsgeometryholecheck.h | 2 +- .../qgsgeometrylineintersectioncheck.h | 2 +- .../qgsgeometrylinelayerintersectioncheck.h | 2 +- .../qgsgeometrymissingvertexcheck.h | 2 +- .../qgsgeometryoverlapcheck.h | 2 +- .../qgsgeometrypointcoveredbylinecheck.h | 2 +- .../qgsgeometrypointinpolygoncheck.h | 2 +- .../qgsgeometrysegmentlengthcheck.h | 2 +- .../testqgsgeometrychecks.cpp | 72 ++++++++++++------- 20 files changed, 67 insertions(+), 43 deletions(-) diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in index 365ea55e4b6..a307de05503 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in @@ -120,7 +120,7 @@ A list of geometry types for which this check can be performed. Flags for this geometry check. %End - virtual void collectErrors( const QMap &featurePools, QList &errors /In,Out/, QStringList &messages /In,Out/, QgsFeedback *feedback = 0, const LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const = 0; + virtual void collectErrors( const QMap &featurePools, QList &errors /In,Out/, QStringList &messages /In,Out/, QgsFeedback *feedback, const LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const = 0; %Docstring The main worker method. Check all features available from ``featurePools`` and write errors found to ``errors``. diff --git a/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.h b/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.h index d9eb54b765b..d334881ef07 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryanglecheck.h @@ -34,7 +34,7 @@ class ANALYSIS_EXPORT QgsGeometryAngleCheck : public QgsGeometryCheck , mMinAngle( configuration.value( QStringLiteral( "minAngle" ), 0.0 ).toDouble() ) {} - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QList compatibleGeometryTypes() const override; diff --git a/src/analysis/vector/geometry_checker/qgsgeometryareacheck.h b/src/analysis/vector/geometry_checker/qgsgeometryareacheck.h index 18da59f1d6f..2224cde8e15 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryareacheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryareacheck.h @@ -32,7 +32,7 @@ class ANALYSIS_EXPORT QgsGeometryAreaCheck : public QgsGeometryCheck , mAreaThreshold( configurationValue( "areaThreshold" ) ) {} QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; QString id() const override { return factoryId(); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheck.h b/src/analysis/vector/geometry_checker/qgsgeometrycheck.h index 20d1ace05be..ffa2c01e051 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheck.h @@ -200,7 +200,7 @@ class ANALYSIS_EXPORT QgsGeometryCheck * * \since QGIS 3.4 */ - virtual void collectErrors( const QMap &featurePools, QList &errors SIP_INOUT, QStringList &messages SIP_INOUT, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const = 0; + virtual void collectErrors( const QMap &featurePools, QList &errors SIP_INOUT, QStringList &messages SIP_INOUT, QgsFeedback *feedback, const LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const = 0; /** * Fix the error \a error with the specified \a method. diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h index e0f162b5797..25965004ca4 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycontainedcheck.h @@ -57,7 +57,7 @@ class ANALYSIS_EXPORT QgsGeometryContainedCheck : public QgsGeometryCheck explicit QgsGeometryContainedCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) : QgsGeometryCheck( context, configuration ) {} QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; QString id() const override { return factoryId(); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.h b/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.h index 31f0d12dd25..bd537f08318 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrydanglecheck.h @@ -29,7 +29,7 @@ class ANALYSIS_EXPORT QgsGeometryDangleCheck : public QgsGeometryCheck void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; QStringList resolutionMethods() const override; QString description() const override { return factoryDescription(); } QString id() const override { return factoryId(); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.h b/src/analysis/vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.h index 741201a2a01..e5f1cfe8d03 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrydegeneratepolygoncheck.h @@ -28,7 +28,7 @@ class ANALYSIS_EXPORT QgsGeometryDegeneratePolygonCheck : public QgsGeometryChec explicit QgsGeometryDegeneratePolygonCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) : QgsGeometryCheck( context, configuration ) {} - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h b/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h index 031340af50e..a8741ddf4b0 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h @@ -55,7 +55,7 @@ class ANALYSIS_EXPORT QgsGeometryDuplicateCheck : public QgsGeometryCheck public: explicit QgsGeometryDuplicateCheck( QgsGeometryCheckContext *context, const QVariantMap &configuration ) : QgsGeometryCheck( context, configuration ) {} - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.h b/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.h index e969d8ba38b..f88a4dee4b2 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryduplicatenodescheck.h @@ -28,7 +28,7 @@ class ANALYSIS_EXPORT QgsGeometryDuplicateNodesCheck : public QgsGeometryCheck static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; } static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; static QString factoryDescription() { return tr( "Duplicate node" ); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.h b/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.h index ca815181949..d6fa8278b39 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryfollowboundariescheck.h @@ -31,7 +31,7 @@ class ANALYSIS_EXPORT QgsGeometryFollowBoundariesCheck : public QgsGeometryCheck static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PolygonGeometry}; } static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; static QString factoryDescription() { return tr( "Polygon does not follow boundaries" ); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h index 32dc0b5e707..5aa9255bbb4 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h @@ -87,7 +87,7 @@ class ANALYSIS_EXPORT QgsGeometryGapCheck : public QgsGeometryCheck explicit QgsGeometryGapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ); QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; diff --git a/src/analysis/vector/geometry_checker/qgsgeometryholecheck.h b/src/analysis/vector/geometry_checker/qgsgeometryholecheck.h index 6d4ffe0a564..b216c16a335 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryholecheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryholecheck.h @@ -28,7 +28,7 @@ class ANALYSIS_EXPORT QgsGeometryHoleCheck : public QgsGeometryCheck static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PolygonGeometry}; } static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; static QString factoryDescription() { return tr( "Polygon with hole" ); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.h b/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.h index c0b51473a27..4cf7db54541 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrylineintersectioncheck.h @@ -29,7 +29,7 @@ class ANALYSIS_EXPORT QgsGeometryLineIntersectionCheck : public QgsGeometryCheck static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry}; } static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; static QString factoryDescription() { return tr( "Intersection" ); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.h b/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.h index 23ac44a917d..6bb5614a50d 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.h @@ -30,7 +30,7 @@ class ANALYSIS_EXPORT QgsGeometryLineLayerIntersectionCheck : public QgsGeometry static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry}; } static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; static QString factoryDescription() { return tr( "Intersection" ); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h index 9b020ce3226..2dd53aa29e3 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h @@ -42,7 +42,7 @@ class ANALYSIS_EXPORT QgsGeometryMissingVertexCheck : public QgsGeometryCheck }; explicit QgsGeometryMissingVertexCheck( const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration ); - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; diff --git a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h index 252e54aef63..1e5b79b1eda 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h @@ -85,7 +85,7 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheck : public QgsGeometryCheck */ QgsGeometryOverlapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ); QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.h b/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.h index bd38356e1f2..8726f80c42e 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrypointcoveredbylinecheck.h @@ -29,7 +29,7 @@ class ANALYSIS_EXPORT QgsGeometryPointCoveredByLineCheck : public QgsGeometryChe static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PointGeometry}; } static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; static QString factoryDescription() { return tr( "Point not covered by line" ); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrypointinpolygoncheck.h b/src/analysis/vector/geometry_checker/qgsgeometrypointinpolygoncheck.h index 9f073de8b6a..cf6cef83bcb 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrypointinpolygoncheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrypointinpolygoncheck.h @@ -29,7 +29,7 @@ class ANALYSIS_EXPORT QgsGeometryPointInPolygonCheck : public QgsGeometryCheck static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PointGeometry}; } static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; static QString factoryDescription() { return tr( "Point not in polygon" ); } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrysegmentlengthcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrysegmentlengthcheck.h index 33c188f7d82..8a62bb02ed7 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrysegmentlengthcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrysegmentlengthcheck.h @@ -30,7 +30,7 @@ class ANALYSIS_EXPORT QgsGeometrySegmentLengthCheck : public QgsGeometryCheck static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; } static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } QList compatibleGeometryTypes() const override { return factoryCompatibleGeometryTypes(); } - void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback = nullptr, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; + void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; void fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList resolutionMethods() const override; static QString factoryDescription() { return tr( "Minimal segment length" ); } diff --git a/tests/src/geometry_checker/testqgsgeometrychecks.cpp b/tests/src/geometry_checker/testqgsgeometrychecks.cpp index f5683ddbbf9..6cb20a6cf20 100644 --- a/tests/src/geometry_checker/testqgsgeometrychecks.cpp +++ b/tests/src/geometry_checker/testqgsgeometrychecks.cpp @@ -41,6 +41,7 @@ #include "qgsgeometrysliverpolygoncheck.h" #include "qgsvectordataproviderfeaturepool.h" #include "qgsproject.h" +#include "qgsfeedback.h" #include "qgsgeometrytypecheck.h" @@ -88,6 +89,7 @@ class TestQgsGeometryChecks: public QObject void testLineLayerIntersectionCheck(); void testMultipartCheck(); void testOverlapCheck(); + void testOverlapCheckNoMaxArea(); void testPointCoveredByLineCheck(); void testPointInPolygonCheck(); void testSegmentLengthCheck(); @@ -125,7 +127,8 @@ void TestQgsGeometryChecks::testAngleCheck() configuration.insert( "minAngle", 15 ); QgsGeometryAngleCheck check( testContext.first, configuration ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QList errs1; @@ -203,7 +206,8 @@ void TestQgsGeometryChecks::testAreaCheck() configuration.insert( "areaThreshold", 0.04 ); QgsGeometryAreaCheck check( testContext.first, configuration ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QList errs1; @@ -295,7 +299,8 @@ void TestQgsGeometryChecks::testContainedCheck() QStringList messages; QgsGeometryContainedCheck check( testContext.first, QVariantMap() ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QList errs1; @@ -332,7 +337,8 @@ void TestQgsGeometryChecks::testDangleCheck() QStringList messages; QgsGeometryDangleCheck check( testContext.first, QVariantMap() ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QList errs1; @@ -369,7 +375,8 @@ void TestQgsGeometryChecks::testDegeneratePolygonCheck() QStringList messages; QgsGeometryDegeneratePolygonCheck check( testContext.first, QVariantMap() ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QList errs1; @@ -404,7 +411,8 @@ void TestQgsGeometryChecks::testDuplicateCheck() QStringList messages; QgsGeometryDuplicateCheck check( testContext.first, QVariantMap() ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QList errs1; @@ -448,7 +456,8 @@ void TestQgsGeometryChecks::testDuplicateNodesCheck() QStringList messages; QgsGeometryDuplicateNodesCheck check( testContext.first, QVariantMap() ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QList errs1; @@ -487,7 +496,8 @@ void TestQgsGeometryChecks::testFollowBoundariesCheck() QList checkErrors; QStringList messages; - QgsGeometryFollowBoundariesCheck( testContext.first, QVariantMap(), testContext.second[layers["follow_ref.shp"]]->layer() ).collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + QgsGeometryFollowBoundariesCheck( testContext.first, QVariantMap(), testContext.second[layers["follow_ref.shp"]]->layer() ).collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QCOMPARE( checkErrors.size(), 2 ); @@ -512,7 +522,8 @@ void TestQgsGeometryChecks::testGapCheck() configuration.insert( "gapThreshold", 0.01 ); QgsGeometryGapCheck check( testContext.first, configuration ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QList errs1; @@ -553,7 +564,8 @@ void TestQgsGeometryChecks::testMissingVertexCheck() QStringList messages; QgsGeometryMissingVertexCheck check( testContext.first, QVariantMap() ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); const QString layerId = testContext.second.first()->layerId(); @@ -582,7 +594,8 @@ void TestQgsGeometryChecks::testHoleCheck() QStringList messages; QgsGeometryHoleCheck check( testContext.first, QVariantMap() ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QList errs1; @@ -620,7 +633,8 @@ void TestQgsGeometryChecks::testLineIntersectionCheck() QStringList messages; QgsGeometryLineIntersectionCheck check( testContext.first, QVariantMap() ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QCOMPARE( checkErrors.size(), 1 ); @@ -648,7 +662,8 @@ void TestQgsGeometryChecks::testLineLayerIntersectionCheck() configuration.insert( "checkLayer", layers["polygon_layer.shp"] ); QgsGeometryLineLayerIntersectionCheck check( testContext.first, configuration ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QCOMPARE( checkErrors.size(), 5 ); @@ -677,7 +692,8 @@ void TestQgsGeometryChecks::testMultipartCheck() QStringList messages; QgsGeometryMultipartCheck check( testContext.first, QVariantMap() ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QVERIFY( searchCheckErrors( checkErrors, layers["point_layer.shp"] ).isEmpty() ); @@ -728,6 +744,7 @@ void TestQgsGeometryChecks::testOverlapCheck() layers.insert( "point_layer.shp", "" ); layers.insert( "line_layer.shp", "" ); layers.insert( "polygon_layer.shp", "" ); + auto testContext = createTestContext( dir, layers ); // Test detection @@ -738,7 +755,8 @@ void TestQgsGeometryChecks::testOverlapCheck() configuration.insert( "maxOverlapArea", 0.01 ); QgsGeometryOverlapCheck check( testContext.first, configuration ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QList errs1; @@ -769,9 +787,9 @@ void TestQgsGeometryChecks::testOverlapCheckNoMaxArea() { QTemporaryDir dir; QMap layers; - layers.insert( QStringLiteral("point_layer.shp"), QString() ); - layers.insert( QStringLiteral("line_layer.shp"), QString() ); - layers.insert( QStringLiteral("polygon_layer.shp"), QString() ); + layers.insert( QStringLiteral( "point_layer.shp" ), QString() ); + layers.insert( QStringLiteral( "line_layer.shp" ), QString() ); + layers.insert( QStringLiteral( "polygon_layer.shp" ), QString() ); auto testContext = createTestContext( dir, layers ); @@ -811,7 +829,8 @@ void TestQgsGeometryChecks::testPointCoveredByLineCheck() QStringList messages; QgsGeometryPointCoveredByLineCheck errs( testContext.first, QVariantMap() ); - errs.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + errs.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QVERIFY( searchCheckErrors( checkErrors, layers["line_layer.shp"] ).isEmpty() ); @@ -837,7 +856,8 @@ void TestQgsGeometryChecks::testPointInPolygonCheck() QStringList messages; QgsGeometryPointInPolygonCheck check( testContext.first, QVariantMap() ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QVERIFY( searchCheckErrors( checkErrors, layers["line_layer.shp"] ).isEmpty() ); @@ -866,7 +886,8 @@ void TestQgsGeometryChecks::testSegmentLengthCheck() configuration.insert( "minSegmentLength", 0.03 ); QgsGeometrySegmentLengthCheck check( testContext.first, configuration ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QCOMPARE( checkErrors.size(), 4 ); @@ -893,7 +914,8 @@ void TestQgsGeometryChecks::testSelfContactCheck() QStringList messages; QgsGeometrySelfContactCheck check( testContext.first, QVariantMap() ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QCOMPARE( checkErrors.size(), 3 ); @@ -919,7 +941,8 @@ void TestQgsGeometryChecks::testSelfIntersectionCheck() QStringList messages; QgsGeometrySelfIntersectionCheck check( testContext.first, QVariantMap() ); - check.collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + check.collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QList errs1; @@ -1026,7 +1049,8 @@ void TestQgsGeometryChecks::testSliverPolygonCheck() configuration.insert( "threshold", 20 ); configuration.insert( "maxArea", 0.04 ); - QgsGeometrySliverPolygonCheck( testContext.first, configuration ).collectErrors( testContext.second, checkErrors, messages ); + QgsFeedback feedback; + QgsGeometrySliverPolygonCheck( testContext.first, configuration ).collectErrors( testContext.second, checkErrors, messages, &feedback ); listErrors( checkErrors, messages ); QCOMPARE( checkErrors.size(), 2 ); From fe5a80c843b9fc4f230c85636a7afa2c61fcbeb5 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 15:18:21 +0200 Subject: [PATCH 057/126] Precision fix --- src/app/qgsgeometryvalidationservice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 7620678ad29..4c77508ac22 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -129,7 +129,7 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) qDeleteAll( mLayerCheckStates[layer].topologyChecks ); // TODO: ownership and lifetime of the context!! - auto context = new QgsGeometryCheckContext( 8, mProject->crs(), mProject->transformContext() ); + auto context = new QgsGeometryCheckContext( log10( layer->geometryOptions()->geometryPrecision() ) * -1, mProject->crs(), mProject->transformContext() ); QList layerChecks; QgsGeometryCheckRegistry *checkRegistry = QgsAnalysis::instance()->geometryCheckRegistry(); From 273d3c871860eb82b2b6f198ce5343d211618eb7 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 15:19:22 +0200 Subject: [PATCH 058/126] Put topology check cancelation into its own routine --- src/app/qgsgeometryvalidationservice.cpp | 42 +++++++++++++----------- src/app/qgsgeometryvalidationservice.h | 2 ++ 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 4c77508ac22..f58b407c8a0 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -174,6 +174,27 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) mLayerCheckStates[layer].topologyChecks = topologyChecks; } +void QgsGeometryValidationService::cancelTopologyCheck( QgsVectorLayer *layer ) +{ + QFutureWatcher *futureWatcher = mLayerCheckStates[layer].topologyCheckFutureWatcher; + if ( futureWatcher ) + { + // Make sure no more checks are started first + futureWatcher->cancel(); + + // Tell all checks to stop asap + const auto feedbacks = mLayerCheckStates[layer].topologyCheckFeedbacks; + for ( QgsFeedback *feedback : feedbacks ) + { + if ( feedback ) + feedback->cancel(); + } + + futureWatcher->waitForFinished(); + mLayerCheckStates[layer].topologyCheckFutureWatcher = nullptr; + } +} + void QgsGeometryValidationService::cancelChecks( QgsVectorLayer *layer, QgsFeatureId fid ) { @@ -204,24 +225,7 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer ) { emit topologyChecksCleared( layer ); - - QFutureWatcher *futureWatcher = mLayerCheckStates[layer].topologyCheckFutureWatcher; - if ( futureWatcher ) - { - // Make sure no more checks are started first - futureWatcher->cancel(); - - // Tell all checks to stop asap - const auto feedbacks = mLayerCheckStates[layer].topologyCheckFeedbacks; - for ( QgsFeedback *feedback : feedbacks ) - { - if ( feedback ) - feedback->cancel(); - } - - // The future watcher will take care of deleting - mLayerCheckStates[layer].topologyCheckFeedbacks.clear(); - } + cancelTopologyCheck( layer ); QgsFeatureIds affectedFeatureIds = layer->editBuffer()->changedGeometries().keys().toSet(); affectedFeatureIds.unite( layer->editBuffer()->addedFeatures().keys().toSet() ); @@ -282,7 +286,7 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer errorLocker.unlock(); } ); - futureWatcher = new QFutureWatcher(); + QFutureWatcher *futureWatcher = new QFutureWatcher(); futureWatcher->setFuture( future ); connect( futureWatcher, &QFutureWatcherBase::finished, this, [&allErrors, layer, feedbacks, futureWatcher, this]() diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index 49afd048f1b..493e23ffebb 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -87,6 +87,8 @@ class QgsGeometryValidationService : public QObject private: void enableLayerChecks( QgsVectorLayer *layer ); + void cancelTopologyCheck( QgsVectorLayer *layer ); + void cancelChecks( QgsVectorLayer *layer, QgsFeatureId fid ); void processFeature( QgsVectorLayer *layer, QgsFeatureId fid ); From 46efc9a67d80d0d079d2391ed408b451144b0bf6 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 15:43:55 +0200 Subject: [PATCH 059/126] Re-initialize layer checks on configuration changes --- src/app/qgsgeometryvalidationservice.cpp | 106 +++++++++++++++-------- src/app/qgsgeometryvalidationservice.h | 7 +- 2 files changed, 73 insertions(+), 40 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index f58b407c8a0..c1e9e3a196a 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -54,22 +54,10 @@ void QgsGeometryValidationService::onLayersAdded( const QList &la QgsVectorLayer *vectorLayer = qobject_cast( layer ); if ( vectorLayer ) { - connect( vectorLayer, &QgsVectorLayer::featureAdded, this, [this, vectorLayer]( QgsFeatureId fid ) + connect( vectorLayer->geometryOptions(), &QgsGeometryOptions::checkConfigurationChanged, this, [this, vectorLayer]() { - onFeatureAdded( vectorLayer, fid ); - } ); - connect( vectorLayer, &QgsVectorLayer::geometryChanged, this, [this, vectorLayer]( QgsFeatureId fid, const QgsGeometry & geometry ) - { - onGeometryChanged( vectorLayer, fid, geometry ); - } ); - connect( vectorLayer, &QgsVectorLayer::featureDeleted, this, [this, vectorLayer]( QgsFeatureId fid ) - { - onFeatureDeleted( vectorLayer, fid ); - } ); - connect( vectorLayer, &QgsVectorLayer::beforeCommitChanges, this, [this, vectorLayer]() - { - onBeforeCommitChanges( vectorLayer ); - } ); + enableLayerChecks( vectorLayer ); + }, Qt::UniqueConnection ); enableLayerChecks( vectorLayer ); } @@ -78,7 +66,7 @@ void QgsGeometryValidationService::onLayersAdded( const QList &la void QgsGeometryValidationService::onFeatureAdded( QgsVectorLayer *layer, QgsFeatureId fid ) { - if ( !mLayerCheckStates[layer].topologyChecks.empty() ) + if ( !mLayerChecks[layer].topologyChecks.empty() ) { // TODO: Cancel topology checks layer->setAllowCommit( false ); @@ -88,7 +76,7 @@ void QgsGeometryValidationService::onFeatureAdded( QgsVectorLayer *layer, QgsFea void QgsGeometryValidationService::onGeometryChanged( QgsVectorLayer *layer, QgsFeatureId fid, const QgsGeometry &geometry ) { - if ( !mLayerCheckStates[layer].topologyChecks.empty() ) + if ( !mLayerChecks[layer].topologyChecks.empty() ) { // TODO: Cancel topology checks layer->setAllowCommit( false ); @@ -101,7 +89,7 @@ void QgsGeometryValidationService::onGeometryChanged( QgsVectorLayer *layer, Qgs void QgsGeometryValidationService::onFeatureDeleted( QgsVectorLayer *layer, QgsFeatureId fid ) { - if ( !mLayerCheckStates[layer].topologyChecks.empty() ) + if ( !mLayerChecks[layer].topologyChecks.empty() ) { // TODO: Cancel topology checks layer->setAllowCommit( false ); @@ -112,7 +100,7 @@ void QgsGeometryValidationService::onFeatureDeleted( QgsVectorLayer *layer, QgsF void QgsGeometryValidationService::onBeforeCommitChanges( QgsVectorLayer *layer ) { - if ( !mLayerCheckStates[layer].topologyChecks.empty() ) // TODO && topologyChecks not fulfilled + if ( !mLayerChecks[layer].topologyChecks.empty() ) // TODO && topologyChecks not fulfilled { if ( !layer->allowCommit() ) { @@ -124,12 +112,29 @@ void QgsGeometryValidationService::onBeforeCommitChanges( QgsVectorLayer *layer void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) { - // TODO: finish all ongoing checks - qDeleteAll( mLayerCheckStates[layer].singleFeatureChecks ); - qDeleteAll( mLayerCheckStates[layer].topologyChecks ); + if ( layer->geometryOptions()->geometryChecks().empty() && !mLayerChecks.contains( layer ) ) + return; + + VectorLayerCheckInformation &checkInformation = mLayerChecks[layer]; + + cancelTopologyCheck( layer ); + + qDeleteAll( checkInformation.singleFeatureChecks ); + qDeleteAll( checkInformation.topologyChecks ); + delete checkInformation.context; + + if ( layer->geometryOptions()->geometryChecks().empty() ) + { + for ( QMetaObject::Connection connection : qgis::as_const( checkInformation.connections ) ) + { + disconnect( connection ); + } + checkInformation.connections.clear(); + return; + } + + checkInformation.context = new QgsGeometryCheckContext( log10( layer->geometryOptions()->geometryPrecision() ) * -1, mProject->crs(), mProject->transformContext() ); - // TODO: ownership and lifetime of the context!! - auto context = new QgsGeometryCheckContext( log10( layer->geometryOptions()->geometryPrecision() ) * -1, mProject->crs(), mProject->transformContext() ); QList layerChecks; QgsGeometryCheckRegistry *checkRegistry = QgsAnalysis::instance()->geometryCheckRegistry(); @@ -144,7 +149,7 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) if ( activeChecks.contains( checkId ) ) { const QVariantMap checkConfiguration = layer->geometryOptions()->checkConfiguration( checkId ); - layerChecks.append( factory->createGeometryCheck( context, checkConfiguration ) ); + layerChecks.append( factory->createGeometryCheck( checkInformation.context, checkConfiguration ) ); } } @@ -155,7 +160,7 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) singleGeometryChecks.append( dynamic_cast( check ) ); } - mLayerCheckStates[layer].singleFeatureChecks = singleGeometryChecks; + checkInformation.singleFeatureChecks = singleGeometryChecks; // Topology checks QList topologyChecks; @@ -167,23 +172,48 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) if ( activeChecks.contains( checkId ) ) { const QVariantMap checkConfiguration = layer->geometryOptions()->checkConfiguration( checkId ); - topologyChecks.append( factory->createGeometryCheck( context, checkConfiguration ) ); + topologyChecks.append( factory->createGeometryCheck( checkInformation.context, checkConfiguration ) ); } } - mLayerCheckStates[layer].topologyChecks = topologyChecks; + checkInformation.topologyChecks = topologyChecks; + + // Connect to all modifications on a layer that can introduce a geometry or topology error + // Also connect to the beforeCommitChanges signal, so we can trigger topology checks + // We keep all connections around in a list, so if in the future all checks get disabled + // we can kill those connections to be sure the layer does not even get a tiny bit of overhead. + checkInformation.connections + << connect( layer, &QgsVectorLayer::featureAdded, this, [this, layer]( QgsFeatureId fid ) + { + onFeatureAdded( layer, fid ); + }, Qt::UniqueConnection ); + checkInformation.connections + << connect( layer, &QgsVectorLayer::geometryChanged, this, [this, layer]( QgsFeatureId fid, const QgsGeometry & geometry ) + { + onGeometryChanged( layer, fid, geometry ); + }, Qt::UniqueConnection ); + checkInformation.connections + << connect( layer, &QgsVectorLayer::featureDeleted, this, [this, layer]( QgsFeatureId fid ) + { + onFeatureDeleted( layer, fid ); + }, Qt::UniqueConnection ); + checkInformation.connections + << connect( layer, &QgsVectorLayer::beforeCommitChanges, this, [this, layer]() + { + onBeforeCommitChanges( layer ); + }, Qt::UniqueConnection ); } void QgsGeometryValidationService::cancelTopologyCheck( QgsVectorLayer *layer ) { - QFutureWatcher *futureWatcher = mLayerCheckStates[layer].topologyCheckFutureWatcher; + QFutureWatcher *futureWatcher = mLayerChecks[layer].topologyCheckFutureWatcher; if ( futureWatcher ) { // Make sure no more checks are started first futureWatcher->cancel(); // Tell all checks to stop asap - const auto feedbacks = mLayerCheckStates[layer].topologyCheckFeedbacks; + const auto feedbacks = mLayerChecks[layer].topologyCheckFeedbacks; for ( QgsFeedback *feedback : feedbacks ) { if ( feedback ) @@ -191,7 +221,7 @@ void QgsGeometryValidationService::cancelTopologyCheck( QgsVectorLayer *layer ) } futureWatcher->waitForFinished(); - mLayerCheckStates[layer].topologyCheckFutureWatcher = nullptr; + mLayerChecks[layer].topologyCheckFutureWatcher = nullptr; } } @@ -206,7 +236,7 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea QgsFeature feature = layer->getFeature( fid ); - const auto &checks = mLayerCheckStates[layer].singleFeatureChecks; + const auto &checks = mLayerChecks[layer].singleFeatureChecks; // The errors are going to be sent out via a signal. We cannot keep ownership in here (or can we?) // nor can we be sure that a single slot is connected to the signal. So make it a shared_ptr. @@ -232,7 +262,7 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer // TODO: ownership of these objects... QgsVectorLayerFeaturePool *featurePool = new QgsVectorLayerFeaturePool( layer ); - QList &allErrors = mLayerCheckStates[layer].topologyCheckErrors; + QList &allErrors = mLayerChecks[layer].topologyCheckErrors; QMap layerIds; QgsFeatureRequest request = QgsFeatureRequest( affectedFeatureIds ).setSubsetOfAttributes( QgsAttributeList() ); @@ -255,13 +285,13 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer mFeaturePools.insert( layer->id(), featurePool ); } - const QList checks = mLayerCheckStates[layer].topologyChecks; + const QList checks = mLayerChecks[layer].topologyChecks; QMap feedbacks; for ( QgsGeometryCheck *check : checks ) feedbacks.insert( check, new QgsFeedback() ); - mLayerCheckStates[layer].topologyCheckFeedbacks = feedbacks.values(); + mLayerChecks[layer].topologyCheckFeedbacks = feedbacks.values(); QFuture future = QtConcurrent::map( checks, [&allErrors, layerFeatureIds, layer, feedbacks, this]( const QgsGeometryCheck * check ) { @@ -296,9 +326,9 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer errorLocker.unlock(); qDeleteAll( feedbacks.values() ); futureWatcher->deleteLater(); - if ( mLayerCheckStates[layer].topologyCheckFutureWatcher == futureWatcher ) - mLayerCheckStates[layer].topologyCheckFutureWatcher = nullptr; + if ( mLayerChecks[layer].topologyCheckFutureWatcher == futureWatcher ) + mLayerChecks[layer].topologyCheckFutureWatcher = nullptr; } ); - mLayerCheckStates[layer].topologyCheckFutureWatcher = futureWatcher; + mLayerChecks[layer].topologyCheckFutureWatcher = futureWatcher; } diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index 493e23ffebb..3e335b6d2fc 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -32,6 +32,7 @@ class QgsSingleGeometryCheckError; class QgsGeometryCheckError; class QgsFeedback; class QgsFeaturePool; +struct QgsGeometryCheckContext; /** * This service connects to all layers in a project and triggers validation @@ -95,17 +96,19 @@ class QgsGeometryValidationService : public QObject QgsProject *mProject = nullptr; - struct VectorCheckState + struct VectorLayerCheckInformation { QList< QgsSingleGeometryCheck * > singleFeatureChecks; QList< QgsGeometryCheck *> topologyChecks; QFutureWatcher *topologyCheckFutureWatcher = nullptr; QList topologyCheckFeedbacks; // will be deleted when topologyCheckFutureWatcher is delteed QList topologyCheckErrors; + QList connections; + QgsGeometryCheckContext *context = nullptr; }; QReadWriteLock mTopologyCheckLock; - QHash mLayerCheckStates; + QHash mLayerChecks; QMap mFeaturePools; }; From b210e7dcaade528900475cacd67e01773d642753 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 15:50:19 +0200 Subject: [PATCH 060/126] Single geometry checks are done just in time --- src/app/qgsgeometryvalidationmodel.cpp | 5 ++++- src/app/qgsgeometryvalidationservice.cpp | 5 ----- src/app/qgsgeometryvalidationservice.h | 6 ------ 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index 8ba224bb2ac..75413d62af0 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -131,10 +131,13 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) case Qt::DecorationRole: { +#if 0 if ( mGeometryValidationService->validationActive( mCurrentLayer, featureItem.fid ) ) return QgsApplication::getThemeIcon( "/mActionTracing.svg" ); else return QVariant(); +#endif + break; } case GeometryCheckErrorRole: @@ -213,7 +216,7 @@ void QgsGeometryValidationModel::onGeometryCheckCompleted( QgsVectorLayer *layer int featureIdx = errorsForFeature( layer, fid ); // The last check for this feature finished: remove - if ( featureIdx > -1 && errors.empty() && !mGeometryValidationService->validationActive( layer, fid ) ) + if ( featureIdx > -1 && errors.empty() ) // && !mGeometryValidationService->validationActive( layer, fid ) ) { if ( mCurrentLayer == layer ) beginRemoveRows( QModelIndex(), featureIdx, featureIdx ); diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index c1e9e3a196a..2ed2f43f877 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -35,11 +35,6 @@ QgsGeometryValidationService::QgsGeometryValidationService( QgsProject *project connect( project, &QgsProject::layersAdded, this, &QgsGeometryValidationService::onLayersAdded ); } -bool QgsGeometryValidationService::validationActive( QgsVectorLayer *layer, QgsFeatureId feature ) const -{ - return false; -} - void QgsGeometryValidationService::fixError( const QgsGeometryCheckError *error, int method ) { QgsGeometryCheck::Changes changes; diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index 3e335b6d2fc..78bc1127e94 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -60,12 +60,6 @@ class QgsGeometryValidationService : public QObject QgsGeometryValidationService( QgsProject *project ); ~QgsGeometryValidationService() = default; - /** - * Returns if a validation is active for the specified \a feature on - * \a layer. - */ - bool validationActive( QgsVectorLayer *layer, QgsFeatureId feature ) const; - void fixError( const QgsGeometryCheckError *error, int method ); void triggerTopologyChecks( QgsVectorLayer *layer ); From a05e787d5a464ccc26db33561546b95d748b502f Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 16:47:10 +0200 Subject: [PATCH 061/126] Add docs for cache warming --- src/analysis/vector/geometry_checker/qgsfeaturepool.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsfeaturepool.h b/src/analysis/vector/geometry_checker/qgsfeaturepool.h index 09573a01f5a..920ed86adb2 100644 --- a/src/analysis/vector/geometry_checker/qgsfeaturepool.h +++ b/src/analysis/vector/geometry_checker/qgsfeaturepool.h @@ -47,8 +47,11 @@ class ANALYSIS_EXPORT QgsFeaturePool : public QgsFeatureSink SIP_ABSTRACT bool getFeature( QgsFeatureId id, QgsFeature &feature ); /** - * Warm the cache ... - * TODO write more docs + * Get features for the provided \a request. No features will be fetched + * from the cache and the request is sent directly to the underlying feature source. + * Results of the request are cached in the pool and the ids of all the features + * are returned. This can be used to warm the cache for a particular area of interest + * (bounding box) or other set of features. */ QgsFeatureIds getFeatures( const QgsFeatureRequest &request ) SIP_SKIP; From 97a9659a830a3b3d0d4498aedca0b085a8841132 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 16:47:29 +0200 Subject: [PATCH 062/126] fix rebase issues --- .../vector/geometry_checker/qgsgeometryduplicatecheck.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h b/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h index a8741ddf4b0..81102f2f4df 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryduplicatecheck.h @@ -70,12 +70,6 @@ class ANALYSIS_EXPORT QgsGeometryDuplicateCheck : public QgsGeometryCheck static QString factoryId(); static QgsGeometryCheck::CheckType factoryCheckType(); - static QList factoryCompatibleGeometryTypes() {return {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}; } - static bool factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP { return factoryCompatibleGeometryTypes().contains( layer->geometryType() ); } - static QString factoryDescription() { return tr( "Duplicate" ); } - static QString factoryId(); - static QgsGeometryCheck::CheckType factoryCheckType(); - enum ResolutionMethod { NoChange, RemoveDuplicates }; }; From f3261e1037789029d8d4bf845fda654c40118b93 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 17:00:47 +0200 Subject: [PATCH 063/126] Centralize invalidateTopologyChecks handling --- src/app/qgsgeometryvalidationservice.cpp | 26 ++++++++++++++---------- src/app/qgsgeometryvalidationservice.h | 2 +- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 2ed2f43f877..65057a394f1 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -63,22 +63,25 @@ void QgsGeometryValidationService::onFeatureAdded( QgsVectorLayer *layer, QgsFea { if ( !mLayerChecks[layer].topologyChecks.empty() ) { - // TODO: Cancel topology checks - layer->setAllowCommit( false ); + invalidateTopologyChecks( layer ); } processFeature( layer, fid ); } void QgsGeometryValidationService::onGeometryChanged( QgsVectorLayer *layer, QgsFeatureId fid, const QgsGeometry &geometry ) { + Q_UNUSED( geometry ) + // It would be nice to use the geometry here for the tests. + // But: + // 1. other codepaths to the checks also have no geometry (feature added / feature deleted) + // 2. and looking it up from the edit buffer (in memory) is really fast. + // so in short: it's still a good idea, but not as important as on first thought. + if ( !mLayerChecks[layer].topologyChecks.empty() ) { - // TODO: Cancel topology checks - layer->setAllowCommit( false ); + invalidateTopologyChecks( layer ); } - Q_UNUSED( geometry ) - cancelChecks( layer, fid ); processFeature( layer, fid ); } @@ -86,11 +89,11 @@ void QgsGeometryValidationService::onFeatureDeleted( QgsVectorLayer *layer, QgsF { if ( !mLayerChecks[layer].topologyChecks.empty() ) { - // TODO: Cancel topology checks - layer->setAllowCommit( false ); + invalidateTopologyChecks( layer ); } - cancelChecks( layer, fid ); + // There should be no geometry errors on an inexistent feature, right? + emit geometryCheckCompleted( layer, fid, QList>() ); } void QgsGeometryValidationService::onBeforeCommitChanges( QgsVectorLayer *layer ) @@ -220,9 +223,10 @@ void QgsGeometryValidationService::cancelTopologyCheck( QgsVectorLayer *layer ) } } -void QgsGeometryValidationService::cancelChecks( QgsVectorLayer *layer, QgsFeatureId fid ) +void QgsGeometryValidationService::invalidateTopologyChecks( QgsVectorLayer *layer ) { - + cancelTopologyCheck( layer ); + layer->setAllowCommit( false ); } void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFeatureId fid ) diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index 78bc1127e94..daaebe50fc7 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -84,7 +84,7 @@ class QgsGeometryValidationService : public QObject void cancelTopologyCheck( QgsVectorLayer *layer ); - void cancelChecks( QgsVectorLayer *layer, QgsFeatureId fid ); + void invalidateTopologyChecks( QgsVectorLayer *layer ); void processFeature( QgsVectorLayer *layer, QgsFeatureId fid ); From 76700cda71e9d960e75680723bb0937559ae30d7 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 18:05:42 +0200 Subject: [PATCH 064/126] Add more debug information on vertex tool assertion kicks in when zooming in a lot with snapping activated --- src/app/vertextool/qgsvertextool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/vertextool/qgsvertextool.cpp b/src/app/vertextool/qgsvertextool.cpp index d6bc51d590e..b1cbf7b4d82 100644 --- a/src/app/vertextool/qgsvertextool.cpp +++ b/src/app/vertextool/qgsvertextool.cpp @@ -1929,7 +1929,7 @@ bool QgsVertexTool::matchEdgeCenterTest( const QgsPointLocator::Match &m, const QgsGeometry lineGeom = QgsGeometry::fromPolylineXY( QgsPolylineXY() << p0 << p1 ); lineGeom = extentGeom.intersection( lineGeom ); QgsPolylineXY polyline = lineGeom.asPolyline(); - Q_ASSERT( polyline.count() == 2 ); + Q_ASSERT_X( polyline.count() == 2, "QgsVertexTool::matchEdgeCenterTest", QgsLineString( polyline ).asWkt().toUtf8().constData() ); p0 = polyline[0]; p1 = polyline[1]; } From 6df73d6d0729de95ae73f00668d8742c26f6a0a7 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 18:11:42 +0200 Subject: [PATCH 065/126] QgsVectorLayerFeaturePool needs to be aware of geometry changes --- .../geometry_checker/qgsfeaturepool.sip.in | 1 + src/analysis/CMakeLists.txt | 2 +- .../geometry_checker/qgsfeaturepool.cpp | 5 +++ .../vector/geometry_checker/qgsfeaturepool.h | 8 ++++ .../qgsvectorlayerfeaturepool.cpp | 41 ++++++++++--------- .../qgsvectorlayerfeaturepool.h | 8 +++- 6 files changed, 43 insertions(+), 22 deletions(-) diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in index 3f4eb0313c7..2186cc9f41b 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in @@ -90,6 +90,7 @@ To be used by implementations of ``deleteFeature``. %End + private: QgsFeaturePool( const QgsFeaturePool &other ); }; diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt index 15d4ca11a9d..6508afcfa30 100644 --- a/src/analysis/CMakeLists.txt +++ b/src/analysis/CMakeLists.txt @@ -186,6 +186,7 @@ SET(QGIS_ANALYSIS_MOC_HDRS vector/geometry_checker/qgsgeometrychecker.h vector/geometry_checker/qgsgeometrycheck.h + vector/geometry_checker/qgsvectorlayerfeaturepool.h ) INCLUDE_DIRECTORIES(SYSTEM ${SPATIALITE_INCLUDE_DIR}) @@ -252,7 +253,6 @@ SET(QGIS_ANALYSIS_HDRS vector/geometry_checker/qgsgeometrycheckerutils.h vector/geometry_checker/qgsfeaturepool.h vector/geometry_checker/qgsvectordataproviderfeaturepool.h - vector/geometry_checker/qgsvectorlayerfeaturepool.h interpolation/qgsinterpolator.h interpolation/qgsgridfilewriter.h diff --git a/src/analysis/vector/geometry_checker/qgsfeaturepool.cpp b/src/analysis/vector/geometry_checker/qgsfeaturepool.cpp index b96bc5f4612..daff03ea3a5 100644 --- a/src/analysis/vector/geometry_checker/qgsfeaturepool.cpp +++ b/src/analysis/vector/geometry_checker/qgsfeaturepool.cpp @@ -140,6 +140,11 @@ void QgsFeaturePool::setFeatureIds( const QgsFeatureIds &ids ) mFeatureIds = ids; } +bool QgsFeaturePool::isFeatureCached( QgsFeatureId fid ) +{ + return mFeatureCache.contains( fid ); +} + QgsCoordinateReferenceSystem QgsFeaturePool::crs() const { return mCrs; diff --git a/src/analysis/vector/geometry_checker/qgsfeaturepool.h b/src/analysis/vector/geometry_checker/qgsfeaturepool.h index 920ed86adb2..f80a0036ff2 100644 --- a/src/analysis/vector/geometry_checker/qgsfeaturepool.h +++ b/src/analysis/vector/geometry_checker/qgsfeaturepool.h @@ -145,6 +145,14 @@ class ANALYSIS_EXPORT QgsFeaturePool : public QgsFeatureSink SIP_ABSTRACT */ void setFeatureIds( const QgsFeatureIds &ids ) SIP_SKIP; + /** + * Checks if the feature \a fid is cached. + * + * \since QGIS 3.4 + * \note not available in Python bindings + */ + bool isFeatureCached( QgsFeatureId fid ) SIP_SKIP; + private: #ifdef SIP_RUN QgsFeaturePool( const QgsFeaturePool &other ) diff --git a/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.cpp b/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.cpp index 0c7a8a25bab..a6027c4c0b3 100644 --- a/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.cpp +++ b/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.cpp @@ -17,29 +17,14 @@ email : matthias@opengis.ch #include "qgsthreadingutils.h" #include "qgsfeaturerequest.h" +#include "qgsvectorlayer.h" QgsVectorLayerFeaturePool::QgsVectorLayerFeaturePool( QgsVectorLayer *layer ) - : QgsFeaturePool( layer ) + : QObject() + , QgsFeaturePool( layer ) { - // Build spatial index - QgsFeature feature; - QgsFeatureRequest req; - QgsFeatureIds featureIds; - - QgsFeatureIterator it = layer->getFeatures( req ); - while ( it.nextFeature( feature ) ) - { - if ( feature.geometry() ) - { - insertFeature( feature ); - featureIds.insert( feature.id() ); - } - else - { - featureIds.remove( feature.id() ); - } - } - setFeatureIds( featureIds ); + connect( layer, &QgsVectorLayer::featureDeleted, this, &QgsVectorLayerFeaturePool::onFeatureDeleted ); + connect( layer, &QgsVectorLayer::geometryChanged, this, &QgsVectorLayerFeaturePool::onGeometryChanged ); } bool QgsVectorLayerFeaturePool::addFeature( QgsFeature &feature, Flags flags ) @@ -147,3 +132,19 @@ void QgsVectorLayerFeaturePool::deleteFeature( QgsFeatureId fid ) } } ); } + +void QgsVectorLayerFeaturePool::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geometry ) +{ + if ( isFeatureCached( fid ) ) + { + QgsFeature feature; + getFeature( fid, feature ); + feature.setGeometry( geometry ); + updateFeature( feature ); + } +} + +void QgsVectorLayerFeaturePool::onFeatureDeleted( QgsFeatureId fid ) +{ + deleteFeature( fid ); +} diff --git a/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.h b/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.h index ceda6945a70..f285cf9a9e5 100644 --- a/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.h +++ b/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.h @@ -27,8 +27,10 @@ email : matthias@opengis.ch * * \since QGIS 3.4 */ -class ANALYSIS_EXPORT QgsVectorLayerFeaturePool : public QgsFeaturePool +class ANALYSIS_EXPORT QgsVectorLayerFeaturePool : public QObject, public QgsFeaturePool { + Q_OBJECT + public: QgsVectorLayerFeaturePool( QgsVectorLayer *layer ); @@ -36,6 +38,10 @@ class ANALYSIS_EXPORT QgsVectorLayerFeaturePool : public QgsFeaturePool bool addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags flags = nullptr ) override; void updateFeature( QgsFeature &feature ) override; void deleteFeature( QgsFeatureId fid ) override; + + private slots: + void onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geometry ); + void onFeatureDeleted( QgsFeatureId fid ); }; #endif // QGSVECTORLAYERFEATUREPOOL_H From 09ee755500afd429c239a78ed59970bcd00a6572 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 18:13:07 +0200 Subject: [PATCH 066/126] One feature pool per layer is enough --- src/app/qgsgeometryvalidationservice.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 65057a394f1..767dc5c9180 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -259,8 +259,13 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer QgsFeatureIds affectedFeatureIds = layer->editBuffer()->changedGeometries().keys().toSet(); affectedFeatureIds.unite( layer->editBuffer()->addedFeatures().keys().toSet() ); - // TODO: ownership of these objects... - QgsVectorLayerFeaturePool *featurePool = new QgsVectorLayerFeaturePool( layer ); + QgsFeaturePool *featurePool = mFeaturePools.value( layer->id() ); + if ( !featurePool ) + { + featurePool = new QgsVectorLayerFeaturePool( layer ); + mFeaturePools.insert( layer->id(), featurePool ); + } + QList &allErrors = mLayerChecks[layer].topologyCheckErrors; QMap layerIds; @@ -279,11 +284,6 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer layerIds.insert( layer->id(), checkFeatureIds ); QgsGeometryCheck::LayerFeatureIds layerFeatureIds( layerIds ); - if ( !mFeaturePools.contains( layer->id() ) ) - { - mFeaturePools.insert( layer->id(), featurePool ); - } - const QList checks = mLayerChecks[layer].topologyChecks; QMap feedbacks; From 62b9f60f7981fe410598952b2afbb9a0a9f1f49f Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 1 Oct 2018 18:13:46 +0200 Subject: [PATCH 067/126] =?UTF-8?q?Some=20=F0=9F=92=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/qgsgeometryvalidationdock.cpp | 38 ++++++++++++++++++--------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index c0ce97f6d74..89f440ce547 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -48,7 +48,7 @@ QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsM mFeatureRubberband->setWidth( scaleFactor ); mFeatureRubberband->setStrokeColor( QColor( 100, 255, 100, 100 ) ); - mErrorRubberband->setColor( QColor( 180, 250, 180, 100 ) ); + mErrorRubberband->setColor( QColor( "#ffee58ff" ) ); mErrorRubberband->setWidth( scaleFactor ); mErrorLocationRubberband->setIcon( QgsRubberBand::ICON_X ); @@ -193,22 +193,36 @@ void QgsGeometryValidationDock::showHighlight( const QModelIndex ¤t ) mFeatureRubberband->setToGeometry( featureGeometry ); - - QPropertyAnimation *animation = new QPropertyAnimation( mFeatureRubberband, "fillColor" ); - animation->setEasingCurve( QEasingCurve::OutQuad ); - connect( animation, &QPropertyAnimation::finished, animation, &QPropertyAnimation::deleteLater ); - connect( animation, &QPropertyAnimation::valueChanged, this, [this] + QPropertyAnimation *featureAnimation = new QPropertyAnimation( mFeatureRubberband, "fillColor" ); + featureAnimation->setEasingCurve( QEasingCurve::OutQuad ); + connect( featureAnimation, &QPropertyAnimation::finished, featureAnimation, &QPropertyAnimation::deleteLater ); + connect( featureAnimation, &QPropertyAnimation::valueChanged, this, [this] { mFeatureRubberband->update(); } ); - animation->setDuration( 2000 ); - animation->setStartValue( QColor( 100, 255, 100, 255 ) ); - animation->setEndValue( QColor( 100, 255, 100, 0 ) ); + featureAnimation->setDuration( 2000 ); + featureAnimation->setStartValue( QColor( 100, 255, 100, 255 ) ); + featureAnimation->setEndValue( QColor( 100, 255, 100, 0 ) ); - animation->start(); + featureAnimation->start(); - // mErrorRubberband->setToGeometry( errorGeometry ); - mErrorLocationRubberband->setToGeometry( QgsGeometry( new QgsPoint( locationGeometry ) ) ); + mErrorRubberband->setToGeometry( errorGeometry ); + + QPropertyAnimation *errorAnimation = new QPropertyAnimation( mErrorRubberband, "fillColor" ); + errorAnimation->setEasingCurve( QEasingCurve::OutQuad ); + connect( errorAnimation, &QPropertyAnimation::finished, featureAnimation, &QPropertyAnimation::deleteLater ); + connect( errorAnimation, &QPropertyAnimation::valueChanged, this, [this] + { + mErrorRubberband->update(); + } ); + + errorAnimation->setStartValue( QColor( "#ffee58ff" ) ); + errorAnimation->setEndValue( QColor( "#ffee5800" ) ); + + errorAnimation->setDuration( 2000 ); + errorAnimation->start(); + + mErrorLocationRubberband->setToGeometry( QgsGeometry( qgis::make_unique( locationGeometry ) ) ); } } From 7f70c21a131acb8cbe928756b1aaac14c030a2e9 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 1 Oct 2018 13:27:23 -0400 Subject: [PATCH 068/126] do not crash when no edit buffer --- src/app/qgsgeometryvalidationservice.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 767dc5c9180..fbc5cff6851 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -256,8 +256,12 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer emit topologyChecksCleared( layer ); cancelTopologyCheck( layer ); - QgsFeatureIds affectedFeatureIds = layer->editBuffer()->changedGeometries().keys().toSet(); - affectedFeatureIds.unite( layer->editBuffer()->addedFeatures().keys().toSet() ); + QgsFeatureIds affectedFeatureIds; + if ( layer->editBuffer() ) + { + affectedFeatureIds = layer->editBuffer()->changedGeometries().keys().toSet(); + affectedFeatureIds.unite( layer->editBuffer()->addedFeatures().keys().toSet() ); + } QgsFeaturePool *featurePool = mFeaturePools.value( layer->id() ); if ( !featurePool ) From 579c97e7ead088453d7170e291216eccb3ba15fb Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 1 Oct 2018 13:50:51 -0400 Subject: [PATCH 069/126] activate icon only when topology checks are available --- src/app/qgsgeometryvalidationdock.cpp | 30 ++++++++++++++++++++++++++- src/app/qgsgeometryvalidationdock.h | 1 + 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 89f440ce547..dfb3aef4b8c 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -13,6 +13,9 @@ email : matthias@opengis.ch * * ***************************************************************************/ +#include + + #include "qgsgeometryvalidationdock.h" #include "qgsgeometryvalidationmodel.h" #include "qgsgeometryvalidationservice.h" @@ -21,8 +24,11 @@ email : matthias@opengis.ch #include "qgsvectorlayer.h" #include "qgsgeometrycheck.h" #include "qgsgeometrycheckerror.h" +#include "qgsanalysis.h" +#include "qgsgeometrycheckregistry.h" +#include "qgsgeometryoptions.h" +#include "qgsgeometrycheckfactory.h" -#include QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsMapCanvas *mapCanvas, QWidget *parent, Qt::WindowFlags flags ) : QgsDockWidget( title, parent, flags ) @@ -34,6 +40,7 @@ QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsM connect( mPreviousButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::gotoPreviousError ); connect( mZoomToProblemButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::zoomToProblem ); connect( mZoomToFeatureButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::zoomToFeature ); + connect( mMapCanvas, &QgsMapCanvas::currentLayerChanged, this, &QgsGeometryValidationDock::onCurrentLayerChanged ); connect( mMapCanvas, &QgsMapCanvas::currentLayerChanged, this, &QgsGeometryValidationDock::updateLayerTransform ); connect( mMapCanvas, &QgsMapCanvas::destinationCrsChanged, this, &QgsGeometryValidationDock::updateLayerTransform ); connect( mMapCanvas, &QgsMapCanvas::transformContextChanged, this, &QgsGeometryValidationDock::updateLayerTransform ); @@ -182,6 +189,27 @@ void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤ } } +void QgsGeometryValidationDock::onCurrentLayerChanged( QgsMapLayer *layer ) +{ + // activate icon + bool enabled = false; + QgsVectorLayer *vl = dynamic_cast( layer ); + if ( vl && vl->isSpatial() ) + { + const QList topologyCheckFactories = QgsAnalysis::instance()->geometryCheckRegistry()->geometryCheckFactories( vl, QgsGeometryCheck::LayerCheck, QgsGeometryCheck::Flag::AvailableInValidation ); + const QStringList activeChecks = vl->geometryOptions()->geometryChecks(); + for ( const QgsGeometryCheckFactory *factory : topologyCheckFactories ) + { + if ( activeChecks.contains( factory->id() ) ) + { + enabled = true; + break; + } + } + } + mTopologyChecksPendingButton->setEnabled( enabled ); +} + void QgsGeometryValidationDock::showHighlight( const QModelIndex ¤t ) { QgsVectorLayer *vlayer = qobject_cast( mMapCanvas->currentLayer() ); diff --git a/src/app/qgsgeometryvalidationdock.h b/src/app/qgsgeometryvalidationdock.h index 8da0c0f9aac..2dd789387fc 100644 --- a/src/app/qgsgeometryvalidationdock.h +++ b/src/app/qgsgeometryvalidationdock.h @@ -43,6 +43,7 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal private slots: void onCurrentErrorChanged( const QModelIndex ¤t, const QModelIndex &previous ); + void onCurrentLayerChanged( QgsMapLayer *layer ); void gotoNextError(); void gotoPreviousError(); void zoomToProblem(); From d4ac9a7c009a4dcf98cc2a49a581ad1468103e5e Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 1 Oct 2018 14:15:48 -0400 Subject: [PATCH 070/126] do not zoom to feature if no extent --- .../vector/geometry_checker/qgsgeometrycheck.sip.in | 1 - src/analysis/vector/geometry_checker/qgsgeometrycheck.h | 2 -- src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h | 3 ++- src/app/qgsgeometryvalidationdock.cpp | 7 +++++-- src/app/qgsgeometryvalidationmodel.cpp | 2 ++ .../qgsgeometrycheckerfixsummarydialog.cpp | 2 +- .../geometry_checker/qgsgeometrycheckerresulttab.cpp | 2 +- 7 files changed, 11 insertions(+), 8 deletions(-) diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in index a307de05503..c4053f20da7 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in @@ -10,7 +10,6 @@ - class QgsGeometryCheck { %Docstring diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheck.h b/src/analysis/vector/geometry_checker/qgsgeometrycheck.h index ffa2c01e051..9db94bd5b65 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheck.h @@ -31,8 +31,6 @@ class QgsGeometryCheckError; class QgsFeaturePool; -#define FEATUREID_NULL std::numeric_limits::min() - class ANALYSIS_EXPORT QgsGeometryCheck { Q_GADGET diff --git a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h index 5aa9255bbb4..70b5db054e1 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h @@ -20,6 +20,7 @@ #include "qgsgeometrycheck.h" #include "qgsgeometrycheckerror.h" +#include "qgsfeatureid.h" class ANALYSIS_EXPORT QgsGeometryGapCheckError : public QgsGeometryCheckError { @@ -30,7 +31,7 @@ class ANALYSIS_EXPORT QgsGeometryGapCheckError : public QgsGeometryCheckError const QMap &neighbors, double area, const QgsRectangle &gapAreaBBox ) - : QgsGeometryCheckError( check, layerId, FEATUREID_NULL, geometry, geometry.constGet()->centroid(), QgsVertexId(), area, ValueArea ) + : QgsGeometryCheckError( check, layerId, FID_NULL, geometry, geometry.constGet()->centroid(), QgsVertexId(), area, ValueArea ) , mNeighbors( neighbors ) , mGapAreaBBox( gapAreaBBox ) { diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index dfb3aef4b8c..ade12df8a99 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -110,8 +110,11 @@ void QgsGeometryValidationDock::zoomToFeature() return; QgsRectangle featureExtent = currentIndex().data( QgsGeometryValidationModel::FeatureExtentRole ).value(); - QgsRectangle mapExtent = mLayerTransform.transform( featureExtent ); - mMapCanvas->zoomToFeatureExtent( mapExtent ); + if ( !featureExtent.isEmpty() ) + { + QgsRectangle mapExtent = mLayerTransform.transform( featureExtent ); + mMapCanvas->zoomToFeatureExtent( mapExtent ); + } } void QgsGeometryValidationDock::triggerTopologyChecks() diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index 75413d62af0..767ff855e4e 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -74,6 +74,8 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) case FeatureExtentRole: { const QgsFeatureId fid = topologyError->featureId(); + if ( FID_IS_NULL( fid ) ) + return QgsRectangle(); const QgsFeature feature = mCurrentLayer->getFeature( fid ); // TODO: this should be cached! return feature.geometry().boundingBox(); } diff --git a/src/plugins/geometry_checker/qgsgeometrycheckerfixsummarydialog.cpp b/src/plugins/geometry_checker/qgsgeometrycheckerfixsummarydialog.cpp index f6aadf670d7..05f49a5047d 100644 --- a/src/plugins/geometry_checker/qgsgeometrycheckerfixsummarydialog.cpp +++ b/src/plugins/geometry_checker/qgsgeometrycheckerfixsummarydialog.cpp @@ -81,7 +81,7 @@ void QgsGeometryCheckerFixSummaryDialog::addError( QTableWidget *table, QgsGeome table->insertRow( row ); table->setItem( row, 0, new QTableWidgetItem( !error->layerId().isEmpty() ? mChecker->featurePools()[error->layerId()]->layer()->name() : "" ) ); QTableWidgetItem *idItem = new QTableWidgetItem(); - idItem->setData( Qt::EditRole, error->featureId() != FEATUREID_NULL ? QVariant( error->featureId() ) : QVariant() ); + idItem->setData( Qt::EditRole, error->featureId() != FID_NULL ? QVariant( error->featureId() ) : QVariant() ); table->setItem( row, 1, idItem ); table->setItem( row, 2, new QTableWidgetItem( error->description() ) ); table->setItem( row, 3, new QTableWidgetItem( posStr ) ); diff --git a/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp b/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp index 330ca4a70b5..935cf59fc92 100644 --- a/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp +++ b/src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp @@ -151,7 +151,7 @@ void QgsGeometryCheckerResultTab::addError( QgsGeometryCheckError *error ) ui.tableWidgetErrors->insertRow( row ); QTableWidgetItem *idItem = new QTableWidgetItem(); - idItem->setData( Qt::EditRole, error->featureId() != FEATUREID_NULL ? QVariant( error->featureId() ) : QVariant() ); + idItem->setData( Qt::EditRole, error->featureId() != FID_NULL ? QVariant( error->featureId() ) : QVariant() ); ui.tableWidgetErrors->setItem( row, 0, new QTableWidgetItem( !error->layerId().isEmpty() ? mChecker->featurePools()[error->layerId()]->layer()->name() : "" ) ); ui.tableWidgetErrors->setItem( row, 1, idItem ); ui.tableWidgetErrors->setItem( row, 2, new QTableWidgetItem( error->description() ) ); From 29febc0c1f2bf605b7d72415680babeed304aa32 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 1 Oct 2018 14:24:59 -0400 Subject: [PATCH 071/126] fix warning conversion QgsFeatureId to QString --- src/app/qgsgeometryvalidationmodel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index 767ff855e4e..d826affd1ba 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -2,6 +2,7 @@ #include "qgsvectorlayer.h" #include "qgssinglegeometrycheck.h" +#include "qgsfeatureid.h" #include @@ -121,7 +122,7 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) mExpressionContext.setFeature( feature ); QString featureTitle = mDisplayExpression.evaluate( &mExpressionContext ).toString(); if ( featureTitle.isEmpty() ) - featureTitle = featureItem.fid; + featureTitle = FID_TO_STRING( featureItem.fid ); if ( featureItem.errors.count() > 1 ) return tr( "%1: %n Errors", "", featureItem.errors.count() ).arg( featureTitle ); From e77bab9d2c8fbb929b994a6e1692c22eb629beec Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 1 Oct 2018 14:37:38 -0400 Subject: [PATCH 072/126] enable zoom button only if error has feature --- src/app/qgsgeometryvalidationdock.cpp | 3 +++ src/app/qgsgeometryvalidationmodel.cpp | 18 ++++++++++++++++-- src/app/qgsgeometryvalidationmodel.h | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index ade12df8a99..68530d12a7e 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -178,6 +178,9 @@ void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤ } } + bool hasFeature = !FID_IS_NULL( current.data( QgsGeometryValidationModel::ErrorFeatureIdRole ) ); + mZoomToFeatureButton->setEnabled( hasFeature ); + showHighlight( current ); switch ( mLastZoomToAction ) diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index d826affd1ba..e34a33a124a 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -91,11 +91,20 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) return topologyError->geometry(); } + case ErrorFeatureIdRole: + { + return topologyError->featureId(); + } + case FeatureGeometryRole: { const QgsFeatureId fid = topologyError->featureId(); - const QgsFeature feature = mCurrentLayer->getFeature( fid ); // TODO: this should be cached! - return feature.geometry(); + if ( !FID_IS_NULL( fid ) ) + { + const QgsFeature feature = mCurrentLayer->getFeature( fid ); // TODO: this should be cached! + return feature.geometry(); + } + return QgsGeometry(); } case ErrorLocationGeometryRole: @@ -149,6 +158,11 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) break; } + case ErrorFeatureIdRole: + { + return featureItem.fid; + } + case FeatureExtentRole: { return mCurrentLayer->getFeature( featureItem.fid ).geometry().boundingBox(); diff --git a/src/app/qgsgeometryvalidationmodel.h b/src/app/qgsgeometryvalidationmodel.h index 96fb136e18b..b3cf0908275 100644 --- a/src/app/qgsgeometryvalidationmodel.h +++ b/src/app/qgsgeometryvalidationmodel.h @@ -18,6 +18,7 @@ class QgsGeometryValidationModel : public QAbstractItemModel FeatureExtentRole = Qt::UserRole, ProblemExtentRole, ErrorGeometryRole, + ErrorFeatureIdRole, FeatureGeometryRole, ErrorLocationGeometryRole, GeometryCheckErrorRole, From 8b32aaebbf831acdf5b058b23cf070137335a434 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 1 Oct 2018 15:39:31 -0400 Subject: [PATCH 073/126] nice UX in validation docker --- src/app/qgsgeometryvalidationdock.cpp | 4 + src/ui/qgsgeometryvalidationdockbase.ui | 147 +++++++++++------------- 2 files changed, 69 insertions(+), 82 deletions(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 68530d12a7e..68660bcdd9b 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -36,6 +36,10 @@ QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsM { setupUi( this ); + QFont font = mProblemDescriptionLabel->font(); + font.setBold( true ); + mProblemDescriptionLabel->setFont( font ); + connect( mNextButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::gotoNextError ); connect( mPreviousButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::gotoPreviousError ); connect( mZoomToProblemButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::zoomToProblem ); diff --git a/src/ui/qgsgeometryvalidationdockbase.ui b/src/ui/qgsgeometryvalidationdockbase.ui index 87bc0981e71..cb31a9de7fe 100644 --- a/src/ui/qgsgeometryvalidationdockbase.ui +++ b/src/ui/qgsgeometryvalidationdockbase.ui @@ -15,7 +15,70 @@ - + + + + + + + Zoom To Problem + + + + :/images/themes/default/mActionZoomToLayer.svg:/images/themes/default/mActionZoomToLayer.svg + + + + + + + Previous + + + Qt::LeftArrow + + + + + + + Zoom To Feature + + + + :/images/themes/default/mActionZoomToSelected.svg:/images/themes/default/mActionZoomToSelected.svg + + + + + + + Next + + + Qt::RightArrow + + + + + + + Detailed Desctiption + + + true + + + + + + + + + + + + @@ -25,39 +88,7 @@
- - - - - - - Zoom To Feature - - - - - - - Zoom To Problem - - - - - - - Detailed Desctiption - - - - - - - - - - - - + ... @@ -68,24 +99,6 @@ - - - - - - Previous - - - - - - - Next - - - - -
@@ -99,36 +112,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 5a11b22d370ddd0ca37e902c83d0462bd3a0dc26 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 1 Oct 2018 16:10:04 -0400 Subject: [PATCH 074/126] use smaller button, alternate row colors --- src/app/qgsgeometryvalidationdock.cpp | 15 ++++++++++----- src/ui/qgsgeometryvalidationdockbase.ui | 18 ++++++++---------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 68660bcdd9b..99acf6a8b25 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -14,7 +14,7 @@ email : matthias@opengis.ch ***************************************************************************/ #include - +#include #include "qgsgeometryvalidationdock.h" #include "qgsgeometryvalidationmodel.h" @@ -39,6 +39,7 @@ QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsM QFont font = mProblemDescriptionLabel->font(); font.setBold( true ); mProblemDescriptionLabel->setFont( font ); + mErrorListView->setAlternatingRowColors( true ); connect( mNextButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::gotoNextError ); connect( mPreviousButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::gotoPreviousError ); @@ -166,20 +167,24 @@ void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤ QgsGeometryCheckError *error = current.data( QgsGeometryValidationModel::GeometryCheckErrorRole ).value(); if ( error ) { - while ( QPushButton *btn = mResolutionWidget->findChild() ) - delete btn; const QStringList resolutionMethods = error->check()->resolutionMethods(); + QGridLayout *layout = new QGridLayout( mResolutionWidget ); int resolutionIndex = 0; for ( const QString &resolutionMethod : resolutionMethods ) { - QPushButton *resolveBtn = new QPushButton( resolutionMethod ); + QToolButton *resolveBtn = new QToolButton( mResolutionWidget ); + resolveBtn->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmCheckGeometry.svg" ) ) ); + layout->addWidget( resolveBtn, resolutionIndex, 0 ); + QLabel *resolveLabel = new QLabel( resolutionMethod, mResolutionWidget ); + resolveLabel->setWordWrap( true ); + layout->addWidget( resolveLabel, resolutionIndex, 1 ); connect( resolveBtn, &QPushButton::clicked, this, [resolutionIndex, error, this]() { mGeometryValidationService->fixError( error, resolutionIndex ); } ); - mResolutionWidget->layout()->addWidget( resolveBtn ); resolutionIndex++; } + mResolutionWidget->setLayout( layout ); } bool hasFeature = !FID_IS_NULL( current.data( QgsGeometryValidationModel::ErrorFeatureIdRole ) ); diff --git a/src/ui/qgsgeometryvalidationdockbase.ui b/src/ui/qgsgeometryvalidationdockbase.ui index cb31a9de7fe..8c298b51f60 100644 --- a/src/ui/qgsgeometryvalidationdockbase.ui +++ b/src/ui/qgsgeometryvalidationdockbase.ui @@ -71,9 +71,7 @@ - - - + @@ -81,13 +79,6 @@ - - - - - - - @@ -99,6 +90,13 @@ + + + + + + + From ff9da2f76c53eb2391f952997b704a87ea9564fb Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 1 Oct 2018 16:36:58 -0400 Subject: [PATCH 075/126] nicer error reporting for overlap check replace overlap feature QPair by a struct this allows using the layer name rather than its ID in the error description --- .../qgsgeometryoverlapcheck.cpp | 11 ++++---- .../qgsgeometryoverlapcheck.h | 27 ++++++++++++++++--- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp index 999b12ffedf..4ecf92966ad 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp @@ -92,11 +92,11 @@ void QgsGeometryOverlapCheck::fixError( const QMap &f QgsGeometryOverlapCheckError *overlapError = static_cast( error ); QgsFeaturePool *featurePoolA = featurePools[ overlapError->layerId() ]; - QgsFeaturePool *featurePoolB = featurePools[ overlapError->overlappedFeature().first ]; + QgsFeaturePool *featurePoolB = featurePools[ overlapError->overlappedFeature().layerId() ]; QgsFeature featureA; QgsFeature featureB; if ( !featurePoolA->getFeature( overlapError->featureId(), featureA ) || - !featurePoolB->getFeature( overlapError->overlappedFeature().second, featureB ) ) + !featurePoolB->getFeature( overlapError->overlappedFeature().featureId(), featureB ) ) { error->setObsolete(); return; @@ -186,7 +186,7 @@ void QgsGeometryOverlapCheck::fixError( const QMap &f diff2->transform( ct, QgsCoordinateTransform::ReverseTransform ); featureB.setGeometry( QgsGeometry( std::move( diff2 ) ) ); - changes[overlapError->overlappedFeature().first][featureB.id()].append( Change( ChangeFeature, ChangeChanged ) ); + changes[overlapError->overlappedFeature().layerId()][featureB.id()].append( Change( ChangeFeature, ChangeChanged ) ); featurePoolB->updateFeature( featureB ); } @@ -249,15 +249,14 @@ bool QgsGeometryOverlapCheck::factoryIsCompatible( QgsVectorLayer *layer ) SIP_S QgsGeometryOverlapCheckError::QgsGeometryOverlapCheckError( const QgsGeometryCheck *check, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, const QgsGeometry &geometry, const QgsPointXY &errorLocation, const QVariant &value, const QgsGeometryCheckerUtils::LayerFeature &overlappedFeature ) : QgsGeometryCheckError( check, layerFeature.layer()->id(), layerFeature.feature().id(), geometry, errorLocation, QgsVertexId(), value, ValueArea ) - , mOverlappedFeature( qMakePair( overlappedFeature.layer()->id(), overlappedFeature.feature().id() ) ) - + , mOverlappedFeature( OverLappedFeature( overlappedFeature.layer(), overlappedFeature.feature().id() ) ) { } QString QgsGeometryOverlapCheckError::description() const { - return QCoreApplication::translate( "QgsGeometryTypeCheckError", "Overlap with %1:%2" ).arg( mOverlappedFeature.first, QString::number( mOverlappedFeature.second ) ); + return QCoreApplication::translate( "QgsGeometryTypeCheckError", "Overlap with %1 at feature %2" ).arg( mOverlappedFeature.layerName(), QString::number( mOverlappedFeature.featureId() ) ); } QgsGeometryCheck::CheckType QgsGeometryOverlapCheck::factoryCheckType() diff --git a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h index 1e5b79b1eda..e7070b62fac 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h @@ -25,13 +25,34 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheckError : public QgsGeometryCheckError { public: + + struct OverLappedFeature + { + public: + OverLappedFeature( QgsVectorLayer *vl, QgsFeatureId fid ) + : mLayerId( vl->id() ) + , mLayerName( vl->name() ) + , mFeatureId( fid ) + {} + + QString layerId() const {return mLayerId;} + QString layerName() const {return mLayerName;} + QgsFeatureId featureId() const {return mFeatureId;} + bool operator==( const OverLappedFeature &other ) const {return mLayerId == other.layerId() && mFeatureId == other.featureId();} + + private: + QString mLayerId; + QString mLayerName; + QgsFeatureId mFeatureId; + }; + QgsGeometryOverlapCheckError( const QgsGeometryCheck *check, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, const QgsGeometry &geometry, const QgsPointXY &errorLocation, const QVariant &value, const QgsGeometryCheckerUtils::LayerFeature &overlappedFeature ); - const QPair &overlappedFeature() const { return mOverlappedFeature; } + const OverLappedFeature &overlappedFeature() const { return mOverlappedFeature; } bool isEqual( QgsGeometryCheckError *other ) const override { @@ -56,7 +77,7 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheckError : public QgsGeometryCheckErro { return false; } - if ( changes.value( mOverlappedFeature.first ).keys().contains( mOverlappedFeature.second ) ) + if ( changes.value( mOverlappedFeature.layerId() ).keys().contains( mOverlappedFeature.featureId() ) ) { return false; } @@ -66,7 +87,7 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheckError : public QgsGeometryCheckErro QString description() const override; private: - QPair mOverlappedFeature; + OverLappedFeature mOverlappedFeature; }; class ANALYSIS_EXPORT QgsGeometryOverlapCheck : public QgsGeometryCheck From 6c069c1910538a34cc101c4611e4ddc9f200d25e Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 1 Oct 2018 17:01:15 -0400 Subject: [PATCH 076/126] better layout in dock --- src/app/qgsgeometryvalidationdock.cpp | 10 +- src/ui/qgsgeometryvalidationdockbase.ui | 165 ++++++++++++++++++------ 2 files changed, 132 insertions(+), 43 deletions(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 99acf6a8b25..2e75e4a2571 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -41,10 +41,10 @@ QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsM mProblemDescriptionLabel->setFont( font ); mErrorListView->setAlternatingRowColors( true ); - connect( mNextButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::gotoNextError ); - connect( mPreviousButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::gotoPreviousError ); - connect( mZoomToProblemButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::zoomToProblem ); - connect( mZoomToFeatureButton, &QPushButton::clicked, this, &QgsGeometryValidationDock::zoomToFeature ); + connect( mNextButton, &QToolButton::clicked, this, &QgsGeometryValidationDock::gotoNextError ); + connect( mPreviousButton, &QToolButton::clicked, this, &QgsGeometryValidationDock::gotoPreviousError ); + connect( mZoomToProblemButton, &QToolButton::clicked, this, &QgsGeometryValidationDock::zoomToProblem ); + connect( mZoomToFeatureButton, &QToolButton::clicked, this, &QgsGeometryValidationDock::zoomToFeature ); connect( mMapCanvas, &QgsMapCanvas::currentLayerChanged, this, &QgsGeometryValidationDock::onCurrentLayerChanged ); connect( mMapCanvas, &QgsMapCanvas::currentLayerChanged, this, &QgsGeometryValidationDock::updateLayerTransform ); connect( mMapCanvas, &QgsMapCanvas::destinationCrsChanged, this, &QgsGeometryValidationDock::updateLayerTransform ); @@ -178,7 +178,7 @@ void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤ QLabel *resolveLabel = new QLabel( resolutionMethod, mResolutionWidget ); resolveLabel->setWordWrap( true ); layout->addWidget( resolveLabel, resolutionIndex, 1 ); - connect( resolveBtn, &QPushButton::clicked, this, [resolutionIndex, error, this]() + connect( resolveBtn, &QToolButton::clicked, this, [resolutionIndex, error, this]() { mGeometryValidationService->fixError( error, resolutionIndex ); } ); diff --git a/src/ui/qgsgeometryvalidationdockbase.ui b/src/ui/qgsgeometryvalidationdockbase.ui index 8c298b51f60..cf41639d816 100644 --- a/src/ui/qgsgeometryvalidationdockbase.ui +++ b/src/ui/qgsgeometryvalidationdockbase.ui @@ -6,8 +6,8 @@ 0 0 - 607 - 973 + 418 + 404 @@ -18,39 +18,19 @@ - - - - Zoom To Problem - - - - :/images/themes/default/mActionZoomToLayer.svg:/images/themes/default/mActionZoomToLayer.svg - - - - - - - Previous - - - Qt::LeftArrow - - - - - - - Zoom To Feature - - - - :/images/themes/default/mActionZoomToSelected.svg:/images/themes/default/mActionZoomToSelected.svg - - - - + + 0 + + + 0 + + + 0 + + + 0 + + Next @@ -60,8 +40,104 @@ - + + + + Previous + + + Qt::LeftArrow + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + Zoom To Feature + + + + :/images/themes/default/mActionZoomToSelected.svg:/images/themes/default/mActionZoomToSelected.svg + + + Qt::ToolButtonTextBesideIcon + + + + + + + Zoom To Problem + + + + :/images/themes/default/mActionZoomToLayer.svg:/images/themes/default/mActionZoomToLayer.svg + + + Qt::ToolButtonTextBesideIcon + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + 0 + 0 + + Detailed Desctiption @@ -70,8 +146,21 @@ - - + + + + + 0 + 0 + + + + + 0 + 0 + + + From 9c6c3aa8374075b71186dc12ccbf56192e5a9d62 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 2 Oct 2018 08:59:39 +0200 Subject: [PATCH 077/126] qobject_cast --- src/app/qgsgeometryvalidationdock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 2e75e4a2571..22d5e90b8a2 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -208,7 +208,7 @@ void QgsGeometryValidationDock::onCurrentLayerChanged( QgsMapLayer *layer ) { // activate icon bool enabled = false; - QgsVectorLayer *vl = dynamic_cast( layer ); + QgsVectorLayer *vl = qobject_cast( layer ); if ( vl && vl->isSpatial() ) { const QList topologyCheckFactories = QgsAnalysis::instance()->geometryCheckRegistry()->geometryCheckFactories( vl, QgsGeometryCheck::LayerCheck, QgsGeometryCheck::Flag::AvailableInValidation ); From b0d8d8ea96f4647f9e4a8d69346b85bba483f709 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 2 Oct 2018 13:44:55 +0200 Subject: [PATCH 078/126] Fix memory management of errors --- src/app/qgsgeometryvalidationservice.cpp | 4 ++-- src/app/qgsgeometryvalidationservice.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index fbc5cff6851..f5f0cf3f3b9 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -270,7 +270,7 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer mFeaturePools.insert( layer->id(), featurePool ); } - QList &allErrors = mLayerChecks[layer].topologyCheckErrors; + QList> &allErrors = mLayerChecks[layer].topologyCheckErrors; QMap layerIds; QgsFeatureRequest request = QgsFeatureRequest( affectedFeatureIds ).setSubsetOfAttributes( QgsAttributeList() ); @@ -306,13 +306,13 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer check->collectErrors( mFeaturePools, errors, messages, feedback, layerFeatureIds ); QgsReadWriteLocker errorLocker( mTopologyCheckLock, QgsReadWriteLocker::Write ); - allErrors.append( errors ); QList > sharedErrors; for ( QgsGeometryCheckError *error : errors ) { sharedErrors.append( std::shared_ptr( error ) ); } + allErrors.append(sharedErrors); if ( !feedback->isCanceled() ) emit topologyChecksUpdated( layer, sharedErrors ); diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index daaebe50fc7..57e69a302dc 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -96,7 +96,7 @@ class QgsGeometryValidationService : public QObject QList< QgsGeometryCheck *> topologyChecks; QFutureWatcher *topologyCheckFutureWatcher = nullptr; QList topologyCheckFeedbacks; // will be deleted when topologyCheckFutureWatcher is delteed - QList topologyCheckErrors; + QList> topologyCheckErrors; QList connections; QgsGeometryCheckContext *context = nullptr; }; From 380d02e65f0bde7fd8f6c1adb7fac5b5bfc4e22d Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 2 Oct 2018 14:00:59 +0200 Subject: [PATCH 079/126] Try to show fixed icons --- src/app/qgsgeometryvalidationmodel.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index e34a33a124a..5d644fad0e1 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -52,8 +52,15 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) switch ( role ) { + case Qt::DecorationRole: + if ( topologyError->status() == QgsGeometryCheckError::StatusFixed ) + return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmCheckGeometry.svg" ) ); + else + return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmLineIntersection.svg" ) ); + case Qt::DisplayRole: FALLTHROUGH; + case DetailsRole: { const QgsFeatureId fid = topologyError->featureId(); From fff52c7bf48d32f544b6a1a60a00ad2eabaab1c9 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Tue, 2 Oct 2018 08:39:45 -0400 Subject: [PATCH 080/126] fix icon path --- src/app/qgsgeometryvalidationmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index 5d644fad0e1..ed3dc0f95ba 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -56,7 +56,7 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) if ( topologyError->status() == QgsGeometryCheckError::StatusFixed ) return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmCheckGeometry.svg" ) ); else - return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmLineIntersection.svg" ) ); + return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmLineIntersections.svg" ) ); case Qt::DisplayRole: FALLTHROUGH; From d0d08ccf396f0860488fa1d86021fd4f4ba40e0c Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 2 Oct 2018 16:42:09 +0200 Subject: [PATCH 081/126] Improve fixError --- src/app/qgsgeometryvalidationservice.cpp | 33 +++++++++++++++++++++--- src/app/qgsgeometryvalidationservice.h | 2 +- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index f5f0cf3f3b9..3c27c987a94 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -35,11 +35,38 @@ QgsGeometryValidationService::QgsGeometryValidationService( QgsProject *project connect( project, &QgsProject::layersAdded, this, &QgsGeometryValidationService::onLayersAdded ); } -void QgsGeometryValidationService::fixError( const QgsGeometryCheckError *error, int method ) +void QgsGeometryValidationService::fixError( QgsGeometryCheckError *error, int method ) { QgsGeometryCheck::Changes changes; - QgsGeometryCheckError *nonconsterr = const_cast( error ); - error->check()->fixError( mFeaturePools, nonconsterr, method, QMap(), changes ); + error->check()->fixError( mFeaturePools, error, method, QMap(), changes ); + error->setFixed( method ); + + QgsFeaturePool *featurePool = mFeaturePools.value( error->layerId() ); + + QgsVectorLayer *layer; + + if ( featurePool ) + layer = featurePool->layer(); + else + { + // Some checks don't tell us on which layer they are because they are able to do cross-layer checks. + // E.g. the gap check will report in such a way + + for ( auto layerCheck = mLayerChecks.constBegin(); layerCheck != mLayerChecks.constEnd(); ++layerCheck ) + { + const QList> &topologyCheckErrors = layerCheck.value().topologyCheckErrors; + for ( const auto &checkError : topologyCheckErrors ) + { + if ( checkError.get() == error ) + { + layer = layerCheck.key(); + break; + } + } + } + } + + emit topologyErrorUpdated( layer, error ); } void QgsGeometryValidationService::onLayersAdded( const QList &layers ) diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index 57e69a302dc..d2b7f107bbc 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -60,7 +60,7 @@ class QgsGeometryValidationService : public QObject QgsGeometryValidationService( QgsProject *project ); ~QgsGeometryValidationService() = default; - void fixError( const QgsGeometryCheckError *error, int method ); + void fixError( QgsGeometryCheckError *error, int method ); void triggerTopologyChecks( QgsVectorLayer *layer ); From 4d40d2fe80906b960f9a2729bc69d1110e09ebe3 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 2 Oct 2018 16:43:52 +0200 Subject: [PATCH 082/126] Handle layer deleted --- src/app/qgsgeometryvalidationservice.cpp | 30 ++++++++++++++++++------ src/app/qgsgeometryvalidationservice.h | 6 +++-- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 3c27c987a94..deb19cae426 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -81,6 +81,12 @@ void QgsGeometryValidationService::onLayersAdded( const QList &la enableLayerChecks( vectorLayer ); }, Qt::UniqueConnection ); + connect( vectorLayer, &QgsVectorLayer::destroyed, this, [vectorLayer, this]() + { + cleanupLayerChecks( vectorLayer ); + mLayerChecks.remove( vectorLayer ); + }); + enableLayerChecks( vectorLayer ); } } @@ -100,8 +106,8 @@ void QgsGeometryValidationService::onGeometryChanged( QgsVectorLayer *layer, Qgs Q_UNUSED( geometry ) // It would be nice to use the geometry here for the tests. // But: - // 1. other codepaths to the checks also have no geometry (feature added / feature deleted) - // 2. and looking it up from the edit buffer (in memory) is really fast. + // 1. other codepaths to the checks also have no geometry (feature added / feature deleted) + // 2. and looking it up from the edit buffer (in memory) is really fast. // so in short: it's still a good idea, but not as important as on first thought. if ( !mLayerChecks[layer].topologyChecks.empty() ) @@ -135,9 +141,9 @@ void QgsGeometryValidationService::onBeforeCommitChanges( QgsVectorLayer *layer } } -void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) +void QgsGeometryValidationService::cleanupLayerChecks(QgsVectorLayer* layer) { - if ( layer->geometryOptions()->geometryChecks().empty() && !mLayerChecks.contains( layer ) ) + if ( !mLayerChecks.contains( layer ) ) return; VectorLayerCheckInformation &checkInformation = mLayerChecks[layer]; @@ -146,7 +152,17 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) qDeleteAll( checkInformation.singleFeatureChecks ); qDeleteAll( checkInformation.topologyChecks ); - delete checkInformation.context; + checkInformation.context.reset(); +} + +void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) +{ + if ( layer->geometryOptions()->geometryChecks().empty() && !mLayerChecks.contains( layer ) ) + return; + + VectorLayerCheckInformation &checkInformation = mLayerChecks[layer]; + + cleanupLayerChecks( layer ); if ( layer->geometryOptions()->geometryChecks().empty() ) { @@ -174,7 +190,7 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) if ( activeChecks.contains( checkId ) ) { const QVariantMap checkConfiguration = layer->geometryOptions()->checkConfiguration( checkId ); - layerChecks.append( factory->createGeometryCheck( checkInformation.context, checkConfiguration ) ); + layerChecks.append( factory->createGeometryCheck( checkInformation.context.get(), checkConfiguration ) ); } } @@ -197,7 +213,7 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) if ( activeChecks.contains( checkId ) ) { const QVariantMap checkConfiguration = layer->geometryOptions()->checkConfiguration( checkId ); - topologyChecks.append( factory->createGeometryCheck( checkInformation.context, checkConfiguration ) ); + topologyChecks.append( factory->createGeometryCheck( checkInformation.context.get(), checkConfiguration ) ); } } diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index d2b7f107bbc..4dbef71e540 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -22,6 +22,7 @@ email : matthias@opengis.ch #include #include "qgsfeature.h" +#include "qgsgeometrycheckcontext.h" class QgsProject; class QgsMapLayer; @@ -32,7 +33,6 @@ class QgsSingleGeometryCheckError; class QgsGeometryCheckError; class QgsFeedback; class QgsFeaturePool; -struct QgsGeometryCheckContext; /** * This service connects to all layers in a project and triggers validation @@ -69,6 +69,7 @@ class QgsGeometryValidationService : public QObject void geometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList> &errors ); void topologyChecksUpdated( QgsVectorLayer *layer, const QList > &errors ); void topologyChecksCleared( QgsVectorLayer *layer ); + void topologyErrorUpdated( QgsVectorLayer *layer, QgsGeometryCheckError *error ); void warning( const QString &message ); @@ -80,6 +81,7 @@ class QgsGeometryValidationService : public QObject void onBeforeCommitChanges( QgsVectorLayer *layer ); private: + void cleanupLayerChecks( QgsVectorLayer *layer ); void enableLayerChecks( QgsVectorLayer *layer ); void cancelTopologyCheck( QgsVectorLayer *layer ); @@ -98,7 +100,7 @@ class QgsGeometryValidationService : public QObject QList topologyCheckFeedbacks; // will be deleted when topologyCheckFutureWatcher is delteed QList> topologyCheckErrors; QList connections; - QgsGeometryCheckContext *context = nullptr; + std::shared_ptr context; }; QReadWriteLock mTopologyCheckLock; From a56062c50db297f7ab4edfc68c187543b9b60ab9 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 2 Oct 2018 16:44:46 +0200 Subject: [PATCH 083/126] Do not allow 0 precision --- src/app/qgsgeometryvalidationservice.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index deb19cae426..d1097963753 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -85,7 +85,7 @@ void QgsGeometryValidationService::onLayersAdded( const QList &la { cleanupLayerChecks( vectorLayer ); mLayerChecks.remove( vectorLayer ); - }); + } ); enableLayerChecks( vectorLayer ); } @@ -141,7 +141,7 @@ void QgsGeometryValidationService::onBeforeCommitChanges( QgsVectorLayer *layer } } -void QgsGeometryValidationService::cleanupLayerChecks(QgsVectorLayer* layer) +void QgsGeometryValidationService::cleanupLayerChecks( QgsVectorLayer *layer ) { if ( !mLayerChecks.contains( layer ) ) return; @@ -174,7 +174,10 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) return; } - checkInformation.context = new QgsGeometryCheckContext( log10( layer->geometryOptions()->geometryPrecision() ) * -1, mProject->crs(), mProject->transformContext() ); + int precision = log10( layer->geometryOptions()->geometryPrecision() ) * -1; + if ( precision == 0 ) + precision = 8; + checkInformation.context = qgis::make_unique( precision, mProject->crs(), mProject->transformContext() ); QList layerChecks; @@ -355,7 +358,8 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer { sharedErrors.append( std::shared_ptr( error ) ); } - allErrors.append(sharedErrors); + + allErrors.append( sharedErrors ); if ( !feedback->isCanceled() ) emit topologyChecksUpdated( layer, sharedErrors ); From 5b5ec8f59f125256c707858c54110297a15fc27e Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 2 Oct 2018 16:45:48 +0200 Subject: [PATCH 084/126] Handle error fixing on frontend size --- src/app/qgsgeometryvalidationdock.cpp | 10 ++++++++++ src/app/qgsgeometryvalidationdock.h | 1 + src/app/qgsgeometryvalidationmodel.cpp | 19 ++++++++++++++++++- src/app/qgsgeometryvalidationmodel.h | 1 + 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 22d5e90b8a2..cfa6db0e169 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -83,6 +83,7 @@ void QgsGeometryValidationDock::setGeometryValidationModel( QgsGeometryValidatio mErrorListView->setModel( mGeometryValidationModel ); connect( mErrorListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsGeometryValidationDock::onCurrentErrorChanged ); + connect( mGeometryValidationModel, &QgsGeometryValidationModel::rowsRemoved, this, &QgsGeometryValidationDock::updateCurrentError ); } void QgsGeometryValidationDock::gotoNextError() @@ -147,6 +148,15 @@ void QgsGeometryValidationDock::setGeometryValidationService( QgsGeometryValidat mGeometryValidationService = geometryValidationService; } +void QgsGeometryValidationDock::updateCurrentError() +{ + mFeatureRubberband->hide(); + mErrorRubberband->hide(); + mErrorLocationRubberband->hide(); + + onCurrentErrorChanged( currentIndex(), QModelIndex() ); +} + QModelIndex QgsGeometryValidationDock::currentIndex() const { return mErrorListView->selectionModel()->currentIndex(); diff --git a/src/app/qgsgeometryvalidationdock.h b/src/app/qgsgeometryvalidationdock.h index 2dd789387fc..223b4467d52 100644 --- a/src/app/qgsgeometryvalidationdock.h +++ b/src/app/qgsgeometryvalidationdock.h @@ -42,6 +42,7 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal void setGeometryValidationService( QgsGeometryValidationService *geometryValidationService ); private slots: + void updateCurrentError(); void onCurrentErrorChanged( const QModelIndex ¤t, const QModelIndex &previous ); void onCurrentLayerChanged( QgsMapLayer *layer ); void gotoNextError(); diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index ed3dc0f95ba..37c2f7b5c72 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -14,6 +14,7 @@ QgsGeometryValidationModel::QgsGeometryValidationModel( QgsGeometryValidationSer connect( mGeometryValidationService, &QgsGeometryValidationService::geometryCheckStarted, this, &QgsGeometryValidationModel::onGeometryCheckStarted ); connect( mGeometryValidationService, &QgsGeometryValidationService::topologyChecksUpdated, this, &QgsGeometryValidationModel::onTopologyChecksUpdated ); connect( mGeometryValidationService, &QgsGeometryValidationService::topologyChecksCleared, this, &QgsGeometryValidationModel::onTopologyChecksCleared ); + connect( mGeometryValidationService, &QgsGeometryValidationService::topologyErrorUpdated, this, &QgsGeometryValidationModel::onTopologyErrorUpdated ); } QModelIndex QgsGeometryValidationModel::index( int row, int column, const QModelIndex &parent ) const @@ -76,7 +77,6 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) { return tr( "%1: %2" ).arg( featureTitle.toString(), topologyError->description() ); } - } case FeatureExtentRole: @@ -328,6 +328,23 @@ void QgsGeometryValidationModel::onTopologyChecksCleared( QgsVectorLayer *layer } } +void QgsGeometryValidationModel::onTopologyErrorUpdated( QgsVectorLayer *layer, QgsGeometryCheckError *error ) +{ + if ( layer == mCurrentLayer ) + { + int i = 0; + for ( const auto ¤tError : qgis::as_const( mTopologyErrorStorage[layer] ) ) + { + if ( currentError.get() == error ) + { + QModelIndex idx = index( i, 0, QModelIndex() ); + emit dataChanged( idx, idx ); + } + ++i; + } + } +} + int QgsGeometryValidationModel::errorsForFeature( QgsVectorLayer *layer, QgsFeatureId fid ) { const auto &layerErrors = mErrorStorage[layer]; diff --git a/src/app/qgsgeometryvalidationmodel.h b/src/app/qgsgeometryvalidationmodel.h index b3cf0908275..96a0a4d8cb7 100644 --- a/src/app/qgsgeometryvalidationmodel.h +++ b/src/app/qgsgeometryvalidationmodel.h @@ -43,6 +43,7 @@ class QgsGeometryValidationModel : public QAbstractItemModel void onGeometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid ); void onTopologyChecksUpdated( QgsVectorLayer *layer, const QList > &errors ); void onTopologyChecksCleared( QgsVectorLayer *layer ); + void onTopologyErrorUpdated( QgsVectorLayer *layer, QgsGeometryCheckError *error ); private: struct FeatureErrors From e65f6b46a732289d17a6d7d5e94f36084ee7c23a Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Tue, 2 Oct 2018 09:01:57 -0400 Subject: [PATCH 085/126] raise dock when errors are added --- src/app/qgsgeometryvalidationdock.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index cfa6db0e169..2957471ad34 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -84,6 +84,7 @@ void QgsGeometryValidationDock::setGeometryValidationModel( QgsGeometryValidatio connect( mErrorListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsGeometryValidationDock::onCurrentErrorChanged ); connect( mGeometryValidationModel, &QgsGeometryValidationModel::rowsRemoved, this, &QgsGeometryValidationDock::updateCurrentError ); + connect( mGeometryValidationModel, &QgsGeometryValidationModel::rowsInserted, this, &QgsGeometryValidationDock::raise ); } void QgsGeometryValidationDock::gotoNextError() From 35d7fdc810f3dedd4ff61e2e3d2d8c799e72801c Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 07:10:29 +0200 Subject: [PATCH 086/126] Fix the selected error --- src/app/qgsgeometryvalidationdock.cpp | 5 +++++ src/app/qgsgeometryvalidationmodel.cpp | 3 ++- src/app/qgsgeometryvalidationservice.cpp | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 2957471ad34..ac4a12a7007 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -173,11 +173,16 @@ void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤ mPreviousButton->setEnabled( current.isValid() && current.row() > 0 ); mProblemDetailWidget->setVisible( current.isValid() ); + + if ( !current.isValid() ) + return; + mProblemDescriptionLabel->setText( current.data( QgsGeometryValidationModel::DetailsRole ).toString() ); QgsGeometryCheckError *error = current.data( QgsGeometryValidationModel::GeometryCheckErrorRole ).value(); if ( error ) { + delete mResolutionWidget->layout(); const QStringList resolutionMethods = error->check()->resolutionMethods(); QGridLayout *layout = new QGridLayout( mResolutionWidget ); int resolutionIndex = 0; diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index 37c2f7b5c72..58298089272 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -333,7 +333,8 @@ void QgsGeometryValidationModel::onTopologyErrorUpdated( QgsVectorLayer *layer, if ( layer == mCurrentLayer ) { int i = 0; - for ( const auto ¤tError : qgis::as_const( mTopologyErrorStorage[layer] ) ) + const auto &errors = mTopologyErrorStorage[layer]; + for ( const auto ¤tError : errors ) { if ( currentError.get() == error ) { diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index d1097963753..e70e2ed4d12 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -66,6 +66,8 @@ void QgsGeometryValidationService::fixError( QgsGeometryCheckError *error, int m } } + layer->triggerRepaint(); + emit topologyErrorUpdated( layer, error ); } From df11d7f19bcd7274a7f7d93aa383b80736e939ae Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 07:21:33 +0200 Subject: [PATCH 087/126] Allow committing on no errors --- src/app/qgsgeometryvalidationservice.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index e70e2ed4d12..de934bdd274 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -319,6 +319,7 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer } QList> &allErrors = mLayerChecks[layer].topologyCheckErrors; + allErrors.clear(); QMap layerIds; QgsFeatureRequest request = QgsFeatureRequest( affectedFeatureIds ).setSubsetOfAttributes( QgsAttributeList() ); From d0728ef7a38e6acdf579c6a49cbf13003540b9b2 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 07:21:45 +0200 Subject: [PATCH 088/126] Avoid crash with invalid indx --- src/app/qgsgeometryvalidationdock.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index ac4a12a7007..70dd06192a6 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -160,7 +160,10 @@ void QgsGeometryValidationDock::updateCurrentError() QModelIndex QgsGeometryValidationDock::currentIndex() const { - return mErrorListView->selectionModel()->currentIndex(); + if ( !mGeometryValidationModel->rowCount() ) + return QModelIndex(); + else + return mErrorListView->selectionModel()->currentIndex(); } void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤t, const QModelIndex &previous ) From 29b1dd741b262217fa89d3ca45b2f164e173b21e Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 07:56:06 +0200 Subject: [PATCH 089/126] More solid precision estimation --- src/app/qgsgeometryvalidationservice.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index de934bdd274..ec7884af00d 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -176,9 +176,17 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) return; } - int precision = log10( layer->geometryOptions()->geometryPrecision() ) * -1; - if ( precision == 0 ) + int precision = 0; + if ( layer->geometryOptions()->geometryPrecision() == 0 ) precision = 8; + else + { + precision = log10( layer->geometryOptions()->geometryPrecision() ) * -1; + + if ( precision < 1 || precision > 13 ) + precision = 8; + } + checkInformation.context = qgis::make_unique( precision, mProject->crs(), mProject->transformContext() ); QList layerChecks; From 8085200330bb24ee5fe8fca07f889676f88ee525 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 10:47:14 +0200 Subject: [PATCH 090/126] Avoid recursive changeGeometry --- .../vector/geometry_checker/qgsvectorlayerfeaturepool.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.cpp b/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.cpp index a6027c4c0b3..a34ab95501a 100644 --- a/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.cpp +++ b/src/analysis/vector/geometry_checker/qgsvectorlayerfeaturepool.cpp @@ -135,12 +135,13 @@ void QgsVectorLayerFeaturePool::deleteFeature( QgsFeatureId fid ) void QgsVectorLayerFeaturePool::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geometry ) { + Q_UNUSED( geometry ) + if ( isFeatureCached( fid ) ) { QgsFeature feature; getFeature( fid, feature ); - feature.setGeometry( geometry ); - updateFeature( feature ); + refreshCache( feature ); } } From 00fa3dbb4a6bd11750b299b116efdac1c5962018 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 10:47:54 +0200 Subject: [PATCH 091/126] Only show details for current error --- src/app/qgsgeometryvalidationdock.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 70dd06192a6..834acd14a3b 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -185,6 +185,9 @@ void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤ QgsGeometryCheckError *error = current.data( QgsGeometryValidationModel::GeometryCheckErrorRole ).value(); if ( error ) { + while ( QWidget *w = mResolutionWidget->findChild() ) + delete w; + delete mResolutionWidget->layout(); const QStringList resolutionMethods = error->check()->resolutionMethods(); QGridLayout *layout = new QGridLayout( mResolutionWidget ); From d17011e8e27d07d0c7988954af9fb5d8ea1be1ff Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 13:31:59 +0200 Subject: [PATCH 092/126] Show dock on error --- src/app/qgisapp.cpp | 4 ++-- src/app/qgsgeometryvalidationdock.cpp | 15 +++++++++++++-- src/app/qgsgeometryvalidationdock.h | 5 ++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index d651c98429a..504fc1bdb57 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -931,7 +931,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh { mInfoBar->pushWarning( tr( "Geometry Validation" ), message ); } ); - mGeometryValidationDock = new QgsGeometryValidationDock( tr( "Geometry Validation" ), mMapCanvas ); + mGeometryValidationDock = new QgsGeometryValidationDock( tr( "Geometry Validation" ), mMapCanvas, this ); mGeometryValidationModel = new QgsGeometryValidationModel( mGeometryValidationService.get(), mGeometryValidationDock ); connect( this, &QgisApp::activeLayerChanged, mGeometryValidationModel, [this]( QgsMapLayer * layer ) { @@ -939,7 +939,6 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh } ); mGeometryValidationDock->setGeometryValidationModel( mGeometryValidationModel ); mGeometryValidationDock->setGeometryValidationService( mGeometryValidationService.get() ); - addDockWidget( Qt::RightDockWidgetArea, mGeometryValidationDock ); endProfile(); QgsApplication::annotationRegistry()->addAnnotationType( QgsAnnotationMetadata( QStringLiteral( "FormAnnotationItem" ), &QgsFormAnnotation::create ) ); @@ -1442,6 +1441,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh { mCentralContainer->setCurrentIndex( 0 ); } ); + mGeometryValidationDock->close(); } // QgisApp ctor QgisApp::QgisApp() diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 834acd14a3b..fb57714b2b0 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -28,11 +28,13 @@ email : matthias@opengis.ch #include "qgsgeometrycheckregistry.h" #include "qgsgeometryoptions.h" #include "qgsgeometrycheckfactory.h" +#include "qgisapp.h" -QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsMapCanvas *mapCanvas, QWidget *parent, Qt::WindowFlags flags ) +QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsMapCanvas *mapCanvas, QgisApp *parent, Qt::WindowFlags flags ) : QgsDockWidget( title, parent, flags ) , mMapCanvas( mapCanvas ) + , mApp( parent ) { setupUi( this ); @@ -84,7 +86,7 @@ void QgsGeometryValidationDock::setGeometryValidationModel( QgsGeometryValidatio connect( mErrorListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsGeometryValidationDock::onCurrentErrorChanged ); connect( mGeometryValidationModel, &QgsGeometryValidationModel::rowsRemoved, this, &QgsGeometryValidationDock::updateCurrentError ); - connect( mGeometryValidationModel, &QgsGeometryValidationModel::rowsInserted, this, &QgsGeometryValidationDock::raise ); + connect( mGeometryValidationModel, &QgsGeometryValidationModel::rowsInserted, this, &QgsGeometryValidationDock::onRowsInserted ); } void QgsGeometryValidationDock::gotoNextError() @@ -139,6 +141,15 @@ void QgsGeometryValidationDock::updateLayerTransform() mLayerTransform = QgsCoordinateTransform( mMapCanvas->currentLayer()->crs(), mMapCanvas->mapSettings().destinationCrs(), mMapCanvas->mapSettings().transformContext() ); } +void QgsGeometryValidationDock::onRowsInserted() +{ + if ( !isVisible() ) + { + mApp->addDockWidget( Qt::RightDockWidgetArea, this ); + } + raise(); +} + QgsGeometryValidationService *QgsGeometryValidationDock::geometryValidationService() const { return mGeometryValidationService; diff --git a/src/app/qgsgeometryvalidationdock.h b/src/app/qgsgeometryvalidationdock.h index 223b4467d52..c8246514998 100644 --- a/src/app/qgsgeometryvalidationdock.h +++ b/src/app/qgsgeometryvalidationdock.h @@ -24,6 +24,7 @@ class QgsMapCanvas; class QgsGeometryValidationModel; class QgsGeometryValidationService; class QgsRubberBand; +class QgisApp; /** * @brief The QgsGeometryValidationDock class @@ -33,7 +34,7 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal Q_OBJECT public: - QgsGeometryValidationDock( const QString &title, QgsMapCanvas *mapCanvas, QWidget *parent = nullptr, Qt::WindowFlags flags = nullptr ); + QgsGeometryValidationDock( const QString &title, QgsMapCanvas *mapCanvas, QgisApp *parent = nullptr, Qt::WindowFlags flags = nullptr ); QgsGeometryValidationModel *geometryValidationModel() const; void setGeometryValidationModel( QgsGeometryValidationModel *geometryValidationModel ); @@ -51,6 +52,7 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal void zoomToFeature(); void triggerTopologyChecks(); void updateLayerTransform(); + void onRowsInserted(); private: @@ -67,6 +69,7 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal QgsGeometryValidationService *mGeometryValidationService = nullptr; QButtonGroup *mZoomToButtonGroup = nullptr; QgsMapCanvas *mMapCanvas = nullptr; + QgisApp *mApp = nullptr; QgsCoordinateTransform mLayerTransform; QModelIndex currentIndex() const; QgsRubberBand *mFeatureRubberband = nullptr; From 7f142ad2b644d09f7da3d173f115cd41d4b9da84 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 13:33:25 +0200 Subject: [PATCH 093/126] Clear errors on stop editing --- src/app/qgsgeometryvalidationdock.cpp | 55 ++++++++++++++++-------- src/app/qgsgeometryvalidationdock.h | 1 + src/app/qgsgeometryvalidationservice.cpp | 23 +++++++++- src/app/qgsgeometryvalidationservice.h | 3 ++ 4 files changed, 63 insertions(+), 19 deletions(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index fb57714b2b0..7aca6f22f78 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -85,6 +85,7 @@ void QgsGeometryValidationDock::setGeometryValidationModel( QgsGeometryValidatio mErrorListView->setModel( mGeometryValidationModel ); connect( mErrorListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsGeometryValidationDock::onCurrentErrorChanged ); + connect( mGeometryValidationModel, &QgsGeometryValidationModel::dataChanged, this, &QgsGeometryValidationDock::onDataChanged ); connect( mGeometryValidationModel, &QgsGeometryValidationModel::rowsRemoved, this, &QgsGeometryValidationDock::updateCurrentError ); connect( mGeometryValidationModel, &QgsGeometryValidationModel::rowsInserted, this, &QgsGeometryValidationDock::onRowsInserted ); } @@ -141,6 +142,15 @@ void QgsGeometryValidationDock::updateLayerTransform() mLayerTransform = QgsCoordinateTransform( mMapCanvas->currentLayer()->crs(), mMapCanvas->mapSettings().destinationCrs(), mMapCanvas->mapSettings().transformContext() ); } +void QgsGeometryValidationDock::onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles ) +{ + Q_UNUSED( bottomRight ) + Q_UNUSED( roles ) + + if ( currentIndex() == topLeft ) + updateCurrentError(); +} + void QgsGeometryValidationDock::onRowsInserted() { if ( !isVisible() ) @@ -162,9 +172,15 @@ void QgsGeometryValidationDock::setGeometryValidationService( QgsGeometryValidat void QgsGeometryValidationDock::updateCurrentError() { + if ( mGeometryValidationModel->rowCount() == 0 ) + mErrorListView->selectionModel()->clearCurrentIndex(); + mFeatureRubberband->hide(); + mFeatureRubberband->update(); mErrorRubberband->hide(); + mErrorRubberband->update(); mErrorLocationRubberband->hide(); + mErrorLocationRubberband->update(); onCurrentErrorChanged( currentIndex(), QModelIndex() ); } @@ -200,31 +216,36 @@ void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤ delete w; delete mResolutionWidget->layout(); - const QStringList resolutionMethods = error->check()->resolutionMethods(); - QGridLayout *layout = new QGridLayout( mResolutionWidget ); - int resolutionIndex = 0; - for ( const QString &resolutionMethod : resolutionMethods ) + + if ( error->status() != QgsGeometryCheckError::StatusFixed ) { - QToolButton *resolveBtn = new QToolButton( mResolutionWidget ); - resolveBtn->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmCheckGeometry.svg" ) ) ); - layout->addWidget( resolveBtn, resolutionIndex, 0 ); - QLabel *resolveLabel = new QLabel( resolutionMethod, mResolutionWidget ); - resolveLabel->setWordWrap( true ); - layout->addWidget( resolveLabel, resolutionIndex, 1 ); - connect( resolveBtn, &QToolButton::clicked, this, [resolutionIndex, error, this]() + const QStringList resolutionMethods = error->check()->resolutionMethods(); + QGridLayout *layout = new QGridLayout( mResolutionWidget ); + int resolutionIndex = 0; + for ( const QString &resolutionMethod : resolutionMethods ) { - mGeometryValidationService->fixError( error, resolutionIndex ); - } ); - resolutionIndex++; + QToolButton *resolveBtn = new QToolButton( mResolutionWidget ); + resolveBtn->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmCheckGeometry.svg" ) ) ); + layout->addWidget( resolveBtn, resolutionIndex, 0 ); + QLabel *resolveLabel = new QLabel( resolutionMethod, mResolutionWidget ); + resolveLabel->setWordWrap( true ); + layout->addWidget( resolveLabel, resolutionIndex, 1 ); + connect( resolveBtn, &QToolButton::clicked, this, [resolutionIndex, error, this]() + { + mGeometryValidationService->fixError( error, resolutionIndex ); + } ); + resolutionIndex++; + } + + mResolutionWidget->setLayout( layout ); + + showHighlight( current ); } - mResolutionWidget->setLayout( layout ); } bool hasFeature = !FID_IS_NULL( current.data( QgsGeometryValidationModel::ErrorFeatureIdRole ) ); mZoomToFeatureButton->setEnabled( hasFeature ); - showHighlight( current ); - switch ( mLastZoomToAction ) { case ZoomToProblem: diff --git a/src/app/qgsgeometryvalidationdock.h b/src/app/qgsgeometryvalidationdock.h index c8246514998..6a3fa78b033 100644 --- a/src/app/qgsgeometryvalidationdock.h +++ b/src/app/qgsgeometryvalidationdock.h @@ -52,6 +52,7 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal void zoomToFeature(); void triggerTopologyChecks(); void updateLayerTransform(); + void onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles ); void onRowsInserted(); private: diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index ec7884af00d..767ae48c108 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -143,6 +143,12 @@ void QgsGeometryValidationService::onBeforeCommitChanges( QgsVectorLayer *layer } } +void QgsGeometryValidationService::onEditingStopped( QgsVectorLayer *layer ) +{ + cancelTopologyCheck( layer ); + clearTopologyChecks( layer ); +} + void QgsGeometryValidationService::cleanupLayerChecks( QgsVectorLayer *layer ) { if ( !mLayerChecks.contains( layer ) ) @@ -256,6 +262,11 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) { onBeforeCommitChanges( layer ); }, Qt::UniqueConnection ); + checkInformation.connections + << connect( layer, &QgsVectorLayer::editingStopped, this, [this, layer]() + { + onEditingStopped( layer ); + }, Qt::UniqueConnection ); } void QgsGeometryValidationService::cancelTopologyCheck( QgsVectorLayer *layer ) @@ -279,6 +290,14 @@ void QgsGeometryValidationService::cancelTopologyCheck( QgsVectorLayer *layer ) } } +void QgsGeometryValidationService::clearTopologyChecks( QgsVectorLayer *layer ) +{ + QList> &allErrors = mLayerChecks[layer].topologyCheckErrors; + allErrors.clear(); + + emit topologyChecksCleared( layer ); +} + void QgsGeometryValidationService::invalidateTopologyChecks( QgsVectorLayer *layer ) { cancelTopologyCheck( layer ); @@ -309,8 +328,8 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer ) { - emit topologyChecksCleared( layer ); cancelTopologyCheck( layer ); + clearTopologyChecks( layer ); QgsFeatureIds affectedFeatureIds; if ( layer->editBuffer() ) @@ -327,7 +346,7 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer } QList> &allErrors = mLayerChecks[layer].topologyCheckErrors; - allErrors.clear(); + QMap layerIds; QgsFeatureRequest request = QgsFeatureRequest( affectedFeatureIds ).setSubsetOfAttributes( QgsAttributeList() ); diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index 4dbef71e540..2ac522fd764 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -79,6 +79,7 @@ class QgsGeometryValidationService : public QObject void onGeometryChanged( QgsVectorLayer *layer, QgsFeatureId fid, const QgsGeometry &geometry ); void onFeatureDeleted( QgsVectorLayer *layer, QgsFeatureId fid ); void onBeforeCommitChanges( QgsVectorLayer *layer ); + void onEditingStopped( QgsVectorLayer *layer ); private: void cleanupLayerChecks( QgsVectorLayer *layer ); @@ -86,6 +87,8 @@ class QgsGeometryValidationService : public QObject void cancelTopologyCheck( QgsVectorLayer *layer ); + void clearTopologyChecks( QgsVectorLayer *layer ); + void invalidateTopologyChecks( QgsVectorLayer *layer ); void processFeature( QgsVectorLayer *layer, QgsFeatureId fid ); From 417fa068b74c1b73199601daea18153b964b73f9 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 13:57:27 +0200 Subject: [PATCH 094/126] Only insert rows if errors is not empty --- src/app/qgsgeometryvalidationmodel.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index 58298089272..ebbed6fd7e0 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -295,6 +295,9 @@ void QgsGeometryValidationModel::onGeometryCheckStarted( QgsVectorLayer *layer, void QgsGeometryValidationModel::onTopologyChecksUpdated( QgsVectorLayer *layer, const QList > &errors ) { + if ( errors.empty() ) + return; + auto &topologyLayerErrors = mTopologyErrorStorage[layer]; if ( layer == currentLayer() ) From d527fed10aaa21de9ff6274fa84892ef6df6e0f0 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 13:57:46 +0200 Subject: [PATCH 095/126] Autosave after successful check --- src/app/qgsgeometryvalidationservice.cpp | 13 ++++++++++++- src/app/qgsgeometryvalidationservice.h | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 767ae48c108..0dcbb9aea4d 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -139,6 +139,9 @@ void QgsGeometryValidationService::onBeforeCommitChanges( QgsVectorLayer *layer { emit warning( tr( "Can not save yet, we'll need to run some topology checks on your dataset first..." ) ); } + + mLayerChecks[layer].commitPending = true; + triggerTopologyChecks( layer ); } } @@ -193,7 +196,7 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) precision = 8; } - checkInformation.context = qgis::make_unique( precision, mProject->crs(), mProject->transformContext() ); + checkInformation.context = qgis::make_unique( 10, mProject->crs(), mProject->transformContext() ); QList layerChecks; @@ -311,6 +314,7 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea QgsFeature feature = layer->getFeature( fid ); const auto &checks = mLayerChecks[layer].singleFeatureChecks; + mLayerChecks[layer].singleFeatureCheckErrors.remove( fid ); // The errors are going to be sent out via a signal. We cannot keep ownership in here (or can we?) // nor can we be sure that a single slot is connected to the signal. So make it a shared_ptr. @@ -323,6 +327,9 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea allErrors.append( std::shared_ptr( error ) ); } + if ( !allErrors.empty() ) + mLayerChecks[layer].singleFeatureCheckErrors.insert( fid, allErrors ); + emit geometryCheckCompleted( layer, fid, allErrors ); } @@ -408,6 +415,10 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer futureWatcher->deleteLater(); if ( mLayerChecks[layer].topologyCheckFutureWatcher == futureWatcher ) mLayerChecks[layer].topologyCheckFutureWatcher = nullptr; + + if ( allErrors.empty() && !mLayerChecks[layer].singleFeatureCheckErrors.empty() && mLayerChecks[layer].commitPending ) + layer->commitChanges(); + mLayerChecks[layer].commitPending = false; } ); mLayerChecks[layer].topologyCheckFutureWatcher = futureWatcher; diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index 2ac522fd764..dde38efae79 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -98,12 +98,14 @@ class QgsGeometryValidationService : public QObject struct VectorLayerCheckInformation { QList< QgsSingleGeometryCheck * > singleFeatureChecks; + QMap > > singleFeatureCheckErrors; QList< QgsGeometryCheck *> topologyChecks; QFutureWatcher *topologyCheckFutureWatcher = nullptr; QList topologyCheckFeedbacks; // will be deleted when topologyCheckFutureWatcher is delteed QList> topologyCheckErrors; QList connections; std::shared_ptr context; + bool commitPending = false; }; QReadWriteLock mTopologyCheckLock; From e9e849a8e1a9212ec2a0d2c8fca6fcf348635715 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Wed, 3 Oct 2018 07:54:50 -0400 Subject: [PATCH 096/126] disable topology check button if layer not editable --- src/app/qgsgeometryvalidationdock.cpp | 32 ++++++++++++++++++++++----- src/app/qgsgeometryvalidationdock.h | 3 +++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 7aca6f22f78..b230093a22a 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -260,13 +260,33 @@ void QgsGeometryValidationDock::onCurrentErrorChanged( const QModelIndex ¤ void QgsGeometryValidationDock::onCurrentLayerChanged( QgsMapLayer *layer ) { - // activate icon - bool enabled = false; - QgsVectorLayer *vl = qobject_cast( layer ); - if ( vl && vl->isSpatial() ) + if ( layer == mCurrentLayer ) + return; + + if ( mCurrentLayer ) { - const QList topologyCheckFactories = QgsAnalysis::instance()->geometryCheckRegistry()->geometryCheckFactories( vl, QgsGeometryCheck::LayerCheck, QgsGeometryCheck::Flag::AvailableInValidation ); - const QStringList activeChecks = vl->geometryOptions()->geometryChecks(); + disconnect( mCurrentLayer, &QgsVectorLayer::editingStarted, this, &QgsGeometryValidationDock::onLayerEditingStatusChanged ); + disconnect( mCurrentLayer, &QgsVectorLayer::editingStopped, this, &QgsGeometryValidationDock::onLayerEditingStatusChanged ); + } + + mCurrentLayer = qobject_cast( layer ); + + if ( mCurrentLayer ) + { + connect( mCurrentLayer, &QgsVectorLayer::editingStarted, this, &QgsGeometryValidationDock::onLayerEditingStatusChanged ); + connect( mCurrentLayer, &QgsVectorLayer::editingStopped, this, &QgsGeometryValidationDock::onLayerEditingStatusChanged ); + } + + onLayerEditingStatusChanged(); +} + +void QgsGeometryValidationDock::onLayerEditingStatusChanged() +{ + bool enabled = false; + if ( mCurrentLayer && mCurrentLayer->isSpatial() && mCurrentLayer->isEditable() ) + { + const QList topologyCheckFactories = QgsAnalysis::instance()->geometryCheckRegistry()->geometryCheckFactories( mCurrentLayer, QgsGeometryCheck::LayerCheck, QgsGeometryCheck::Flag::AvailableInValidation ); + const QStringList activeChecks = mCurrentLayer->geometryOptions()->geometryChecks(); for ( const QgsGeometryCheckFactory *factory : topologyCheckFactories ) { if ( activeChecks.contains( factory->id() ) ) diff --git a/src/app/qgsgeometryvalidationdock.h b/src/app/qgsgeometryvalidationdock.h index 6a3fa78b033..914b785f7b1 100644 --- a/src/app/qgsgeometryvalidationdock.h +++ b/src/app/qgsgeometryvalidationdock.h @@ -25,6 +25,7 @@ class QgsGeometryValidationModel; class QgsGeometryValidationService; class QgsRubberBand; class QgisApp; +class QgsVectorLayer; /** * @brief The QgsGeometryValidationDock class @@ -46,6 +47,7 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal void updateCurrentError(); void onCurrentErrorChanged( const QModelIndex ¤t, const QModelIndex &previous ); void onCurrentLayerChanged( QgsMapLayer *layer ); + void onLayerEditingStatusChanged(); void gotoNextError(); void gotoPreviousError(); void zoomToProblem(); @@ -76,6 +78,7 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal QgsRubberBand *mFeatureRubberband = nullptr; QgsRubberBand *mErrorRubberband = nullptr; QgsRubberBand *mErrorLocationRubberband = nullptr; + QgsVectorLayer *mCurrentLayer; }; #endif // QGSGEOMETRYVALIDATIONPANEL_H From 5942cf77cc6d6172d28ffba539c348ecebc3cb43 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Wed, 3 Oct 2018 08:17:42 -0400 Subject: [PATCH 097/126] fix bold font --- src/app/qgsgeometryvalidationdock.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index b230093a22a..8187f9f0a16 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -38,9 +38,7 @@ QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsM { setupUi( this ); - QFont font = mProblemDescriptionLabel->font(); - font.setBold( true ); - mProblemDescriptionLabel->setFont( font ); + mProblemDescriptionLabel->setStyleSheet( QStringLiteral( "font: bold" ) ); mErrorListView->setAlternatingRowColors( true ); connect( mNextButton, &QToolButton::clicked, this, &QgsGeometryValidationDock::gotoNextError ); From c0088f73057b66c463571695ee37d9113676bd5a Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 14:34:35 +0200 Subject: [PATCH 098/126] Correctly invalidate single geometry results on delete --- src/app/qgsgeometryvalidationservice.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 0dcbb9aea4d..881f4718657 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -127,6 +127,8 @@ void QgsGeometryValidationService::onFeatureDeleted( QgsVectorLayer *layer, QgsF invalidateTopologyChecks( layer ); } + mLayerChecks[layer].singleFeatureCheckErrors.remove( fid ); + // There should be no geometry errors on an inexistent feature, right? emit geometryCheckCompleted( layer, fid, QList>() ); } From f64b1f1174f01682d34bae3d450390b9ffae52ec Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 14:34:49 +0200 Subject: [PATCH 099/126] Better feedback for the user --- src/app/qgisapp.cpp | 5 +--- src/app/qgsgeometryvalidationservice.cpp | 31 ++++++++++++++++++++++-- src/app/qgsgeometryvalidationservice.h | 13 ++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 504fc1bdb57..63e3890d81a 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -927,10 +927,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh startProfile( QStringLiteral( "Geometry validation" ) ); mGeometryValidationService = qgis::make_unique( QgsProject::instance() ); - connect( mGeometryValidationService.get(), &QgsGeometryValidationService::warning, this, [this]( const QString & message ) - { - mInfoBar->pushWarning( tr( "Geometry Validation" ), message ); - } ); + mGeometryValidationService->setMessageBar( mInfoBar ); mGeometryValidationDock = new QgsGeometryValidationDock( tr( "Geometry Validation" ), mMapCanvas, this ); mGeometryValidationModel = new QgsGeometryValidationModel( mGeometryValidationService.get(), mGeometryValidationDock ); connect( this, &QgisApp::activeLayerChanged, mGeometryValidationModel, [this]( QgsMapLayer * layer ) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 881f4718657..a665861dcd2 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -25,6 +25,8 @@ email : matthias@opengis.ch #include "qgsvectorlayerfeaturepool.h" #include "qgsfeedback.h" #include "qgsreadwritelocker.h" +#include "qgsmessagebar.h" +#include "qgsmessagebaritem.h" #include #include @@ -139,7 +141,7 @@ void QgsGeometryValidationService::onBeforeCommitChanges( QgsVectorLayer *layer { if ( !layer->allowCommit() ) { - emit warning( tr( "Can not save yet, we'll need to run some topology checks on your dataset first..." ) ); + showMessage( tr( "Running geometry validation checks before saving ..." ) ); } mLayerChecks[layer].commitPending = true; @@ -154,6 +156,14 @@ void QgsGeometryValidationService::onEditingStopped( QgsVectorLayer *layer ) clearTopologyChecks( layer ); } +void QgsGeometryValidationService::showMessage( const QString &message ) +{ + mMessageBar->popWidget( mMessageBarItem ); + mMessageBarItem = QgsMessageBar::createMessage( tr( "Geometry Validation" ), message ); + mMessageBarItem->setDuration( 5 ); + mMessageBar->pushItem( mMessageBarItem ); +} + void QgsGeometryValidationService::cleanupLayerChecks( QgsVectorLayer *layer ) { if ( !mLayerChecks.contains( layer ) ) @@ -335,6 +345,11 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea emit geometryCheckCompleted( layer, fid, allErrors ); } +void QgsGeometryValidationService::setMessageBar( QgsMessageBar *messageBar ) +{ + mMessageBar = messageBar; +} + void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer ) { cancelTopologyCheck( layer ); @@ -418,8 +433,20 @@ void QgsGeometryValidationService::triggerTopologyChecks( QgsVectorLayer *layer if ( mLayerChecks[layer].topologyCheckFutureWatcher == futureWatcher ) mLayerChecks[layer].topologyCheckFutureWatcher = nullptr; - if ( allErrors.empty() && !mLayerChecks[layer].singleFeatureCheckErrors.empty() && mLayerChecks[layer].commitPending ) + if ( !allErrors.empty() || !mLayerChecks[layer].singleFeatureCheckErrors.empty() ) + { + if ( mLayerChecks[layer].commitPending ) + showMessage( tr( "Geometry errors have been found. Please fix the errors before saving the layer." ) ); + else + showMessage( tr( "Geometry errors have been found." ) ); + } + if ( allErrors.empty() && mLayerChecks[layer].singleFeatureCheckErrors.empty() && mLayerChecks[layer].commitPending ) + { layer->commitChanges(); + mMessageBar->popWidget( mMessageBarItem ); + mMessageBarItem = nullptr; + } + mLayerChecks[layer].commitPending = false; } ); diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index dde38efae79..aecd93cbc2d 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -33,10 +33,16 @@ class QgsSingleGeometryCheckError; class QgsGeometryCheckError; class QgsFeedback; class QgsFeaturePool; +class QgsMessageBar; +class QgsMessageBarItem; /** * This service connects to all layers in a project and triggers validation * of features whenever they are edited. + * It is responsible for executing validation checks and sending out signals + * upon failure and success. + * It will also make sure, that a layer can only be saved, if all errors have + * been resolved. */ class QgsGeometryValidationService : public QObject { @@ -64,6 +70,8 @@ class QgsGeometryValidationService : public QObject void triggerTopologyChecks( QgsVectorLayer *layer ); + void setMessageBar( QgsMessageBar *messageBar ); + signals: void geometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid ); void geometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList> &errors ); @@ -72,6 +80,7 @@ class QgsGeometryValidationService : public QObject void topologyErrorUpdated( QgsVectorLayer *layer, QgsGeometryCheckError *error ); void warning( const QString &message ); + void clearWarning(); private slots: void onLayersAdded( const QList &layers ); @@ -82,6 +91,7 @@ class QgsGeometryValidationService : public QObject void onEditingStopped( QgsVectorLayer *layer ); private: + void showMessage( const QString &message ); void cleanupLayerChecks( QgsVectorLayer *layer ); void enableLayerChecks( QgsVectorLayer *layer ); @@ -111,6 +121,9 @@ class QgsGeometryValidationService : public QObject QReadWriteLock mTopologyCheckLock; QHash mLayerChecks; QMap mFeaturePools; + QgsMessageBar *mMessageBar = nullptr; + QgsMessageBarItem *mMessageBarItem = nullptr; + }; #endif // QGSGEOMETRYVALIDATIONSERVICE_H From df44275e865e6b762b7703954100812f9c92600a Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 19:54:57 +0200 Subject: [PATCH 100/126] Update core sip files --- python/core/core_auto.sip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index 53f6d59ec5a..a5435310e34 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -54,7 +54,6 @@ %Include auto_generated/qgsfields.sip %Include auto_generated/qgsfileutils.sip %Include auto_generated/qgsfontutils.sip -%Include auto_generated/qgsgeometryoptions.sip %Include auto_generated/qgsgeometrysimplifier.sip %Include auto_generated/qgshistogram.sip %Include auto_generated/qgshstoreutils.sip @@ -327,6 +326,7 @@ %Include auto_generated/qgsfieldproxymodel.sip %Include auto_generated/qgsfiledownloader.sip %Include auto_generated/qgsfeaturefiltermodel.sip +%Include auto_generated/qgsgeometryoptions.sip %Include auto_generated/qgsgeometryvalidator.sip %Include auto_generated/qgsgml.sip %Include auto_generated/qgsgmlschema.sip From 178ff9c174c06415ba40144a0b611bb37512691a Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 19:56:16 +0200 Subject: [PATCH 101/126] Fix docstring --- src/analysis/vector/geometry_checker/qgsfeaturepool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analysis/vector/geometry_checker/qgsfeaturepool.h b/src/analysis/vector/geometry_checker/qgsfeaturepool.h index f80a0036ff2..7c19a09a718 100644 --- a/src/analysis/vector/geometry_checker/qgsfeaturepool.h +++ b/src/analysis/vector/geometry_checker/qgsfeaturepool.h @@ -148,8 +148,8 @@ class ANALYSIS_EXPORT QgsFeaturePool : public QgsFeatureSink SIP_ABSTRACT /** * Checks if the feature \a fid is cached. * - * \since QGIS 3.4 * \note not available in Python bindings + * \since QGIS 3.4 */ bool isFeatureCached( QgsFeatureId fid ) SIP_SKIP; From e70a15b355c4b1232af485f6e22d5adf55375a51 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 19:56:43 +0200 Subject: [PATCH 102/126] Fix typo --- src/ui/qgsgeometryvalidationdockbase.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/qgsgeometryvalidationdockbase.ui b/src/ui/qgsgeometryvalidationdockbase.ui index cf41639d816..058fba2a5f8 100644 --- a/src/ui/qgsgeometryvalidationdockbase.ui +++ b/src/ui/qgsgeometryvalidationdockbase.ui @@ -139,7 +139,7 @@ - Detailed Desctiption + Detailed Description true From d0de3d3811bc5708eecfaf404077165613ad117a Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 19:59:18 +0200 Subject: [PATCH 103/126] Fix license headers --- src/app/qgsgeometryvalidationmodel.cpp | 15 +++++++++++++++ src/app/qgsgeometryvalidationmodel.h | 14 ++++++++++++++ src/core/qgsgeometrycheckqueue.cpp | 6 ------ src/core/qgsgeometrycheckqueue.h | 15 --------------- 4 files changed, 29 insertions(+), 21 deletions(-) delete mode 100644 src/core/qgsgeometrycheckqueue.cpp delete mode 100644 src/core/qgsgeometrycheckqueue.h diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index ebbed6fd7e0..756c303a118 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -1,3 +1,18 @@ +/*************************************************************************** + qgsgeometryvalidationmodel.cpp + -------------------------------------- +Date : 6.9.2018 +Copyright : (C) 2018 by Matthias Kuhn +email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + #include "qgsgeometryvalidationmodel.h" #include "qgsvectorlayer.h" diff --git a/src/app/qgsgeometryvalidationmodel.h b/src/app/qgsgeometryvalidationmodel.h index 96a0a4d8cb7..297514878e0 100644 --- a/src/app/qgsgeometryvalidationmodel.h +++ b/src/app/qgsgeometryvalidationmodel.h @@ -1,3 +1,17 @@ +/*************************************************************************** + qgsgeometryvalidationmodel.h + -------------------------------------- +Date : 6.9.2018 +Copyright : (C) 2018 by Matthias Kuhn +email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ #ifndef QGSGEOMETRYVALIDATIONMODEL_H #define QGSGEOMETRYVALIDATIONMODEL_H diff --git a/src/core/qgsgeometrycheckqueue.cpp b/src/core/qgsgeometrycheckqueue.cpp deleted file mode 100644 index e9b9573f80f..00000000000 --- a/src/core/qgsgeometrycheckqueue.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "qgsgeometrycheckqueue.h" - -QgsGeometryCheckQueue::QgsGeometryCheckQueue() -{ - -} diff --git a/src/core/qgsgeometrycheckqueue.h b/src/core/qgsgeometrycheckqueue.h deleted file mode 100644 index 0f5fc0da47b..00000000000 --- a/src/core/qgsgeometrycheckqueue.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef QGSGEOMETRYCHECKQUEUE_H -#define QGSGEOMETRYCHECKQUEUE_H - -#include - -class QgsGeometryCheckQueue -{ - public: - QgsGeometryCheckQueue(); - - private: - -}; - -#endif // QGSGEOMETRYCHECKQUEUE_H From b4626fd80be30b55c6f05bef6729425ceb64f8d6 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 3 Oct 2018 21:30:27 +0200 Subject: [PATCH 104/126] Silly, Mapp is definitely a speilling error --- src/app/qgsgeometryvalidationdock.cpp | 4 ++-- src/app/qgsgeometryvalidationdock.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 8187f9f0a16..49ce5c76f13 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -34,7 +34,7 @@ email : matthias@opengis.ch QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsMapCanvas *mapCanvas, QgisApp *parent, Qt::WindowFlags flags ) : QgsDockWidget( title, parent, flags ) , mMapCanvas( mapCanvas ) - , mApp( parent ) + , mQgisApp( parent ) { setupUi( this ); @@ -153,7 +153,7 @@ void QgsGeometryValidationDock::onRowsInserted() { if ( !isVisible() ) { - mApp->addDockWidget( Qt::RightDockWidgetArea, this ); + mQgisApp->addDockWidget( Qt::RightDockWidgetArea, this ); } raise(); } diff --git a/src/app/qgsgeometryvalidationdock.h b/src/app/qgsgeometryvalidationdock.h index 914b785f7b1..ed1cf12dc1a 100644 --- a/src/app/qgsgeometryvalidationdock.h +++ b/src/app/qgsgeometryvalidationdock.h @@ -72,7 +72,7 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal QgsGeometryValidationService *mGeometryValidationService = nullptr; QButtonGroup *mZoomToButtonGroup = nullptr; QgsMapCanvas *mMapCanvas = nullptr; - QgisApp *mApp = nullptr; + QgisApp *mQgisApp = nullptr; QgsCoordinateTransform mLayerTransform; QModelIndex currentIndex() const; QgsRubberBand *mFeatureRubberband = nullptr; From fc6f55bc01a7bd3a864c056be9f60e8f7c077cb8 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Fri, 5 Oct 2018 08:39:51 +0200 Subject: [PATCH 105/126] Initialize to nullptr --- src/app/qgsgeometryvalidationdock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/qgsgeometryvalidationdock.h b/src/app/qgsgeometryvalidationdock.h index ed1cf12dc1a..88cd1751ac6 100644 --- a/src/app/qgsgeometryvalidationdock.h +++ b/src/app/qgsgeometryvalidationdock.h @@ -78,7 +78,7 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal QgsRubberBand *mFeatureRubberband = nullptr; QgsRubberBand *mErrorRubberband = nullptr; QgsRubberBand *mErrorLocationRubberband = nullptr; - QgsVectorLayer *mCurrentLayer; + QgsVectorLayer *mCurrentLayer = nullptr; }; #endif // QGSGEOMETRYVALIDATIONPANEL_H From 1f1ec0f634b37376879703fae2a5dbe5322ff840 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Fri, 12 Oct 2018 07:35:59 -0400 Subject: [PATCH 106/126] fix dox and use metaEnum + switch to handle resolution method --- src/analysis/CMakeLists.txt | 2 +- .../geometry_checker/qgsgeometrygapcheck.cpp | 36 +++++++++++-------- .../geometry_checker/qgsgeometrygapcheck.h | 9 ++++- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt index 6508afcfa30..969eafb9871 100644 --- a/src/analysis/CMakeLists.txt +++ b/src/analysis/CMakeLists.txt @@ -187,6 +187,7 @@ SET(QGIS_ANALYSIS_MOC_HDRS vector/geometry_checker/qgsgeometrychecker.h vector/geometry_checker/qgsgeometrycheck.h vector/geometry_checker/qgsvectorlayerfeaturepool.h + vector/geometry_checker/qgsgeometrygapcheck.h ) INCLUDE_DIRECTORIES(SYSTEM ${SPATIALITE_INCLUDE_DIR}) @@ -292,7 +293,6 @@ SET(QGIS_ANALYSIS_HDRS vector/geometry_checker/qgsgeometryduplicatecheck.h vector/geometry_checker/qgsgeometryduplicatenodescheck.h vector/geometry_checker/qgsgeometryfollowboundariescheck.h - vector/geometry_checker/qgsgeometrygapcheck.h vector/geometry_checker/qgsgeometrymissingvertexcheck.h vector/geometry_checker/qgsgeometryholecheck.h vector/geometry_checker/qgsgeometrylineintersectioncheck.h diff --git a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp index d80965c4f39..535797aee46 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.cpp @@ -136,25 +136,31 @@ void QgsGeometryGapCheck::collectErrors( const QMap & void QgsGeometryGapCheck::fixError( const QMap &featurePools, QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { - if ( method == NoChange ) + QMetaEnum metaEnum = QMetaEnum::fromType(); + if ( !metaEnum.isValid() || !metaEnum.valueToKey( method ) ) { - error->setFixed( method ); - } - else if ( method == MergeLongestEdge ) - { - QString errMsg; - if ( mergeWithNeighbor( featurePools, static_cast( error ), changes, errMsg ) ) - { - error->setFixed( method ); - } - else - { - error->setFixFailed( tr( "Failed to merge with neighbor: %1" ).arg( errMsg ) ); - } + error->setFixFailed( tr( "Unknown method" ) ); } else { - error->setFixFailed( tr( "Unknown method" ) ); + ResolutionMethod methodValue = static_cast( method ); + switch ( methodValue ) + { + case NoChange: + error->setFixed( method ); + break; + case MergeLongestEdge: + QString errMsg; + if ( mergeWithNeighbor( featurePools, static_cast( error ), changes, errMsg ) ) + { + error->setFixed( method ); + } + else + { + error->setFixFailed( tr( "Failed to merge with neighbor: %1" ).arg( errMsg ) ); + } + break; + } } } diff --git a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h index 70b5db054e1..880bae69979 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrygapcheck.h @@ -76,8 +76,15 @@ class ANALYSIS_EXPORT QgsGeometryGapCheckError : public QgsGeometryCheckError class ANALYSIS_EXPORT QgsGeometryGapCheck : public QgsGeometryCheck { + Q_GADGET public: - enum ResolutionMethod { MergeLongestEdge, NoChange }; + //! Resolution methods for geometry gap checks + enum ResolutionMethod + { + MergeLongestEdge, + NoChange + }; + Q_ENUM( ResolutionMethod ) /** * The \a configuration accepts a "gapThreshold" key which specifies From 98843ca185b16b0bc7f4d93e71f955651e3feabb Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Fri, 12 Oct 2018 08:02:19 -0400 Subject: [PATCH 107/126] fix bad rebase --- src/app/qgisapp.h | 17 ++++++++++++++++- src/app/qgsvectorlayerproperties.cpp | 3 --- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index 38584202aed..fcbe0353956 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -694,6 +694,13 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow //! Returns pointer to the identify map tool - used by identify tool in 3D view QgsMapToolIdentifyAction *identifyMapTool() const { return mMapTools.mIdentify; } + /** + * Take screenshots for user documentation + * @param saveDirectory path were the screenshots will be saved + * @param categories an int as a flag value of QgsAppScreenShots::Categories + */ + void takeAppScreenShots( const QString &saveDirectory, const int categories = 0 ); + public slots: //! save current vector layer void saveAsFile( QgsMapLayer *layer = nullptr, bool onlySelected = false ); @@ -1967,6 +1974,9 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow //! Populates project "load from" / "save to" menu based on project storages (when the menu is about to be shown) void populateProjectStorageMenu( QMenu *menu, bool saving ); + //! Create the option dialog + QgsOptions *createOptionsDialog( QWidget *parent = nullptr ); + QgisAppStyleSheet *mStyleSheetBuilder = nullptr; // actions for menus and toolbars ----------------- @@ -2139,7 +2149,6 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow //! interface to QgisApp for plugins QgisAppInterface *mQgisInterface = nullptr; - friend class QgisAppInterface; QSplashScreen *mSplash = nullptr; //! list of recently opened/saved project files @@ -2305,7 +2314,13 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow //! True if we are blocking the activeLayerChanged signal from being emitted bool mBlockActiveLayerChanged = false; + std::unique_ptr mGeometryValidationService; + QgsGeometryValidationModel *mGeometryValidationModel = nullptr; + QgsGeometryValidationDock *mGeometryValidationDock = nullptr; + friend class TestQgisAppPython; + friend class QgisAppInterface; + friend class QgsAppScreenShots; }; #ifdef ANDROID diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index f4db288ede6..8857af49e9a 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -793,9 +793,6 @@ void QgsVectorLayerProperties::apply() } mLayer->geometryOptions()->setGeometryChecks( activeChecks ); - // update symbology - emit refreshLegend( mLayer->id() ); - mLayer->triggerRepaint(); // notify the project we've made a change QgsProject::instance()->setDirty( true ); From 1024c1be2cd0109c7b98bd62316866b55a1c471a Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Fri, 12 Oct 2018 09:19:45 -0400 Subject: [PATCH 108/126] fix typo --- .../geometry_checker/qgsgeometryoverlapcheck.cpp | 2 +- .../vector/geometry_checker/qgsgeometryoverlapcheck.h | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp index 4ecf92966ad..9948b2bca1c 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.cpp @@ -249,7 +249,7 @@ bool QgsGeometryOverlapCheck::factoryIsCompatible( QgsVectorLayer *layer ) SIP_S QgsGeometryOverlapCheckError::QgsGeometryOverlapCheckError( const QgsGeometryCheck *check, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, const QgsGeometry &geometry, const QgsPointXY &errorLocation, const QVariant &value, const QgsGeometryCheckerUtils::LayerFeature &overlappedFeature ) : QgsGeometryCheckError( check, layerFeature.layer()->id(), layerFeature.feature().id(), geometry, errorLocation, QgsVertexId(), value, ValueArea ) - , mOverlappedFeature( OverLappedFeature( overlappedFeature.layer(), overlappedFeature.feature().id() ) ) + , mOverlappedFeature( OverlappedFeature( overlappedFeature.layer(), overlappedFeature.feature().id() ) ) { } diff --git a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h index e7070b62fac..fcaa05d09cb 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometryoverlapcheck.h @@ -26,10 +26,10 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheckError : public QgsGeometryCheckErro { public: - struct OverLappedFeature + struct OverlappedFeature { public: - OverLappedFeature( QgsVectorLayer *vl, QgsFeatureId fid ) + OverlappedFeature( QgsVectorLayer *vl, QgsFeatureId fid ) : mLayerId( vl->id() ) , mLayerName( vl->name() ) , mFeatureId( fid ) @@ -38,7 +38,7 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheckError : public QgsGeometryCheckErro QString layerId() const {return mLayerId;} QString layerName() const {return mLayerName;} QgsFeatureId featureId() const {return mFeatureId;} - bool operator==( const OverLappedFeature &other ) const {return mLayerId == other.layerId() && mFeatureId == other.featureId();} + bool operator==( const OverlappedFeature &other ) const {return mLayerId == other.layerId() && mFeatureId == other.featureId();} private: QString mLayerId; @@ -52,7 +52,7 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheckError : public QgsGeometryCheckErro const QgsPointXY &errorLocation, const QVariant &value, const QgsGeometryCheckerUtils::LayerFeature &overlappedFeature ); - const OverLappedFeature &overlappedFeature() const { return mOverlappedFeature; } + const OverlappedFeature &overlappedFeature() const { return mOverlappedFeature; } bool isEqual( QgsGeometryCheckError *other ) const override { @@ -87,7 +87,7 @@ class ANALYSIS_EXPORT QgsGeometryOverlapCheckError : public QgsGeometryCheckErro QString description() const override; private: - OverLappedFeature mOverlappedFeature; + OverlappedFeature mOverlappedFeature; }; class ANALYSIS_EXPORT QgsGeometryOverlapCheck : public QgsGeometryCheck From 8dce0e8b9ece37a4a7ce33fa8d91adf57ead119b Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Fri, 12 Oct 2018 09:43:35 -0400 Subject: [PATCH 109/126] fetch feature without attributes --- src/app/qgsgeometryvalidationservice.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index a665861dcd2..dfb533db107 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -323,7 +323,7 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea { emit geometryCheckStarted( layer, fid ); - QgsFeature feature = layer->getFeature( fid ); + QgsGeometry geometry = layer->getGeometry( fid ); const auto &checks = mLayerChecks[layer].singleFeatureChecks; mLayerChecks[layer].singleFeatureCheckErrors.remove( fid ); @@ -333,7 +333,7 @@ void QgsGeometryValidationService::processFeature( QgsVectorLayer *layer, QgsFea QList> allErrors; for ( QgsSingleGeometryCheck *check : checks ) { - const auto errors = check->processGeometry( feature.geometry() ); + const auto errors = check->processGeometry( geometry ); for ( auto error : errors ) allErrors.append( std::shared_ptr( error ) ); From 3aebf0fe8dbb6c67fe0e2565672a73dadc308131 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Fri, 12 Oct 2018 11:01:00 -0400 Subject: [PATCH 110/126] default constructors --- src/app/qgsgeometryvalidationservice.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.h b/src/app/qgsgeometryvalidationservice.h index aecd93cbc2d..0f2d397e08a 100644 --- a/src/app/qgsgeometryvalidationservice.h +++ b/src/app/qgsgeometryvalidationservice.h @@ -51,8 +51,7 @@ class QgsGeometryValidationService : public QObject public: struct FeatureError { - FeatureError() - {} + FeatureError() = default; FeatureError( QgsFeatureId fid, QgsGeometry::Error error ) : featureId( fid ) , error( error ) @@ -64,7 +63,6 @@ class QgsGeometryValidationService : public QObject typedef QList FeatureErrors; QgsGeometryValidationService( QgsProject *project ); - ~QgsGeometryValidationService() = default; void fixError( QgsGeometryCheckError *error, int method ); From 36e83f31c03697f0c160d514a67970b373174f17 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 15 Oct 2018 13:54:07 +0200 Subject: [PATCH 111/126] Tag geometry check classes as technology preview --- .../geometry_checker/qgsfeaturepool.sip.in | 6 ++++++ .../geometry_checker/qgsgeometrycheck.sip.in | 20 ++++++++----------- .../qgsgeometrycheckerror.sip.in | 12 +++++------ .../qgsgeometrycheckerutils.sip.in | 13 ++++++------ .../qgsgeometrycheckfactory.sip.in | 8 -------- .../qgsgeometrycheckregistry.sip.in | 4 ++++ .../qgssinglegeometrycheck.sip.in | 8 ++++++++ .../vector/geometry_checker/qgsfeaturepool.h | 3 +++ .../geometry_checker/qgsgeometrycheck.h | 10 ++++++++++ .../qgsgeometrycheckcontext.h | 1 + .../geometry_checker/qgsgeometrycheckerror.h | 8 +++++++- .../qgsgeometrycheckerutils.h | 9 +++++++++ .../qgsgeometrycheckfactory.h | 8 ++++++-- .../qgsgeometrycheckregistry.h | 1 + .../geometry_checker/qgssinglegeometrycheck.h | 2 ++ 15 files changed, 77 insertions(+), 36 deletions(-) diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in index 2186cc9f41b..10e75d5ffda 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in @@ -15,6 +15,12 @@ class QgsFeaturePool : QgsFeatureSink /Abstract/ { %Docstring A feature pool is based on a vector layer and caches features. + +.. note:: + + This class is a technology preview and unstable API. + +.. versionadded:: 3.4 %End %TypeHeaderCode diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in index c4053f20da7..9e28feaba7d 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in @@ -13,20 +13,16 @@ class QgsGeometryCheck { %Docstring -************************************************************************* -qgsgeometrycheck.h ---------------------- -begin : September 2014 -copyright : (C) 2014 by Sandro Mani / Sourcepole AG -email : smani at sourcepole dot ch -************************************************************************** +This class manages all known geometry check factories. -This program is free software; you can redistribute it and/or modify * -it under the terms of the GNU General Public License as published by * -the Free Software Foundation; either version 2 of the License, or * -(at your option) any later version. * +QgsGeometryCheckRegistry is not usually directly created, but rather accessed through +:py:func:`QgsAnalysis.geometryCheckRegistry()` -************************************************************************** +.. note:: + + This class is a technology preview and unstable API. + +.. versionadded:: 3.4 %End %TypeHeaderCode diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in index e6b44f0eb99..2897170b279 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in @@ -11,18 +11,16 @@ - class QgsGeometryCheckError { %Docstring -************************************************************************* +This represents an error reported by a geometry check. -This program is free software; you can redistribute it and/or modify * -it under the terms of the GNU General Public License as published by * -the Free Software Foundation; either version 2 of the License, or * -(at your option) any later version. * +.. note:: -************************************************************************** + This class is a technology preview and unstable API. + +.. versionadded:: 3.4 %End %TypeHeaderCode diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in index 44f2ea3b4fe..abd27671a9f 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in @@ -10,17 +10,18 @@ + class QgsGeometryCheckerUtils { %Docstring -************************************************************************* -This program is free software; you can redistribute it and/or modify * -it under the terms of the GNU General Public License as published by * -the Free Software Foundation; either version 2 of the License, or * -(at your option) any later version. * +Contains utilities required for geometry checks. -************************************************************************** +.. note:: + + This class is a technology preview and unstable API. + +.. versionadded:: 3.4 %End %TypeHeaderCode diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip.in index 9fee4526fad..07f84fee124 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip.in @@ -12,14 +12,6 @@ -class QgsGeometryCheckFactory /Abstract/ -{ - -%TypeHeaderCode -#include "qgsgeometrycheckfactory.h" -%End - public: - virtual ~QgsGeometryCheckFactory(); virtual QgsGeometryCheck *createGeometryCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ) const = 0 /Factory/; diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckregistry.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckregistry.sip.in index 108d181a967..0e1c5f107c4 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckregistry.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckregistry.sip.in @@ -19,6 +19,10 @@ This class manages all known geometry check factories. QgsGeometryCheckRegistry is not usually directly created, but rather accessed through :py:func:`QgsAnalysis.geometryCheckRegistry()` +.. note:: + + This class is a technology preview and unstable API. + .. versionadded:: 3.4 %End diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgssinglegeometrycheck.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgssinglegeometrycheck.sip.in index 674c14ee0e7..9c50a9accf6 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgssinglegeometrycheck.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgssinglegeometrycheck.sip.in @@ -17,6 +17,10 @@ class QgsSingleGeometryCheckError An error from a QgsSingleGeometryCheck. +.. note:: + + This class is a technology preview and unstable API. + .. versionadded:: 3.4 %End @@ -78,6 +82,10 @@ class QgsGeometryCheckErrorSingle : QgsGeometryCheckError Wraps a QgsSingleGeometryError into a standard :py:class:`QgsGeometryCheckError`. The single error can be obtained via singleError. +.. note:: + + This class is a technology preview and unstable API. + .. versionadded:: 3.4 %End diff --git a/src/analysis/vector/geometry_checker/qgsfeaturepool.h b/src/analysis/vector/geometry_checker/qgsfeaturepool.h index 7c19a09a718..4339dc94483 100644 --- a/src/analysis/vector/geometry_checker/qgsfeaturepool.h +++ b/src/analysis/vector/geometry_checker/qgsfeaturepool.h @@ -31,6 +31,9 @@ class QgsVectorLayer; /** * \ingroup analysis * A feature pool is based on a vector layer and caches features. + * + * \note This class is a technology preview and unstable API. + * \since QGIS 3.4 */ class ANALYSIS_EXPORT QgsFeaturePool : public QgsFeatureSink SIP_ABSTRACT { diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheck.h b/src/analysis/vector/geometry_checker/qgsgeometrycheck.h index 9db94bd5b65..8b595a5dac2 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheck.h @@ -31,6 +31,16 @@ class QgsGeometryCheckError; class QgsFeaturePool; +/** + * \ingroup analysis + * This class manages all known geometry check factories. + * + * QgsGeometryCheckRegistry is not usually directly created, but rather accessed through + * QgsAnalysis::geometryCheckRegistry(). + * + * \note This class is a technology preview and unstable API. + * \since QGIS 3.4 + */ class ANALYSIS_EXPORT QgsGeometryCheck { Q_GADGET diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckcontext.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckcontext.h index 2cd5edf7e74..64b96475cf8 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckcontext.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckcontext.h @@ -24,6 +24,7 @@ /** * Base configuration for geometry checks. * + * \note This class is a technology preview and unstable API. * \since QGIS 3.4 */ struct ANALYSIS_EXPORT QgsGeometryCheckContext diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h index 9a06138877f..8dd79d7bf99 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckerror.h @@ -25,7 +25,13 @@ class QgsPointXY; - +/** + * \ingroup analysis + * This represents an error reported by a geometry check. + * + * \note This class is a technology preview and unstable API. + * \since QGIS 3.4 + */ class ANALYSIS_EXPORT QgsGeometryCheckError { public: diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h index 9d9165c8a61..e75461813a1 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckerutils.h @@ -28,6 +28,15 @@ class QgsGeometryEngine; class QgsFeaturePool; class QgsFeedback; +/** + * \ingroup analysis + * + * Contains utilities required for geometry checks. + * + * \note This class is a technology preview and unstable API. + * \since QGIS 3.4 + */ + class ANALYSIS_EXPORT QgsGeometryCheckerUtils { public: diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h index 5b000aeea1c..00988d8cbd1 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h @@ -34,8 +34,12 @@ struct QgsGeometryCheckContext; /** * \ingroup analysis - */ -class ANALYSIS_EXPORT QgsGeometryCheckFactory SIP_ABSTRACT + * + * A factory for geometry checks. + * + * \note This class is a technology preview and unstable API. + * \since QGIS 3.4 + */class ANALYSIS_EXPORT QgsGeometryCheckFactory SIP_ABSTRACT { public: diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h index d9809ff9566..0d5489a004c 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckregistry.h @@ -35,6 +35,7 @@ struct QgsGeometryCheckContext; * QgsGeometryCheckRegistry is not usually directly created, but rather accessed through * QgsAnalysis::geometryCheckRegistry(). * + * \note This class is a technology preview and unstable API. * \since QGIS 3.4 */ class ANALYSIS_EXPORT QgsGeometryCheckRegistry diff --git a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.h b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.h index fa6166967a4..461ca960559 100644 --- a/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.h +++ b/src/analysis/vector/geometry_checker/qgssinglegeometrycheck.h @@ -33,6 +33,7 @@ class QgsSingleGeometryCheck; * * An error from a QgsSingleGeometryCheck. * + * \note This class is a technology preview and unstable API. * \since QGIS 3.4 */ class ANALYSIS_EXPORT QgsSingleGeometryCheckError @@ -104,6 +105,7 @@ class ANALYSIS_EXPORT QgsSingleGeometryCheckError * Wraps a QgsSingleGeometryError into a standard QgsGeometryCheckError. * The single error can be obtained via singleError. * + * \note This class is a technology preview and unstable API. * \since QGIS 3.4 */ class ANALYSIS_EXPORT QgsGeometryCheckErrorSingle : public QgsGeometryCheckError From dfe788e9cefe4e0e42654b72f0980d924ef50b03 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 15 Oct 2018 14:04:40 +0200 Subject: [PATCH 112/126] No unique connections on lambdas --- src/app/qgsgeometryvalidationservice.cpp | 61 +++++++++++++----------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index dfb533db107..2ca073697ce 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -83,7 +83,7 @@ void QgsGeometryValidationService::onLayersAdded( const QList &la connect( vectorLayer->geometryOptions(), &QgsGeometryOptions::checkConfigurationChanged, this, [this, vectorLayer]() { enableLayerChecks( vectorLayer ); - }, Qt::UniqueConnection ); + } ); connect( vectorLayer, &QgsVectorLayer::destroyed, this, [vectorLayer, this]() { @@ -253,35 +253,38 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) checkInformation.topologyChecks = topologyChecks; - // Connect to all modifications on a layer that can introduce a geometry or topology error - // Also connect to the beforeCommitChanges signal, so we can trigger topology checks - // We keep all connections around in a list, so if in the future all checks get disabled - // we can kill those connections to be sure the layer does not even get a tiny bit of overhead. - checkInformation.connections - << connect( layer, &QgsVectorLayer::featureAdded, this, [this, layer]( QgsFeatureId fid ) + if ( checkInformation.connections.empty() ) { - onFeatureAdded( layer, fid ); - }, Qt::UniqueConnection ); - checkInformation.connections - << connect( layer, &QgsVectorLayer::geometryChanged, this, [this, layer]( QgsFeatureId fid, const QgsGeometry & geometry ) - { - onGeometryChanged( layer, fid, geometry ); - }, Qt::UniqueConnection ); - checkInformation.connections - << connect( layer, &QgsVectorLayer::featureDeleted, this, [this, layer]( QgsFeatureId fid ) - { - onFeatureDeleted( layer, fid ); - }, Qt::UniqueConnection ); - checkInformation.connections - << connect( layer, &QgsVectorLayer::beforeCommitChanges, this, [this, layer]() - { - onBeforeCommitChanges( layer ); - }, Qt::UniqueConnection ); - checkInformation.connections - << connect( layer, &QgsVectorLayer::editingStopped, this, [this, layer]() - { - onEditingStopped( layer ); - }, Qt::UniqueConnection ); + // Connect to all modifications on a layer that can introduce a geometry or topology error + // Also connect to the beforeCommitChanges signal, so we can trigger topology checks + // We keep all connections around in a list, so if in the future all checks get disabled + // we can kill those connections to be sure the layer does not even get a tiny bit of overhead. + checkInformation.connections + << connect( layer, &QgsVectorLayer::featureAdded, this, [this, layer]( QgsFeatureId fid ) + { + onFeatureAdded( layer, fid ); + } ); + checkInformation.connections + << connect( layer, &QgsVectorLayer::geometryChanged, this, [this, layer]( QgsFeatureId fid, const QgsGeometry & geometry ) + { + onGeometryChanged( layer, fid, geometry ); + } ); + checkInformation.connections + << connect( layer, &QgsVectorLayer::featureDeleted, this, [this, layer]( QgsFeatureId fid ) + { + onFeatureDeleted( layer, fid ); + } ); + checkInformation.connections + << connect( layer, &QgsVectorLayer::beforeCommitChanges, this, [this, layer]() + { + onBeforeCommitChanges( layer ); + } ); + checkInformation.connections + << connect( layer, &QgsVectorLayer::editingStopped, this, [this, layer]() + { + onEditingStopped( layer ); + } ); + } } void QgsGeometryValidationService::cancelTopologyCheck( QgsVectorLayer *layer ) From 98959f98c5b09040d03844167cc72dba22ff5e9b Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 15 Oct 2018 14:24:46 +0200 Subject: [PATCH 113/126] Improve the raise dock code --- src/app/qgsgeometryvalidationdock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 49ce5c76f13..1d5fcbd4975 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -155,7 +155,7 @@ void QgsGeometryValidationDock::onRowsInserted() { mQgisApp->addDockWidget( Qt::RightDockWidgetArea, this ); } - raise(); + setUserVisible( true ); } QgsGeometryValidationService *QgsGeometryValidationDock::geometryValidationService() const From 8ba78e267682821e004bf95774c01d4341593804 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 15 Oct 2018 14:25:13 +0200 Subject: [PATCH 114/126] Code modernisation --- src/app/qgsgeometryvalidationmodel.h | 3 +-- src/app/qgsgeometryvalidationservice.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/app/qgsgeometryvalidationmodel.h b/src/app/qgsgeometryvalidationmodel.h index 297514878e0..7000ec5622f 100644 --- a/src/app/qgsgeometryvalidationmodel.h +++ b/src/app/qgsgeometryvalidationmodel.h @@ -62,8 +62,7 @@ class QgsGeometryValidationModel : public QAbstractItemModel private: struct FeatureErrors { - FeatureErrors() - {} + FeatureErrors() = default; FeatureErrors( QgsFeatureId fid ) : fid( fid ) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 2ca073697ce..0e85037defa 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -45,7 +45,7 @@ void QgsGeometryValidationService::fixError( QgsGeometryCheckError *error, int m QgsFeaturePool *featurePool = mFeaturePools.value( error->layerId() ); - QgsVectorLayer *layer; + QgsVectorLayer *layer = nullptr; if ( featurePool ) layer = featurePool->layer(); From 5c9a0da9a681de4104cd55b9ce4f4bb729775c03 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 15 Oct 2018 14:25:28 +0200 Subject: [PATCH 115/126] Safety check --- src/app/qgsgeometryvalidationservice.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 0e85037defa..886c6a5b0bd 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -68,9 +68,12 @@ void QgsGeometryValidationService::fixError( QgsGeometryCheckError *error, int m } } - layer->triggerRepaint(); + if ( layer ) + { + layer->triggerRepaint(); - emit topologyErrorUpdated( layer, error ); + emit topologyErrorUpdated( layer, error ); + } } void QgsGeometryValidationService::onLayersAdded( const QList &layers ) From 29db8db9ba91a2234d984ac1afe0ce9a6b130854 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 15 Oct 2018 14:25:39 +0200 Subject: [PATCH 116/126] Fix typos --- src/app/qgsgeometryvalidationservice.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 886c6a5b0bd..1881f6ff98f 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -134,7 +134,7 @@ void QgsGeometryValidationService::onFeatureDeleted( QgsVectorLayer *layer, QgsF mLayerChecks[layer].singleFeatureCheckErrors.remove( fid ); - // There should be no geometry errors on an inexistent feature, right? + // There should be no geometry errors on a non-existent feature, right? emit geometryCheckCompleted( layer, fid, QList>() ); } @@ -144,7 +144,7 @@ void QgsGeometryValidationService::onBeforeCommitChanges( QgsVectorLayer *layer { if ( !layer->allowCommit() ) { - showMessage( tr( "Running geometry validation checks before saving ..." ) ); + showMessage( tr( "Running geometry validation checks before saving…" ) ); } mLayerChecks[layer].commitPending = true; From 55d3faf7f53b2b589011099a27b038e88db6e573 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 15 Oct 2018 14:34:37 +0200 Subject: [PATCH 117/126] Use switch instead of else if --- .../qgsgeometrymissingvertexcheck.cpp | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp index bd180725573..663dffd716e 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp +++ b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp @@ -72,28 +72,42 @@ void QgsGeometryMissingVertexCheck::fixError( const QMap(); + if ( !metaEnum.isValid() || !metaEnum.valueToKey( method ) ) { - error->setFixed( method ); + error->setFixFailed( tr( "Unknown method" ) ); } - if ( method == AddMissingVertex ) + else { - QgsFeaturePool *featurePool = featurePools[ error->layerId() ]; + ResolutionMethod methodValue = static_cast( method ); + switch ( methodValue ) + { + case NoChange: + error->setFixed( method ); + break; - QgsFeature feature; - featurePool->getFeature( error->featureId(), feature ); + case AddMissingVertex: + { + QgsFeaturePool *featurePool = featurePools[ error->layerId() ]; - QgsPointXY pointOnSegment; // Should be equal to location - int vertexIndex; - QgsGeometry geometry = feature.geometry(); - geometry.closestSegmentWithContext( error->location(), pointOnSegment, vertexIndex ); - geometry.insertVertex( QgsPoint( error->location() ), vertexIndex ); - feature.setGeometry( geometry ); + QgsFeature feature; + featurePool->getFeature( error->featureId(), feature ); - featurePool->updateFeature( feature ); - // TODO update "changes" structure + QgsPointXY pointOnSegment; // Should be equal to location + int vertexIndex; + QgsGeometry geometry = feature.geometry(); + geometry.closestSegmentWithContext( error->location(), pointOnSegment, vertexIndex ); + geometry.insertVertex( QgsPoint( error->location() ), vertexIndex ); + feature.setGeometry( geometry ); - error->setFixed( method ); + featurePool->updateFeature( feature ); + // TODO update "changes" structure + + error->setFixed( method ); + } + break; + } } } From 190ee53bfba83993535e38377860bf5ae3648a52 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 15 Oct 2018 14:51:50 +0200 Subject: [PATCH 118/126] Use enum instead of int --- src/analysis/CMakeLists.txt | 3 +-- .../vector/geometry_checker/qgsgeometrymissingvertexcheck.h | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt index 969eafb9871..b2d57f2b59b 100644 --- a/src/analysis/CMakeLists.txt +++ b/src/analysis/CMakeLists.txt @@ -152,7 +152,6 @@ SET(QGIS_ANALYSIS_SRCS vector/geometry_checker/qgsgeometryduplicatecheck.cpp vector/geometry_checker/qgsgeometryduplicatenodescheck.cpp vector/geometry_checker/qgsgeometryfollowboundariescheck.cpp - vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp vector/geometry_checker/qgsgeometrygapcheck.cpp vector/geometry_checker/qgsgeometrymissingvertexcheck.cpp vector/geometry_checker/qgsgeometryholecheck.cpp @@ -188,6 +187,7 @@ SET(QGIS_ANALYSIS_MOC_HDRS vector/geometry_checker/qgsgeometrycheck.h vector/geometry_checker/qgsvectorlayerfeaturepool.h vector/geometry_checker/qgsgeometrygapcheck.h + vector/geometry_checker/qgsgeometrymissingvertexcheck.h ) INCLUDE_DIRECTORIES(SYSTEM ${SPATIALITE_INCLUDE_DIR}) @@ -293,7 +293,6 @@ SET(QGIS_ANALYSIS_HDRS vector/geometry_checker/qgsgeometryduplicatecheck.h vector/geometry_checker/qgsgeometryduplicatenodescheck.h vector/geometry_checker/qgsgeometryfollowboundariescheck.h - vector/geometry_checker/qgsgeometrymissingvertexcheck.h vector/geometry_checker/qgsgeometryholecheck.h vector/geometry_checker/qgsgeometrylineintersectioncheck.h vector/geometry_checker/qgsgeometrylinelayerintersectioncheck.h diff --git a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h index 2dd53aa29e3..aa10b5f05ff 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrymissingvertexcheck.h @@ -34,12 +34,15 @@ class QgsCurvePolygon; */ class ANALYSIS_EXPORT QgsGeometryMissingVertexCheck : public QgsGeometryCheck { + Q_GADGET + public: enum ResolutionMethod { NoChange, AddMissingVertex }; + Q_ENUM( ResolutionMethod ) explicit QgsGeometryMissingVertexCheck( const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration ); void collectErrors( const QMap &featurePools, QList &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids = LayerFeatureIds() ) const override; From e379f94f6daebf0fac43524abb4d748d9f7739ea Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 15 Oct 2018 14:52:28 +0200 Subject: [PATCH 119/126] Parsing colors is slow --- src/app/qgsgeometryvalidationdock.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 1d5fcbd4975..7bc32bae93b 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -60,7 +60,7 @@ QgsGeometryValidationDock::QgsGeometryValidationDock( const QString &title, QgsM mFeatureRubberband->setWidth( scaleFactor ); mFeatureRubberband->setStrokeColor( QColor( 100, 255, 100, 100 ) ); - mErrorRubberband->setColor( QColor( "#ffee58ff" ) ); + mErrorRubberband->setColor( QColor( 255, 238, 88, 255 ) ); mErrorRubberband->setWidth( scaleFactor ); mErrorLocationRubberband->setIcon( QgsRubberBand::ICON_X ); @@ -332,8 +332,8 @@ void QgsGeometryValidationDock::showHighlight( const QModelIndex ¤t ) mErrorRubberband->update(); } ); - errorAnimation->setStartValue( QColor( "#ffee58ff" ) ); - errorAnimation->setEndValue( QColor( "#ffee5800" ) ); + errorAnimation->setStartValue( QColor( 255, 238, 88, 255 ) ); + errorAnimation->setEndValue( QColor( 255, 238, 88, 0 ) ); errorAnimation->setDuration( 2000 ); errorAnimation->start(); From 15034224fdc9e92cf627e84abc67f53e59fb84e3 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 15 Oct 2018 14:52:47 +0200 Subject: [PATCH 120/126] No fallthrough required --- src/app/qgsgeometryvalidationmodel.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index 756c303a118..28c4a713d0a 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -75,8 +75,6 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmLineIntersections.svg" ) ); case Qt::DisplayRole: - FALLTHROUGH; - case DetailsRole: { const QgsFeatureId fid = topologyError->featureId(); From 128a226389a231b2a88b147e01c801c732df86b0 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 15 Oct 2018 14:53:00 +0200 Subject: [PATCH 121/126] Use precision from settings --- src/app/qgsgeometryvalidationservice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/qgsgeometryvalidationservice.cpp b/src/app/qgsgeometryvalidationservice.cpp index 1881f6ff98f..6d59d0881a7 100644 --- a/src/app/qgsgeometryvalidationservice.cpp +++ b/src/app/qgsgeometryvalidationservice.cpp @@ -211,7 +211,7 @@ void QgsGeometryValidationService::enableLayerChecks( QgsVectorLayer *layer ) precision = 8; } - checkInformation.context = qgis::make_unique( 10, mProject->crs(), mProject->transformContext() ); + checkInformation.context = qgis::make_unique( precision, mProject->crs(), mProject->transformContext() ); QList layerChecks; From 3f42395baa653cb08877b8b3ec7e657420f3b1ab Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 15 Oct 2018 15:06:41 +0200 Subject: [PATCH 122/126] Resolve some TODOs --- src/app/qgsgeometryvalidationmodel.cpp | 24 +++++++++++++++++++----- src/app/qgsgeometryvalidationmodel.h | 6 +++++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/app/qgsgeometryvalidationmodel.cpp b/src/app/qgsgeometryvalidationmodel.cpp index 28c4a713d0a..8ac5809759a 100644 --- a/src/app/qgsgeometryvalidationmodel.cpp +++ b/src/app/qgsgeometryvalidationmodel.cpp @@ -78,7 +78,7 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) case DetailsRole: { const QgsFeatureId fid = topologyError->featureId(); - const QgsFeature feature = mCurrentLayer->getFeature( fid ); // TODO: this should be cached! + const QgsFeature feature = getFeature( fid ); mExpressionContext.setFeature( feature ); const QVariant featureTitle = mDisplayExpression.evaluate( &mExpressionContext ); @@ -97,7 +97,7 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) const QgsFeatureId fid = topologyError->featureId(); if ( FID_IS_NULL( fid ) ) return QgsRectangle(); - const QgsFeature feature = mCurrentLayer->getFeature( fid ); // TODO: this should be cached! + const QgsFeature feature = getFeature( fid ); return feature.geometry().boundingBox(); } @@ -121,7 +121,7 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) const QgsFeatureId fid = topologyError->featureId(); if ( !FID_IS_NULL( fid ) ) { - const QgsFeature feature = mCurrentLayer->getFeature( fid ); // TODO: this should be cached! + const QgsFeature feature = getFeature( fid ); return feature.geometry(); } return QgsGeometry(); @@ -147,7 +147,7 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) { case Qt::DisplayRole: { - QgsFeature feature = mCurrentLayer->getFeature( featureItem.fid ); // TODO: this should be cached! + QgsFeature feature = getFeature( featureItem.fid ); mExpressionContext.setFeature( feature ); QString featureTitle = mDisplayExpression.evaluate( &mExpressionContext ).toString(); if ( featureTitle.isEmpty() ) @@ -185,7 +185,7 @@ QVariant QgsGeometryValidationModel::data( const QModelIndex &index, int role ) case FeatureExtentRole: { - return mCurrentLayer->getFeature( featureItem.fid ).geometry().boundingBox(); + return getFeature( featureItem.fid ).geometry().boundingBox(); } case ErrorLocationGeometryRole: @@ -232,11 +232,13 @@ void QgsGeometryValidationModel::setCurrentLayer( QgsVectorLayer *currentLayer ) beginResetModel(); mCurrentLayer = currentLayer; + mCachedFeature.setValid( false ); if ( mCurrentLayer ) { mDisplayExpression = mCurrentLayer ? mCurrentLayer->displayExpression() : QString(); mExpressionContext = QgsExpressionContext( QgsExpressionContextUtils::globalProjectLayerScopes( mCurrentLayer ) ); mDisplayExpression.prepare( &mExpressionContext ); + mRequiredAttributes = mDisplayExpression.referencedColumns().toList(); } else { @@ -375,3 +377,15 @@ int QgsGeometryValidationModel::errorsForFeature( QgsVectorLayer *layer, QgsFeat } return -1; } + +QgsFeature QgsGeometryValidationModel::getFeature( QgsFeatureId fid ) const +{ + if ( fid != mCachedFeature.id() || !mCachedFeature.isValid() ) + { + QgsFeatureRequest request; + request.setFilterFid( fid ); + request.setSubsetOfAttributes( mRequiredAttributes, mCurrentLayer->fields() ); + mCachedFeature = mCurrentLayer->getFeature( fid ); + } + return mCachedFeature; +} diff --git a/src/app/qgsgeometryvalidationmodel.h b/src/app/qgsgeometryvalidationmodel.h index 7000ec5622f..ecfe297c3ef 100644 --- a/src/app/qgsgeometryvalidationmodel.h +++ b/src/app/qgsgeometryvalidationmodel.h @@ -68,19 +68,23 @@ class QgsGeometryValidationModel : public QAbstractItemModel : fid( fid ) {} - QgsFeatureId fid; // TODO INITIALIZE PROPERLY + QgsFeatureId fid = FID_NULL; QList> errors; }; int errorsForFeature( QgsVectorLayer *layer, QgsFeatureId fid ); + QgsFeature getFeature( QgsFeatureId fid ) const; + QgsGeometryValidationService *mGeometryValidationService = nullptr; QgsVectorLayer *mCurrentLayer = nullptr; mutable QgsExpression mDisplayExpression; + mutable QStringList mRequiredAttributes; mutable QgsExpressionContext mExpressionContext; QMap > mErrorStorage; QMap > > mTopologyErrorStorage; + mutable QgsFeature mCachedFeature; }; #endif // QGSGEOMETRYVALIDATIONMODEL_H From 0bb6a16145f3b7450632ecdcc4c30721b2ea3a6a Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 15 Oct 2018 15:25:45 +0200 Subject: [PATCH 123/126] Standardize geometry checker tests --- tests/src/geometry_checker/CMakeLists.txt | 31 +++++++++++++---------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/tests/src/geometry_checker/CMakeLists.txt b/tests/src/geometry_checker/CMakeLists.txt index 99f171885ef..4bccfe06628 100644 --- a/tests/src/geometry_checker/CMakeLists.txt +++ b/tests/src/geometry_checker/CMakeLists.txt @@ -33,25 +33,28 @@ INCLUDE_DIRECTORIES(SYSTEM #No relinking and full RPATH for the install tree #See: http://www.cmake.org/Wiki/CMake_RPATH_handling#No_relinking_and_full_RPATH_for_the_install_tree -MACRO (ADD_QGIS_TEST testname testsrc) - SET(qgis_${testname}_SRCS ${testsrc} ${util_SRCS}) - ADD_CUSTOM_TARGET(qgis_${testname}moc ALL DEPENDS ${qgis_${testname}_MOC_SRCS}) - ADD_EXECUTABLE(qgis_${testname} ${qgis_${testname}_SRCS}) - SET_TARGET_PROPERTIES(qgis_${testname} PROPERTIES AUTOMOC TRUE) - TARGET_LINK_LIBRARIES(qgis_${testname} - ${Qt5Xml_LIBRARIES} +MACRO (ADD_QGIS_TEST TESTSRC) + SET (TESTNAME ${TESTSRC}) + STRING(REPLACE "test" "" TESTNAME ${TESTNAME}) + STRING(REPLACE "qgs" "" TESTNAME ${TESTNAME}) + STRING(REPLACE ".cpp" "" TESTNAME ${TESTNAME}) + SET (TESTNAME "qgis_${TESTNAME}test") + + SET(${TESTNAME}_SRCS ${TESTSRC} ${util_SRCS}) + SET(${TESTNAME}_MOC_CPPS ${TESTSRC}) + ADD_EXECUTABLE(${TESTNAME} ${${TESTNAME}_SRCS}) + SET_TARGET_PROPERTIES(${TESTNAME} PROPERTIES AUTOMOC TRUE) + TARGET_LINK_LIBRARIES(${TESTNAME} ${Qt5Core_LIBRARIES} - ${Qt5Svg_LIBRARIES} ${Qt5Test_LIBRARIES} - ${PROJ_LIBRARY} - ${GEOS_LIBRARY} - ${GDAL_LIBRARY} - qgis_core qgis_analysis) - ADD_TEST(qgis_${testname} ${CMAKE_CURRENT_BINARY_DIR}/../../../output/bin/qgis_${testname} -maxwarnings 10000) + ADD_TEST(${TESTNAME} ${CMAKE_BINARY_DIR}/output/bin/${TESTNAME} -maxwarnings 10000) + #SET_TARGET_PROPERTIES(qgis_${testname} PROPERTIES + # INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/${QGIS_LIB_DIR} + # INSTALL_RPATH_USE_LINK_PATH true ) ENDMACRO (ADD_QGIS_TEST) ############################################################# # Tests: -ADD_QGIS_TEST( qgsgeometrycheckstest testqgsgeometrychecks.cpp ) +ADD_QGIS_TEST(testqgsgeometrychecks.cpp) From 3d31d7244270d0f1ac3501a5f98198b1305e295d Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 15 Oct 2018 16:05:59 +0200 Subject: [PATCH 124/126] Fix python bindings --- .../qgsgeometrycheckfactory.sip.in | 18 ++++++++++++++++++ .../geometry_checker/qgsgeometrycheckfactory.h | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip.in index 07f84fee124..c2dea49db5a 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip.in @@ -12,6 +12,24 @@ +class QgsGeometryCheckFactory /Abstract/ +{ +%Docstring + +A factory for geometry checks. + +.. note:: + + This class is a technology preview and unstable API. + +.. versionadded:: 3.4 +%End + +%TypeHeaderCode +#include "qgsgeometrycheckfactory.h" +%End + public: + virtual ~QgsGeometryCheckFactory(); virtual QgsGeometryCheck *createGeometryCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ) const = 0 /Factory/; diff --git a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h index 00988d8cbd1..b131698dec3 100644 --- a/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h +++ b/src/analysis/vector/geometry_checker/qgsgeometrycheckfactory.h @@ -39,7 +39,8 @@ struct QgsGeometryCheckContext; * * \note This class is a technology preview and unstable API. * \since QGIS 3.4 - */class ANALYSIS_EXPORT QgsGeometryCheckFactory SIP_ABSTRACT + */ +class ANALYSIS_EXPORT QgsGeometryCheckFactory SIP_ABSTRACT { public: From 8da29109937e55906e4cb44dd75db70624f70f50 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Mon, 15 Oct 2018 17:29:21 +0200 Subject: [PATCH 125/126] Add some tests for QgsVectorLayerFeaturePool --- tests/src/geometry_checker/CMakeLists.txt | 1 + .../testqgsvectorlayerfeaturepool.cpp | 171 ++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 tests/src/geometry_checker/testqgsvectorlayerfeaturepool.cpp diff --git a/tests/src/geometry_checker/CMakeLists.txt b/tests/src/geometry_checker/CMakeLists.txt index 4bccfe06628..a9f6fa32512 100644 --- a/tests/src/geometry_checker/CMakeLists.txt +++ b/tests/src/geometry_checker/CMakeLists.txt @@ -58,3 +58,4 @@ ENDMACRO (ADD_QGIS_TEST) # Tests: ADD_QGIS_TEST(testqgsgeometrychecks.cpp) +ADD_QGIS_TEST(testqgsvectorlayerfeaturepool.cpp) diff --git a/tests/src/geometry_checker/testqgsvectorlayerfeaturepool.cpp b/tests/src/geometry_checker/testqgsvectorlayerfeaturepool.cpp new file mode 100644 index 00000000000..a8c211280b2 --- /dev/null +++ b/tests/src/geometry_checker/testqgsvectorlayerfeaturepool.cpp @@ -0,0 +1,171 @@ +/*************************************************************************** + testqgsgeometrychecks.cpp + -------------------------------------- + Date : September 2017 + Copyright : (C) 2017 Sandro Mani + Email : manisandro at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgstest.h" +#include "qgsvectorlayerfeaturepool.h" +#include "qgsvectorlayer.h" + + +class TestQgsVectorLayerFeaturePool : public QObject +{ + Q_OBJECT + + private slots: + void initTestCase(); + void cleanupTestCase(); + void getFeatures(); + void addFeature(); + void deleteFeature(); + void changeGeometry(); + + private: + std::unique_ptr createPopulatedLayer(); +}; + +void TestQgsVectorLayerFeaturePool::initTestCase() +{ + QgsApplication::initQgis(); +} + +void TestQgsVectorLayerFeaturePool::cleanupTestCase() +{ + QgsApplication::exitQgis(); +} + +void TestQgsVectorLayerFeaturePool::getFeatures() +{ + std::unique_ptr vl = createPopulatedLayer(); + QgsVectorLayerFeaturePool pool( vl.get() ); + + QgsFeatureIds ids1 = pool.getFeatures( QgsFeatureRequest().setFilterRect( QgsRectangle( 0, 0, 10, 10 ) ) ); + + // One feature is within the requested area + QCOMPARE( ids1.size(), 1 ); + + QgsFeatureIds ids2 = pool.getIntersects( QgsRectangle( 0, 0, 10, 10 ) ); + // Also one within the spatial index + QCOMPARE( ids2.size(), 1 ); +} + +void TestQgsVectorLayerFeaturePool::addFeature() +{ + std::unique_ptr vl = createPopulatedLayer(); + QgsVectorLayerFeaturePool pool( vl.get() ); + + QgsFeatureIds ids1 = pool.getFeatures( QgsFeatureRequest().setFilterRect( QgsRectangle( 0, 0, 10, 10 ) ) ); + + // One feature is within the requested area + QCOMPARE( ids1.size(), 1 ); + + QgsFeatureIds ids2 = pool.getIntersects( QgsRectangle( 0, 0, 10, 10 ) ); + // Also one within the spatial index + QCOMPARE( ids2.size(), 1 ); + + QgsFeature feature; + feature.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((1 1, 9 1, 9 9, 1 9, 1 1))" ) ) ); + + // Add another feature... + vl->startEditing(); + + pool.addFeature( feature ); + + QgsFeatureIds ids3 = pool.getIntersects( QgsRectangle( 0, 0, 10, 10 ) ); + + // Two features within the requested area + QCOMPARE( ids3.size(), 2 ); +} + +void TestQgsVectorLayerFeaturePool::deleteFeature() +{ + std::unique_ptr vl = createPopulatedLayer(); + + QgsVectorLayerFeaturePool pool( vl.get() ); + + QgsFeatureIds ids1 = pool.getFeatures( QgsFeatureRequest().setFilterRect( QgsRectangle( 0, 0, 10, 10 ) ) ); + + // One feature is within the requested area + QCOMPARE( ids1.size(), 1 ); + + QgsFeatureIds ids2 = pool.getIntersects( QgsRectangle( 0, 0, 10, 10 ) ); + // Also one within the spatial index + QCOMPARE( ids2.size(), 1 ); + + vl->startEditing(); + // Delete a feature outside the AOI (0, 0, 10, 10) + pool.deleteFeature( 2 ); + + QgsFeatureIds ids3 = pool.getIntersects( QgsRectangle( 0, 0, 10, 10 ) ); + + // Still 1 feature + QCOMPARE( ids3.size(), 1 ); + + // Delete a feature inside the AOI (0, 0, 10, 10) + pool.deleteFeature( 1 ); + + QgsFeatureIds ids4 = pool.getIntersects( QgsRectangle( 0, 0, 10, 10 ) ); + + // No more features here + QCOMPARE( ids4.size(), 0 ); +} + +void TestQgsVectorLayerFeaturePool::changeGeometry() +{ + std::unique_ptr vl = createPopulatedLayer(); + + QgsVectorLayerFeaturePool pool( vl.get() ); + + // One feature is within the requested area + QgsFeatureIds ids1 = pool.getFeatures( QgsFeatureRequest().setFilterRect( QgsRectangle( 0, 0, 10, 10 ) ) ); + QCOMPARE( ids1.size(), 1 ); + + // Also one when using the spatial index + QgsFeatureIds ids2 = pool.getIntersects( QgsRectangle( 0, 0, 10, 10 ) ); + QCOMPARE( ids2.size(), 1 ); + + vl->startEditing(); + QgsFeature feat; + pool.getFeature( 1, feat ); + + // Update a feature to be outside the AOI + feat.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((100 100, 110 100, 110 110, 100 110, 100 100))" ) ) ); + vl->updateFeature( feat ); + + // No features in the AOI + QgsFeatureIds ids3 = pool.getIntersects( QgsRectangle( 0, 0, 10, 10 ) ); + QCOMPARE( ids3.size(), 0 ); + + // Update a feature to be inside the AOI + feat.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((0 0, 10 0, 10 10, 0 10, 0 0))" ) ) ); + vl->updateFeature( feat ); + + // One there again + QgsFeatureIds ids4 = pool.getIntersects( QgsRectangle( 0, 0, 10, 10 ) ); + QCOMPARE( ids4.size(), 1 ); +} + +std::unique_ptr TestQgsVectorLayerFeaturePool::createPopulatedLayer() +{ + std::unique_ptr vl = qgis::make_unique( QStringLiteral( "Polygon" ), QStringLiteral( "Polygons" ), QStringLiteral( "memory" ) ); + + QgsFeature feature; + feature.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((0 0, 10 0, 10 10, 0 10, 0 0))" ) ) ); + vl->dataProvider()->addFeature( feature ); + feature.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((100 100, 110 100, 110 110, 100 110, 100 100))" ) ) ); + vl->dataProvider()->addFeature( feature ); + + return vl; +} +QGSTEST_MAIN( TestQgsVectorLayerFeaturePool ) +#include "testqgsvectorlayerfeaturepool.moc" From 6c97fcf80f6d10780dfc634e533e9a33b24ad906 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Tue, 16 Oct 2018 10:12:30 +0200 Subject: [PATCH 126/126] Avoid crash when disconnecting layer --- src/app/qgsgeometryvalidationdock.cpp | 8 ++++++++ src/app/qgsgeometryvalidationdock.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/app/qgsgeometryvalidationdock.cpp b/src/app/qgsgeometryvalidationdock.cpp index 7bc32bae93b..172057ba1eb 100644 --- a/src/app/qgsgeometryvalidationdock.cpp +++ b/src/app/qgsgeometryvalidationdock.cpp @@ -265,6 +265,7 @@ void QgsGeometryValidationDock::onCurrentLayerChanged( QgsMapLayer *layer ) { disconnect( mCurrentLayer, &QgsVectorLayer::editingStarted, this, &QgsGeometryValidationDock::onLayerEditingStatusChanged ); disconnect( mCurrentLayer, &QgsVectorLayer::editingStopped, this, &QgsGeometryValidationDock::onLayerEditingStatusChanged ); + disconnect( mCurrentLayer, &QgsVectorLayer::destroyed, this, &QgsGeometryValidationDock::onLayerDestroyed ); } mCurrentLayer = qobject_cast( layer ); @@ -273,6 +274,7 @@ void QgsGeometryValidationDock::onCurrentLayerChanged( QgsMapLayer *layer ) { connect( mCurrentLayer, &QgsVectorLayer::editingStarted, this, &QgsGeometryValidationDock::onLayerEditingStatusChanged ); connect( mCurrentLayer, &QgsVectorLayer::editingStopped, this, &QgsGeometryValidationDock::onLayerEditingStatusChanged ); + connect( mCurrentLayer, &QgsVectorLayer::destroyed, this, &QgsGeometryValidationDock::onLayerDestroyed ); } onLayerEditingStatusChanged(); @@ -297,6 +299,12 @@ void QgsGeometryValidationDock::onLayerEditingStatusChanged() mTopologyChecksPendingButton->setEnabled( enabled ); } +void QgsGeometryValidationDock::onLayerDestroyed( QObject *layer ) +{ + if ( layer == mCurrentLayer ) + mCurrentLayer = nullptr; +} + void QgsGeometryValidationDock::showHighlight( const QModelIndex ¤t ) { QgsVectorLayer *vlayer = qobject_cast( mMapCanvas->currentLayer() ); diff --git a/src/app/qgsgeometryvalidationdock.h b/src/app/qgsgeometryvalidationdock.h index 88cd1751ac6..853161cd430 100644 --- a/src/app/qgsgeometryvalidationdock.h +++ b/src/app/qgsgeometryvalidationdock.h @@ -48,6 +48,7 @@ class QgsGeometryValidationDock : public QgsDockWidget, public Ui_QgsGeometryVal void onCurrentErrorChanged( const QModelIndex ¤t, const QModelIndex &previous ); void onCurrentLayerChanged( QgsMapLayer *layer ); void onLayerEditingStatusChanged(); + void onLayerDestroyed( QObject *layer ); void gotoNextError(); void gotoPreviousError(); void zoomToProblem();