QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgsgeometrygapcheck.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgeometrygapcheck.cpp
3  ---------------------
4  begin : September 2015
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 
17 #include "qgsgeometryengine.h"
18 #include "qgsgeometrygapcheck.h"
19 #include "qgsgeometrycollection.h"
20 #include "qgsfeaturepool.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsvectorlayerutils.h"
23 #include "qgsfeedback.h"
24 #include "qgsapplication.h"
25 #include "qgsproject.h"
27 #include "qgspolygon.h"
28 #include "qgscurve.h"
29 #include "qgssnappingutils.h"
30 
31 #include "geos_c.h"
32 
33 QgsGeometryGapCheck::QgsGeometryGapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration )
34  : QgsGeometryCheck( context, configuration )
35  , mGapThresholdMapUnits( configuration.value( QStringLiteral( "gapThreshold" ) ).toDouble() )
36 {
37 }
38 
39 void QgsGeometryGapCheck::prepare( const QgsGeometryCheckContext *context, const QVariantMap &configuration )
40 {
41  if ( configuration.value( QStringLiteral( "allowedGapsEnabled" ) ).toBool() )
42  {
43  QgsVectorLayer *layer = context->project()->mapLayer<QgsVectorLayer *>( configuration.value( "allowedGapsLayer" ).toString() );
44  if ( layer )
45  {
46  mAllowedGapsLayer = layer;
47  mAllowedGapsSource = qgis::make_unique<QgsVectorLayerFeatureSource>( layer );
48 
49  mAllowedGapsBuffer = configuration.value( QStringLiteral( "allowedGapsBuffer" ) ).toDouble();
50  }
51  }
52  else
53  {
54  mAllowedGapsSource.reset();
55  }
56 }
57 
58 void QgsGeometryGapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
59 {
60  if ( feedback )
61  feedback->setProgress( feedback->progress() + 1.0 );
62 
63  std::unique_ptr<QgsAbstractGeometry> allowedGapsGeom;
64  std::unique_ptr<QgsGeometryEngine> allowedGapsGeomEngine;
65 
66  if ( mAllowedGapsSource )
67  {
68  QVector<QgsGeometry> allowedGaps;
69  QgsFeatureRequest request;
71  QgsFeatureIterator iterator = mAllowedGapsSource->getFeatures( request );
72  QgsFeature feature;
73 
74  while ( iterator.nextFeature( feature ) )
75  {
76  QgsGeometry geom = feature.geometry();
77  QgsGeometry gg = geom.buffer( mAllowedGapsBuffer, 20 );
78  allowedGaps.append( gg );
79  }
80 
81  std::unique_ptr< QgsGeometryEngine > allowedGapsEngine = QgsGeometryCheckerUtils::createGeomEngine( nullptr, mContext->tolerance );
82 
83  // Create union of allowed gaps
84  QString errMsg;
85  allowedGapsGeom.reset( allowedGapsEngine->combine( allowedGaps, &errMsg ) );
86  allowedGapsGeomEngine = QgsGeometryCheckerUtils::createGeomEngine( allowedGapsGeom.get(), mContext->tolerance );
87  allowedGapsGeomEngine->prepareGeometry();
88  }
89 
90  QVector<QgsGeometry> geomList;
91  QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
92  const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), nullptr, mContext, true );
93  for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
94  {
95  geomList.append( layerFeature.geometry() );
96 
97  if ( feedback && feedback->isCanceled() )
98  {
99  geomList.clear();
100  break;
101  }
102  }
103 
104  if ( geomList.isEmpty() )
105  {
106  return;
107  }
108 
109  std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( nullptr, mContext->tolerance );
110 
111  // Create union of geometry
112  QString errMsg;
113  std::unique_ptr<QgsAbstractGeometry> unionGeom( geomEngine->combine( geomList, &errMsg ) );
114  if ( !unionGeom )
115  {
116  messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
117  return;
118  }
119 
120  // Get envelope of union
121  geomEngine = QgsGeometryCheckerUtils::createGeomEngine( unionGeom.get(), mContext->tolerance );
122  geomEngine->prepareGeometry();
123  std::unique_ptr<QgsAbstractGeometry> envelope( geomEngine->envelope( &errMsg ) );
124  if ( !envelope )
125  {
126  messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
127  return;
128  }
129 
130  // Buffer envelope
131  geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope.get(), mContext->tolerance );
132  geomEngine->prepareGeometry();
133  QgsAbstractGeometry *bufEnvelope = geomEngine->buffer( 2, 0, GEOSBUF_CAP_SQUARE, GEOSBUF_JOIN_MITRE, 4. ); //#spellok //#spellok
134  envelope.reset( bufEnvelope );
135 
136  // Compute difference between envelope and union to obtain gap polygons
137  geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope.get(), mContext->tolerance );
138  geomEngine->prepareGeometry();
139  std::unique_ptr<QgsAbstractGeometry> diffGeom( geomEngine->difference( unionGeom.get(), &errMsg ) );
140  if ( !diffGeom )
141  {
142  messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
143  return;
144  }
145 
146  // For each gap polygon which does not lie on the boundary, get neighboring polygons and add error
147  QgsGeometryPartIterator parts = diffGeom->parts();
148  while ( parts.hasNext() )
149  {
150  const QgsAbstractGeometry *gapGeom = parts.next();
151  // Skip the gap between features and boundingbox
152  const double spacing = context()->tolerance;
153  if ( gapGeom->boundingBox().snappedToGrid( spacing ) == envelope->boundingBox().snappedToGrid( spacing ) )
154  {
155  continue;
156  }
157 
158  // Skip gaps above threshold
159  if ( ( mGapThresholdMapUnits > 0 && gapGeom->area() > mGapThresholdMapUnits ) || gapGeom->area() < mContext->reducedTolerance )
160  {
161  continue;
162  }
163 
164  QgsRectangle gapAreaBBox = gapGeom->boundingBox();
165 
166  // Get neighboring polygons
167  QMap<QString, QgsFeatureIds> neighboringIds;
168  const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds.keys(), gapAreaBBox, compatibleGeometryTypes(), mContext );
169  std::unique_ptr< QgsGeometryEngine > gapGeomEngine = QgsGeometryCheckerUtils::createGeomEngine( gapGeom, mContext->tolerance );
170  gapGeomEngine->prepareGeometry();
171  for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
172  {
173  const QgsGeometry geom = layerFeature.geometry();
174  if ( gapGeomEngine->distance( geom.constGet() ) < mContext->tolerance )
175  {
176  neighboringIds[layerFeature.layer()->id()].insert( layerFeature.feature().id() );
177  gapAreaBBox.combineExtentWith( geom.boundingBox() );
178  }
179  }
180 
181  if ( neighboringIds.isEmpty() )
182  {
183  continue;
184  }
185 
186  if ( allowedGapsGeomEngine && allowedGapsGeomEngine->contains( gapGeom ) )
187  {
188  continue;
189  }
190 
191  // Add error
192  double area = gapGeom->area();
193  QgsRectangle gapBbox = gapGeom->boundingBox();
194  errors.append( new QgsGeometryGapCheckError( this, QString(), QgsGeometry( gapGeom->clone() ), neighboringIds, area, gapBbox, gapAreaBBox ) );
195  }
196 }
197 
198 void QgsGeometryGapCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
199 {
200  QMetaEnum metaEnum = QMetaEnum::fromType<QgsGeometryGapCheck::ResolutionMethod>();
201  if ( !metaEnum.isValid() || !metaEnum.valueToKey( method ) )
202  {
203  error->setFixFailed( tr( "Unknown method" ) );
204  }
205  else
206  {
207  ResolutionMethod methodValue = static_cast<ResolutionMethod>( method );
208  switch ( methodValue )
209  {
210  case NoChange:
211  error->setFixed( method );
212  break;
213 
214  case MergeLongestEdge:
215  {
216  QString errMsg;
217  if ( mergeWithNeighbor( featurePools, static_cast<QgsGeometryGapCheckError *>( error ), changes, errMsg, LongestSharedEdge ) )
218  {
219  error->setFixed( method );
220  }
221  else
222  {
223  error->setFixFailed( tr( "Failed to merge with neighbor: %1" ).arg( errMsg ) );
224  }
225  break;
226  }
227 
228  case AddToAllowedGaps:
229  {
230  QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mAllowedGapsLayer.data() );
231  if ( layer )
232  {
233  if ( !layer->isEditable() && !layer->startEditing() )
234  {
235  error->setFixFailed( tr( "Could not start editing layer %1" ).arg( layer->name() ) );
236  }
237  else
238  {
239  QgsFeature feature = QgsVectorLayerUtils::createFeature( layer, error->geometry() );
240  QgsFeatureList features = QgsVectorLayerUtils::makeFeatureCompatible( feature, layer );
241  if ( !layer->addFeatures( features ) )
242  {
243  error->setFixFailed( tr( "Could not add feature to layer %1" ).arg( layer->name() ) );
244  }
245  else
246  {
247  error->setFixed( method );
248  }
249  }
250  }
251  else
252  {
253  error->setFixFailed( tr( "Allowed gaps layer could not be resolved" ) );
254  }
255  break;
256  }
257 
258  case CreateNewFeature:
259  {
260  QgsGeometryGapCheckError *gapCheckError = static_cast<QgsGeometryGapCheckError *>( error );
261  QgsProject *project = QgsProject::instance();
262  QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( project->mapLayer( gapCheckError->neighbors().keys().first() ) );
263  if ( layer )
264  {
265  const QgsGeometry geometry = error->geometry();
267  QgsFeature feature = QgsVectorLayerUtils::createFeature( layer, geometry, QgsAttributeMap(), &context );
268  if ( !layer->addFeature( feature ) )
269  {
270  error->setFixFailed( tr( "Could not add feature" ) );
271  }
272  else
273  {
274  error->setFixed( method );
275  }
276  }
277  else
278  {
279  error->setFixFailed( tr( "Could not resolve target layer %1 to add feature" ).arg( error->layerId() ) );
280  }
281  break;
282  }
283 
284  case MergeLargestArea:
285  {
286  QString errMsg;
287  if ( mergeWithNeighbor( featurePools, static_cast<QgsGeometryGapCheckError *>( error ), changes, errMsg, LargestArea ) )
288  {
289  error->setFixed( method );
290  }
291  else
292  {
293  error->setFixFailed( tr( "Failed to merge with neighbor: %1" ).arg( errMsg ) );
294  }
295  break;
296  }
297  }
298  }
299 }
300 
301 bool QgsGeometryGapCheck::mergeWithNeighbor( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryGapCheckError *err, Changes &changes, QString &errMsg, Condition condition ) const
302 {
303  double maxVal = 0.;
304  QString mergeLayerId;
305  QgsFeature mergeFeature;
306  int mergePartIdx = -1;
307 
308  const QgsGeometry geometry = err->geometry();
309  const QgsAbstractGeometry *errGeometry = QgsGeometryCheckerUtils::getGeomPart( geometry.constGet(), 0 );
310 
311  const auto layerIds = err->neighbors().keys();
312  QList<QgsFeature> neighbours;
313 
314  // Search for touching neighboring geometries
315  for ( const QString &layerId : layerIds )
316  {
317  QgsFeaturePool *featurePool = featurePools.value( layerId );
318  std::unique_ptr<QgsAbstractGeometry> errLayerGeom( errGeometry->clone() );
320  errLayerGeom->transform( ct, QgsCoordinateTransform::ReverseTransform );
321 
322  const auto featureIds = err->neighbors().value( layerId );
323 
324  for ( QgsFeatureId testId : featureIds )
325  {
326  QgsFeature feature;
327  if ( !featurePool->getFeature( testId, feature ) )
328  {
329  continue;
330  }
331 
332  QgsGeometry transformedGeometry = feature.geometry();
333  transformedGeometry.transform( ct );
334  feature.setGeometry( transformedGeometry );
335  neighbours.append( feature );
336  }
337 
338  for ( const QgsFeature &testFeature : neighbours )
339  {
340  const QgsGeometry featureGeom = testFeature.geometry();
341  const QgsAbstractGeometry *testGeom = featureGeom.constGet();
342  for ( int iPart = 0, nParts = testGeom->partCount(); iPart < nParts; ++iPart )
343  {
344  double val = 0;
345  switch ( condition )
346  {
347  case LongestSharedEdge:
349  break;
350 
351  case LargestArea:
352  // We might get a neighbour where we touch only a corner
354  val = QgsGeometryCheckerUtils::getGeomPart( testGeom, iPart )->area();
355  break;
356  }
357 
358  if ( val > maxVal )
359  {
360  maxVal = val;
361  mergeFeature = testFeature;
362  mergePartIdx = iPart;
363  mergeLayerId = layerId;
364  }
365  }
366  }
367  }
368 
369  if ( maxVal == 0. )
370  {
371  return false;
372  }
373 
374  // Create an index of all neighbouring vertices
375  QgsSpatialIndex neighbourVerticesIndex( QgsSpatialIndex::Flag::FlagStoreFeatureGeometries );
376  int id = 0;
377  for ( const QgsFeature &neighbour : neighbours )
378  {
379  QgsVertexIterator vit = neighbour.geometry().vertices();
380  while ( vit.hasNext() )
381  {
382  QgsPoint pt = vit.next();
383  QgsFeature f;
384  f.setId( id ); // required for SpatialIndex to return the correct result
385  f.setGeometry( QgsGeometry( pt.clone() ) );
386  neighbourVerticesIndex.addFeature( f );
387  id++;
388  }
389  }
390 
391  // Snap to the closest vertex
392  QgsPolyline snappedRing;
393  QgsVertexIterator iterator = errGeometry->vertices();
394  while ( iterator.hasNext() )
395  {
396  QgsPoint pt = iterator.next();
397  QgsGeometry closestGeom = neighbourVerticesIndex.geometry( neighbourVerticesIndex.nearestNeighbor( QgsPointXY( pt ) ).first() );
398  if ( !closestGeom.isEmpty() )
399  {
400  snappedRing.append( QgsPoint( closestGeom.vertexAt( 0 ) ) );
401  }
402  }
403 
404  std::unique_ptr<QgsPolygon> snappedErrGeom = qgis::make_unique<QgsPolygon>();
405  snappedErrGeom->setExteriorRing( new QgsLineString( snappedRing ) );
406 
407  // Merge geometries
408  QgsFeaturePool *featurePool = featurePools[ mergeLayerId ];
409  std::unique_ptr<QgsAbstractGeometry> errLayerGeom( snappedErrGeom->clone() );
411  errLayerGeom->transform( ct, QgsCoordinateTransform::ReverseTransform );
412  const QgsGeometry mergeFeatureGeom = mergeFeature.geometry();
413  const QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.constGet();
414  std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( errLayerGeom.get(), 0 );
415  std::unique_ptr<QgsAbstractGeometry> combinedGeom( geomEngine->combine( QgsGeometryCheckerUtils::getGeomPart( mergeGeom, mergePartIdx ), &errMsg ) );
416  if ( !combinedGeom || combinedGeom->isEmpty() || !QgsWkbTypes::isSingleType( combinedGeom->wkbType() ) )
417  {
418  return false;
419  }
420 
421  // Add merged polygon to destination geometry
422  replaceFeatureGeometryPart( featurePools, mergeLayerId, mergeFeature, mergePartIdx, combinedGeom.release(), changes );
423 
424  return true;
425 }
426 
427 
429 {
430  QStringList methods = QStringList()
431  << tr( "Add gap area to neighboring polygon with longest shared edge" )
432  << tr( "No action" );
433  if ( mAllowedGapsSource )
434  methods << tr( "Add gap to allowed exceptions" );
435 
436  return methods;
437 }
438 
439 QList<QgsGeometryCheckResolutionMethod> QgsGeometryGapCheck::availableResolutionMethods() const
440 {
441  QList<QgsGeometryCheckResolutionMethod> fixes
442  {
443  QgsGeometryCheckResolutionMethod( MergeLongestEdge, tr( "Add to longest shared edge" ), tr( "Add the gap area to the neighbouring polygon with the longest shared edge." ), false ),
444  QgsGeometryCheckResolutionMethod( CreateNewFeature, tr( "Create new feature" ), tr( "Create a new feature from the gap area." ), false ),
445  QgsGeometryCheckResolutionMethod( MergeLargestArea, tr( "Add to largest neighbouring area" ), tr( "Add the gap area to the neighbouring polygon with the largest area." ), false )
446  };
447 
448  if ( mAllowedGapsSource )
449  fixes << QgsGeometryCheckResolutionMethod( AddToAllowedGaps, tr( "Add Gap to Allowed Exceptions" ), tr( "Create a new feature from the gap geometry on the allowed exceptions layer." ), true );
450 
451  fixes << QgsGeometryCheckResolutionMethod( NoChange, tr( "No action" ), tr( "Do not perform any action and mark this error as fixed." ), false );
452 
453  return fixes;
454 }
455 
457 {
458  return factoryDescription();
459 }
460 
461 QString QgsGeometryGapCheck::id() const
462 {
463  return factoryId();
464 }
465 
466 QgsGeometryCheck::Flags QgsGeometryGapCheck::flags() const
467 {
468  return factoryFlags();
469 }
470 
472 QString QgsGeometryGapCheck::factoryDescription()
473 {
474  return tr( "Gap" );
475 }
476 
477 QString QgsGeometryGapCheck::factoryId()
478 {
479  return QStringLiteral( "QgsGeometryGapCheck" );
480 }
481 
482 QgsGeometryCheck::Flags QgsGeometryGapCheck::factoryFlags()
483 {
485 }
486 
487 QList<QgsWkbTypes::GeometryType> QgsGeometryGapCheck::factoryCompatibleGeometryTypes()
488 {
490 }
491 
492 bool QgsGeometryGapCheck::factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP
493 {
494  return factoryCompatibleGeometryTypes().contains( layer->geometryType() );
495 }
496 
497 QgsGeometryCheck::CheckType QgsGeometryGapCheck::factoryCheckType()
498 {
500 }
502 
504 {
505  return mContextBoundingBox;
506 }
507 
509 {
510  QgsGeometryGapCheckError *err = dynamic_cast<QgsGeometryGapCheckError *>( other );
512 }
513 
515 {
516  QgsGeometryGapCheckError *err = dynamic_cast<QgsGeometryGapCheckError *>( other );
517  return err && err->layerId() == layerId() && err->neighbors() == neighbors();
518 }
519 
521 {
523  // Static cast since this should only get called if isEqual == true
524  const QgsGeometryGapCheckError *err = static_cast<const QgsGeometryGapCheckError *>( other );
525  mNeighbors = err->mNeighbors;
526  mGapAreaBBox = err->mGapAreaBBox;
527 }
528 
530 {
531  return true;
532 }
533 
535 {
536  return mGapAreaBBox;
537 }
538 
539 QMap<QString, QgsFeatureIds> QgsGeometryGapCheckError::involvedFeatures() const
540 {
541  return mNeighbors;
542 }
543 
545 {
546 
548  return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmCheckGeometry.svg" ) );
549  else
550  return QgsApplication::getThemeIcon( QStringLiteral( "/checks/SliverOrGap.svg" ) );
551 }
Abstract base class for all geometries.
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
virtual QgsRectangle boundingBox() const =0
Returns the minimal bounding box for the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
virtual double area() const
Returns the planar, 2-dimensional area of the geometry.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Class for doing transforms between two map coordinate systems.
@ ReverseTransform
Transform from destination to source CRS.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
A feature pool is based on a vector layer and caches features.
QgsCoordinateReferenceSystem crs() const
The coordinate reference system of this layer.
bool getFeature(QgsFeatureId id, QgsFeature &feature)
Retrieves the feature with the specified id into feature.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
void setId(QgsFeatureId id)
Sets the feature ID for this feature.
Definition: qgsfeature.cpp:114
QgsGeometry geometry
Definition: qgsfeature.h:67
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:144
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
bool isCanceled() const
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
double progress() const
Returns the current progress reported by the feedback object.
Definition: qgsfeedback.h:80
Base configuration for geometry checks.
const QgsProject * project() const
The project can be used to resolve additional layers.
const double reducedTolerance
The tolerance to allow for in geometry checks.
const QgsCoordinateTransformContext transformContext
The coordinate transform context with which transformations will be done.
const QgsCoordinateReferenceSystem mapCrs
The coordinate system in which calculations should be done.
const double tolerance
The tolerance to allow for in geometry checks.
This represents an error reported by a geometry check.
const QString & layerId() const
The id of the layer on which this error has been detected.
@ StatusFixed
The error is fixed.
Status status() const
The status of the error.
virtual void update(const QgsGeometryCheckError *other)
Update this error with the information from other.
const QgsGeometryCheck * mCheck
const QgsPointXY & location() const
The location of the error in map units.
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.
QgsGeometry geometry() const
The geometry of the error in map units.
This class implements a resolution for problems detected in geometry checks.
This class implements a geometry check.
QMap< QString, QMap< QgsFeatureId, QList< QgsGeometryCheck::Change > > > Changes
A collection of changes.
void replaceFeatureGeometryPart(const QMap< QString, QgsFeaturePool * > &featurePools, const QString &layerId, QgsFeature &feature, int partIdx, QgsAbstractGeometry *newPartGeom, Changes &changes) const
Replaces a part in a feature geometry.
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.
const QgsGeometryCheckContext * context() const
Returns the context.
A layer feature combination to uniquely identify and access a feature in a set of layers.
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)
static QgsAbstractGeometry * getGeomPart(QgsAbstractGeometry *geom, int partIdx)
static double sharedEdgeLength(const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol)
static bool pointsFuzzyEqual(const QgsPointXY &p1, const QgsPointXY &p2, double tol)
Determine whether two points are equal up to the specified tolerance.
An error produced by a QgsGeometryGapCheck.
void update(const QgsGeometryCheckError *other) override
Update this error with the information from other.
QMap< QString, QgsFeatureIds > involvedFeatures() const override
Returns a list of involved features.
bool closeMatch(QgsGeometryCheckError *other) const override
Check if this error is almost equal to other.
QIcon icon() const override
Returns an icon that should be shown for this kind of error.
QgsRectangle affectedAreaBBox() const override
The bounding box of the affected area of the error.
QgsRectangle contextBoundingBox() const override
The context of the error.
const QMap< QString, QgsFeatureIds > & neighbors() const
A map of layers and feature ids of the neighbors of the gap.
bool isEqual(QgsGeometryCheckError *other) const override
Check if this error is equal to other.
bool handleChanges(const QgsGeometryCheck::Changes &) override
Apply a list of changes.
QgsGeometryGapCheck(const QgsGeometryCheckContext *context, const QVariantMap &configuration)
The configuration accepts a "gapThreshold" key which specifies the maximum gap size in squared map un...
void collectErrors(const QMap< QString, QgsFeaturePool * > &featurePools, QList< QgsGeometryCheckError * > &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids=LayerFeatureIds()) const override
The main worker method.
Q_DECL_DEPRECATED QStringList resolutionMethods() const override
Returns a list of descriptions for available resolutions for errors.
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.
void prepare(const QgsGeometryCheckContext *context, const QVariantMap &configuration) override
Will be run in the main thread before collectErrors is called (which may be run from a background thr...
QList< QgsGeometryCheckResolutionMethod > availableResolutionMethods() const override
Returns a list of available resolution methods.
QList< QgsWkbTypes::GeometryType > compatibleGeometryTypes() const override
A list of geometry types for which this check can be performed.
QString id() const override
Returns an id for this check.
ResolutionMethod
Resolution methods for geometry gap checks.
@ CreateNewFeature
Create a new feature with the gap geometry.
@ AddToAllowedGaps
Add gap geometry to allowed gaps layer.
@ MergeLongestEdge
Merge the gap with the polygon with the longest shared edge.
@ NoChange
Do not handle the error.
@ MergeLargestArea
Merge with neighbouring polygon with largest area.
QgsGeometryCheck::Flags flags() const override
Flags for this geometry check.
Java-style iterator for traversal of parts of a geometry.
bool hasNext() const SIP_HOLDGIL
Find out whether there are more parts.
QgsAbstractGeometry * next()
Returns next part of the geometry (undefined behavior if hasNext() returns false before calling next(...
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
QString name
Definition: qgsmaplayer.h:88
A class to represent a 2D point.
Definition: qgspointxy.h:44
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:38
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition: qgspoint.cpp:104
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:99
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:501
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:359
QgsRectangle snappedToGrid(double spacing) const
Returns a copy of this rectangle that is snapped to a grid with the specified spacing between the gri...
A spatial index for QgsFeature objects.
static QgsFeatureList makeFeatureCompatible(const QgsFeature &feature, const QgsVectorLayer *layer, QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags())
Converts input feature to be compatible with the given layer.
static QgsFeature createFeature(const QgsVectorLayer *layer, const QgsGeometry &geometry=QgsGeometry(), const QgsAttributeMap &attributes=QgsAttributeMap(), QgsExpressionContext *context=nullptr)
Creates a new feature ready for insertion into a layer.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE bool startEditing()
Makes the layer editable.
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) FINAL
Adds a list of features to the sink.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) FINAL
Adds a single feature to the sink.
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...
static bool isSingleType(Type type) SIP_HOLDGIL
Returns true if the WKB type is a single type.
Definition: qgswkbtypes.h:822
#define SIP_SKIP
Definition: qgis_sip.h:126
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:38
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:614
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
QList< int > QgsAttributeList
Definition: qgsfield.h:26
QgsPointSequence QgsPolyline
Polyline as represented as a vector of points.
Definition: qgsgeometry.h:71
A list of layers and feature ids for each of these layers.
QMap< QString, QgsFeatureIds > toMap() const