QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
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 "qgsapplication.h"
19#include "qgscurve.h"
20#include "qgscurvepolygon.h"
21#include "qgsfeedback.h"
23#include "qgsgeometryengine.h"
24#include "qgsgeometryutils.h"
25#include "qgsmultipolygon.h"
26
27#include <QString>
28
29#include "moc_qgsgeometrymissingvertexcheck.cpp"
30
31using namespace Qt::StringLiterals;
32
34 : QgsGeometryCheck( context, geometryCheckConfiguration )
35
36{}
37
38QgsGeometryCheck::Result QgsGeometryMissingVertexCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
39{
40 Q_UNUSED( messages )
41 if ( feedback )
42 feedback->setProgress( feedback->progress() + 1.0 );
43
44 QMap<QString, QSet<QVariant>> uniqueIds;
45 QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
46 QgsFeaturePool *featurePool = featurePools.value( featureIds.firstKey() );
47 if ( !featurePool )
48 {
49 QgsDebugError( u"Could not retrieve feature pool for %1"_s.arg( featureIds.firstKey() ) );
51 }
52 const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), nullptr, mContext, true );
53 for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
54 {
55 if ( feedback && feedback->isCanceled() )
56 {
58 }
59
60 if ( context()->uniqueIdFieldIndex != -1 )
61 {
62 QgsGeometryCheck::Result result = checkUniqueId( layerFeature, uniqueIds );
64 {
65 return result;
66 }
67 }
68
69 const QgsGeometry geometry = layerFeature.geometry();
70 const QgsAbstractGeometry *geom = geometry.constGet();
71
73 {
74 processPolygon( polygon, featurePool, errors, layerFeature, feedback );
75 }
77 {
78 const int numGeometries = collection->numGeometries();
79 for ( int i = 0; i < numGeometries; ++i )
80 {
81 if ( const QgsCurvePolygon *polygon = qgsgeometry_cast<const QgsCurvePolygon *>( collection->geometryN( i ) ) )
82 {
83 processPolygon( polygon, featurePool, errors, layerFeature, feedback );
84 }
85 }
86 }
87 }
89}
90
91void QgsGeometryMissingVertexCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
92{
93 Q_UNUSED( featurePools )
94 Q_UNUSED( changes )
95
96 QMetaEnum metaEnum = QMetaEnum::fromType<QgsGeometryMissingVertexCheck::ResolutionMethod>();
97 if ( !metaEnum.isValid() || !metaEnum.valueToKey( method ) )
98 {
99 error->setFixFailed( tr( "Unknown method" ) );
100 }
101 else
102 {
103 ResolutionMethod methodValue = static_cast<ResolutionMethod>( method );
104 switch ( methodValue )
105 {
106 case NoChange:
107 error->setFixed( method );
108 break;
109
110 case AddMissingVertex:
111 {
112 QgsFeaturePool *featurePool = featurePools[error->layerId()];
113
114 QgsFeature feature;
115 featurePool->getFeature( error->featureId(), feature );
116
117 QgsPointXY pointOnSegment; // Should be equal to location
118 int vertexIndex;
119 QgsGeometry geometry = feature.geometry();
120 geometry.closestSegmentWithContext( error->location(), pointOnSegment, vertexIndex );
121 geometry.insertVertex( QgsPoint( error->location() ), vertexIndex );
122 feature.setGeometry( geometry );
123
124 featurePool->updateFeature( feature );
125 // TODO update "changes" structure
126
127 error->setFixed( method );
128 }
129 break;
130 }
131 }
132}
133
135{
136 static QStringList methods = QStringList()
137 << tr( "No action" )
138 << tr( "Add missing vertex" );
139 return methods;
140}
141
143{
144 return factoryDescription();
145}
146
147void QgsGeometryMissingVertexCheck::processPolygon( const QgsCurvePolygon *polygon, QgsFeaturePool *featurePool, QList<QgsGeometryCheckError *> &errors, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, QgsFeedback *feedback ) const
148{
149 const QgsFeature &currentFeature = layerFeature.feature();
150 auto boundaries = std::make_unique<QgsMultiPolygon>();
151
152 std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometry::createGeometryEngine( polygon->exteriorRing()->clone(), mContext->tolerance ) );
153 boundaries->addGeometry( geomEngine->buffer( mContext->tolerance, 5 ) );
154
155 const int numRings = polygon->numInteriorRings();
156 for ( int i = 0; i < numRings; ++i )
157 {
158 geomEngine.reset( QgsGeometry::createGeometryEngine( polygon->interiorRing( i ), mContext->tolerance ) );
159 boundaries->addGeometry( geomEngine->buffer( mContext->tolerance, 5 ) );
160 }
161
162 geomEngine.reset( QgsGeometry::createGeometryEngine( boundaries.get(), mContext->tolerance ) );
163 geomEngine->prepareGeometry();
164
165 const QgsFeatureIds fids = featurePool->getIntersects( boundaries->boundingBox() );
166
167 QgsFeature compareFeature;
168 for ( QgsFeatureId fid : fids )
169 {
170 if ( fid == currentFeature.id() )
171 continue;
172
173 if ( featurePool->getFeature( fid, compareFeature ) )
174 {
175 if ( feedback && feedback->isCanceled() )
176 break;
177
178 const QgsGeometry compareGeometry = compareFeature.geometry();
179 QgsVertexIterator vertexIterator = compareGeometry.vertices();
180 while ( vertexIterator.hasNext() )
181 {
182 const QgsPoint &pt = vertexIterator.next();
183 if ( geomEngine->intersects( &pt ) )
184 {
185 QgsVertexId vertexId;
186 QgsPoint closestVertex = QgsGeometryUtils::closestVertex( *polygon, pt, vertexId );
187
188 if ( closestVertex.distance( pt ) > mContext->tolerance )
189 {
190 bool alreadyReported = false;
191 for ( QgsGeometryCheckError *error : std::as_const( errors ) )
192 {
193 // Only list missing vertices once
194 if ( error->featureId() == currentFeature.id() && error->location() == QgsPointXY( pt ) )
195 {
196 alreadyReported = true;
197 break;
198 }
199 }
200 if ( !alreadyReported )
201 {
202 auto error = std::make_unique<QgsGeometryMissingVertexCheckError>( this, layerFeature, QgsPointXY( pt ) );
203 error->setAffectedAreaBBox( contextBoundingBox( polygon, vertexId, pt ) );
204 QMap<QString, QgsFeatureIds> involvedFeatures;
205 involvedFeatures[layerFeature.layerId()].insert( layerFeature.feature().id() );
206 involvedFeatures[featurePool->layerId()].insert( fid );
207 error->setInvolvedFeatures( involvedFeatures );
208
209 errors.append( error.release() );
210 }
211 }
212 }
213 }
214 }
215 }
216}
217
218QgsRectangle QgsGeometryMissingVertexCheck::contextBoundingBox( const QgsCurvePolygon *polygon, const QgsVertexId &vertexId, const QgsPoint &point ) const
219{
220 QgsVertexId vertexBefore;
221 QgsVertexId vertexAfter;
222
223 polygon->adjacentVertices( vertexId, vertexBefore, vertexAfter );
224
225 QgsPoint ptBefore = polygon->vertexAt( vertexBefore );
226 QgsPoint ptAt = polygon->vertexAt( vertexId );
227 QgsPoint ptAfter = polygon->vertexAt( vertexAfter );
228
229 double length = std::abs( ptAt.distance( ptBefore ) ) + std::abs( ptAt.distance( ptAfter ) );
230
231 QgsRectangle rect( point.x() - length / 2, point.y() - length / 2, point.x() + length / 2, point.y() + length / 2 );
232 return rect;
233}
234
236{
237 return factoryId();
238}
239
241{
242 return factoryCompatibleGeometryTypes();
243}
244
246{
247 return factoryFlags();
248}
249
251{
252 return factoryCheckType();
253}
254
256QList<Qgis::GeometryType> QgsGeometryMissingVertexCheck::factoryCompatibleGeometryTypes()
257{
259}
260
261bool QgsGeometryMissingVertexCheck::factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP
262{
263 return factoryCompatibleGeometryTypes().contains( layer->geometryType() );
264}
265
266QString QgsGeometryMissingVertexCheck::factoryDescription()
267{
268 return tr( "Missing Vertex" );
269}
270
271QString QgsGeometryMissingVertexCheck::factoryId()
272{
273 return u"QgsGeometryMissingVertexCheck"_s;
274}
275
276QgsGeometryCheck::Flags QgsGeometryMissingVertexCheck::factoryFlags()
277{
279}
280
281QgsGeometryCheck::CheckType QgsGeometryMissingVertexCheck::factoryCheckType()
282{
284}
286
291
293{
294 return mAffectedAreaBBox;
295}
296
301
303{
304 return mInvolvedFeatures;
305}
306
308{
309 mInvolvedFeatures = involvedFeatures;
310}
311
313{
315 return QgsApplication::getThemeIcon( u"/algorithms/mAlgorithmCheckGeometry.svg"_s );
316 else
317 return QgsApplication::getThemeIcon( u"/checks/MissingVertex.svg"_s );
318}
@ Polygon
Polygons.
Definition qgis.h:368
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.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
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.
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:60
QgsFeatureId id
Definition qgsfeature.h:68
QgsGeometry geometry
Definition qgsfeature.h:71
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:55
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:63
double progress() const
Returns the current progress reported by the feedback object.
Definition qgsfeedback.h:79
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.
const QgsVertexId & vidx() const
The id of the affected vertex.
QgsFeatureId featureId() const
The id of the feature on which this error has been detected.
QgsGeometryCheckError(const QgsGeometryCheck *check, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, const QgsPointXY &errorLocation, QgsVertexId vidx=QgsVertexId(), const QVariant &value=QVariant(), ValueType valueType=ValueOther)
Create a new geometry check error with the parent check and for the layerFeature pair at the errorLoc...
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.
ValueType valueType() const
The type of the value.
QVariant value() const
An additional value for the error.
const QgsGeometryCheck * check() const
The geometry check that created this error.
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.
Base class for geometry checks.
QMap< QString, QMap< QgsFeatureId, QList< QgsGeometryCheck::Change > > > Changes
A collection of changes.
QFlags< Flag > Flags
const QgsGeometryCheckContext * mContext
@ AvailableInValidation
This geometry check should be available in layer validation on the vector layer properties.
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.
Result checkUniqueId(const QgsGeometryCheckerUtils::LayerFeature layerFeature, QMap< QString, QSet< QVariant > > &uniqueIds) const
Checks that there are no duplicated unique IDs.
Result
Result of the geometry checker operation.
@ Canceled
User canceled calculation.
@ Success
Operation completed successfully.
QgsGeometryCheck(const QgsGeometryCheckContext *context, const QVariantMap &configuration)
Create a new geometry check.
const QgsGeometryCheckContext * context() const
Returns the context.
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.
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.
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.
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< Qgis::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.
QgsGeometryCheck::Result collectErrors(const QMap< QString, QgsFeaturePool * > &featurePools, QList< QgsGeometryCheckError * > &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids=LayerFeatureIds()) const override
The main worker method.
ResolutionMethod
The available resolutions for missing vertex check.
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.
double closestSegmentWithContext(const QgsPointXY &point, QgsPointXY &minDistPoint, int &nextVertexIndex, int *leftOrRightOfSegment=nullptr, double epsilon=Qgis::DEFAULT_SEGMENT_EPSILON) const
Searches for the closest segment of geometry to the given point.
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
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...
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
Represents a 2D point.
Definition qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
double x
Definition qgspoint.h:56
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition qgspoint.h:391
double y
Definition qgspoint.h:57
A rectangle specified with double values.
Represents a vector layer which manages a vector based dataset.
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:134
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
#define QgsDebugError(str)
Definition qgslogger.h:59
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:34