QGIS API Documentation 3.39.0-Master (d85f3c2a281)
Loading...
Searching...
No Matches
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
59
64
65QPointer<QgsVectorLayer> QgsGeometryCheckerUtils::LayerFeature::layer() const
66{
67 return mFeaturePool->layerPtr();
68}
69
71{
72 return mFeaturePool->layerId();
73}
74
79
81{
82 return QStringLiteral( "%1:%2" ).arg( mFeaturePool->layerName() ).arg( mFeature.id() );
83}
84
86{
87 return layerId() == other.layerId() && mFeature.id() == other.mFeature.id();
88}
89
91{
92 return layerId() != other.layerId() || mFeature.id() != other.mFeature.id();
93}
94
96
97QgsGeometryCheckerUtils::LayerFeatures::iterator::iterator( const QStringList::const_iterator &layerIt, const LayerFeatures *parent )
98 : mLayerIt( layerIt )
99 , mFeatureIt( QgsFeatureIds::const_iterator() )
100 , mParent( parent )
101{
102 nextLayerFeature( true );
103}
104
106 : mLayerIt( rh.mLayerIt )
107 , mFeatureIt( rh.mFeatureIt )
108 , mParent( rh.mParent )
109 , mCurrentFeature( std::make_unique<LayerFeature>( *rh.mCurrentFeature.get() ) )
110{
111}
112
114{
115 return mMapCrs;
116}
120
127
129{
130 Q_ASSERT( mCurrentFeature );
131 return *mCurrentFeature;
132}
133
135{
136 return mLayerIt != other.mLayerIt || mFeatureIt != other.mFeatureIt;
137}
138
144bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextLayerFeature( bool begin )
145{
146 if ( !begin && nextFeature( false ) )
147 {
148 return true;
149 }
150 while ( nextLayer( begin ) )
151 {
152 begin = false;
153 if ( nextFeature( true ) )
154 {
155 return true;
156 }
157 }
158 // End
159 mFeatureIt = QgsFeatureIds::const_iterator();
160 mCurrentFeature.reset();
161 return false;
162}
163
164bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextLayer( bool begin )
165{
166 if ( !begin )
167 {
168 ++mLayerIt;
169 }
170 while ( true )
171 {
172 if ( mLayerIt == mParent->mLayerIds.end() )
173 {
174 break;
175 }
176 if ( mParent->mGeometryTypes.contains( mParent->mFeaturePools[*mLayerIt]->geometryType() ) )
177 {
178 mFeatureIt = mParent->mFeatureIds[*mLayerIt].constBegin();
179 return true;
180 }
181 ++mLayerIt;
182 }
183 return false;
184}
185
186bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextFeature( bool begin )
187{
188 QgsFeaturePool *featurePool = mParent->mFeaturePools[*mLayerIt];
189 const QgsFeatureIds &featureIds = mParent->mFeatureIds[*mLayerIt];
190 if ( !begin )
191 {
192 ++mFeatureIt;
193 }
194 while ( true )
195 {
196 if ( mFeatureIt == featureIds.end() )
197 {
198 break;
199 }
200 if ( mParent->mFeedback )
201 mParent->mFeedback->setProgress( mParent->mFeedback->progress() + 1.0 );
202 QgsFeature feature;
203 if ( featurePool->getFeature( *mFeatureIt, feature ) && !feature.geometry().isNull() )
204 {
205 mCurrentFeature = std::make_unique<LayerFeature>( featurePool, feature, mParent->mContext, mParent->mUseMapCrs );
206 return true;
207 }
208 ++mFeatureIt;
209 }
210 return false;
211}
212
214
215QgsGeometryCheckerUtils::LayerFeatures::LayerFeatures( const QMap<QString, QgsFeaturePool *> &featurePools,
216 const QMap<QString, QgsFeatureIds> &featureIds,
217 const QList<Qgis::GeometryType> &geometryTypes,
218 QgsFeedback *feedback,
219 const QgsGeometryCheckContext *context,
220 bool useMapCrs )
221 : mFeaturePools( featurePools )
222 , mFeatureIds( featureIds )
223 , mLayerIds( featurePools.keys() )
224 , mGeometryTypes( geometryTypes )
225 , mFeedback( feedback )
226 , mContext( context )
227 , mUseMapCrs( useMapCrs )
228{}
229
230QgsGeometryCheckerUtils::LayerFeatures::LayerFeatures( const QMap<QString, QgsFeaturePool *> &featurePools,
231 const QList<QString> &layerIds, const QgsRectangle &extent,
232 const QList<Qgis::GeometryType> &geometryTypes,
233 const QgsGeometryCheckContext *context )
234 : mFeaturePools( featurePools )
235 , mLayerIds( layerIds )
236 , mExtent( extent )
237 , mGeometryTypes( geometryTypes )
238 , mContext( context )
239 , mUseMapCrs( true )
240{
241 for ( const QString &layerId : layerIds )
242 {
243 const QgsFeaturePool *featurePool = featurePools[layerId];
244 if ( geometryTypes.contains( featurePool->geometryType() ) )
245 {
246 const QgsCoordinateTransform ct( featurePool->crs(), context->mapCrs, context->transformContext );
247 mFeatureIds.insert( layerId, featurePool->getIntersects( ct.transform( extent, Qgis::TransformDirection::Reverse ) ) );
248 }
249 else
250 {
251 mFeatureIds.insert( layerId, QgsFeatureIds() );
252 }
253 }
254}
255
260
265
267
268std::unique_ptr<QgsGeometryEngine> QgsGeometryCheckerUtils::createGeomEngine( const QgsAbstractGeometry *geometry, double tolerance )
269{
270 return std::make_unique<QgsGeos>( geometry, tolerance );
271}
272
274{
275 if ( QgsGeometryCollection *collection = qgsgeometry_cast<QgsGeometryCollection *>( geom ) )
276 {
277 return collection->geometryN( partIdx );
278 }
279 return geom;
280}
281
283{
284 if ( QgsGeometryCollection *collection = qgsgeometry_cast<QgsGeometryCollection *>( geom ) )
285 {
286 return collection->geometryN( partIdx );
287 }
288 return geom;
289}
290
291QList<const QgsLineString *> QgsGeometryCheckerUtils::polygonRings( const QgsPolygon *polygon )
292{
293 QList<const QgsLineString *> rings;
294 if ( const QgsLineString *exterior = qgsgeometry_cast<const QgsLineString *>( polygon->exteriorRing() ) )
295 {
296 rings.append( exterior );
297 }
298 for ( int iInt = 0, nInt = polygon->numInteriorRings(); iInt < nInt; ++iInt )
299 {
300 if ( const QgsLineString *interior = qgsgeometry_cast<const QgsLineString *>( polygon->interiorRing( iInt ) ) )
301 {
302 rings.append( interior );
303 }
304 }
305 return rings;
306}
307
309{
310 if ( QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom ) )
311 {
312 for ( int nParts = geom->partCount(), iPart = nParts - 1; iPart >= 0; --iPart )
313 {
314 if ( !qgsgeometry_cast<QgsSurface *>( geomCollection->geometryN( iPart ) ) )
315 {
316 geomCollection->removeGeometry( iPart );
317 }
318 }
319 }
320}
321
322double pointLineDist( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q )
323{
324 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() );
325 const double dx = p2.x() - p1.x();
326 const double dy = p2.y() - p1.y();
327 return nom / std::sqrt( dx * dx + dy * dy );
328}
329
330bool QgsGeometryCheckerUtils::pointOnLine( const QgsPoint &p, const QgsLineString *line, double tol, bool excludeExtremities )
331{
332 const int nVerts = line->vertexCount();
333 for ( int i = 0 + excludeExtremities; i < nVerts - 1 - excludeExtremities; ++i )
334 {
335 const QgsPoint p1 = line->vertexAt( QgsVertexId( 0, 0, i ) );
336 const QgsPoint p2 = line->vertexAt( QgsVertexId( 0, 0, i + 1 ) );
337 const double dist = pointLineDist( p1, p2, p );
338 if ( dist < tol )
339 {
340 return true;
341 }
342 }
343 return false;
344}
345
346QList<QgsPoint> QgsGeometryCheckerUtils::lineIntersections( const QgsLineString *line1, const QgsLineString *line2, double tol )
347{
348 QList<QgsPoint> intersections;
349 QgsPoint inter;
350 bool intersection = false;
351 for ( int i = 0, n = line1->vertexCount() - 1; i < n; ++i )
352 {
353 for ( int j = 0, m = line2->vertexCount() - 1; j < m; ++j )
354 {
355 const QgsPoint p1 = line1->vertexAt( QgsVertexId( 0, 0, i ) );
356 const QgsPoint p2 = line1->vertexAt( QgsVertexId( 0, 0, i + 1 ) );
357 const QgsPoint q1 = line2->vertexAt( QgsVertexId( 0, 0, j ) );
358 const QgsPoint q2 = line2->vertexAt( QgsVertexId( 0, 0, j + 1 ) );
359 if ( QgsGeometryUtils::segmentIntersection( p1, p2, q1, q2, inter, intersection, tol ) )
360 {
361 intersections.append( inter );
362 }
363 }
364 }
365 return intersections;
366}
367
369{
370 double len = 0;
371
372 // Test every pair of segments for shared edges
373 for ( int iPart1 = 0, nParts1 = geom1->partCount(); iPart1 < nParts1; ++iPart1 )
374 {
375 for ( int iRing1 = 0, nRings1 = geom1->ringCount( iPart1 ); iRing1 < nRings1; ++iRing1 )
376 {
377 for ( int iVert1 = 0, jVert1 = 1, nVerts1 = geom1->vertexCount( iPart1, iRing1 ); jVert1 < nVerts1; iVert1 = jVert1++ )
378 {
379 const QgsPoint p1 = geom1->vertexAt( QgsVertexId( iPart1, iRing1, iVert1 ) );
380 const QgsPoint p2 = geom1->vertexAt( QgsVertexId( iPart1, iRing1, jVert1 ) );
381 const double lambdap1 = 0.;
382 const double lambdap2 = std::sqrt( QgsGeometryUtils::sqrDistance2D( p1, p2 ) );
383 QgsVector d;
384 try
385 {
386 d = QgsVector( p2.x() - p1.x(), p2.y() - p1.y() ).normalized();
387 }
388 catch ( const QgsException & )
389 {
390 // Edge has zero length, skip
391 continue;
392 }
393
394 for ( int iPart2 = 0, nParts2 = geom2->partCount(); iPart2 < nParts2; ++iPart2 )
395 {
396 for ( int iRing2 = 0, nRings2 = geom2->ringCount( iPart2 ); iRing2 < nRings2; ++iRing2 )
397 {
398 for ( int iVert2 = 0, jVert2 = 1, nVerts2 = geom2->vertexCount( iPart2, iRing2 ); jVert2 < nVerts2; iVert2 = jVert2++ )
399 {
400 const QgsPoint q1 = geom2->vertexAt( QgsVertexId( iPart2, iRing2, iVert2 ) );
401 const QgsPoint q2 = geom2->vertexAt( QgsVertexId( iPart2, iRing2, jVert2 ) );
402
403 // Check whether q1 and q2 are on the line p1, p
404 if ( pointLineDist( p1, p2, q1 ) <= tol && pointLineDist( p1, p2, q2 ) <= tol )
405 {
406 // Get length common edge
407 double lambdaq1 = QgsVector( q1.x() - p1.x(), q1.y() - p1.y() ) * d;
408 double lambdaq2 = QgsVector( q2.x() - p1.x(), q2.y() - p1.y() ) * d;
409 if ( lambdaq1 > lambdaq2 )
410 {
411 std::swap( lambdaq1, lambdaq2 );
412 }
413 const double lambda1 = std::max( lambdaq1, lambdap1 );
414 const double lambda2 = std::min( lambdaq2, lambdap2 );
415 len += std::max( 0., lambda2 - lambda1 );
416 }
417 }
418 }
419 }
420 }
421 }
422 }
423 return len;
424}
@ Reverse
Reverse/inverse transform (from destination to source)
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.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Class for doing transforms between two map coordinate systems.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform the point from the source CRS to the destination CRS.
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent.
Custom exception class for Coordinate Reference System related exceptions.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from 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.
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:58
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
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.
QgsCoordinateReferenceSystem layerCrs() const
The layer CRS.
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)
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)
Compute the intersection between two segments.
static Q_DECL_DEPRECATED double sqrDistance2D(double x1, double y1, double x2, double y2)
Returns the squared 2D distance between (x1, y1) and (x2, y2).
A geometry is the spatial representation of a feature.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
Line string geometry type, with support for z-dimension and m-values.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double x
Definition qgspoint.h:52
double y
Definition qgspoint.h:53
Polygon geometry type.
Definition qgspolygon.h:33
A rectangle specified with double values.
A class to represent a vector.
Definition qgsvector.h:30
QgsVector normalized() const
Returns the vector's normalized (or "unit") vector (ie same angle but length of 1....
Definition qgsvector.cpp:28
QSet< QgsFeatureId > QgsFeatureIds
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:30