LCOV - code coverage report
Current view: top level - core - qgsgeometryvalidator.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 275 0.0 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :   qgsgeometryvalidator.cpp - geometry validation thread
       3                 :            :   -------------------------------------------------------------------
       4                 :            : Date                 : 03.01.2012
       5                 :            : Copyright            : (C) 2012 by Juergen E. Fischer
       6                 :            : email                : jef at norbit dot de
       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 "qgis.h"
      17                 :            : #include "qgsgeometryvalidator.h"
      18                 :            : #include "qgsgeometry.h"
      19                 :            : #include "qgslogger.h"
      20                 :            : #include "qgsgeos.h"
      21                 :            : #include "qgsgeometrycollection.h"
      22                 :            : #include "qgspolygon.h"
      23                 :            : 
      24                 :          0 : QgsGeometryValidator::QgsGeometryValidator( const QgsGeometry &geometry, QVector<QgsGeometry::Error> *errors, QgsGeometry::ValidationMethod method )
      25                 :          0 :   : mGeometry( geometry )
      26                 :          0 :   , mErrors( errors )
      27                 :          0 :   , mStop( false )
      28                 :          0 :   , mErrorCount( 0 )
      29                 :          0 :   , mMethod( method )
      30                 :          0 : {
      31                 :          0 : }
      32                 :            : 
      33                 :          0 : QgsGeometryValidator::~QgsGeometryValidator()
      34                 :          0 : {
      35                 :          0 :   stop();
      36                 :          0 :   wait();
      37                 :          0 : }
      38                 :            : 
      39                 :          0 : void QgsGeometryValidator::stop()
      40                 :            : {
      41                 :          0 :   mStop = true;
      42                 :          0 : }
      43                 :            : 
      44                 :          0 : void QgsGeometryValidator::checkRingIntersections( int partIndex0, int ringIndex0, const QgsLineString *ring0, int partIndex1, int ringIndex1, const QgsLineString *ring1 )
      45                 :            : {
      46                 :          0 :   for ( int i = 0; !mStop && i < ring0->numPoints() - 1; i++ )
      47                 :            :   {
      48                 :          0 :     const double ring0XAti = ring0->xAt( i );
      49                 :          0 :     const double ring0YAti = ring0->yAt( i );
      50                 :          0 :     QgsVector v( ring0->xAt( i + 1 ) - ring0XAti, ring0->yAt( i + 1 ) - ring0YAti );
      51                 :            : 
      52                 :          0 :     for ( int j = 0; !mStop && j < ring1->numPoints() - 1; j++ )
      53                 :            :     {
      54                 :          0 :       const double ring1XAtj = ring1->xAt( j );
      55                 :          0 :       const double ring1YAtj = ring1->yAt( j );
      56                 :          0 :       QgsVector w( ring1->xAt( j + 1 ) - ring1XAtj, ring1->yAt( j + 1 ) - ring1YAtj );
      57                 :            : 
      58                 :            :       double sX;
      59                 :            :       double sY;
      60                 :          0 :       if ( intersectLines( ring0XAti, ring0YAti, v, ring1XAtj, ring1YAtj, w, sX, sY ) )
      61                 :            :       {
      62                 :          0 :         double d = -distLine2Point( ring0XAti, ring0YAti, v.perpVector(), sX, sY );
      63                 :            : 
      64                 :          0 :         if ( d >= 0 && d <= v.length() )
      65                 :            :         {
      66                 :          0 :           d = -distLine2Point( ring1XAtj, ring1YAtj, w.perpVector(), sX, sY );
      67                 :          0 :           if ( d > 0 && d < w.length() &&
      68                 :          0 :                ring0->pointN( i + 1 ) != ring1->pointN( j + 1 ) && ring0->pointN( i + 1 ) != ring1->pointN( j ) &&
      69                 :          0 :                ring0->pointN( i + 0 ) != ring1->pointN( j + 1 ) && ring0->pointN( i + 0 ) != ring1->pointN( j ) )
      70                 :            :           {
      71                 :          0 :             const QString msg = QObject::tr( "segment %1 of ring %2 of polygon %3 intersects segment %4 of ring %5 of polygon %6 at %7, %8" )
      72                 :          0 :                                 .arg( i ).arg( ringIndex0 ).arg( partIndex0 )
      73                 :          0 :                                 .arg( j ).arg( ringIndex1 ).arg( partIndex1 )
      74                 :          0 :                                 .arg( sX ).arg( sY );
      75                 :          0 :             emit errorFound( QgsGeometry::Error( msg, QgsPointXY( sX, sY ) ) );
      76                 :          0 :             mErrorCount++;
      77                 :          0 :           }
      78                 :          0 :         }
      79                 :          0 :       }
      80                 :          0 :     }
      81                 :          0 :   }
      82                 :          0 : }
      83                 :            : 
      84                 :          0 : void QgsGeometryValidator::validatePolyline( int i, const QgsLineString *line, bool ring )
      85                 :            : {
      86                 :          0 :   if ( !line )
      87                 :          0 :     return;
      88                 :            : 
      89                 :          0 :   if ( ring )
      90                 :            :   {
      91                 :          0 :     if ( line->numPoints() < 4 )
      92                 :            :     {
      93                 :          0 :       QString msg = QObject::tr( "ring %1 with less than four points" ).arg( i );
      94                 :          0 :       QgsDebugMsgLevel( msg, 2 );
      95                 :          0 :       emit errorFound( QgsGeometry::Error( msg ) );
      96                 :          0 :       mErrorCount++;
      97                 :            :       return;
      98                 :          0 :     }
      99                 :            : 
     100                 :          0 :     if ( !line->isClosed() )
     101                 :            :     {
     102                 :          0 :       QString msg = QObject::tr( "ring %1 not closed" ).arg( i );
     103                 :          0 :       QgsDebugMsgLevel( msg, 2 );
     104                 :          0 :       emit errorFound( QgsGeometry::Error( msg ) );
     105                 :          0 :       mErrorCount++;
     106                 :            :       return;
     107                 :          0 :     }
     108                 :          0 :   }
     109                 :          0 :   else if ( line->numPoints() < 2 )
     110                 :            :   {
     111                 :          0 :     QString msg = QObject::tr( "line %1 with less than two points" ).arg( i );
     112                 :          0 :     QgsDebugMsgLevel( msg, 2 );
     113                 :          0 :     emit errorFound( QgsGeometry::Error( msg ) );
     114                 :          0 :     mErrorCount++;
     115                 :            :     return;
     116                 :          0 :   }
     117                 :            : 
     118                 :          0 :   std::unique_ptr< QgsLineString > noDupes;
     119                 :            : 
     120                 :            :   // test for duplicate nodes, and if we find any flag errors and then remove them so that the subsequent
     121                 :            :   // tests work OK.
     122                 :          0 :   const QVector< QgsVertexId > duplicateNodes = line->collectDuplicateNodes( 1E-8 );
     123                 :          0 :   if ( !duplicateNodes.empty() )
     124                 :            :   {
     125                 :          0 :     noDupes.reset( line->clone() );
     126                 :          0 :     for ( int j = duplicateNodes.size() - 1; j >= 0; j-- )
     127                 :            :     {
     128                 :          0 :       const QgsVertexId duplicateVertex = duplicateNodes.at( j );
     129                 :          0 :       const QgsPointXY duplicationLocation = noDupes->vertexAt( duplicateVertex );
     130                 :          0 :       noDupes->deleteVertex( duplicateVertex );
     131                 :          0 :       int n = 1;
     132                 :            : 
     133                 :            :       // count how many other points exist at this location too
     134                 :          0 :       for ( int k = j - 1; k >= 0; k-- )
     135                 :            :       {
     136                 :          0 :         const QgsVertexId prevDupe = duplicateNodes.at( k );
     137                 :          0 :         const QgsPoint prevPoint = noDupes->vertexAt( prevDupe );
     138                 :          0 :         if ( qgsDoubleNear( duplicationLocation.x(), prevPoint.x(), 1E-8 ) && qgsDoubleNear( duplicationLocation.y(), prevPoint.y(), 1E-8 ) )
     139                 :            :         {
     140                 :          0 :           noDupes->deleteVertex( prevDupe );
     141                 :          0 :           n++;
     142                 :          0 :         }
     143                 :            :         else
     144                 :            :         {
     145                 :          0 :           break;
     146                 :            :         }
     147                 :          0 :       }
     148                 :            : 
     149                 :          0 :       j -= n - 1;
     150                 :            : 
     151                 :          0 :       QString msg = QObject::tr( "line %1 contains %n duplicate nodes starting at vertex %2", "number of duplicate nodes", n + 1 ).arg( i + 1 ).arg( duplicateVertex.vertex - n + 1 );
     152                 :          0 :       QgsDebugMsgLevel( msg, 2 );
     153                 :          0 :       emit errorFound( QgsGeometry::Error( msg, duplicationLocation ) );
     154                 :          0 :       mErrorCount++;
     155                 :          0 :     }
     156                 :          0 :     line = noDupes.get();
     157                 :          0 :   }
     158                 :            : 
     159                 :          0 :   for ( int j = 0; !mStop && j < line->numPoints() - 3; j++ )
     160                 :            :   {
     161                 :          0 :     const double xAtJ = line->xAt( j );
     162                 :          0 :     const double yAtJ = line->yAt( j );
     163                 :          0 :     QgsVector v( line->xAt( j + 1 ) - xAtJ, line->yAt( j + 1 ) - yAtJ );
     164                 :          0 :     double vl = v.length();
     165                 :            : 
     166                 :          0 :     int n = ( j == 0 && ring ) ? line->numPoints() - 2 : line->numPoints() - 1;
     167                 :            : 
     168                 :          0 :     for ( int k = j + 2; !mStop && k < n; k++ )
     169                 :            :     {
     170                 :          0 :       const double xAtK = line->xAt( k );
     171                 :          0 :       const double yAtK = line->yAt( k );
     172                 :            : 
     173                 :          0 :       QgsVector w( line->xAt( k + 1 ) - xAtK, line->yAt( k + 1 ) - yAtK );
     174                 :            : 
     175                 :            :       double sX;
     176                 :            :       double sY;
     177                 :          0 :       if ( !intersectLines( xAtJ, yAtJ, v, xAtK, yAtK, w, sX, sY ) )
     178                 :          0 :         continue;
     179                 :            : 
     180                 :          0 :       double d = 0.0;
     181                 :            :       try
     182                 :            :       {
     183                 :          0 :         d = -distLine2Point( xAtJ, yAtJ, v.perpVector(), sX, sY );
     184                 :          0 :       }
     185                 :            :       catch ( QgsException &e )
     186                 :            :       {
     187                 :          0 :         Q_UNUSED( e )
     188                 :          0 :         QgsDebugMsg( "Error validating: " + e.what() );
     189                 :            :         continue;
     190                 :          0 :       }
     191                 :          0 :       if ( d < 0 || d > vl )
     192                 :          0 :         continue;
     193                 :            : 
     194                 :            :       try
     195                 :            :       {
     196                 :          0 :         d = -distLine2Point( xAtK, yAtK, w.perpVector(), sX, sY );
     197                 :          0 :       }
     198                 :            :       catch ( QgsException &e )
     199                 :            :       {
     200                 :          0 :         Q_UNUSED( e )
     201                 :          0 :         QgsDebugMsg( "Error validating: " + e.what() );
     202                 :            :         continue;
     203                 :          0 :       }
     204                 :            : 
     205                 :          0 :       if ( d <= 0 || d >= w.length() )
     206                 :          0 :         continue;
     207                 :            : 
     208                 :          0 :       QString msg = QObject::tr( "segments %1 and %2 of line %3 intersect at %4, %5" ).arg( j ).arg( k ).arg( i ).arg( sX ).arg( sY );
     209                 :          0 :       QgsDebugMsgLevel( msg, 2 );
     210                 :          0 :       emit errorFound( QgsGeometry::Error( msg, QgsPointXY( sX, sY ) ) );
     211                 :          0 :       mErrorCount++;
     212                 :          0 :     }
     213                 :          0 :   }
     214                 :          0 : }
     215                 :            : 
     216                 :          0 : void QgsGeometryValidator::validatePolygon( int partIndex, const QgsPolygon *polygon )
     217                 :            : {
     218                 :            :   // check if holes are inside polygon
     219                 :          0 :   for ( int i = 0; !mStop && i < polygon->numInteriorRings(); ++i )
     220                 :            :   {
     221                 :          0 :     if ( !ringInRing( static_cast< const QgsLineString * >( polygon->interiorRing( i ) ), static_cast< const QgsLineString * >( polygon->exteriorRing() ) ) )
     222                 :            :     {
     223                 :          0 :       QString msg = QObject::tr( "ring %1 of polygon %2 not in exterior ring" ).arg( i + 1 ).arg( partIndex );
     224                 :          0 :       QgsDebugMsg( msg );
     225                 :          0 :       emit errorFound( QgsGeometry::Error( msg ) );
     226                 :          0 :       mErrorCount++;
     227                 :          0 :     }
     228                 :          0 :   }
     229                 :            : 
     230                 :            :   // check holes for intersections
     231                 :          0 :   for ( int i = 0; !mStop && i < polygon->numInteriorRings(); i++ )
     232                 :            :   {
     233                 :          0 :     for ( int j = i + 1; !mStop && j < polygon->numInteriorRings(); j++ )
     234                 :            :     {
     235                 :          0 :       checkRingIntersections( partIndex, i + 1, qgsgeometry_cast< QgsLineString * >( polygon->interiorRing( i ) ),
     236                 :          0 :                               partIndex, j + 1, qgsgeometry_cast< QgsLineString * >( polygon->interiorRing( j ) ) );
     237                 :          0 :     }
     238                 :          0 :   }
     239                 :            : 
     240                 :            :   // check if rings are self-intersecting
     241                 :          0 :   validatePolyline( 0, qgsgeometry_cast< const QgsLineString * >( polygon->exteriorRing() ), true );
     242                 :          0 :   for ( int i = 0; !mStop && i < polygon->numInteriorRings(); i++ )
     243                 :            :   {
     244                 :          0 :     validatePolyline( i + 1, qgsgeometry_cast< const QgsLineString * >( polygon->interiorRing( i ) ), true );
     245                 :          0 :   }
     246                 :          0 : }
     247                 :            : 
     248                 :          0 : void QgsGeometryValidator::run()
     249                 :            : {
     250                 :          0 :   mErrorCount = 0;
     251                 :          0 :   if ( mGeometry.isNull() )
     252                 :            :   {
     253                 :          0 :     return;
     254                 :            :   }
     255                 :            : 
     256                 :          0 :   switch ( mMethod )
     257                 :            :   {
     258                 :            :     case QgsGeometry::ValidatorGeos:
     259                 :            :     {
     260                 :            :       // avoid calling geos for trivial point geometries
     261                 :          0 :       if ( QgsWkbTypes::geometryType( mGeometry.wkbType() ) == QgsWkbTypes::PointGeometry )
     262                 :            :       {
     263                 :          0 :         return;
     264                 :            :       }
     265                 :            : 
     266                 :          0 :       QgsGeos geos( mGeometry.constGet() );
     267                 :          0 :       QString error;
     268                 :          0 :       QgsGeometry errorLoc;
     269                 :          0 :       if ( !geos.isValid( &error, true, &errorLoc ) )
     270                 :            :       {
     271                 :          0 :         if ( errorLoc.isNull() )
     272                 :            :         {
     273                 :          0 :           emit errorFound( QgsGeometry::Error( error ) );
     274                 :          0 :           mErrorCount++;
     275                 :          0 :         }
     276                 :            :         else
     277                 :            :         {
     278                 :          0 :           const QgsPointXY point = errorLoc.asPoint();
     279                 :          0 :           emit errorFound( QgsGeometry::Error( error, point ) );
     280                 :          0 :           mErrorCount++;
     281                 :            :         }
     282                 :          0 :       }
     283                 :            : 
     284                 :            :       break;
     285                 :          0 :     }
     286                 :            : 
     287                 :            :     case QgsGeometry::ValidatorQgisInternal:
     288                 :            :     {
     289                 :          0 :       switch ( QgsWkbTypes::flatType( mGeometry.constGet()->wkbType() ) )
     290                 :            :       {
     291                 :            :         case QgsWkbTypes::Point:
     292                 :            :         case QgsWkbTypes::MultiPoint:
     293                 :          0 :           break;
     294                 :            : 
     295                 :            :         case QgsWkbTypes::LineString:
     296                 :          0 :           validatePolyline( 0, qgsgeometry_cast< const QgsLineString * >( mGeometry.constGet() ) );
     297                 :          0 :           break;
     298                 :            : 
     299                 :            :         case QgsWkbTypes::MultiLineString:
     300                 :            :         {
     301                 :          0 :           const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( mGeometry.constGet() );
     302                 :          0 :           for ( int i = 0; !mStop && i < collection->numGeometries(); i++ )
     303                 :          0 :             validatePolyline( i, qgsgeometry_cast< const QgsLineString * >( collection->geometryN( i ) ) );
     304                 :          0 :           break;
     305                 :            :         }
     306                 :            : 
     307                 :            :         case QgsWkbTypes::Polygon:
     308                 :          0 :           validatePolygon( 0, qgsgeometry_cast< const QgsPolygon * >( mGeometry.constGet() ) );
     309                 :          0 :           break;
     310                 :            : 
     311                 :            :         case QgsWkbTypes::MultiPolygon:
     312                 :            :         {
     313                 :          0 :           const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( mGeometry.constGet() );
     314                 :          0 :           for ( int i = 0; !mStop && i < collection->numGeometries(); i++ )
     315                 :          0 :             validatePolygon( i, qgsgeometry_cast< const QgsPolygon * >( collection->geometryN( i ) ) );
     316                 :            : 
     317                 :          0 :           for ( int i = 0; !mStop && i < collection->numGeometries(); i++ )
     318                 :            :           {
     319                 :          0 :             const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( collection->geometryN( i ) );
     320                 :          0 :             if ( !poly->exteriorRing() || poly->exteriorRing()->isEmpty() )
     321                 :            :             {
     322                 :          0 :               emit errorFound( QgsGeometry::Error( QObject::tr( "Polygon %1 has no rings" ).arg( i ) ) );
     323                 :          0 :               mErrorCount++;
     324                 :          0 :               continue;
     325                 :            :             }
     326                 :            : 
     327                 :          0 :             for ( int j = i + 1;  !mStop && j < collection->numGeometries(); j++ )
     328                 :            :             {
     329                 :          0 :               const QgsPolygon *poly2 = qgsgeometry_cast< const QgsPolygon * >( collection->geometryN( j ) );
     330                 :          0 :               if ( !poly2->exteriorRing() || poly2->exteriorRing()->isEmpty() )
     331                 :          0 :                 continue;
     332                 :            : 
     333                 :          0 :               if ( ringInRing( qgsgeometry_cast< const QgsLineString * >( poly->exteriorRing() ),
     334                 :          0 :                                qgsgeometry_cast< const QgsLineString * >( poly2->exteriorRing() ) ) )
     335                 :            :               {
     336                 :          0 :                 emit errorFound( QgsGeometry::Error( QObject::tr( "Polygon %1 lies inside polygon %2" ).arg( i ).arg( j ) ) );
     337                 :          0 :                 mErrorCount++;
     338                 :          0 :               }
     339                 :          0 :               else if ( ringInRing( static_cast< const QgsLineString * >( poly2->exteriorRing() ),
     340                 :          0 :                                     static_cast< const QgsLineString * >( poly->exteriorRing() ) ) )
     341                 :            :               {
     342                 :          0 :                 emit errorFound( QgsGeometry::Error( QObject::tr( "Polygon %1 lies inside polygon %2" ).arg( j ).arg( i ) ) );
     343                 :          0 :                 mErrorCount++;
     344                 :          0 :               }
     345                 :            :               else
     346                 :            :               {
     347                 :          0 :                 checkRingIntersections( i, 0, qgsgeometry_cast< const QgsLineString * >( poly->exteriorRing() ),
     348                 :          0 :                                         j, 0, qgsgeometry_cast< const QgsLineString * >( poly2->exteriorRing() ) );
     349                 :            :               }
     350                 :          0 :             }
     351                 :          0 :           }
     352                 :          0 :           break;
     353                 :            :         }
     354                 :            : 
     355                 :            :         case QgsWkbTypes::Unknown:
     356                 :            :         {
     357                 :          0 :           emit errorFound( QgsGeometry::Error( QObject::tr( "Unknown geometry type %1" ).arg( mGeometry.wkbType() ) ) );
     358                 :          0 :           mErrorCount++;
     359                 :          0 :           break;
     360                 :            :         }
     361                 :            : 
     362                 :            :         default:
     363                 :          0 :           break;
     364                 :            :       }
     365                 :            : 
     366                 :          0 :       if ( mStop )
     367                 :            :       {
     368                 :          0 :         emit validationFinished( QObject::tr( "Geometry validation was aborted." ) );
     369                 :          0 :       }
     370                 :          0 :       else if ( mErrorCount > 0 )
     371                 :            :       {
     372                 :          0 :         emit validationFinished( QObject::tr( "Geometry has %1 errors." ).arg( mErrorCount ) );
     373                 :          0 :       }
     374                 :            :       else
     375                 :            :       {
     376                 :          0 :         emit validationFinished( QObject::tr( "Geometry is valid." ) );
     377                 :            :       }
     378                 :          0 :       break;
     379                 :            :     }
     380                 :            :   }
     381                 :          0 : }
     382                 :            : 
     383                 :          0 : void QgsGeometryValidator::addError( const QgsGeometry::Error &e )
     384                 :            : {
     385                 :          0 :   if ( mErrors )
     386                 :          0 :     *mErrors << e;
     387                 :          0 : }
     388                 :            : 
     389                 :          0 : void QgsGeometryValidator::validateGeometry( const QgsGeometry &geometry, QVector<QgsGeometry::Error> &errors, QgsGeometry::ValidationMethod method )
     390                 :            : {
     391                 :          0 :   QgsGeometryValidator *gv = new QgsGeometryValidator( geometry, &errors, method );
     392                 :          0 :   connect( gv, &QgsGeometryValidator::errorFound, gv, &QgsGeometryValidator::addError );
     393                 :          0 :   gv->run();
     394                 :          0 :   gv->wait();
     395                 :          0 : }
     396                 :            : 
     397                 :            : //
     398                 :            : // distance of point q from line through p in direction v
     399                 :            : // return >0  => q lies left of the line
     400                 :            : //        <0  => q lies right of the line
     401                 :            : //
     402                 :          0 : double QgsGeometryValidator::distLine2Point( double px, double py, QgsVector v, double qX, double qY )
     403                 :            : {
     404                 :          0 :   const double l = v.length();
     405                 :          0 :   if ( qgsDoubleNear( l, 0 ) )
     406                 :            :   {
     407                 :          0 :     throw QgsException( QObject::tr( "invalid line" ) );
     408                 :            :   }
     409                 :            : 
     410                 :          0 :   return ( v.x() * ( qY - py ) - v.y() * ( qX - px ) ) / l;
     411                 :          0 : }
     412                 :            : 
     413                 :          0 : bool QgsGeometryValidator::intersectLines( double px, double py, QgsVector v, double qx, double qy, QgsVector w, double &sX, double &sY )
     414                 :            : {
     415                 :          0 :   double d = v.y() * w.x() - v.x() * w.y();
     416                 :            : 
     417                 :          0 :   if ( qgsDoubleNear( d, 0 ) )
     418                 :          0 :     return false;
     419                 :            : 
     420                 :          0 :   double dx = qx - px;
     421                 :          0 :   double dy = qy - py;
     422                 :          0 :   double k = ( dy * w.x() - dx * w.y() ) / d;
     423                 :            : 
     424                 :          0 :   sX = px  + v.x() * k;
     425                 :          0 :   sY = py + v.y() * k;
     426                 :            : 
     427                 :          0 :   return true;
     428                 :          0 : }
     429                 :            : 
     430                 :          0 : bool QgsGeometryValidator::pointInRing( const QgsLineString *ring, double pX, double pY )
     431                 :            : {
     432                 :          0 :   if ( !ring->boundingBox().contains( pX, pY ) )
     433                 :          0 :     return false;
     434                 :            : 
     435                 :          0 :   bool inside = false;
     436                 :          0 :   int j = ring->numPoints() - 1;
     437                 :            : 
     438                 :          0 :   for ( int i = 0; !mStop && i < ring->numPoints(); i++ )
     439                 :            :   {
     440                 :          0 :     const double xAti = ring->xAt( i );
     441                 :          0 :     const double yAti = ring->yAt( i );
     442                 :          0 :     const double xAtj = ring->xAt( j );
     443                 :          0 :     const double yAtj = ring->yAt( j );
     444                 :            : 
     445                 :          0 :     if ( qgsDoubleNear( xAti, pX ) && qgsDoubleNear( yAti, pY ) )
     446                 :          0 :       return true;
     447                 :            : 
     448                 :          0 :     if ( ( yAti < pY && yAtj >= pY ) ||
     449                 :          0 :          ( yAtj < pY && yAti >= pY ) )
     450                 :            :     {
     451                 :          0 :       if ( xAti + ( pY - yAti ) / ( yAtj - yAti ) * ( xAtj - xAti ) <= pX )
     452                 :          0 :         inside = !inside;
     453                 :          0 :     }
     454                 :            : 
     455                 :          0 :     j = i;
     456                 :          0 :   }
     457                 :            : 
     458                 :          0 :   return inside;
     459                 :          0 : }
     460                 :            : 
     461                 :          0 : bool QgsGeometryValidator::ringInRing( const QgsLineString *inside, const QgsLineString *outside )
     462                 :            : {
     463                 :          0 :   if ( !outside->boundingBox().contains( inside->boundingBox() ) )
     464                 :          0 :     return false;
     465                 :            : 
     466                 :          0 :   for ( int i = 0; !mStop && i < inside->numPoints(); i++ )
     467                 :            :   {
     468                 :          0 :     if ( !pointInRing( outside, inside->xAt( i ), inside->yAt( i ) ) )
     469                 :          0 :       return false;
     470                 :          0 :   }
     471                 :            : 
     472                 :          0 :   return true;
     473                 :          0 : }

Generated by: LCOV version 1.14