QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsgeometrymissingvertexcheck.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgeometrymissingvertexcheck.cpp
3 ---------------------
4 begin : September 2018
5 copyright : (C) 2018 Matthias Kuhn
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
17
18#include "qgsfeedback.h"
20#include "qgsmultipolygon.h"
21#include "qgscurvepolygon.h"
22#include "qgscurve.h"
23#include "qgslinestring.h"
24#include "qgsgeometryengine.h"
25#include "qgsgeometryutils.h"
26#include "qgsapplication.h"
27
28QgsGeometryMissingVertexCheck::QgsGeometryMissingVertexCheck( const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration )
29 : QgsGeometryCheck( context, geometryCheckConfiguration )
30
31{}
32
33void QgsGeometryMissingVertexCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
34{
35 Q_UNUSED( messages )
36 if ( feedback )
37 feedback->setProgress( feedback->progress() + 1.0 );
38
39 QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
40
41 QgsFeaturePool *featurePool = featurePools.value( featureIds.firstKey() );
42
43 const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), nullptr, mContext, true );
44
45 for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
46 {
47 if ( feedback && feedback->isCanceled() )
48 {
49 break;
50 }
51
52 const QgsGeometry geometry = layerFeature.geometry();
53 const QgsAbstractGeometry *geom = geometry.constGet();
54
55 if ( QgsCurvePolygon *polygon = qgsgeometry_cast<QgsCurvePolygon *>( geom ) )
56 {
57 processPolygon( polygon, featurePool, errors, layerFeature, feedback );
58 }
59 else if ( QgsGeometryCollection *collection = qgsgeometry_cast<QgsGeometryCollection *>( geom ) )
60 {
61 const int numGeometries = collection->numGeometries();
62 for ( int i = 0; i < numGeometries; ++i )
63 {
64 if ( QgsCurvePolygon *polygon = qgsgeometry_cast<QgsCurvePolygon *>( collection->geometryN( i ) ) )
65 {
66 processPolygon( polygon, featurePool, errors, layerFeature, feedback );
67 }
68 }
69 }
70 }
71}
72
73void QgsGeometryMissingVertexCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
74{
75 Q_UNUSED( featurePools )
76 Q_UNUSED( changes )
77
78 QMetaEnum metaEnum = QMetaEnum::fromType<QgsGeometryMissingVertexCheck::ResolutionMethod>();
79 if ( !metaEnum.isValid() || !metaEnum.valueToKey( method ) )
80 {
81 error->setFixFailed( tr( "Unknown method" ) );
82 }
83 else
84 {
85 ResolutionMethod methodValue = static_cast<ResolutionMethod>( method );
86 switch ( methodValue )
87 {
88 case NoChange:
89 error->setFixed( method );
90 break;
91
93 {
94 QgsFeaturePool *featurePool = featurePools[ error->layerId() ];
95
96 QgsFeature feature;
97 featurePool->getFeature( error->featureId(), feature );
98
99 QgsPointXY pointOnSegment; // Should be equal to location
100 int vertexIndex;
101 QgsGeometry geometry = feature.geometry();
102 geometry.closestSegmentWithContext( error->location(), pointOnSegment, vertexIndex );
103 geometry.insertVertex( QgsPoint( error->location() ), vertexIndex );
104 feature.setGeometry( geometry );
105
106 featurePool->updateFeature( feature );
107 // TODO update "changes" structure
108
109 error->setFixed( method );
110 }
111 break;
112 }
113 }
114}
115
117{
118 static QStringList methods = QStringList()
119 << tr( "No action" )
120 << tr( "Add missing vertex" );
121 return methods;
122}
123
125{
126 return factoryDescription();
127}
128
129void QgsGeometryMissingVertexCheck::processPolygon( const QgsCurvePolygon *polygon, QgsFeaturePool *featurePool, QList<QgsGeometryCheckError *> &errors, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, QgsFeedback *feedback ) const
130{
131 const QgsFeature &currentFeature = layerFeature.feature();
132 std::unique_ptr<QgsMultiPolygon> boundaries = std::make_unique<QgsMultiPolygon>();
133
134 std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( polygon->exteriorRing()->clone(), mContext->tolerance );
135 boundaries->addGeometry( geomEngine->buffer( mContext->tolerance, 5 ) );
136
137 const int numRings = polygon->numInteriorRings();
138 for ( int i = 0; i < numRings; ++i )
139 {
141 boundaries->addGeometry( geomEngine->buffer( mContext->tolerance, 5 ) );
142 }
143
144 geomEngine = QgsGeometryCheckerUtils::createGeomEngine( boundaries.get(), mContext->tolerance );
145 geomEngine->prepareGeometry();
146
147 const QgsFeatureIds fids = featurePool->getIntersects( boundaries->boundingBox() );
148
149 QgsFeature compareFeature;
150 for ( QgsFeatureId fid : fids )
151 {
152 if ( fid == currentFeature.id() )
153 continue;
154
155 if ( featurePool->getFeature( fid, compareFeature ) )
156 {
157 if ( feedback && feedback->isCanceled() )
158 break;
159
160 const QgsGeometry compareGeometry = compareFeature.geometry();
161 QgsVertexIterator vertexIterator = compareGeometry.vertices();
162 while ( vertexIterator.hasNext() )
163 {
164 const QgsPoint &pt = vertexIterator.next();
165 if ( geomEngine->intersects( &pt ) )
166 {
167 QgsVertexId vertexId;
168 QgsPoint closestVertex = QgsGeometryUtils::closestVertex( *polygon, pt, vertexId );
169
170 if ( closestVertex.distance( pt ) > mContext->tolerance )
171 {
172 bool alreadyReported = false;
173 for ( QgsGeometryCheckError *error : std::as_const( errors ) )
174 {
175 // Only list missing vertices once
176 if ( error->featureId() == currentFeature.id() && error->location() == QgsPointXY( pt ) )
177 {
178 alreadyReported = true;
179 break;
180 }
181 }
182 if ( !alreadyReported )
183 {
184 std::unique_ptr<QgsGeometryMissingVertexCheckError> error = std::make_unique<QgsGeometryMissingVertexCheckError>( this, layerFeature, QgsPointXY( pt ) );
185 error->setAffectedAreaBBox( contextBoundingBox( polygon, vertexId, pt ) );
186 QMap<QString, QgsFeatureIds> involvedFeatures;
187 involvedFeatures[layerFeature.layerId()].insert( layerFeature.feature().id() );
188 involvedFeatures[featurePool->layerId()].insert( fid );
189 error->setInvolvedFeatures( involvedFeatures );
190
191 errors.append( error.release() );
192 }
193 }
194 }
195 }
196 }
197 }
198}
199
200QgsRectangle QgsGeometryMissingVertexCheck::contextBoundingBox( const QgsCurvePolygon *polygon, const QgsVertexId &vertexId, const QgsPoint &point ) const
201{
202 QgsVertexId vertexBefore;
203 QgsVertexId vertexAfter;
204
205 polygon->adjacentVertices( vertexId, vertexBefore, vertexAfter );
206
207 QgsPoint ptBefore = polygon->vertexAt( vertexBefore );
208 QgsPoint ptAt = polygon->vertexAt( vertexId );
209 QgsPoint ptAfter = polygon->vertexAt( vertexAfter );
210
211 double length = std::abs( ptAt.distance( ptBefore ) ) + std::abs( ptAt.distance( ptAfter ) );
212
213 QgsRectangle rect( point.x() - length / 2, point.y() - length / 2, point.x() + length / 2, point.y() + length / 2 );
214 return rect;
215}
216
218{
219 return factoryId();
220}
221
222QList<QgsWkbTypes::GeometryType> QgsGeometryMissingVertexCheck::compatibleGeometryTypes() const
223{
224 return factoryCompatibleGeometryTypes();
225}
226
227QgsGeometryCheck::Flags QgsGeometryMissingVertexCheck::flags() const
228{
229 return factoryFlags();
230}
231
233{
234 return factoryCheckType();
235}
236
238QList<QgsWkbTypes::GeometryType> QgsGeometryMissingVertexCheck::factoryCompatibleGeometryTypes()
239{
241}
242
243bool QgsGeometryMissingVertexCheck::factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP
244{
245 return factoryCompatibleGeometryTypes().contains( layer->geometryType() );
246}
247
248QString QgsGeometryMissingVertexCheck::factoryDescription()
249{
250 return tr( "Missing Vertex" );
251}
252
253QString QgsGeometryMissingVertexCheck::factoryId()
254{
255 return QStringLiteral( "QgsGeometryMissingVertexCheck" );
256}
257
258QgsGeometryCheck::Flags QgsGeometryMissingVertexCheck::factoryFlags()
259{
261}
262
263QgsGeometryCheck::CheckType QgsGeometryMissingVertexCheck::factoryCheckType()
264{
266}
268
270 : QgsGeometryCheckError( check, layerFeature, errorLocation, vidx, value, valueType )
271{
272}
273
275{
276 return mAffectedAreaBBox;
277}
278
280{
281 mAffectedAreaBBox = affectedAreaBBox;
282}
283
285{
286 return mInvolvedFeatures;
287}
288
289void QgsGeometryMissingVertexCheckError::setInvolvedFeatures( const QMap<QString, QgsFeatureIds> &involvedFeatures )
290{
291 mInvolvedFeatures = involvedFeatures;
292}
293
295{
296
298 return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmCheckGeometry.svg" ) );
299 else
300 return QgsApplication::getThemeIcon( QStringLiteral( "/checks/MissingVertex.svg" ) );
301}
Abstract base class for all geometries.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Curve polygon geometry type.
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
void adjacentVertices(QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex) const override
Returns the vertices adjacent to a specified vertex within a geometry.
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
A feature pool is based on a vector layer and caches features.
virtual void updateFeature(QgsFeature &feature)=0
Updates a feature in this pool.
QString layerId() const
The layer id of the layer.
QgsFeatureIds getIntersects(const QgsRectangle &rect) const
Gets all feature ids in the bounding box rect.
bool getFeature(QgsFeatureId id, QgsFeature &feature)
Retrieves the feature with the specified id into feature.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:170
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
double progress() const SIP_HOLDGIL
Returns the current progress reported by the feedback object.
Definition: qgsfeedback.h:80
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
Base configuration for geometry checks.
const double tolerance
The tolerance to allow for in geometry checks.
This represents an error reported by a geometry check.
ValueType
Describes the type of an error value.
@ StatusFixed
The error is fixed.
Status status() const
The status of the error.
QgsFeatureId featureId() const
The id of the feature on which this error has been detected.
void setFixed(int method)
Set the status to fixed and specify the method that has been used to fix the error.
void setFixFailed(const QString &reason)
Set the error status to failed and specify the reason for failure.
const QString & layerId() const
The id of the layer on which this error has been detected.
const QgsPointXY & location() const
The location of the error in map units.
This class implements a geometry check.
QMap< QString, QMap< QgsFeatureId, QList< QgsGeometryCheck::Change > > > Changes
A collection of changes.
const QgsGeometryCheckContext * mContext
@ AvailableInValidation
This geometry check should be available in layer validation on the vector layer peroperties.
CheckType
The type of a check.
@ LayerCheck
The check controls a whole layer (topology checks)
QMap< QString, QgsFeatureIds > allLayerFeatureIds(const QMap< QString, QgsFeaturePool * > &featurePools) const
Returns all layers and feature ids.
A layer feature combination to uniquely identify and access a feature in a set of layers.
QgsFeature feature() const
Returns the feature.
Contains a set of layers and feature ids in those layers to pass to a geometry check.
static std::unique_ptr< QgsGeometryEngine > createGeomEngine(const QgsAbstractGeometry *geometry, double tolerance)
Geometry collection.
QIcon icon() const override
Returns an icon that should be shown for this kind of error.
void setAffectedAreaBBox(const QgsRectangle &affectedAreaBBox)
Set the bounding box of the affected area.
void setInvolvedFeatures(const QMap< QString, QgsFeatureIds > &involvedFeatures)
The two involved features, that share a common boundary but not all common vertices on this boundary.
QMap< QString, QgsFeatureIds > involvedFeatures() const override
Returns a list of involved features.
QgsRectangle affectedAreaBBox() const override
The bounding box of the affected area of the error.
QgsGeometryMissingVertexCheckError(const QgsGeometryCheck *check, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, const QgsPointXY &errorLocation, QgsVertexId vidx=QgsVertexId(), const QVariant &value=QVariant(), ValueType valueType=ValueOther)
Create a new missing vertex check error.
QgsGeometryCheck::Flags flags() const override
Flags for this geometry check.
QString description() const override
Returns a human readable description for this check.
void fixError(const QMap< QString, QgsFeaturePool * > &featurePools, QgsGeometryCheckError *error, int method, const QMap< QString, int > &mergeAttributeIndices, Changes &changes) const override
Fixes the error error with the specified method.
QList< QgsWkbTypes::GeometryType > compatibleGeometryTypes() const override
A list of geometry types for which this check can be performed.
Q_DECL_DEPRECATED QStringList resolutionMethods() const override
Returns a list of descriptions for available resolutions for errors.
QString id() const override
Returns an id for this check.
QgsGeometryCheck::CheckType checkType() const override
Returns the check type.
ResolutionMethod
The available resolutions for missing vertex check.
void collectErrors(const QMap< QString, QgsFeaturePool * > &featurePools, QList< QgsGeometryCheckError * > &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids=LayerFeatureIds()) const override
The main worker method.
QgsGeometryMissingVertexCheck(const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration)
Creates a new missing vertex geometry check with context and the provided geometryCheckConfiguration.
static QgsPoint closestVertex(const QgsAbstractGeometry &geom, const QgsPoint &pt, QgsVertexId &id)
Returns the closest vertex to a geometry for a specified point.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
bool insertVertex(double x, double y, int beforeVertex)
Insert a new vertex before the given vertex index, ring and item (first number is index 0) If the req...
double closestSegmentWithContext(const QgsPointXY &point, QgsPointXY &minDistPoint, int &nextVertexIndex, int *leftOrRightOfSegment=nullptr, double epsilon=DEFAULT_SEGMENT_EPSILON) const
Searches for the closest segment of geometry to the given point.
A class to represent a 2D point.
Definition: qgspointxy.h:59
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
double distance(double x, double y) const SIP_HOLDGIL
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:343
Q_GADGET double x
Definition: qgspoint.h:52
double y
Definition: qgspoint.h:53
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Represents a vector layer which manages a vector based data sets.
Java-style iterator for traversal of vertices of a geometry.
bool hasNext() const
Find out whether there are more vertices.
QgsPoint next()
Returns next vertex of the geometry (undefined behavior if hasNext() returns false before calling nex...
#define SIP_SKIP
Definition: qgis_sip.h:126
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
A list of layers and feature ids for each of these layers.
QMap< QString, QgsFeatureIds > toMap() const
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31