QGIS API Documentation  3.6.0-Noosa (5873452)
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 
27 QgsGeometryMissingVertexCheck::QgsGeometryMissingVertexCheck( const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration )
28  : QgsGeometryCheck( context, geometryCheckConfiguration )
29 
30 {}
31 
32 void QgsGeometryMissingVertexCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
33 {
34  Q_UNUSED( messages )
35  if ( feedback )
36  feedback->setProgress( feedback->progress() + 1.0 );
37 
38  QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
39 
40  QgsFeaturePool *featurePool = featurePools.value( featureIds.firstKey() );
41 
42  const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), nullptr, mContext, true );
43 
44  for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
45  {
46  if ( feedback && feedback->isCanceled() )
47  {
48  break;
49  }
50 
51  const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
52 
53  if ( QgsCurvePolygon *polygon = qgsgeometry_cast<QgsCurvePolygon *>( geom ) )
54  {
55  processPolygon( polygon, featurePool, errors, layerFeature, feedback );
56  }
57  else if ( QgsGeometryCollection *collection = qgsgeometry_cast<QgsGeometryCollection *>( geom ) )
58  {
59  const int numGeometries = collection->numGeometries();
60  for ( int i = 0; i < numGeometries; ++i )
61  {
62  if ( QgsCurvePolygon *polygon = qgsgeometry_cast<QgsCurvePolygon *>( collection->geometryN( i ) ) )
63  {
64  processPolygon( polygon, featurePool, errors, layerFeature, feedback );
65  }
66  }
67  }
68  }
69 }
70 
71 void QgsGeometryMissingVertexCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
72 {
73  Q_UNUSED( featurePools )
74  Q_UNUSED( changes )
75 
76  QMetaEnum metaEnum = QMetaEnum::fromType<QgsGeometryMissingVertexCheck::ResolutionMethod>();
77  if ( !metaEnum.isValid() || !metaEnum.valueToKey( method ) )
78  {
79  error->setFixFailed( tr( "Unknown method" ) );
80  }
81  else
82  {
83  ResolutionMethod methodValue = static_cast<ResolutionMethod>( method );
84  switch ( methodValue )
85  {
86  case NoChange:
87  error->setFixed( method );
88  break;
89 
90  case AddMissingVertex:
91  {
92  QgsFeaturePool *featurePool = featurePools[ error->layerId() ];
93 
94  QgsFeature feature;
95  featurePool->getFeature( error->featureId(), feature );
96 
97  QgsPointXY pointOnSegment; // Should be equal to location
98  int vertexIndex;
99  QgsGeometry geometry = feature.geometry();
100  geometry.closestSegmentWithContext( error->location(), pointOnSegment, vertexIndex );
101  geometry.insertVertex( QgsPoint( error->location() ), vertexIndex );
102  feature.setGeometry( geometry );
103 
104  featurePool->updateFeature( feature );
105  // TODO update "changes" structure
106 
107  error->setFixed( method );
108  }
109  break;
110  }
111  }
112 }
113 
115 {
116  static QStringList methods = QStringList()
117  << tr( "No action" )
118  << tr( "Add missing vertex" );
119  return methods;
120 }
121 
123 {
124  return factoryDescription();
125 }
126 
127 void QgsGeometryMissingVertexCheck::processPolygon( const QgsCurvePolygon *polygon, QgsFeaturePool *featurePool, QList<QgsGeometryCheckError *> &errors, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, QgsFeedback *feedback ) const
128 {
129  const QgsFeature &currentFeature = layerFeature.feature();
130  std::unique_ptr<QgsMultiPolygon> boundaries = qgis::make_unique<QgsMultiPolygon>();
131 
132  std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( polygon->exteriorRing(), mContext->tolerance );
133  boundaries->addGeometry( geomEngine->buffer( mContext->tolerance, 5 ) );
134 
135  const int numRings = polygon->numInteriorRings();
136  for ( int i = 0; i < numRings; ++i )
137  {
139  boundaries->addGeometry( geomEngine->buffer( mContext->tolerance, 5 ) );
140  }
141 
142  geomEngine = QgsGeometryCheckerUtils::createGeomEngine( boundaries.get(), mContext->tolerance );
143  geomEngine->prepareGeometry();
144 
145  const QgsFeatureIds fids = featurePool->getIntersects( boundaries->boundingBox() );
146 
147  QgsFeature compareFeature;
148  for ( QgsFeatureId fid : fids )
149  {
150  if ( fid == currentFeature.id() )
151  continue;
152 
153  if ( featurePool->getFeature( fid, compareFeature, feedback ) )
154  {
155  if ( feedback && feedback->isCanceled() )
156  break;
157 
158  QgsVertexIterator vertexIterator = compareFeature.geometry().vertices();
159  while ( vertexIterator.hasNext() )
160  {
161  const QgsPoint &pt = vertexIterator.next();
162  if ( geomEngine->intersects( &pt ) )
163  {
164  QgsVertexId vertexId;
165  QgsPoint closestVertex = QgsGeometryUtils::closestVertex( *polygon, pt, vertexId );
166 
167  if ( closestVertex.distance( pt ) > mContext->tolerance )
168  {
169  bool alreadyReported = false;
170  for ( QgsGeometryCheckError *error : qgis::as_const( errors ) )
171  {
172  // Only list missing vertices once
173  if ( error->featureId() == currentFeature.id() && error->location() == QgsPointXY( pt ) )
174  {
175  alreadyReported = true;
176  break;
177  }
178  }
179  if ( !alreadyReported )
180  errors.append( new QgsGeometryCheckError( this, layerFeature, QgsPointXY( pt ) ) );
181  }
182  }
183  }
184  }
185  }
186 }
187 
189 {
190  return factoryId();
191 }
192 
193 QList<QgsWkbTypes::GeometryType> QgsGeometryMissingVertexCheck::compatibleGeometryTypes() const
194 {
195  return factoryCompatibleGeometryTypes();
196 }
197 
198 QgsGeometryCheck::Flags QgsGeometryMissingVertexCheck::flags() const
199 {
200  return factoryFlags();
201 }
202 
204 {
205  return factoryCheckType();
206 }
207 
209 QList<QgsWkbTypes::GeometryType> QgsGeometryMissingVertexCheck::factoryCompatibleGeometryTypes()
210 {
212 }
213 
214 bool QgsGeometryMissingVertexCheck::factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP
215 {
216  return factoryCompatibleGeometryTypes().contains( layer->geometryType() );
217 }
218 
219 QString QgsGeometryMissingVertexCheck::factoryDescription()
220 {
221  return tr( "Missing Vertex" );
222 }
223 
224 QString QgsGeometryMissingVertexCheck::factoryId()
225 {
226  return QStringLiteral( "QgsGeometryMissingVertexCheck" );
227 }
228 
229 QgsGeometryCheck::Flags QgsGeometryMissingVertexCheck::factoryFlags()
230 {
232 }
233 
234 QgsGeometryCheck::CheckType QgsGeometryMissingVertexCheck::factoryCheckType()
235 {
237 }
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.
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.
bool getFeature(QgsFeatureId id, QgsFeature &feature, QgsFeedback *feedback=nullptr)
Retrieves the feature with the specified id into feature.
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.
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:276
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
QMap< QString, QgsFeatureIds > toMap() const
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:106
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 cancelation 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
A layer feature combination to uniquely identify and access a feature in a set of layers...
ResolutionMethod
The available resolutions for missing vertex check.
const QgsFeature & feature() const
Returns the feature.
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.
void collectErrors(const QMap< QString, QgsFeaturePool *> &featurePools, QList< QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids=LayerFeatureIds()) const override
The main worker method.
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.
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 setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
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.
QgsGeometry geometry
Definition: qgsfeature.h:67
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)