QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgsgeometrymissingvertexcheck.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgeometrymissingvertexcheck.cpp
3  ---------------------
4  begin : September 2018
5  copyright : (C) 2018 Matthias Kuhn
6  email : [email protected]
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"
19 #include "qgsgeometrycollection.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 
28 QgsGeometryMissingVertexCheck::QgsGeometryMissingVertexCheck( const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration )
29  : QgsGeometryCheck( context, geometryCheckConfiguration )
30 
31 {}
32 
33 void 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 
73 void 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 
92  case AddMissingVertex:
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 
129 void 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 = qgis::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 : qgis::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 = qgis::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 
200 QgsRectangle 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 
222 QList<QgsWkbTypes::GeometryType> QgsGeometryMissingVertexCheck::compatibleGeometryTypes() const
223 {
224  return factoryCompatibleGeometryTypes();
225 }
226 
227 QgsGeometryCheck::Flags QgsGeometryMissingVertexCheck::flags() const
228 {
229  return factoryFlags();
230 }
231 
233 {
234  return factoryCheckType();
235 }
236 
238 QList<QgsWkbTypes::GeometryType> QgsGeometryMissingVertexCheck::factoryCompatibleGeometryTypes()
239 {
241 }
242 
243 bool QgsGeometryMissingVertexCheck::factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP
244 {
245  return factoryCompatibleGeometryTypes().contains( layer->geometryType() );
246 }
247 
248 QString QgsGeometryMissingVertexCheck::factoryDescription()
249 {
250  return tr( "Missing Vertex" );
251 }
252 
253 QString QgsGeometryMissingVertexCheck::factoryId()
254 {
255  return QStringLiteral( "QgsGeometryMissingVertexCheck" );
256 }
257 
258 QgsGeometryCheck::Flags QgsGeometryMissingVertexCheck::factoryFlags()
259 {
261 }
262 
263 QgsGeometryCheck::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 
284 QMap<QString, QgsFeatureIds> QgsGeometryMissingVertexCheckError::involvedFeatures() const
285 {
286  return mInvolvedFeatures;
287 }
288 
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 }
QgsFeatureId id
Definition: qgsfeature.h:64
double closestSegmentWithContext(const QgsPointXY &point, QgsPointXY &minDistPoint, int &afterVertex, int *leftOf=nullptr, double epsilon=DEFAULT_SEGMENT_EPSILON) const
Searches for the closest segment of geometry to the given point.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
double y
Definition: qgspoint.h:42
QgsFeatureIds getIntersects(const QgsRectangle &rect) const
Gets all feature ids in the bounding box rect.
QStringList resolutionMethods() const override
Returns a list of descriptions for available resolutions for errors.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
QgsGeometryCheck::CheckType checkType() const override
Returns the check type.
QgsGeometryCheck::Flags flags() const override
Flags for this geometry check.
double progress() const
Returns the current progress reported by the feedback object.
Definition: qgsfeedback.h:80
Java-style iterator for traversal of vertices of a geometry.
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.
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:276
QIcon icon() const override
Returns an icon that should be shown for this kind of error.
A class to represent a 2D point.
Definition: qgspointxy.h:43
const QgsPointXY & location() const
The location of the error in map units.
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
QMap< QString, QgsFeatureIds > toMap() const
QMap< QString, QgsFeatureIds > involvedFeatures() const override
Returns a list of involved features.
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
Curve polygon geometry type.
Contains a set of layers and feature ids in those layers to pass to a geometry check.
CheckType
The type of a check.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
QList< QgsWkbTypes::GeometryType > compatibleGeometryTypes() const override
A list of geometry types for which this check can be performed.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:111
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
ValueType
Describes the type of an error value.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
static QgsPoint closestVertex(const QgsAbstractGeometry &geom, const QgsPoint &pt, QgsVertexId &id)
Returns the closest vertex to a geometry for a specified point.
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...
virtual void updateFeature(QgsFeature &feature)=0
Updates a feature in this pool.
void setFixFailed(const QString &reason)
Set the error status to failed and specify the reason for failure.
Base class for feedback objects to be used for cancellation of something running in a worker thread...
Definition: qgsfeedback.h:44
Base configuration for geometry checks.
void fixError(const QMap< QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap< QString, int > &mergeAttributeIndices, Changes &changes) const override
Fix the error error with the specified method.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
QString id() const override
Returns an id for this check.
Utility class for identifying a unique vertex within a geometry.
Geometry collection.
#define SIP_SKIP
Definition: qgis_sip.h:119
bool getFeature(QgsFeatureId id, QgsFeature &feature)
Retrieves the feature with the specified id into feature.
A layer feature combination to uniquely identify and access a feature in a set of layers...
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
ResolutionMethod
The available resolutions for missing vertex check.
This class implements a geometry check.
Abstract base class for all geometries.
QMap< QString, QgsFeatureIds > allLayerFeatureIds(const QMap< QString, QgsFeaturePool *> &featurePools) const
Returns all layers and feature ids.
const QString & layerId() const
The id of the layer on which this error has been detected.
const double tolerance
The tolerance to allow for in geometry checks.
QgsGeometryMissingVertexCheck(const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration)
Creates a new missing vertex geometry check with context and the provided geometryCheckConfiguration...
const QgsGeometryCheckContext * mContext
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
A list of layers and feature ids for each of these layers.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
void collectErrors(const QMap< QString, QgsFeaturePool *> &featurePools, QList< QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids=LayerFeatureIds()) const override
The main worker method.
void adjacentVertices(QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex) const override
Returns the vertices adjacent to a specified vertex within a geometry.
Status status() const
The status of the error.
QMap< QString, QMap< QgsFeatureId, QList< QgsGeometryCheck::Change > > > Changes
A collection of changes.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
A feature pool is based on a vector layer and caches features.
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
QgsPoint next()
Returns next vertex of the geometry (undefined behavior if hasNext() returns false before calling nex...
void setFixed(int method)
Set the status to fixed and specify the method that has been used to fix the error.
void setInvolvedFeatures(const QMap< QString, QgsFeatureIds > &involvedFeatures)
The two involved features, that share a common boundary but not all common vertices on this boundary...
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
void setAffectedAreaBBox(const QgsRectangle &affectedAreaBBox)
Set the bounding box of the affected area.
QgsFeatureId featureId() const
The id of the feature on which this error has been detected.
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry, including all geometry parts and rings.
QgsRectangle affectedAreaBBox() const override
The bounding box of the affected area of the error.
QgsGeometry geometry
Definition: qgsfeature.h:67
QgsFeature feature() const
Returns the feature.
QString description() const override
Returns a human readable description for this check.
This represents an error reported by a geometry check.
static std::unique_ptr< QgsGeometryEngine > createGeomEngine(const QgsAbstractGeometry *geometry, double tolerance)
bool hasNext() const
Find out whether there are more vertices.
const QgsCurve * exteriorRing() const
Returns the curve polygon&#39;s exterior ring.
Represents a vector layer which manages a vector based data sets.
This geometry check should be available in layer validation on the vector layer peroperties.
The check controls a whole layer (topology checks)
QString layerId() const
The layer id of the layer.
double x
Definition: qgspoint.h:41