Branch data Line data Source code
1 : : /***************************************************************************
2 : : * qgsgeometrycheckerutils.cpp *
3 : : * ------------------- *
4 : : * copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
5 : : * email : smani@sourcepole.ch *
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 : :
17 : : #include "qgsgeometrycheckcontext.h"
18 : : #include "qgsgeometrycheckerutils.h"
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 : :
32 : 1081 : QgsGeometryCheckerUtils::LayerFeature::LayerFeature( const QgsFeaturePool *pool,
33 : : const QgsFeature &feature,
34 : : const QgsGeometryCheckContext *context,
35 : : bool useMapCrs )
36 : 1081 : : mFeaturePool( pool )
37 : 1081 : , mFeature( feature )
38 : 1081 : , mMapCrs( useMapCrs )
39 : : {
40 : 1081 : mGeometry = feature.geometry();
41 : 1081 : const QgsCoordinateTransform transform( pool->crs(), context->mapCrs, context->transformContext );
42 : 1081 : if ( useMapCrs && context->mapCrs.isValid() && !transform.isShortCircuited() )
43 : : {
44 : : try
45 : : {
46 : 0 : mGeometry.transform( transform );
47 : 0 : }
48 : : catch ( const QgsCsException & )
49 : : {
50 : 0 : QgsDebugMsg( QStringLiteral( "Shrug. What shall we do with a geometry that cannot be converted?" ) );
51 : 0 : }
52 : 0 : }
53 : 1081 : }
54 : :
55 : 886 : QgsFeature QgsGeometryCheckerUtils::LayerFeature::feature() const
56 : : {
57 : 886 : return mFeature;
58 : : }
59 : :
60 : 717 : QPointer<QgsVectorLayer> QgsGeometryCheckerUtils::LayerFeature::layer() const
61 : : {
62 : 717 : return mFeaturePool->layerPtr();
63 : : }
64 : :
65 : 746 : QString QgsGeometryCheckerUtils::LayerFeature::layerId() const
66 : : {
67 : 746 : return mFeaturePool->layerId();
68 : : }
69 : :
70 : 1117 : QgsGeometry QgsGeometryCheckerUtils::LayerFeature::geometry() const
71 : : {
72 : 1117 : return mGeometry;
73 : : }
74 : :
75 : 29 : QString QgsGeometryCheckerUtils::LayerFeature::id() const
76 : : {
77 : 58 : return QStringLiteral( "%1:%2" ).arg( mFeaturePool->layerName() ).arg( mFeature.id() );
78 : 0 : }
79 : :
80 : 144 : bool QgsGeometryCheckerUtils::LayerFeature::operator==( const LayerFeature &other ) const
81 : : {
82 : 144 : return layerId() == other.layerId() && mFeature.id() == other.mFeature.id();
83 : 0 : }
84 : :
85 : 0 : bool QgsGeometryCheckerUtils::LayerFeature::operator!=( const LayerFeature &other ) const
86 : : {
87 : 0 : return layerId() != other.layerId() || mFeature.id() != other.mFeature.id();
88 : 0 : }
89 : :
90 : : /////////////////////////////////////////////////////////////////////////////
91 : :
92 : 432 : QgsGeometryCheckerUtils::LayerFeatures::iterator::iterator( const QStringList::const_iterator &layerIt, const LayerFeatures *parent )
93 : 432 : : mLayerIt( layerIt )
94 : 432 : , mFeatureIt( QgsFeatureIds::const_iterator() )
95 : 432 : , mParent( parent )
96 : : {
97 : 432 : nextLayerFeature( true );
98 : 432 : }
99 : :
100 : 0 : QgsGeometryCheckerUtils::LayerFeatures::iterator::iterator( const QgsGeometryCheckerUtils::LayerFeatures::iterator &rh )
101 : : {
102 : 0 : mLayerIt = rh.mLayerIt;
103 : 0 : mFeatureIt = rh.mFeatureIt;
104 : 0 : mParent = rh.mParent;
105 : 0 : mCurrentFeature = std::make_unique<LayerFeature>( *rh.mCurrentFeature.get() );
106 : 0 : }
107 : :
108 : 105 : bool QgsGeometryCheckerUtils::LayerFeature::useMapCrs() const
109 : : {
110 : 105 : return mMapCrs;
111 : : }
112 : 432 : QgsGeometryCheckerUtils::LayerFeatures::iterator::~iterator()
113 : : {
114 : 432 : }
115 : :
116 : 0 : QgsGeometryCheckerUtils::LayerFeatures::iterator QgsGeometryCheckerUtils::LayerFeatures::iterator::operator++( int )
117 : : {
118 : 0 : iterator tmp( *this );
119 : 0 : ++*this;
120 : 0 : return tmp;
121 : 0 : }
122 : :
123 : 1073 : const QgsGeometryCheckerUtils::LayerFeature &QgsGeometryCheckerUtils::LayerFeatures::iterator::operator*() const
124 : : {
125 : : Q_ASSERT( mCurrentFeature );
126 : 1073 : return *mCurrentFeature;
127 : : }
128 : :
129 : 1283 : bool QgsGeometryCheckerUtils::LayerFeatures::iterator::operator!=( const QgsGeometryCheckerUtils::LayerFeatures::iterator &other )
130 : : {
131 : 1283 : return mLayerIt != other.mLayerIt || mFeatureIt != other.mFeatureIt;
132 : : }
133 : :
134 : 1067 : const QgsGeometryCheckerUtils::LayerFeatures::iterator &QgsGeometryCheckerUtils::LayerFeatures::iterator::operator++()
135 : : {
136 : 1067 : nextLayerFeature( false );
137 : 1067 : return *this;
138 : : }
139 : 1499 : bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextLayerFeature( bool begin )
140 : : {
141 : 1499 : if ( !begin && nextFeature( false ) )
142 : : {
143 : 841 : return true;
144 : : }
145 : 732 : while ( nextLayer( begin ) )
146 : : {
147 : 306 : begin = false;
148 : 306 : if ( nextFeature( true ) )
149 : : {
150 : 232 : return true;
151 : : }
152 : : }
153 : : // End
154 : 426 : mFeatureIt = QgsFeatureIds::const_iterator();
155 : 426 : mCurrentFeature.reset();
156 : 426 : return false;
157 : 1499 : }
158 : :
159 : 732 : bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextLayer( bool begin )
160 : : {
161 : 732 : if ( !begin )
162 : : {
163 : 300 : ++mLayerIt;
164 : 300 : }
165 : 920 : while ( true )
166 : : {
167 : 920 : if ( mLayerIt == mParent->mLayerIds.end() )
168 : : {
169 : 426 : break;
170 : : }
171 : 494 : if ( mParent->mGeometryTypes.contains( mParent->mFeaturePools[*mLayerIt]->geometryType() ) )
172 : : {
173 : 306 : mFeatureIt = mParent->mFeatureIds[*mLayerIt].constBegin();
174 : 306 : return true;
175 : : }
176 : 188 : ++mLayerIt;
177 : : }
178 : 426 : return false;
179 : 732 : }
180 : :
181 : 1373 : bool QgsGeometryCheckerUtils::LayerFeatures::iterator::nextFeature( bool begin )
182 : : {
183 : 1373 : QgsFeaturePool *featurePool = mParent->mFeaturePools[*mLayerIt];
184 : 1373 : const QgsFeatureIds &featureIds = mParent->mFeatureIds[*mLayerIt];
185 : 1373 : if ( !begin )
186 : : {
187 : 1067 : ++mFeatureIt;
188 : 1067 : }
189 : 1373 : while ( true )
190 : : {
191 : 1373 : if ( mFeatureIt == featureIds.end() )
192 : : {
193 : 300 : break;
194 : : }
195 : 1073 : if ( mParent->mFeedback )
196 : 490 : mParent->mFeedback->setProgress( mParent->mFeedback->progress() + 1.0 );
197 : 1073 : QgsFeature feature;
198 : 1073 : if ( featurePool->getFeature( *mFeatureIt, feature ) && !feature.geometry().isNull() )
199 : : {
200 : 1073 : mCurrentFeature = std::make_unique<LayerFeature>( featurePool, feature, mParent->mContext, mParent->mUseMapCrs );
201 : 1073 : return true;
202 : : }
203 : 0 : ++mFeatureIt;
204 : 1073 : }
205 : 300 : return false;
206 : 1373 : }
207 : :
208 : 189 : /////////////////////////////////////////////////////////////////////////////
209 : :
210 : 54 : 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 : 27 : : mFeaturePools( featurePools )
217 : 27 : , mFeatureIds( featureIds )
218 : 27 : , mLayerIds( featurePools.keys() )
219 : 27 : , mGeometryTypes( geometryTypes )
220 : 27 : , mFeedback( feedback )
221 : 27 : , mContext( context )
222 : 27 : , mUseMapCrs( useMapCrs )
223 : 27 : {}
224 : :
225 : 189 : 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 : 189 : : mFeaturePools( featurePools )
230 : 189 : , mLayerIds( layerIds )
231 : 189 : , mExtent( extent )
232 : 189 : , mGeometryTypes( geometryTypes )
233 : 189 : , mContext( context )
234 : 189 : , mUseMapCrs( true )
235 : : {
236 : 621 : for ( const QString &layerId : layerIds )
237 : : {
238 : 432 : const QgsFeaturePool *featurePool = featurePools[layerId];
239 : 432 : if ( geometryTypes.contains( featurePool->geometryType() ) )
240 : : {
241 : 267 : QgsCoordinateTransform ct( featurePool->crs(), context->mapCrs, context->transformContext );
242 : 267 : mFeatureIds.insert( layerId, featurePool->getIntersects( ct.transform( extent, QgsCoordinateTransform::ReverseTransform ) ) );
243 : 267 : }
244 : : else
245 : : {
246 : 165 : mFeatureIds.insert( layerId, QgsFeatureIds() );
247 : : }
248 : : }
249 : 189 : }
250 : :
251 : 216 : QgsGeometryCheckerUtils::LayerFeatures::iterator QgsGeometryCheckerUtils::LayerFeatures::begin() const
252 : : {
253 : 216 : return iterator( mLayerIds.constBegin(), this );
254 : : }
255 : :
256 : 216 : QgsGeometryCheckerUtils::LayerFeatures::iterator QgsGeometryCheckerUtils::LayerFeatures::end() const
257 : : {
258 : 216 : return iterator( mLayerIds.end(), this );
259 : : }
260 : :
261 : : /////////////////////////////////////////////////////////////////////////////
262 : :
263 : 339 : std::unique_ptr<QgsGeometryEngine> QgsGeometryCheckerUtils::createGeomEngine( const QgsAbstractGeometry *geometry, double tolerance )
264 : : {
265 : 339 : return std::make_unique<QgsGeos>( geometry, tolerance );
266 : : }
267 : :
268 : 17 : QgsAbstractGeometry *QgsGeometryCheckerUtils::getGeomPart( QgsAbstractGeometry *geom, int partIdx )
269 : : {
270 : 17 : if ( dynamic_cast<QgsGeometryCollection *>( geom ) )
271 : : {
272 : 11 : return static_cast<QgsGeometryCollection *>( geom )->geometryN( partIdx );
273 : : }
274 : 6 : return geom;
275 : 17 : }
276 : :
277 : 249 : const QgsAbstractGeometry *QgsGeometryCheckerUtils::getGeomPart( const QgsAbstractGeometry *geom, int partIdx )
278 : : {
279 : 249 : if ( dynamic_cast<const QgsGeometryCollection *>( geom ) )
280 : : {
281 : 222 : return static_cast<const QgsGeometryCollection *>( geom )->geometryN( partIdx );
282 : : }
283 : 27 : return geom;
284 : 249 : }
285 : :
286 : 9 : QList<const QgsLineString *> QgsGeometryCheckerUtils::polygonRings( const QgsPolygon *polygon )
287 : : {
288 : 9 : QList<const QgsLineString *> rings;
289 : 9 : if ( const QgsLineString *exterior = dynamic_cast<const QgsLineString *>( polygon->exteriorRing() ) )
290 : : {
291 : 9 : rings.append( exterior );
292 : 9 : }
293 : 10 : for ( int iInt = 0, nInt = polygon->numInteriorRings(); iInt < nInt; ++iInt )
294 : : {
295 : 1 : if ( const QgsLineString *interior = dynamic_cast<const QgsLineString *>( polygon->interiorRing( iInt ) ) )
296 : : {
297 : 1 : rings.append( interior );
298 : 1 : }
299 : 1 : }
300 : 9 : return rings;
301 : 9 : }
302 : :
303 : 11 : void QgsGeometryCheckerUtils::filter1DTypes( QgsAbstractGeometry *geom )
304 : : {
305 : 11 : if ( qgsgeometry_cast<QgsGeometryCollection *>( geom ) )
306 : : {
307 : 2 : QgsGeometryCollection *geomCollection = static_cast<QgsGeometryCollection *>( geom );
308 : 6 : for ( int nParts = geom->partCount(), iPart = nParts - 1; iPart >= 0; --iPart )
309 : : {
310 : 4 : if ( !qgsgeometry_cast<QgsSurface *>( geomCollection->geometryN( iPart ) ) )
311 : : {
312 : 0 : geomCollection->removeGeometry( iPart );
313 : 0 : }
314 : 4 : }
315 : 2 : }
316 : 11 : }
317 : :
318 : 598 : double pointLineDist( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q )
319 : : {
320 : 598 : 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 : 598 : double dx = p2.x() - p1.x();
322 : 598 : double dy = p2.y() - p1.y();
323 : 598 : return nom / std::sqrt( dx * dx + dy * dy );
324 : : }
325 : :
326 : 44 : bool QgsGeometryCheckerUtils::pointOnLine( const QgsPoint &p, const QgsLineString *line, double tol, bool excludeExtremities )
327 : : {
328 : 44 : int nVerts = line->vertexCount();
329 : 137 : for ( int i = 0 + excludeExtremities; i < nVerts - 1 - excludeExtremities; ++i )
330 : : {
331 : 109 : QgsPoint p1 = line->vertexAt( QgsVertexId( 0, 0, i ) );
332 : 109 : QgsPoint p2 = line->vertexAt( QgsVertexId( 0, 0, i + 1 ) );
333 : 109 : double dist = pointLineDist( p1, p2, p );
334 : 109 : if ( dist < tol )
335 : : {
336 : 16 : return true;
337 : : }
338 : 109 : }
339 : 28 : return false;
340 : 44 : }
341 : :
342 : 19 : QList<QgsPoint> QgsGeometryCheckerUtils::lineIntersections( const QgsLineString *line1, const QgsLineString *line2, double tol )
343 : : {
344 : 19 : QList<QgsPoint> intersections;
345 : 19 : QgsPoint inter;
346 : 19 : bool intersection = false;
347 : 97 : for ( int i = 0, n = line1->vertexCount() - 1; i < n; ++i )
348 : : {
349 : 426 : for ( int j = 0, m = line2->vertexCount() - 1; j < m; ++j )
350 : : {
351 : 348 : QgsPoint p1 = line1->vertexAt( QgsVertexId( 0, 0, i ) );
352 : 348 : QgsPoint p2 = line1->vertexAt( QgsVertexId( 0, 0, i + 1 ) );
353 : 348 : QgsPoint q1 = line2->vertexAt( QgsVertexId( 0, 0, j ) );
354 : 348 : QgsPoint q2 = line2->vertexAt( QgsVertexId( 0, 0, j + 1 ) );
355 : 348 : if ( QgsGeometryUtils::segmentIntersection( p1, p2, q1, q2, inter, intersection, tol ) )
356 : : {
357 : 6 : intersections.append( inter );
358 : 6 : }
359 : 348 : }
360 : 78 : }
361 : 19 : return intersections;
362 : 19 : }
363 : :
364 : 18 : double QgsGeometryCheckerUtils::sharedEdgeLength( const QgsAbstractGeometry *geom1, const QgsAbstractGeometry *geom2, double tol )
365 : : {
366 : 18 : double len = 0;
367 : :
368 : : // Test every pair of segments for shared edges
369 : 36 : for ( int iPart1 = 0, nParts1 = geom1->partCount(); iPart1 < nParts1; ++iPart1 )
370 : : {
371 : 37 : for ( int iRing1 = 0, nRings1 = geom1->ringCount( iPart1 ); iRing1 < nRings1; ++iRing1 )
372 : : {
373 : 101 : for ( int iVert1 = 0, jVert1 = 1, nVerts1 = geom1->vertexCount( iPart1, iRing1 ); jVert1 < nVerts1; iVert1 = jVert1++ )
374 : : {
375 : 82 : QgsPoint p1 = geom1->vertexAt( QgsVertexId( iPart1, iRing1, iVert1 ) );
376 : 82 : QgsPoint p2 = geom1->vertexAt( QgsVertexId( iPart1, iRing1, jVert1 ) );
377 : 82 : double lambdap1 = 0.;
378 : 82 : double lambdap2 = std::sqrt( QgsGeometryUtils::sqrDistance2D( p1, p2 ) );
379 : 82 : QgsVector d;
380 : : try
381 : : {
382 : 82 : d = QgsVector( p2.x() - p1.x(), p2.y() - p1.y() ).normalized();
383 : 82 : }
384 : : catch ( const QgsException & )
385 : : {
386 : : // Edge has zero length, skip
387 : : continue;
388 : 0 : }
389 : :
390 : 164 : for ( int iPart2 = 0, nParts2 = geom2->partCount(); iPart2 < nParts2; ++iPart2 )
391 : : {
392 : 164 : for ( int iRing2 = 0, nRings2 = geom2->ringCount( iPart2 ); iRing2 < nRings2; ++iRing2 )
393 : : {
394 : 464 : for ( int iVert2 = 0, jVert2 = 1, nVerts2 = geom2->vertexCount( iPart2, iRing2 ); jVert2 < nVerts2; iVert2 = jVert2++ )
395 : : {
396 : 382 : QgsPoint q1 = geom2->vertexAt( QgsVertexId( iPart2, iRing2, iVert2 ) );
397 : 382 : QgsPoint q2 = geom2->vertexAt( QgsVertexId( iPart2, iRing2, jVert2 ) );
398 : :
399 : : // Check whether q1 and q2 are on the line p1, p
400 : 382 : if ( pointLineDist( p1, p2, q1 ) <= tol && pointLineDist( p1, p2, q2 ) <= tol )
401 : : {
402 : : // Get length common edge
403 : 42 : double lambdaq1 = QgsVector( q1.x() - p1.x(), q1.y() - p1.y() ) * d;
404 : 42 : double lambdaq2 = QgsVector( q2.x() - p1.x(), q2.y() - p1.y() ) * d;
405 : 42 : if ( lambdaq1 > lambdaq2 )
406 : : {
407 : 31 : std::swap( lambdaq1, lambdaq2 );
408 : 31 : }
409 : 42 : double lambda1 = std::max( lambdaq1, lambdap1 );
410 : 42 : double lambda2 = std::min( lambdaq2, lambdap2 );
411 : 42 : len += std::max( 0., lambda2 - lambda1 );
412 : 42 : }
413 : 382 : }
414 : 82 : }
415 : 82 : }
416 : 82 : }
417 : 19 : }
418 : 18 : }
419 : 18 : return len;
420 : 0 : }
|