Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsgeometrycheck.h
3 : : ---------------------
4 : : begin : September 2014
5 : : copyright : (C) 2014 by Sandro Mani / Sourcepole AG
6 : : email : smani at sourcepole dot 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 QGS_GEOMETRY_CHECK_H
17 : : #define QGS_GEOMETRY_CHECK_H
18 : :
19 : : #include <QApplication>
20 : : #include <limits>
21 : : #include <QStringList>
22 : : #include <QPointer>
23 : :
24 : : #include "qgis_analysis.h"
25 : : #include "qgsfeature.h"
26 : : #include "qgsvectorlayer.h"
27 : : #include "qgsgeometry.h"
28 : : #include "qgsgeometrycheckerutils.h"
29 : : #include "qgsgeometrycheckresolutionmethod.h"
30 : : #include "qgssettings.h"
31 : :
32 : : class QgsGeometryCheckError;
33 : : class QgsFeaturePool;
34 : :
35 : : /**
36 : : * \ingroup analysis
37 : : * \brief This class implements a geometry check.
38 : : *
39 : : * Geometry checks run over a set of features and can detect errors like topological
40 : : * or other issues which are reported in the geometry validation panel in QGIS and
41 : : * help a user to create valid geometries.
42 : : *
43 : : * Implementing a custom geometry check consists of the following parts
44 : : *
45 : : * ### Writing the check
46 : : *
47 : : * A new subclass of QgsGeometryCheck needs to be written and at least the following
48 : : * abstract methods need to be implemented:
49 : : *
50 : : * - compatibleGeometryTypes(): A list of geometry types to which this check applies
51 : : * - resolutionMethods(): A list of names for (automated) resolution methods that can be used to fix errors of this type
52 : : * - description(): A description for the geometry check.
53 : : * - id(): A unique id for this check.
54 : : * - checkType(): One of QgsGeometryCheck.LayerCheck, QgsGeometryCheck.FeatureCheck,QgsGeometryCheck.FeatureNodeCheck
55 : : * - collectErrors(): This method will be called to validate geometries. All geometries which should be validated are passed
56 : : * into this method with the parameter ids and should be retrieved from the available featurePools to make
57 : : * use of caching. New errors should be appended to the error list and other message strings to messages.
58 : : * The method needs to return a tuple (errors, messages).
59 : : *
60 : : * ### Creating a geometry check factory
61 : : *
62 : : * A Geometry check factory manages meta information for checks. There will always be one single
63 : : * geometry check factory created per check type, but it's possible that multiple QgsGeometryCheck
64 : : * instances are created and used in parallel.
65 : : *
66 : : * A new subclass of QgsGeometryCheckFactory needs to be written and at least the following
67 : : * abstract methods need to be implemented:
68 : : *
69 : : * - QgsGeometryCheckFactory::createGeometryCheck(): Needs to return a new subclassed QgsGeometryCheck object that has been written in the previous step.
70 : : * - QgsGeometryCheckFactory::id(): A unique id for this geometry check.
71 : : * - QgsGeometryCheckFactory::description(): A description for this geometry check that can be presented to the user for more explanation.
72 : : * - QgsGeometryCheckFactory::isCompatible(): Returns a boolean that determines if this check is available for a given layer. This often
73 : : * checks for the geometry type of the layer.
74 : : * - QgsGeometryCheckFactory::flags(): Returns additional flags for a geometry check. If unsure return QgsGeometryCheck.AvailableInValidation.
75 : : * - QgsGeometryCheckFactory::checkType(): Returns the type of this geometry check.
76 : : *
77 : : * ### Registering the geometry check
78 : : *
79 : : * Finally the geometry check factory needs to be registered in QGIS, so the system
80 : : * is aware of the available geometry checks.
81 : : *
82 : : * \code{.py}
83 : : * # Make sure you always keep a
84 : : * checkFactory = MyGeometryCheckFactory()
85 : : * QgsAnalysis.geometryCheckRegistry().registerGeometryCheck(checkFactory)
86 : : * \endcode
87 : : *
88 : : * \note This class is a technology preview and unstable API.
89 : : * \since QGIS 3.4
90 : : */
91 : : class ANALYSIS_EXPORT QgsGeometryCheck
92 : : {
93 : : Q_GADGET
94 : :
95 : : public:
96 : :
97 : : /**
98 : : * A list of layers and feature ids for each of these layers.
99 : : * In C++, the member `ids` can be accessed directly.
100 : : * In Python some accessor methods will need to be written.
101 : : *
102 : : * \since QGIS 3.4
103 : : */
104 : 27 : struct ANALYSIS_EXPORT LayerFeatureIds
105 : : {
106 : 27 : LayerFeatureIds() = default;
107 : : LayerFeatureIds( const QMap<QString, QgsFeatureIds> &idsIn ) SIP_SKIP;
108 : :
109 : : QMap<QString, QgsFeatureIds> ids SIP_SKIP;
110 : :
111 : : #ifndef SIP_RUN
112 : 0 : QMap<QString, QgsFeatureIds> toMap() const
113 : : {
114 : 0 : return ids;
115 : : }
116 : :
117 : 27 : bool isEmpty() const
118 : : {
119 : 27 : return ids.isEmpty();
120 : : }
121 : : #endif
122 : : };
123 : :
124 : : /**
125 : : * Description of a change to indicate at which level a change occurred.
126 : : *
127 : : * \since Python bindings since QGIS 3.4
128 : : */
129 : : enum ChangeWhat
130 : : {
131 : : ChangeFeature, //!< This change happens on feature level
132 : : ChangePart, //!< This change happens on part level
133 : : ChangeRing, //!< This change happens on ring level
134 : : ChangeNode //!< This change happens on node level
135 : : };
136 : :
137 : : /**
138 : : * Description of the type of a change.
139 : : *
140 : : * \since Python bindings since QGIS 3.4
141 : : */
142 : : enum ChangeType
143 : : {
144 : : ChangeAdded, //!< Something has been added
145 : : ChangeRemoved, //!< Something has been removed
146 : : ChangeChanged //!< Something has been updated
147 : : };
148 : :
149 : : /**
150 : : * The type of a check.
151 : : *
152 : : * \since Python bindings since QGIS 3.4
153 : : */
154 : : enum CheckType
155 : : {
156 : : FeatureNodeCheck, //!< The check controls individual nodes
157 : : FeatureCheck, //!< The check controls geometries as a whole
158 : : LayerCheck //!< The check controls a whole layer (topology checks)
159 : : };
160 : :
161 : : /**
162 : : * Flags for geometry checks.
163 : : */
164 : : enum Flag
165 : : {
166 : : AvailableInValidation = 1 << 1 //!< This geometry check should be available in layer validation on the vector layer peroperties
167 : : };
168 : : Q_DECLARE_FLAGS( Flags, Flag )
169 : : Q_FLAG( Flags )
170 : :
171 : : /**
172 : : * Descripts a change to fix a geometry.
173 : : *
174 : : * \since Python bindings since QGIS 3.4
175 : : */
176 : : struct Change
177 : : {
178 : : Change() = default;
179 : :
180 : : /**
181 : : * Create a new Change
182 : : */
183 : 85 : Change( QgsGeometryCheck::ChangeWhat _what, QgsGeometryCheck::ChangeType _type, QgsVertexId _vidx = QgsVertexId() )
184 : 85 : : what( _what )
185 : 85 : , type( _type )
186 : 85 : , vidx( _vidx )
187 : 85 : {}
188 : :
189 : : /**
190 : : * What level this change affects.
191 : : */
192 : : QgsGeometryCheck::ChangeWhat what;
193 : :
194 : : /**
195 : : * What action this change performs.
196 : : */
197 : : QgsGeometryCheck::ChangeType type;
198 : :
199 : : /**
200 : : * The index of the part / ring / vertex, depending on \see what.
201 : : */
202 : : QgsVertexId vidx;
203 : 43 : bool operator==( const QgsGeometryCheck::Change &other )
204 : : {
205 : 43 : return what == other.what && type == other.type && vidx == other.vidx;
206 : : }
207 : : };
208 : :
209 : : /**
210 : : * A collection of changes.
211 : : * Grouped by layer id and feature id.
212 : : */
213 : : typedef QMap<QString, QMap<QgsFeatureId, QList<QgsGeometryCheck::Change> > > Changes;
214 : :
215 : : /**
216 : : * Create a new geometry check.
217 : : */
218 : : QgsGeometryCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration );
219 : 26 : virtual ~QgsGeometryCheck() = default;
220 : :
221 : : /**
222 : : * Will be run in the main thread before collectErrors is called (which may be run from a background thread).
223 : : *
224 : : * \since QGIS 3.10
225 : : */
226 : : virtual void prepare( const QgsGeometryCheckContext *context, const QVariantMap &configuration );
227 : :
228 : : #ifndef SIP_RUN
229 : :
230 : : /**
231 : : * Returns the configuration value with the \a name, saved in the QGIS settings for
232 : : * this geometry check. If no configuration could be found, \a defaultValue is returned.
233 : : */
234 : : template <class T>
235 : 8 : T configurationValue( const QString &name, const QVariant &defaultValue = QVariant() )
236 : : {
237 : 8 : return mConfiguration.value( name, QgsSettings().value( "/geometry_checker/" + id() + "/" + name, defaultValue ) ).value<T>();
238 : 0 : }
239 : : #endif
240 : :
241 : : /**
242 : : * Returns if this geometry check is compatible with \a layer.
243 : : * By default it checks for the geometry type in \a compatibleGeometryTypes().
244 : : *
245 : : * \since QGIS 3.4
246 : : */
247 : : virtual bool isCompatible( QgsVectorLayer *layer ) const;
248 : :
249 : : /**
250 : : * A list of geometry types for which this check can be performed.
251 : : *
252 : : * \since QGIS 3.4
253 : : */
254 : : virtual QList<QgsWkbTypes::GeometryType> compatibleGeometryTypes() const = 0;
255 : :
256 : : /**
257 : : * Flags for this geometry check.
258 : : */
259 : : virtual QgsGeometryCheck::Flags flags() const;
260 : :
261 : : /**
262 : : * The main worker method.
263 : : * Check all features available from \a featurePools and write errors found to \a errors.
264 : : * Other status messages can be written to \a messages.
265 : : * Progress should be reported to \a feedback. Only features and layers listed in \a ids should be checked.
266 : : *
267 : : * \since QGIS 3.4
268 : : */
269 : : virtual void collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors SIP_INOUT, QStringList &messages SIP_INOUT, QgsFeedback *feedback, const LayerFeatureIds &ids = QgsGeometryCheck::LayerFeatureIds() ) const = 0;
270 : :
271 : : /**
272 : : * Fixes the error \a error with the specified \a method.
273 : : * Is executed on the main thread.
274 : : *
275 : : * \see availableResolutionMethods()
276 : : * \since QGIS 3.4
277 : : */
278 : : virtual void fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes SIP_INOUT ) const SIP_SKIP;
279 : :
280 : : /**
281 : : * Returns a list of available resolution methods.
282 : : *
283 : : * \since QGIS 3.12
284 : : */
285 : : virtual QList<QgsGeometryCheckResolutionMethod> availableResolutionMethods() const;
286 : :
287 : : /**
288 : : * Returns a list of descriptions for available resolutions for errors.
289 : : * The index will be passed as ``method`` to \see fixError().
290 : : *
291 : : * \deprecated since QGIS 3.12, use availableResolutionMethods() instead
292 : : * \since QGIS 3.4
293 : : */
294 : : Q_DECL_DEPRECATED virtual QStringList resolutionMethods() const SIP_DEPRECATED;
295 : :
296 : : /**
297 : : * Returns a human readable description for this check.
298 : : *
299 : : * \since QGIS 3.4
300 : : */
301 : : virtual QString description() const = 0;
302 : :
303 : : /**
304 : : * Returns an id for this check.
305 : : *
306 : : * \since QGIS 3.4
307 : : */
308 : : virtual QString id() const = 0;
309 : :
310 : : /**
311 : : * Returns the check type.
312 : : *
313 : : * \since QGIS 3.4
314 : : */
315 : : virtual CheckType checkType() const = 0;
316 : :
317 : : /**
318 : : * Returns the context
319 : : *
320 : : * \since QGIS 3.4
321 : : */
322 : 183 : const QgsGeometryCheckContext *context() const { return mContext; }
323 : :
324 : : protected:
325 : :
326 : : /**
327 : : * Returns all layers and feature ids.
328 : : *
329 : : * \note Not available in Python bindings
330 : : * \since QGIS 3.4
331 : : */
332 : : QMap<QString, QgsFeatureIds> allLayerFeatureIds( const QMap<QString, QgsFeaturePool *> &featurePools ) const SIP_SKIP;
333 : :
334 : : /**
335 : : * Replaces a part in a feature geometry.
336 : : *
337 : : * \note Not available in Python bindings
338 : : * \since QGIS 3.4
339 : : */
340 : : void replaceFeatureGeometryPart( const QMap<QString, QgsFeaturePool *> &featurePools, const QString &layerId, QgsFeature &feature, int partIdx, QgsAbstractGeometry *newPartGeom, Changes &changes ) const SIP_SKIP;
341 : :
342 : : /**
343 : : * Deletes a part of a feature geometry.
344 : : *
345 : : * \note Not available in Python bindings
346 : : * \since QGIS 3.4
347 : : */
348 : : void deleteFeatureGeometryPart( const QMap<QString, QgsFeaturePool *> &featurePools, const QString &layerId, QgsFeature &feature, int partIdx, Changes &changes ) const SIP_SKIP;
349 : :
350 : : /**
351 : : * Deletes a ring in a feature geometry.
352 : : *
353 : : * \note Not available in Python bindings
354 : : * \since QGIS 3.4
355 : : */
356 : : void deleteFeatureGeometryRing( const QMap<QString, QgsFeaturePool *> &featurePools, const QString &layerId, QgsFeature &feature, int partIdx, int ringIdx, Changes &changes ) const SIP_SKIP;
357 : :
358 : : const QgsGeometryCheckContext *mContext;
359 : : QVariantMap mConfiguration;
360 : :
361 : : /**
362 : : * Determines the scale factor of a layer to the map coordinate reference system.
363 : : *
364 : : * \note Not available in Python bindings
365 : : * \since QGIS 3.4
366 : : */
367 : : double scaleFactor( const QPointer<QgsVectorLayer> &layer ) const SIP_SKIP;
368 : : };
369 : :
370 : : #endif // QGS_GEOMETRY_CHECK_H
|