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