QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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 }
QgsVertexId::part
int part
Definition: qgsabstractgeometry.h:1080
QgsGeometryCheckError::setFixFailed
void setFixFailed(const QString &reason)
Set the error status to failed and specify the reason for failure.
Definition: qgsgeometrycheckerror.cpp:109
QgsGeometryAreaCheck::collectErrors
void collectErrors(const QMap< QString, QgsFeaturePool * > &featurePools, QList< QgsGeometryCheckError * > &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids=LayerFeatureIds()) const override
The main worker method.
Definition: qgsgeometryareacheck.cpp:23
QgsVertexId::isValid
bool isValid() const
Returns true if the vertex id is valid.
Definition: qgsabstractgeometry.h:1051
QgsGeometryCheckError::ValueArea
@ ValueArea
The value is an area.
Definition: qgsgeometrycheckerror.h:56
QgsAbstractGeometry::wkbType
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Definition: qgsabstractgeometry.h:189
QgsGeometryCheckerUtils::getGeomPart
static QgsAbstractGeometry * getGeomPart(QgsAbstractGeometry *geom, int partIdx)
Definition: qgsgeometrycheckerutils.cpp:268
qgsgeometryareacheck.h
QgsGeometryCheckError::setObsolete
void setObsolete()
Set the error status to obsolete.
Definition: qgsgeometrycheckerror.h:166
QgsGeometryCheckError::featureId
QgsFeatureId featureId() const
The id of the feature on which this error has been detected.
Definition: qgsgeometrycheckerror.h:90
QgsGeometryCheck::Changes
QMap< QString, QMap< QgsFeatureId, QList< QgsGeometryCheck::Change > > > Changes
A collection of changes.
Definition: qgsgeometrycheck.h:214
QgsGeometryCheck::replaceFeatureGeometryPart
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.
Definition: qgsgeometrycheck.cpp:92
QgsGeometryCheck::allLayerFeatureIds
QMap< QString, QgsFeatureIds > allLayerFeatureIds(const QMap< QString, QgsFeaturePool * > &featurePools) const
Returns all layers and feature ids.
Definition: qgsgeometrycheck.cpp:82
QgsGeometryCheck::LayerFeatureIds::isEmpty
bool isEmpty() const
Definition: qgsgeometrycheck.h:118
QgsFeature::geometry
QgsGeometry geometry
Definition: qgsfeature.h:71
QgsAbstractGeometry::partCount
virtual int partCount() const =0
Returns count of parts contained in the geometry.
qgsgeometrycheckerror.h
QgsGeometryAreaCheck::Delete
@ Delete
Definition: qgsgeometryareacheck.h:31
QgsGeometryAreaCheck::fixError
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.
Definition: qgsgeometryareacheck.cpp:44
QgsFeaturePool::getFeature
bool getFeature(QgsFeatureId id, QgsFeature &feature)
Retrieves the feature with the specified id into feature.
Definition: qgsfeaturepool.cpp:39
QgsGeometryCheck::LayerFeatureIds::toMap
QMap< QString, QgsFeatureIds > toMap() const
Definition: qgsgeometrycheck.h:113
qgsfeaturepool.h
QgsGeometryAreaCheck::compatibleGeometryTypes
QList< QgsWkbTypes::GeometryType > compatibleGeometryTypes() const override
A list of geometry types for which this check can be performed.
Definition: qgsgeometryareacheck.h:37
QgsFeature::id
QgsFeatureId id
Definition: qgsfeature.h:68
QgsAbstractGeometry::area
virtual double area() const
Returns the planar, 2-dimensional area of the geometry.
Definition: qgsabstractgeometry.cpp:142
QgsGeometryCollection
Geometry collection.
Definition: qgsgeometrycollection.h:35
qgsgeometryengine.h
qgsgeometrycheckcontext.h
QgsGeometryAreaCheck::MergeLargestArea
@ MergeLargestArea
Definition: qgsgeometryareacheck.h:31
QgsGeometryCheckerUtils::sharedEdgeLength
static double sharedEdgeLength(const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol)
Definition: qgsgeometrycheckerutils.cpp:364
QgsGeometryCheckError::setFixed
void setFixed(int method)
Set the status to fixed and specify the method that has been used to fix the error.
Definition: qgsgeometrycheckerror.cpp:98
QgsGeometryCheck::scaleFactor
double scaleFactor(const QPointer< QgsVectorLayer > &layer) const
Determines the scale factor of a layer to the map coordinate reference system.
Definition: qgsgeometrycheck.cpp:171
QgsFeedback
Definition: qgsfeedback.h:43
QgsGeometryCheckerUtils::LayerFeature
Definition: qgsgeometrycheckerutils.h:51
QgsFeature::attribute
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:262
QgsGeometryCheckContext::reducedTolerance
const double reducedTolerance
The tolerance to allow for in geometry checks.
Definition: qgsgeometrycheckcontext.h:69
QgsGeometryCheck::LayerFeatureIds
A list of layers and feature ids for each of these layers.
Definition: qgsgeometrycheck.h:105
QgsGeometry::constGet
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:128
QgsGeometryAreaCheck::MergeLongestEdge
@ MergeLongestEdge
Definition: qgsgeometryareacheck.h:31
QgsWkbTypes::isSingleType
static bool isSingleType(Type type)
Returns true if the WKB type is a single type.
Definition: qgswkbtypes.h:821
QgsGeometryCheckError::vidx
const QgsVertexId & vidx() const
The id of the affected vertex.
Definition: qgsgeometrycheckerror.h:140
QgsAbstractGeometry::isEmpty
virtual bool isEmpty() const
Returns true if the geometry is empty.
Definition: qgsabstractgeometry.cpp:298
QgsFeatureIds
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
QgsAbstractGeometry
Abstract base class for all geometries.
Definition: qgsabstractgeometry.h:71
QgsGeometryCheck::mContext
const QgsGeometryCheckContext * mContext
Definition: qgsgeometrycheck.h:359
QgsFeaturePool::layer
QgsVectorLayer * layer() const
Gets a pointer to the underlying layer.
Definition: qgsfeaturepool.cpp:107
QgsAbstractGeometry::centroid
virtual QgsPoint centroid() const
Returns the centroid of the geometry.
Definition: qgsabstractgeometry.cpp:167
QgsGeometry
Definition: qgsgeometry.h:122
QgsGeometryCheckerUtils::LayerFeatures
Definition: qgsgeometrycheckerutils.h:112
QgsVertexId
Utility class for identifying a unique vertex within a geometry.
Definition: qgsabstractgeometry.h:1033
QgsGeometryCheckerUtils::createGeomEngine
static std::unique_ptr< QgsGeometryEngine > createGeomEngine(const QgsAbstractGeometry *geometry, double tolerance)
Definition: qgsgeometrycheckerutils.cpp:263
QgsGeometry::boundingBox
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Definition: qgsgeometry.cpp:962
QgsGeometryAreaCheck::resolutionMethods
Q_DECL_DEPRECATED QStringList resolutionMethods() const override
Returns a list of descriptions for available resolutions for errors.
Definition: qgsgeometryareacheck.cpp:209
qgsgeometrycollection.h
QgsGeometryAreaCheck::NoChange
@ NoChange
Definition: qgsgeometryareacheck.h:31
QgsFeature
Definition: qgsfeature.h:55
QgsGeometryCheckError::layerId
const QString & layerId() const
The id of the layer on which this error has been detected.
Definition: qgsgeometrycheckerror.h:85
QgsGeometryCheck::deleteFeatureGeometryPart
void deleteFeatureGeometryPart(const QMap< QString, QgsFeaturePool * > &featurePools, const QString &layerId, QgsFeature &feature, int partIdx, Changes &changes) const
Deletes a part of a feature geometry.
Definition: qgsgeometrycheck.cpp:115
QgsGeometryCheckError
Definition: qgsgeometrycheckerror.h:35
QgsFeaturePool::getIntersects
QgsFeatureIds getIntersects(const QgsRectangle &rect) const
Gets all feature ids in the bounding box rect.
Definition: qgsfeaturepool.cpp:100
QgsFeaturePool
Definition: qgsfeaturepool.h:37
QgsFeatureId
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
QgsGeometryCollection::area
double area() const override
Returns the planar, 2-dimensional area of the geometry.
Definition: qgsgeometrycollection.cpp:654
QgsGeometryAreaCheck::MergeIdenticalAttribute
@ MergeIdenticalAttribute
Definition: qgsgeometryareacheck.h:31