1 /***************************************************************************
2 qgsgeometryselfcontactcheck.cpp
3 ---------------------
4 begin : September 2017
5 copyright : (C) 2017 by Sandro Mani / Sourcepole AG
6 email : smani at sourcepole dot ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16 #include "qgsgeometrycheckcontext.h"
17 #include "qgsgeometryselfcontactcheck.h"
18 #include "qgsgeometryutils.h"
19 #include "qgsfeaturepool.h"
20
processGeometry(const QgsGeometry & geometry) const21 QList<QgsSingleGeometryCheckError *> QgsGeometrySelfContactCheck::processGeometry( const QgsGeometry &geometry ) const
22 {
23 QList<QgsSingleGeometryCheckError *> errors;
24 const QgsAbstractGeometry *geom = geometry.constGet();
25 for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
26 {
27 for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
28 {
29 // Test for self-contacts
30 int n = geom->vertexCount( iPart, iRing );
31 bool isClosed = geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) == geom->vertexAt( QgsVertexId( iPart, iRing, n - 1 ) );
32
33 // Geometry ring without duplicate nodes
34 QVector<int> vtxMap;
35 QVector<QgsPoint> ring;
36 vtxMap.append( 0 );
37 ring.append( geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) );
38 for ( int i = 1; i < n; ++i )
39 {
40 QgsPoint p = geom->vertexAt( QgsVertexId( iPart, iRing, i ) );
41 if ( QgsGeometryUtils::sqrDistance2D( p, ring.last() ) > mContext->tolerance * mContext->tolerance )
42 {
43 vtxMap.append( i );
44 ring.append( p );
45 }
46 }
47 while ( !ring.empty() && QgsGeometryUtils::sqrDistance2D( ring.front(), ring.back() ) < mContext->tolerance * mContext->tolerance )
48 {
49 vtxMap.pop_back();
50 ring.pop_back();
51 }
52 if ( !ring.empty() && isClosed )
53 {
54 vtxMap.append( n - 1 );
55 ring.append( ring.front() );
56 }
57 n = ring.size();
58
59 // For each vertex, check whether it lies on a segment
60 for ( int iVert = 0, nVerts = n - isClosed; iVert < nVerts; ++iVert )
61 {
62 const QgsPoint &p = ring[iVert];
63 for ( int i = 0, j = 1; j < n; i = j++ )
64 {
65 if ( iVert == i || iVert == j || ( isClosed && iVert == 0 && j == n - 1 ) )
66 {
67 continue;
68 }
69 const QgsPoint &si = ring[i];
70 const QgsPoint &sj = ring[j];
71 QgsPoint q = QgsGeometryUtils::projectPointOnSegment( p, si, sj );
72 if ( QgsGeometryUtils::sqrDistance2D( p, q ) < mContext->tolerance * mContext->tolerance )
73 {
74 errors.append( new QgsSingleGeometryCheckError( this, geometry, QgsGeometry( p.clone() ), QgsVertexId( iPart, iRing, vtxMap[iVert] ) ) );
75 break; // No need to report same contact on different segments multiple times
76 }
77 }
78 }
79 }
80 }
81 return errors;
82 }
83
fixError(const QMap<QString,QgsFeaturePool * > & featurePools,QgsGeometryCheckError * error,int method,const QMap<QString,int> &,Changes &) const84 void QgsGeometrySelfContactCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
85 {
86 Q_UNUSED( featurePools )
87 if ( method == NoChange )
88 {
89 error->setFixed( method );
90 }
91 else
92 {
93 error->setFixFailed( tr( "Unknown method" ) );
94 }
95 }
96
resolutionMethods() const97 QStringList QgsGeometrySelfContactCheck::resolutionMethods() const
98 {
99 static QStringList methods = QStringList() << tr( "No action" );
100 return methods;
101 }
102
factoryCheckType()103 QgsGeometryCheck::CheckType QgsGeometrySelfContactCheck::factoryCheckType()
104 {
105 return QgsGeometryCheck::FeatureNodeCheck;
106 }
107