QGIS API Documentation 4.1.0-Master (60fea48833c)
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
18
19#include "qgsfeaturepool.h"
20#include "qgsfeedback.h"
21#include "qgsgeometry.h"
22#include "qgsgeometrycheck.h"
25#include "qgsgeometryutils.h"
26#include "qgsgeos.h"
27#include "qgspolygon.h"
28#include "qgssurface.h"
29#include "qgsvectorlayer.h"
30
31#include <QString>
32#include <qmath.h>
33
34using namespace Qt::StringLiterals;
35
37 : mFeaturePool( pool )
38 , mFeature( feature )
39 , mGeometry( feature.geometry() )
40 , mMapCrs( useMapCrs )
41{
42 const QgsCoordinateTransform transform( pool->crs(), context->mapCrs, context->transformContext );
43 if ( useMapCrs && context->mapCrs.isValid() && !transform.isShortCircuited() )
44 {
45 try
46 {
47 mGeometry.transform( transform );
48 }
49 catch ( const QgsCsException & )
50 {
51 QgsDebugError( u"Shrug. What shall we do with a geometry that cannot be converted?"_s );
52 }
53 }
54}
55
60
65
66QPointer<QgsVectorLayer> QgsGeometryCheckerUtils::LayerFeature::layer() const
67{
68 return mFeaturePool->layerPtr();
69}
70
72{
73 return mFeaturePool->layerId();
74}
75
80
82{
83 return u"%1:%2"_s.arg( mFeaturePool->layerName() ).arg( mFeature.id() );
84}
85
87{
88 return layerId() == other.layerId() && mFeature.id() == other.mFeature.id();
89}
90
92{
93 return layerId() != other.layerId() || mFeature.id() != other.mFeature.id();
94}
95
97
98QgsGeometryCheckerUtils::LayerFeatures::iterator::iterator( const QStringList::const_iterator &layerIt, const LayerFeatures *parent )
99 : mLayerIt( layerIt )
100 , mFeatureIt( QgsFeatureIds::const_iterator() )
101 , mParent( parent )
102{
103 nextLayerFeature( true );
104}
105
107 : mLayerIt( rh.mLayerIt )
108 , mFeatureIt( rh.mFeatureIt )
109 , mParent( rh.mParent )
110 , mCurrentFeature( std::make_unique<LayerFeature>( *rh.mCurrentFeature.get() ) )
111{}
112
114{
115 return mMapCrs;
116}
119
126
128{
129 Q_ASSERT( mCurrentFeature );
130 return *mCurrentFeature;
131}
132
134{
135 return mLayerIt != other.mLayerIt || mFeatureIt != other.mFeatureIt;
136}
137
143bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextLayerFeature( bool begin )
144{
145 if ( !begin && nextFeature( false ) )
146 {
147 return true;
148 }
149 while ( nextLayer( begin ) )
150 {
151 begin = false;
152 if ( nextFeature( true ) )
153 {
154 return true;
155 }
156 }
157 // End
158 mFeatureIt = QgsFeatureIds::const_iterator();
159 mCurrentFeature.reset();
160 return false;
161}
162
163bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextLayer( bool begin )
164{
165 if ( !begin )
166 {
167 ++mLayerIt;
168 }
169 while ( true )
170 {
171 if ( mLayerIt == mParent->mLayerIds.end() )
172 {
173 break;
174 }
175 if ( mParent->mGeometryTypes.contains( mParent->mFeaturePools[*mLayerIt]->geometryType() ) )
176 {
177 mFeatureIt = mParent->mFeatureIds[*mLayerIt].constBegin();
178 return true;
179 }
180 ++mLayerIt;
181 }
182 return false;
183}
184
185bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextFeature( bool begin )
186{
187 QgsFeaturePool *featurePool = mParent->mFeaturePools[*mLayerIt];
188 const QgsFeatureIds &featureIds = mParent->mFeatureIds[*mLayerIt];
189 if ( !begin )
190 {
191 ++mFeatureIt;
192 }
193 while ( true )
194 {
195 if ( mFeatureIt == featureIds.end() )
196 {
197 break;
198 }
199 if ( mParent->mFeedback )
200 mParent->mFeedback->setProgress( mParent->mFeedback->progress() + 1.0 );
201 QgsFeature feature;
202 if ( featurePool->getFeature( *mFeatureIt, feature ) && !feature.geometry().isNull() )
203 {
204 mCurrentFeature = std::make_unique<LayerFeature>( featurePool, feature, mParent->mContext, mParent->mUseMapCrs );
205 return true;
206 }
207 ++mFeatureIt;
208 }
209 return false;
210}
211
213
215 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)
222 : mFeaturePools( featurePools )
223 , mFeatureIds( featureIds )
224 , mLayerIds( featurePools.keys() )
225 , mGeometryTypes( geometryTypes )
226 , mFeedback( feedback )
227 , mContext( context )
228 , mUseMapCrs( useMapCrs )
229{}
230
232 const QMap<QString, QgsFeaturePool *> &featurePools, const QList<QString> &layerIds, const QgsRectangle &extent, const QList<Qgis::GeometryType> &geometryTypes, const QgsGeometryCheckContext *context
233)
234 : mFeaturePools( featurePools )
235 , mLayerIds( layerIds )
236 , mExtent( extent )
237 , mGeometryTypes( geometryTypes )
238 , mContext( context )
239{
240 for ( const QString &layerId : layerIds )
241 {
242 const QgsFeaturePool *featurePool = featurePools[layerId];
243 if ( geometryTypes.contains( featurePool->geometryType() ) )
244 {
245 const QgsCoordinateTransform ct( featurePool->crs(), context->mapCrs, context->transformContext );
246 mFeatureIds.insert( layerId, featurePool->getIntersects( ct.transform( extent, Qgis::TransformDirection::Reverse ) ) );
247 }
248 else
249 {
250 mFeatureIds.insert( layerId, QgsFeatureIds() );
251 }
252 }
253}
254
259
264
266
268{
270 {
271 return collection->geometryN( partIdx );
272 }
273 return geom;
274}
275
277{
279 {
280 return collection->geometryN( partIdx );
281 }
282 return geom;
283}
284
285QList<const QgsLineString *> QgsGeometryCheckerUtils::polygonRings( const QgsPolygon *polygon )
286{
287 QList<const QgsLineString *> rings;
288 if ( const QgsLineString *exterior = qgsgeometry_cast<const QgsLineString *>( polygon->exteriorRing() ) )
289 {
290 rings.append( exterior );
291 }
292 for ( int iInt = 0, nInt = polygon->numInteriorRings(); iInt < nInt; ++iInt )
293 {
294 if ( const QgsLineString *interior = qgsgeometry_cast<const QgsLineString *>( polygon->interiorRing( iInt ) ) )
295 {
296 rings.append( interior );
297 }
298 }
299 return rings;
300}
301
303{
305 {
306 for ( int nParts = geom->partCount(), iPart = nParts - 1; iPart >= 0; --iPart )
307 {
308 if ( !qgsgeometry_cast<QgsSurface *>( geomCollection->geometryN( iPart ) ) )
309 {
310 geomCollection->removeGeometry( iPart );
311 }
312 }
313 }
314}
315
316double pointLineDist( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q )
317{
318 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() );
319 const double dx = p2.x() - p1.x();
320 const double dy = p2.y() - p1.y();
321 return nom / std::sqrt( dx * dx + dy * dy );
322}
323
324bool QgsGeometryCheckerUtils::pointOnLine( const QgsPoint &p, const QgsLineString *line, double tol, bool excludeExtremities )
325{
326 const int nVerts = line->vertexCount();
327 for ( int i = 0 + excludeExtremities; i < nVerts - 1 - excludeExtremities; ++i )
328 {
329 const QgsPoint p1 = line->vertexAt( QgsVertexId( 0, 0, i ) );
330 const QgsPoint p2 = line->vertexAt( QgsVertexId( 0, 0, i + 1 ) );
331 const double dist = pointLineDist( p1, p2, p );
332 if ( dist < tol )
333 {
334 return true;
335 }
336 }
337 return false;
338}
339
340QList<QgsPoint> QgsGeometryCheckerUtils::lineIntersections( const QgsLineString *line1, const QgsLineString *line2, double tol )
341{
342 QList<QgsPoint> intersections;
343 QgsPoint inter;
344 bool intersection = false;
345 for ( int i = 0, n = line1->vertexCount() - 1; i < n; ++i )
346 {
347 for ( int j = 0, m = line2->vertexCount() - 1; j < m; ++j )
348 {
349 const QgsPoint p1 = line1->vertexAt( QgsVertexId( 0, 0, i ) );
350 const QgsPoint p2 = line1->vertexAt( QgsVertexId( 0, 0, i + 1 ) );
351 const QgsPoint q1 = line2->vertexAt( QgsVertexId( 0, 0, j ) );
352 const QgsPoint q2 = line2->vertexAt( QgsVertexId( 0, 0, j + 1 ) );
353 if ( QgsGeometryUtils::segmentIntersection( p1, p2, q1, q2, inter, intersection, tol ) )
354 {
355 intersections.append( inter );
356 }
357 }
358 }
359 return intersections;
360}
361
363{
364 double len = 0;
365
366 // Test every pair of segments for shared edges
367 for ( int iPart1 = 0, nParts1 = geom1->partCount(); iPart1 < nParts1; ++iPart1 )
368 {
369 for ( int iRing1 = 0, nRings1 = geom1->ringCount( iPart1 ); iRing1 < nRings1; ++iRing1 )
370 {
371 for ( int iVert1 = 0, jVert1 = 1, nVerts1 = geom1->vertexCount( iPart1, iRing1 ); jVert1 < nVerts1; iVert1 = jVert1++ )
372 {
373 const QgsPoint p1 = geom1->vertexAt( QgsVertexId( iPart1, iRing1, iVert1 ) );
374 const QgsPoint p2 = geom1->vertexAt( QgsVertexId( iPart1, iRing1, jVert1 ) );
375 const double lambdap1 = 0.;
376 const double lambdap2 = std::sqrt( QgsGeometryUtils::sqrDistance2D( p1, p2 ) );
377 QgsVector d;
378 try
379 {
380 d = QgsVector( p2.x() - p1.x(), p2.y() - p1.y() ).normalized();
381 }
382 catch ( const QgsException & )
383 {
384 // Edge has zero length, skip
385 continue;
386 }
387
388 for ( int iPart2 = 0, nParts2 = geom2->partCount(); iPart2 < nParts2; ++iPart2 )
389 {
390 for ( int iRing2 = 0, nRings2 = geom2->ringCount( iPart2 ); iRing2 < nRings2; ++iRing2 )
391 {
392 for ( int iVert2 = 0, jVert2 = 1, nVerts2 = geom2->vertexCount( iPart2, iRing2 ); jVert2 < nVerts2; iVert2 = jVert2++ )
393 {
394 const QgsPoint q1 = geom2->vertexAt( QgsVertexId( iPart2, iRing2, iVert2 ) );
395 const QgsPoint q2 = geom2->vertexAt( QgsVertexId( iPart2, iRing2, jVert2 ) );
396
397 // Check whether q1 and q2 are on the line p1, p
398 if ( pointLineDist( p1, p2, q1 ) <= tol && pointLineDist( p1, p2, q2 ) <= tol )
399 {
400 // Get length common edge
401 double lambdaq1 = QgsVector( q1.x() - p1.x(), q1.y() - p1.y() ) * d;
402 double lambdaq2 = QgsVector( q2.x() - p1.x(), q2.y() - p1.y() ) * d;
403 if ( lambdaq1 > lambdaq2 )
404 {
405 std::swap( lambdaq1, lambdaq2 );
406 }
407 const double lambda1 = std::max( lambdaq1, lambdap1 );
408 const double lambda2 = std::min( lambdaq2, lambdap2 );
409 len += std::max( 0., lambda2 - lambda1 );
410 }
411 }
412 }
413 }
414 }
415 }
416 }
417 return len;
418}
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2766
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.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Handles coordinate transforms between two 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:60
QgsFeatureId id
Definition qgsfeature.h:68
QgsGeometry geometry
Definition qgsfeature.h:71
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.
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 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.
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:53
double x
Definition qgspoint.h:56
double y
Definition qgspoint.h:57
Polygon geometry type.
Definition qgspolygon.h:37
A rectangle specified with double values.
Represent a 2-dimensional vector.
Definition qgsvector.h:34
QgsVector normalized() const
Returns the vector's normalized (or "unit") vector (ie same angle but length of 1....
Definition qgsvector.cpp:33
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QSet< QgsFeatureId > QgsFeatureIds
double pointLineDist(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q)
#define QgsDebugError(str)
Definition qgslogger.h:59
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:34