QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgsgeometryareacheck.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgeometryareacheck.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 "qgsgeometrycollection.h"
19 #include "qgsgeometryareacheck.h"
20 #include "qgsfeaturepool.h"
21 #include "qgsgeometrycheckerror.h"
22 
23 void QgsGeometryAreaCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
24 {
25  Q_UNUSED( messages )
26  QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
27  QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext );
28  for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
29  {
30  const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
31  double layerToMapUnits = scaleFactor( layerFeature.layer() );
32  for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
33  {
34  double value;
35  const QgsAbstractGeometry *part = QgsGeometryCheckerUtils::getGeomPart( geom, iPart );
36  if ( checkThreshold( layerToMapUnits, part, value ) )
37  {
38  errors.append( new QgsGeometryCheckError( this, layerFeature, part->centroid(), QgsVertexId( iPart ), value * layerToMapUnits * layerToMapUnits, QgsGeometryCheckError::ValueArea ) );
39  }
40  }
41  }
42 }
43 
44 void QgsGeometryAreaCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const
45 {
46  QgsFeaturePool *featurePool = featurePools[ error->layerId() ];
47  QgsFeature feature;
48  if ( !featurePool->getFeature( error->featureId(), feature ) )
49  {
50  error->setObsolete();
51  return;
52  }
53 
54  const QgsGeometry g = feature.geometry();
55  const QgsAbstractGeometry *geom = g.constGet();
56  QgsVertexId vidx = error->vidx();
57 
58  double layerToMapUnits = scaleFactor( featurePool->layer() );
59 
60  // Check if polygon still exists
61  if ( !vidx.isValid( geom ) )
62  {
63  error->setObsolete();
64  return;
65  }
66 
67  // Check if error still applies
68  double value;
69  if ( !checkThreshold( layerToMapUnits, QgsGeometryCheckerUtils::getGeomPart( geom, vidx.part ), value ) )
70  {
71  error->setObsolete();
72  return;
73  }
74 
75  // Fix with selected method
76  if ( method == NoChange )
77  {
78  error->setFixed( method );
79  }
80  else if ( method == Delete )
81  {
82  deleteFeatureGeometryPart( featurePools, error->layerId(), feature, vidx.part, changes );
83  error->setFixed( method );
84  }
85  else if ( method == MergeLongestEdge || method == MergeLargestArea || method == MergeIdenticalAttribute )
86  {
87  QString errMsg;
88  if ( mergeWithNeighbor( featurePools, error->layerId(), feature, vidx.part, method, mergeAttributeIndices[error->layerId()], changes, errMsg ) )
89  {
90  error->setFixed( method );
91  }
92  else
93  {
94  error->setFixFailed( tr( "Failed to merge with neighbor: %1" ).arg( errMsg ) );
95  }
96  }
97  else
98  {
99  error->setFixFailed( tr( "Unknown method" ) );
100  }
101 }
102 
103 bool QgsGeometryAreaCheck::checkThreshold( double layerToMapUnits, const QgsAbstractGeometry *geom, double &value ) const
104 {
105  value = geom->area();
106  double threshold = mAreaThreshold / ( layerToMapUnits * layerToMapUnits );
107  return value < threshold;
108 }
109 
110 bool QgsGeometryAreaCheck::mergeWithNeighbor( const QMap<QString, QgsFeaturePool *> &featurePools,
111  const QString &layerId, QgsFeature &feature,
112  int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const
113 {
114  QgsFeaturePool *featurePool = featurePools[ layerId ];
115 
116  double maxVal = 0.;
117  QgsFeature mergeFeature;
118  int mergePartIdx = -1;
119  bool matchFound = false;
120  QgsGeometry featureGeometry = feature.geometry();
121  const QgsAbstractGeometry *geom = featureGeometry.constGet();
122 
123  // Search for touching neighboring geometries
124  const QgsFeatureIds intersects = featurePool->getIntersects( featureGeometry.boundingBox() );
125  for ( QgsFeatureId testId : intersects )
126  {
127  QgsFeature testFeature;
128  if ( !featurePool->getFeature( testId, testFeature ) )
129  {
130  continue;
131  }
132  QgsGeometry testFeatureGeom = testFeature.geometry();
133  const QgsAbstractGeometry *testGeom = testFeatureGeom.constGet();
134  for ( int testPartIdx = 0, nTestParts = testGeom->partCount(); testPartIdx < nTestParts; ++testPartIdx )
135  {
136  if ( testId == feature.id() && testPartIdx == partIdx )
137  {
138  continue;
139  }
141  if ( len > 0. )
142  {
143  if ( method == MergeLongestEdge || method == MergeLargestArea )
144  {
145  double val;
146  if ( method == MergeLongestEdge )
147  {
148  val = len;
149  }
150  else
151  {
152  if ( dynamic_cast<const QgsGeometryCollection *>( testGeom ) )
153  val = static_cast<const QgsGeometryCollection *>( testGeom )->geometryN( testPartIdx )->area();
154  else
155  val = testGeom->area();
156  }
157  if ( val > maxVal )
158  {
159  maxVal = val;
160  mergeFeature = testFeature;
161  mergePartIdx = testPartIdx;
162  }
163  }
164  else if ( method == MergeIdenticalAttribute )
165  {
166  if ( testFeature.attribute( mergeAttributeIndex ) == feature.attribute( mergeAttributeIndex ) )
167  {
168  mergeFeature = testFeature;
169  mergePartIdx = testPartIdx;
170  matchFound = true;
171  break;
172  }
173  }
174  }
175  }
176  if ( matchFound )
177  {
178  break;
179  }
180  }
181 
182  if ( !matchFound && maxVal == 0. )
183  {
184  return method == MergeIdenticalAttribute;
185  }
186 
187  // Merge geometries
188  QgsGeometry mergeFeatureGeom = mergeFeature.geometry();
189  const QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.constGet();
190  std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometryCheckerUtils::createGeomEngine( QgsGeometryCheckerUtils::getGeomPart( mergeGeom, mergePartIdx ), mContext->reducedTolerance ) );
191  QgsAbstractGeometry *combinedGeom = geomEngine->combine( QgsGeometryCheckerUtils::getGeomPart( geom, partIdx ), &errMsg );
192  if ( !combinedGeom || combinedGeom->isEmpty() || !QgsWkbTypes::isSingleType( combinedGeom->wkbType() ) )
193  {
194  return false;
195  }
196 
197  // Replace polygon in merge geometry
198  if ( mergeFeature.id() == feature.id() && mergePartIdx > partIdx )
199  {
200  --mergePartIdx;
201  }
202  replaceFeatureGeometryPart( featurePools, layerId, mergeFeature, mergePartIdx, combinedGeom, changes );
203  // Remove polygon from source geometry
204  deleteFeatureGeometryPart( featurePools, layerId, feature, partIdx, changes );
205 
206  return true;
207 }
208 
210 {
211  static QStringList methods = QStringList()
212  << tr( "Merge with neighboring polygon with longest shared edge" )
213  << tr( "Merge with neighboring polygon with largest area" )
214  << tr( "Merge with neighboring polygon with identical attribute value, if any, or leave as is" )
215  << tr( "Delete feature" )
216  << tr( "No action" );
217  return methods;
218 }
QgsFeatureId id
Definition: qgsfeature.h:64
QList< QgsWkbTypes::GeometryType > compatibleGeometryTypes() const override
A list of geometry types for which this check can be performed.
QgsFeatureIds getIntersects(const QgsRectangle &rect) const
Gets all feature ids in the bounding box rect.
virtual bool isEmpty() const
Returns true if the geometry is empty.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
double scaleFactor(const QPointer< QgsVectorLayer > &layer) const
Determines the scale factor of a layer to the map coordinate reference system.
static QgsAbstractGeometry * getGeomPart(QgsAbstractGeometry *geom, int partIdx)
bool isValid() const
Returns true if the vertex id is valid.
QMap< QString, QgsFeatureIds > toMap() const
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
Contains a set of layers and feature ids in those layers to pass to a geometry check.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:111
void setObsolete()
Set the error status to obsolete.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
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.
void setFixFailed(const QString &reason)
Set the error status to failed and specify the reason for failure.
virtual QgsPoint centroid() const
Returns the centroid of the geometry.
Base class for feedback objects to be used for cancellation of something running in a worker thread...
Definition: qgsfeedback.h:44
QgsVectorLayer * layer() const
Gets a pointer to the underlying layer.
static double sharedEdgeLength(const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol)
Utility class for identifying a unique vertex within a geometry.
Geometry collection.
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...
double area() const override
Returns the area of the geometry.
void collectErrors(const QMap< QString, QgsFeaturePool *> &featurePools, QList< QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids=LayerFeatureIds()) const override
The main worker method.
virtual double area() const
Returns the area of the geometry.
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 QgsGeometryCheckContext * mContext
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
const double reducedTolerance
The tolerance to allow for in geometry checks.
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.
static bool isSingleType(Type type)
Returns true if the WKB type is a single type.
Definition: qgswkbtypes.h:550
QMap< QString, QMap< QgsFeatureId, QList< QgsGeometryCheck::Change > > > Changes
A collection of changes.
A feature pool is based on a vector layer and caches features.
void setFixed(int method)
Set the status to fixed and specify the method that has been used to fix the error.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
QgsFeatureId featureId() const
The id of the feature on which this error has been detected.
QgsGeometry geometry
Definition: qgsfeature.h:67
This represents an error reported by a geometry check.
static std::unique_ptr< QgsGeometryEngine > createGeomEngine(const QgsAbstractGeometry *geometry, double tolerance)
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.
QStringList resolutionMethods() const override
Returns a list of descriptions for available resolutions for errors.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:262
const QgsVertexId & vidx() const
The id of the affected vertex.
void deleteFeatureGeometryPart(const QMap< QString, QgsFeaturePool *> &featurePools, const QString &layerId, QgsFeature &feature, int partIdx, Changes &changes) const
Deletes a part of a feature geometry.
virtual int partCount() const =0
Returns count of parts contained in the geometry.