1 /***************************************************************************
2                       qgsgeometryvalidationservice.h
3                      --------------------------------------
4 Date                 : 7.9.2018
5 Copyright            : (C) 2018 by Matthias Kuhn
6 email                : matthias@opengis.ch
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 #ifndef QGSGEOMETRYVALIDATIONSERVICE_H
17 #define QGSGEOMETRYVALIDATIONSERVICE_H
18 
19 #include <QObject>
20 #include <QMap>
21 #include <QFuture>
22 #include <QReadWriteLock>
23 
24 #include "qgsfeature.h"
25 #include "qgsgeometrycheckcontext.h"
26 
27 class QgsProject;
28 class QgsMapLayer;
29 class QgsVectorLayer;
30 class QgsGeometryCheck;
31 class QgsSingleGeometryCheck;
32 class QgsSingleGeometryCheckError;
33 class QgsGeometryCheckError;
34 class QgsFeedback;
35 class QgsFeaturePool;
36 class QgsMessageBar;
37 class QgsMessageBarItem;
38 
39 /**
40  * This service connects to all layers in a project and triggers validation
41  * of features whenever they are edited.
42  * It is responsible for executing validation checks and sending out signals
43  * upon failure and success.
44  * It will also make sure, that a layer can only be saved, if all errors have
45  * been resolved.
46  */
47 class QgsGeometryValidationService : public QObject
48 {
49     Q_OBJECT
50 
51   public:
52     struct FeatureError
53     {
54       FeatureError() = default;
FeatureErrorFeatureError55       FeatureError( QgsFeatureId fid, QgsGeometry::Error error )
56         : featureId( fid )
57         , error( error )
58       {}
59       QgsFeatureId featureId = std::numeric_limits<QgsFeatureId>::min();
60       QgsGeometry::Error error;
61     };
62 
63     typedef QList<FeatureError> FeatureErrors;
64 
65     QgsGeometryValidationService( QgsProject *project );
66 
67     void fixError( QgsGeometryCheckError *error, int method );
68 
69     void triggerTopologyChecks( QgsVectorLayer *layer, bool stopEditing );
70 
71     void setMessageBar( QgsMessageBar *messageBar );
72 
73   signals:
74 
75     /**
76      * Emitted when geometry checks for this layer have been disabled and
77      * any existing cached result should be cleared.
78      */
79     void singleGeometryCheckCleared( QgsVectorLayer *layer );
80 
81     void geometryCheckStarted( QgsVectorLayer *layer, QgsFeatureId fid );
82     void geometryCheckCompleted( QgsVectorLayer *layer, QgsFeatureId fid, const QList<std::shared_ptr<QgsSingleGeometryCheckError>> &errors );
83     void topologyChecksUpdated( QgsVectorLayer *layer, const QList<std::shared_ptr<QgsGeometryCheckError> > &errors );
84     void topologyChecksCleared( QgsVectorLayer *layer );
85     void topologyErrorUpdated( QgsVectorLayer *layer, QgsGeometryCheckError *error );
86 
87     void warning( const QString &message );
88     void clearWarning();
89 
90   private slots:
91     void onLayersAdded( const QList<QgsMapLayer *> &layers );
92     void onFeatureAdded( QgsVectorLayer *layer, QgsFeatureId fid );
93     void onGeometryChanged( QgsVectorLayer *layer, QgsFeatureId fid, const QgsGeometry &geometry );
94     void onFeatureDeleted( QgsVectorLayer *layer, QgsFeatureId fid );
95     void onBeforeCommitChanges( QgsVectorLayer *layer, bool stopEditing );
96     void onEditingStopped( QgsVectorLayer *layer );
97 
98   private:
99     void showMessage( const QString &message );
100     void cleanupLayerChecks( QgsVectorLayer *layer );
101     void enableLayerChecks( QgsVectorLayer *layer );
102 
103     void cancelTopologyCheck( QgsVectorLayer *layer );
104 
105     void clearTopologyChecks( QgsVectorLayer *layer );
106 
107     void invalidateTopologyChecks( QgsVectorLayer *layer );
108 
109     void processFeature( QgsVectorLayer *layer, QgsFeatureId fid );
110 
111     QgsProject *mProject = nullptr;
112 
113     struct VectorLayerCheckInformation
114     {
115       QList< QgsSingleGeometryCheck * > singleFeatureChecks;
116       QMap<QgsFeatureId, QList< std::shared_ptr<QgsSingleGeometryCheckError > > > singleFeatureCheckErrors;
117       QList< QgsGeometryCheck *> topologyChecks;
118       QFutureWatcher<void> *topologyCheckFutureWatcher = nullptr;
119       QList<QgsFeedback *> topologyCheckFeedbacks; // will be deleted when topologyCheckFutureWatcher is delteed
120       QList<std::shared_ptr<QgsGeometryCheckError>> topologyCheckErrors;
121       QList<QMetaObject::Connection> connections;
122       std::shared_ptr<QgsGeometryCheckContext> context;
123       bool commitPending = false;
124     };
125 
126     QReadWriteLock mTopologyCheckLock;
127     QHash<QgsVectorLayer *, VectorLayerCheckInformation> mLayerChecks;
128     QMap<QString, QgsFeaturePool *> mFeaturePools;
129     QgsMessageBar *mMessageBar = nullptr;
130     QgsMessageBarItem *mMessageBarItem = nullptr;
131 
132     // when checks do complete successfully and changes need to be saved
133     // this variable is used to indicate that it's safe to bypass the checks
134     bool mBypassChecks = false;
135 
136 };
137 
138 #endif // QGSGEOMETRYVALIDATIONSERVICE_H
139