QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
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"
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 , mGeometry( feature.geometry() )
39 , mMapCrs( useMapCrs )
40{
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 QgsDebugError( QStringLiteral( "Shrug. What shall we do with a geometry that cannot be converted?" ) );
51 }
52 }
53}
54
56{
57 return mFeature;
58}
59
60QPointer<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
92QgsGeometryCheckerUtils::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 : mLayerIt( rh.mLayerIt )
102 , mFeatureIt( rh.mFeatureIt )
103 , mParent( rh.mParent )
104 , mCurrentFeature( std::make_unique<LayerFeature>( *rh.mCurrentFeature.get() ) )
105{
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}
139bool 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
159bool 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
181bool 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
210QgsGeometryCheckerUtils::LayerFeatures::LayerFeatures( const QMap<QString, QgsFeaturePool *> &featurePools,
211 const QMap<QString, QgsFeatureIds> &featureIds,
212 const QList<Qgis::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
225QgsGeometryCheckerUtils::LayerFeatures::LayerFeatures( const QMap<QString, QgsFeaturePool *> &featurePools,
226 const QList<QString> &layerIds, const QgsRectangle &extent,
227 const QList<Qgis::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
263std::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
286QList<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
318double 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
326bool 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
342QList<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:67
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.
Qgis::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.
iterator end() const
One after the last feature to stop iterating.
LayerFeatures(const QMap< QString, QgsFeaturePool * > &featurePools, const QMap< QString, QgsFeatureIds > &featureIds, const QList< Qgis::GeometryType > &geometryTypes, QgsFeedback *feedback, const QgsGeometryCheckContext *context, bool useMapCrs=false)
Creates a new set of layer and features.
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.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within 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:164
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:166
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
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 QgsDebugError(str)
Definition: qgslogger.h:38
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31