QGIS API Documentation  3.23.0-Master (eb871beae0)
qgsgeometrycheckerutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * qgsgeometrycheckerutils.cpp *
3  * ------------------- *
4  * copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
5  * email : [email protected] *
6  ***************************************************************************/
7 
8 /***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
19 #include "qgsgeometry.h"
20 #include "qgsgeometryutils.h"
21 #include "qgsfeaturepool.h"
22 #include "qgspolygon.h"
23 #include "qgsgeos.h"
24 #include "qgsgeometrycollection.h"
25 #include "qgssurface.h"
26 #include "qgsvectorlayer.h"
27 #include "qgsgeometrycheck.h"
28 #include "qgsfeedback.h"
29 
30 #include <qmath.h>
31 
33  const QgsFeature &feature,
34  const QgsGeometryCheckContext *context,
35  bool useMapCrs )
36  : mFeaturePool( pool )
37  , mFeature( feature )
38  , mMapCrs( useMapCrs )
39 {
40  mGeometry = feature.geometry();
41  const QgsCoordinateTransform transform( pool->crs(), context->mapCrs, context->transformContext );
42  if ( useMapCrs && context->mapCrs.isValid() && !transform.isShortCircuited() )
43  {
44  try
45  {
46  mGeometry.transform( transform );
47  }
48  catch ( const QgsCsException & )
49  {
50  QgsDebugMsg( QStringLiteral( "Shrug. What shall we do with a geometry that cannot be converted?" ) );
51  }
52  }
53 }
54 
56 {
57  return mFeature;
58 }
59 
60 QPointer<QgsVectorLayer> QgsGeometryCheckerUtils::LayerFeature::layer() const
61 {
62  return mFeaturePool->layerPtr();
63 }
64 
66 {
67  return mFeaturePool->layerId();
68 }
69 
71 {
72  return mGeometry;
73 }
74 
76 {
77  return QStringLiteral( "%1:%2" ).arg( mFeaturePool->layerName() ).arg( mFeature.id() );
78 }
79 
81 {
82  return layerId() == other.layerId() && mFeature.id() == other.mFeature.id();
83 }
84 
86 {
87  return layerId() != other.layerId() || mFeature.id() != other.mFeature.id();
88 }
89 
91 
92 QgsGeometryCheckerUtils::LayerFeatures::iterator::iterator( const QStringList::const_iterator &layerIt, const LayerFeatures *parent )
93  : mLayerIt( layerIt )
94  , mFeatureIt( QgsFeatureIds::const_iterator() )
95  , mParent( parent )
96 {
97  nextLayerFeature( true );
98 }
99 
101 {
102  mLayerIt = rh.mLayerIt;
103  mFeatureIt = rh.mFeatureIt;
104  mParent = rh.mParent;
105  mCurrentFeature = std::make_unique<LayerFeature>( *rh.mCurrentFeature.get() );
106 }
107 
109 {
110  return mMapCrs;
111 }
113 {
114 }
115 
117 {
118  const iterator tmp( *this );
119  ++*this;
120  return tmp;
121 }
122 
124 {
125  Q_ASSERT( mCurrentFeature );
126  return *mCurrentFeature;
127 }
128 
130 {
131  return mLayerIt != other.mLayerIt || mFeatureIt != other.mFeatureIt;
132 }
133 
135 {
136  nextLayerFeature( false );
137  return *this;
138 }
139 bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextLayerFeature( bool begin )
140 {
141  if ( !begin && nextFeature( false ) )
142  {
143  return true;
144  }
145  while ( nextLayer( begin ) )
146  {
147  begin = false;
148  if ( nextFeature( true ) )
149  {
150  return true;
151  }
152  }
153  // End
154  mFeatureIt = QgsFeatureIds::const_iterator();
155  mCurrentFeature.reset();
156  return false;
157 }
158 
159 bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextLayer( bool begin )
160 {
161  if ( !begin )
162  {
163  ++mLayerIt;
164  }
165  while ( true )
166  {
167  if ( mLayerIt == mParent->mLayerIds.end() )
168  {
169  break;
170  }
171  if ( mParent->mGeometryTypes.contains( mParent->mFeaturePools[*mLayerIt]->geometryType() ) )
172  {
173  mFeatureIt = mParent->mFeatureIds[*mLayerIt].constBegin();
174  return true;
175  }
176  ++mLayerIt;
177  }
178  return false;
179 }
180 
181 bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextFeature( bool begin )
182 {
183  QgsFeaturePool *featurePool = mParent->mFeaturePools[*mLayerIt];
184  const QgsFeatureIds &featureIds = mParent->mFeatureIds[*mLayerIt];
185  if ( !begin )
186  {
187  ++mFeatureIt;
188  }
189  while ( true )
190  {
191  if ( mFeatureIt == featureIds.end() )
192  {
193  break;
194  }
195  if ( mParent->mFeedback )
196  mParent->mFeedback->setProgress( mParent->mFeedback->progress() + 1.0 );
197  QgsFeature feature;
198  if ( featurePool->getFeature( *mFeatureIt, feature ) && !feature.geometry().isNull() )
199  {
200  mCurrentFeature = std::make_unique<LayerFeature>( featurePool, feature, mParent->mContext, mParent->mUseMapCrs );
201  return true;
202  }
203  ++mFeatureIt;
204  }
205  return false;
206 }
207 
209 
210 QgsGeometryCheckerUtils::LayerFeatures::LayerFeatures( const QMap<QString, QgsFeaturePool *> &featurePools,
211  const QMap<QString, QgsFeatureIds> &featureIds,
212  const QList<QgsWkbTypes::GeometryType> &geometryTypes,
213  QgsFeedback *feedback,
214  const QgsGeometryCheckContext *context,
215  bool useMapCrs )
216  : mFeaturePools( featurePools )
217  , mFeatureIds( featureIds )
218  , mLayerIds( featurePools.keys() )
219  , mGeometryTypes( geometryTypes )
220  , mFeedback( feedback )
221  , mContext( context )
222  , mUseMapCrs( useMapCrs )
223 {}
224 
225 QgsGeometryCheckerUtils::LayerFeatures::LayerFeatures( const QMap<QString, QgsFeaturePool *> &featurePools,
226  const QList<QString> &layerIds, const QgsRectangle &extent,
227  const QList<QgsWkbTypes::GeometryType> &geometryTypes,
228  const QgsGeometryCheckContext *context )
229  : mFeaturePools( featurePools )
230  , mLayerIds( layerIds )
231  , mExtent( extent )
232  , mGeometryTypes( geometryTypes )
233  , mContext( context )
234  , mUseMapCrs( true )
235 {
236  for ( const QString &layerId : layerIds )
237  {
238  const QgsFeaturePool *featurePool = featurePools[layerId];
239  if ( geometryTypes.contains( featurePool->geometryType() ) )
240  {
241  const QgsCoordinateTransform ct( featurePool->crs(), context->mapCrs, context->transformContext );
242  mFeatureIds.insert( layerId, featurePool->getIntersects( ct.transform( extent, Qgis::TransformDirection::Reverse ) ) );
243  }
244  else
245  {
246  mFeatureIds.insert( layerId, QgsFeatureIds() );
247  }
248  }
249 }
250 
252 {
253  return iterator( mLayerIds.constBegin(), this );
254 }
255 
257 {
258  return iterator( mLayerIds.end(), this );
259 }
260 
262 
263 std::unique_ptr<QgsGeometryEngine> QgsGeometryCheckerUtils::createGeomEngine( const QgsAbstractGeometry *geometry, double tolerance )
264 {
265  return std::make_unique<QgsGeos>( geometry, tolerance );
266 }
267 
269 {
270  if ( dynamic_cast<QgsGeometryCollection *>( geom ) )
271  {
272  return static_cast<QgsGeometryCollection *>( geom )->geometryN( partIdx );
273  }
274  return geom;
275 }
276 
278 {
279  if ( dynamic_cast<const QgsGeometryCollection *>( geom ) )
280  {
281  return static_cast<const QgsGeometryCollection *>( geom )->geometryN( partIdx );
282  }
283  return geom;
284 }
285 
286 QList<const QgsLineString *> QgsGeometryCheckerUtils::polygonRings( const QgsPolygon *polygon )
287 {
288  QList<const QgsLineString *> rings;
289  if ( const QgsLineString *exterior = dynamic_cast<const QgsLineString *>( polygon->exteriorRing() ) )
290  {
291  rings.append( exterior );
292  }
293  for ( int iInt = 0, nInt = polygon->numInteriorRings(); iInt < nInt; ++iInt )
294  {
295  if ( const QgsLineString *interior = dynamic_cast<const QgsLineString *>( polygon->interiorRing( iInt ) ) )
296  {
297  rings.append( interior );
298  }
299  }
300  return rings;
301 }
302 
304 {
305  if ( qgsgeometry_cast<QgsGeometryCollection *>( geom ) )
306  {
307  QgsGeometryCollection *geomCollection = static_cast<QgsGeometryCollection *>( geom );
308  for ( int nParts = geom->partCount(), iPart = nParts - 1; iPart >= 0; --iPart )
309  {
310  if ( !qgsgeometry_cast<QgsSurface *>( geomCollection->geometryN( iPart ) ) )
311  {
312  geomCollection->removeGeometry( iPart );
313  }
314  }
315  }
316 }
317 
318 double pointLineDist( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q )
319 {
320  const double nom = std::fabs( ( p2.y() - p1.y() ) * q.x() - ( p2.x() - p1.x() ) * q.y() + p2.x() * p1.y() - p2.y() * p1.x() );
321  const double dx = p2.x() - p1.x();
322  const double dy = p2.y() - p1.y();
323  return nom / std::sqrt( dx * dx + dy * dy );
324 }
325 
326 bool QgsGeometryCheckerUtils::pointOnLine( const QgsPoint &p, const QgsLineString *line, double tol, bool excludeExtremities )
327 {
328  const int nVerts = line->vertexCount();
329  for ( int i = 0 + excludeExtremities; i < nVerts - 1 - excludeExtremities; ++i )
330  {
331  const QgsPoint p1 = line->vertexAt( QgsVertexId( 0, 0, i ) );
332  const QgsPoint p2 = line->vertexAt( QgsVertexId( 0, 0, i + 1 ) );
333  const double dist = pointLineDist( p1, p2, p );
334  if ( dist < tol )
335  {
336  return true;
337  }
338  }
339  return false;
340 }
341 
342 QList<QgsPoint> QgsGeometryCheckerUtils::lineIntersections( const QgsLineString *line1, const QgsLineString *line2, double tol )
343 {
344  QList<QgsPoint> intersections;
345  QgsPoint inter;
346  bool intersection = false;
347  for ( int i = 0, n = line1->vertexCount() - 1; i < n; ++i )
348  {
349  for ( int j = 0, m = line2->vertexCount() - 1; j < m; ++j )
350  {
351  const QgsPoint p1 = line1->vertexAt( QgsVertexId( 0, 0, i ) );
352  const QgsPoint p2 = line1->vertexAt( QgsVertexId( 0, 0, i + 1 ) );
353  const QgsPoint q1 = line2->vertexAt( QgsVertexId( 0, 0, j ) );
354  const QgsPoint q2 = line2->vertexAt( QgsVertexId( 0, 0, j + 1 ) );
355  if ( QgsGeometryUtils::segmentIntersection( p1, p2, q1, q2, inter, intersection, tol ) )
356  {
357  intersections.append( inter );
358  }
359  }
360  }
361  return intersections;
362 }
363 
365 {
366  double len = 0;
367 
368  // Test every pair of segments for shared edges
369  for ( int iPart1 = 0, nParts1 = geom1->partCount(); iPart1 < nParts1; ++iPart1 )
370  {
371  for ( int iRing1 = 0, nRings1 = geom1->ringCount( iPart1 ); iRing1 < nRings1; ++iRing1 )
372  {
373  for ( int iVert1 = 0, jVert1 = 1, nVerts1 = geom1->vertexCount( iPart1, iRing1 ); jVert1 < nVerts1; iVert1 = jVert1++ )
374  {
375  const QgsPoint p1 = geom1->vertexAt( QgsVertexId( iPart1, iRing1, iVert1 ) );
376  const QgsPoint p2 = geom1->vertexAt( QgsVertexId( iPart1, iRing1, jVert1 ) );
377  const double lambdap1 = 0.;
378  const double lambdap2 = std::sqrt( QgsGeometryUtils::sqrDistance2D( p1, p2 ) );
379  QgsVector d;
380  try
381  {
382  d = QgsVector( p2.x() - p1.x(), p2.y() - p1.y() ).normalized();
383  }
384  catch ( const QgsException & )
385  {
386  // Edge has zero length, skip
387  continue;
388  }
389 
390  for ( int iPart2 = 0, nParts2 = geom2->partCount(); iPart2 < nParts2; ++iPart2 )
391  {
392  for ( int iRing2 = 0, nRings2 = geom2->ringCount( iPart2 ); iRing2 < nRings2; ++iRing2 )
393  {
394  for ( int iVert2 = 0, jVert2 = 1, nVerts2 = geom2->vertexCount( iPart2, iRing2 ); jVert2 < nVerts2; iVert2 = jVert2++ )
395  {
396  const QgsPoint q1 = geom2->vertexAt( QgsVertexId( iPart2, iRing2, iVert2 ) );
397  const QgsPoint q2 = geom2->vertexAt( QgsVertexId( iPart2, iRing2, jVert2 ) );
398 
399  // Check whether q1 and q2 are on the line p1, p
400  if ( pointLineDist( p1, p2, q1 ) <= tol && pointLineDist( p1, p2, q2 ) <= tol )
401  {
402  // Get length common edge
403  double lambdaq1 = QgsVector( q1.x() - p1.x(), q1.y() - p1.y() ) * d;
404  double lambdaq2 = QgsVector( q2.x() - p1.x(), q2.y() - p1.y() ) * d;
405  if ( lambdaq1 > lambdaq2 )
406  {
407  std::swap( lambdaq1, lambdaq2 );
408  }
409  const double lambda1 = std::max( lambdaq1, lambdap1 );
410  const double lambda2 = std::min( lambdaq2, lambdap2 );
411  len += std::max( 0., lambda2 - lambda1 );
412  }
413  }
414  }
415  }
416  }
417  }
418  }
419  return len;
420 }
Abstract base class for all geometries.
virtual int ringCount(int part=0) const =0
Returns the number of rings of which this geometry is built.
virtual int vertexCount(int part=0, int ring=0) const =0
Returns the number of vertices of which this geometry is built.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Class for doing transforms between two map coordinate systems.
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
Definition: qgscurve.cpp:180
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
Definition: qgscurve.cpp:198
Defines a QGIS exception class.
Definition: qgsexception.h:35
A feature pool is based on a vector layer and caches features.
QgsWkbTypes::GeometryType geometryType() const
The geometry type of this layer.
QgsCoordinateReferenceSystem crs() const
The coordinate reference system of this layer.
QgsFeatureIds getIntersects(const QgsRectangle &rect) const
Gets all feature ids in the bounding box rect.
bool getFeature(QgsFeatureId id, QgsFeature &feature)
Retrieves the feature with the specified id into feature.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
Base configuration for 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.
A layer feature combination to uniquely identify and access a feature in a set of layers.
LayerFeature(const QgsFeaturePool *pool, const QgsFeature &feature, const QgsGeometryCheckContext *context, bool useMapCrs)
Create a new layer/feature combination.
QgsGeometry geometry() const
Returns the geometry of this feature.
QString id() const
Returns a combination of the layerId and the feature id.
bool operator==(const QgsGeometryCheckerUtils::LayerFeature &other) const
QgsFeature feature() const
Returns the feature.
QPointer< QgsVectorLayer > layer() const
The layer.
bool operator!=(const QgsGeometryCheckerUtils::LayerFeature &other) const
bool useMapCrs() const
Returns if the geometry is reprojected to the map CRS or not.
An iterator over all features in a QgsGeometryCheckerUtils::LayerFeatures.
const iterator & operator++()
Increments the item the iterator currently points to by one and returns the new iterator.
const QgsGeometryCheckerUtils::LayerFeature & operator*() const
Dereferences the item at the current iterator location.
iterator(const QStringList::const_iterator &layerIt, const LayerFeatures *parent)
Creates a new iterator.
Contains a set of layers and feature ids in those layers to pass to a geometry check.
LayerFeatures(const QMap< QString, QgsFeaturePool * > &featurePools, const QMap< QString, QgsFeatureIds > &featureIds, const QList< QgsWkbTypes::GeometryType > &geometryTypes, QgsFeedback *feedback, const QgsGeometryCheckContext *context, bool useMapCrs=false)
Creates a new set of layer and features.
iterator end() const
One after the last feature to stop iterating.
iterator begin() const
The first feature to start iterating.
static void filter1DTypes(QgsAbstractGeometry *geom)
static std::unique_ptr< QgsGeometryEngine > createGeomEngine(const QgsAbstractGeometry *geometry, double tolerance)
static QList< const QgsLineString * > polygonRings(const QgsPolygon *polygon)
static QgsAbstractGeometry * getGeomPart(QgsAbstractGeometry *geom, int partIdx)
static double sharedEdgeLength(const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol)
static QList< QgsPoint > lineIntersections(const QgsLineString *line1, const QgsLineString *line2, double tol)
static bool pointOnLine(const QgsPoint &p, const QgsLineString *line, double tol, bool excludeExtremities=false)
Geometry collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
static double sqrDistance2D(const QgsPoint &pt1, const QgsPoint &pt2) SIP_HOLDGIL
Returns the squared 2D distance between two points.
static bool segmentIntersection(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q1, const QgsPoint &q2, QgsPoint &intersectionPoint, bool &isIntersection, double tolerance=1e-8, bool acceptImproperIntersection=false) SIP_HOLDGIL
Compute the intersection between two segments.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
Q_GADGET bool isNull
Definition: qgsgeometry.h:127
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
Q_GADGET double x
Definition: qgspoint.h:52
double y
Definition: qgspoint.h:53
Polygon geometry type.
Definition: qgspolygon.h:34
A rectangle specified with double values.
Definition: qgsrectangle.h:42
A class to represent a vector.
Definition: qgsvector.h:30
QgsVector normalized() const SIP_THROW(QgsException)
Returns the vector's normalized (or "unit") vector (ie same angle but length of 1....
Definition: qgsvector.cpp:28
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
double pointLineDist(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q)
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31