LCOV - code coverage report
Current view: top level - analysis/vector/geometry_checker - qgsgeometrymissingvertexcheck.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 92 157 58.6 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :     qgsgeometrymissingvertexcheck.cpp
       3                 :            :     ---------------------
       4                 :            :     begin                : September 2018
       5                 :            :     copyright            : (C) 2018 Matthias Kuhn
       6                 :            :     email                : matthias@opengis.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 "qgsgeometrymissingvertexcheck.h"
      17                 :            : 
      18                 :            : #include "qgsfeedback.h"
      19                 :            : #include "qgsgeometrycollection.h"
      20                 :            : #include "qgsmultipolygon.h"
      21                 :            : #include "qgscurvepolygon.h"
      22                 :            : #include "qgscurve.h"
      23                 :            : #include "qgslinestring.h"
      24                 :            : #include "qgsgeometryengine.h"
      25                 :            : #include "qgsgeometryutils.h"
      26                 :            : #include "qgsapplication.h"
      27                 :            : 
      28                 :          1 : QgsGeometryMissingVertexCheck::QgsGeometryMissingVertexCheck( const QgsGeometryCheckContext *context, const QVariantMap &geometryCheckConfiguration )
      29                 :          1 :   : QgsGeometryCheck( context, geometryCheckConfiguration )
      30                 :            : 
      31                 :          2 : {}
      32                 :            : 
      33                 :          1 : void QgsGeometryMissingVertexCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
      34                 :            : {
      35                 :          1 :   Q_UNUSED( messages )
      36                 :          1 :   if ( feedback )
      37                 :          1 :     feedback->setProgress( feedback->progress() + 1.0 );
      38                 :            : 
      39                 :          1 :   QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
      40                 :            : 
      41                 :          1 :   QgsFeaturePool *featurePool = featurePools.value( featureIds.firstKey() );
      42                 :            : 
      43                 :          1 :   const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), nullptr, mContext, true );
      44                 :            : 
      45                 :          7 :   for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
      46                 :            :   {
      47                 :          6 :     if ( feedback && feedback->isCanceled() )
      48                 :            :     {
      49                 :          0 :       break;
      50                 :            :     }
      51                 :            : 
      52                 :          6 :     const QgsGeometry geometry = layerFeature.geometry();
      53                 :          6 :     const QgsAbstractGeometry *geom = geometry.constGet();
      54                 :            : 
      55                 :          6 :     if ( QgsCurvePolygon *polygon = qgsgeometry_cast<QgsCurvePolygon *>( geom ) )
      56                 :            :     {
      57                 :          0 :       processPolygon( polygon, featurePool, errors, layerFeature, feedback );
      58                 :          0 :     }
      59                 :          6 :     else if ( QgsGeometryCollection *collection = qgsgeometry_cast<QgsGeometryCollection *>( geom ) )
      60                 :            :     {
      61                 :          6 :       const int numGeometries = collection->numGeometries();
      62                 :         12 :       for ( int i = 0; i < numGeometries; ++i )
      63                 :            :       {
      64                 :          6 :         if ( QgsCurvePolygon *polygon = qgsgeometry_cast<QgsCurvePolygon *>( collection->geometryN( i ) ) )
      65                 :            :         {
      66                 :          6 :           processPolygon( polygon, featurePool, errors, layerFeature, feedback );
      67                 :          6 :         }
      68                 :          6 :       }
      69                 :          6 :     }
      70                 :          6 :   }
      71                 :          1 : }
      72                 :            : 
      73                 :          0 : void QgsGeometryMissingVertexCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
      74                 :            : {
      75                 :          0 :   Q_UNUSED( featurePools )
      76                 :          0 :   Q_UNUSED( changes )
      77                 :            : 
      78                 :          0 :   QMetaEnum metaEnum = QMetaEnum::fromType<QgsGeometryMissingVertexCheck::ResolutionMethod>();
      79                 :          0 :   if ( !metaEnum.isValid() || !metaEnum.valueToKey( method ) )
      80                 :            :   {
      81                 :          0 :     error->setFixFailed( tr( "Unknown method" ) );
      82                 :          0 :   }
      83                 :            :   else
      84                 :            :   {
      85                 :          0 :     ResolutionMethod methodValue = static_cast<ResolutionMethod>( method );
      86                 :          0 :     switch ( methodValue )
      87                 :            :     {
      88                 :            :       case NoChange:
      89                 :          0 :         error->setFixed( method );
      90                 :          0 :         break;
      91                 :            : 
      92                 :            :       case AddMissingVertex:
      93                 :            :       {
      94                 :          0 :         QgsFeaturePool *featurePool = featurePools[ error->layerId() ];
      95                 :            : 
      96                 :          0 :         QgsFeature feature;
      97                 :          0 :         featurePool->getFeature( error->featureId(), feature );
      98                 :            : 
      99                 :          0 :         QgsPointXY pointOnSegment; // Should be equal to location
     100                 :            :         int vertexIndex;
     101                 :          0 :         QgsGeometry geometry = feature.geometry();
     102                 :          0 :         geometry.closestSegmentWithContext( error->location(), pointOnSegment, vertexIndex );
     103                 :          0 :         geometry.insertVertex( QgsPoint( error->location() ), vertexIndex );
     104                 :          0 :         feature.setGeometry( geometry );
     105                 :            : 
     106                 :          0 :         featurePool->updateFeature( feature );
     107                 :            :         // TODO update "changes" structure
     108                 :            : 
     109                 :          0 :         error->setFixed( method );
     110                 :          0 :       }
     111                 :          0 :       break;
     112                 :            :     }
     113                 :            :   }
     114                 :          0 : }
     115                 :            : 
     116                 :          0 : QStringList QgsGeometryMissingVertexCheck::resolutionMethods() const
     117                 :            : {
     118                 :          0 :   static QStringList methods = QStringList()
     119                 :          0 :                                << tr( "No action" )
     120                 :          0 :                                << tr( "Add missing vertex" );
     121                 :          0 :   return methods;
     122                 :          0 : }
     123                 :            : 
     124                 :          0 : QString QgsGeometryMissingVertexCheck::description() const
     125                 :            : {
     126                 :          0 :   return factoryDescription();
     127                 :            : }
     128                 :            : 
     129                 :          6 : void QgsGeometryMissingVertexCheck::processPolygon( const QgsCurvePolygon *polygon, QgsFeaturePool *featurePool, QList<QgsGeometryCheckError *> &errors, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, QgsFeedback *feedback ) const
     130                 :            : {
     131                 :          6 :   const QgsFeature &currentFeature = layerFeature.feature();
     132                 :          6 :   std::unique_ptr<QgsMultiPolygon> boundaries = std::make_unique<QgsMultiPolygon>();
     133                 :            : 
     134                 :          6 :   std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( polygon->exteriorRing()->clone(), mContext->tolerance );
     135                 :          6 :   boundaries->addGeometry( geomEngine->buffer( mContext->tolerance, 5 ) );
     136                 :            : 
     137                 :          6 :   const int numRings = polygon->numInteriorRings();
     138                 :          6 :   for ( int i = 0; i < numRings; ++i )
     139                 :            :   {
     140                 :          0 :     geomEngine = QgsGeometryCheckerUtils::createGeomEngine( polygon->interiorRing( i ), mContext->tolerance );
     141                 :          0 :     boundaries->addGeometry( geomEngine->buffer( mContext->tolerance, 5 ) );
     142                 :          0 :   }
     143                 :            : 
     144                 :          6 :   geomEngine = QgsGeometryCheckerUtils::createGeomEngine( boundaries.get(), mContext->tolerance );
     145                 :          6 :   geomEngine->prepareGeometry();
     146                 :            : 
     147                 :          6 :   const QgsFeatureIds fids = featurePool->getIntersects( boundaries->boundingBox() );
     148                 :            : 
     149                 :          6 :   QgsFeature compareFeature;
     150                 :         32 :   for ( QgsFeatureId fid : fids )
     151                 :            :   {
     152                 :         26 :     if ( fid == currentFeature.id() )
     153                 :          6 :       continue;
     154                 :            : 
     155                 :         20 :     if ( featurePool->getFeature( fid, compareFeature ) )
     156                 :            :     {
     157                 :         20 :       if ( feedback && feedback->isCanceled() )
     158                 :          0 :         break;
     159                 :            : 
     160                 :         20 :       const QgsGeometry compareGeometry = compareFeature.geometry();
     161                 :         20 :       QgsVertexIterator vertexIterator = compareGeometry.vertices();
     162                 :        152 :       while ( vertexIterator.hasNext() )
     163                 :            :       {
     164                 :        132 :         const QgsPoint &pt = vertexIterator.next();
     165                 :        132 :         if ( geomEngine->intersects( &pt ) )
     166                 :            :         {
     167                 :         48 :           QgsVertexId vertexId;
     168                 :         48 :           QgsPoint closestVertex = QgsGeometryUtils::closestVertex( *polygon, pt, vertexId );
     169                 :            : 
     170                 :         48 :           if ( closestVertex.distance( pt ) > mContext->tolerance )
     171                 :            :           {
     172                 :          6 :             bool alreadyReported = false;
     173                 :         17 :             for ( QgsGeometryCheckError *error : std::as_const( errors ) )
     174                 :            :             {
     175                 :            :               // Only list missing vertices once
     176                 :         14 :               if ( error->featureId() == currentFeature.id() && error->location() == QgsPointXY( pt ) )
     177                 :            :               {
     178                 :          1 :                 alreadyReported = true;
     179                 :          1 :                 break;
     180                 :            :               }
     181                 :            :             }
     182                 :          6 :             if ( !alreadyReported )
     183                 :            :             {
     184                 :          5 :               std::unique_ptr<QgsGeometryMissingVertexCheckError> error = std::make_unique<QgsGeometryMissingVertexCheckError>( this, layerFeature, QgsPointXY( pt ) );
     185                 :          5 :               error->setAffectedAreaBBox( contextBoundingBox( polygon, vertexId, pt ) );
     186                 :          5 :               QMap<QString, QgsFeatureIds> involvedFeatures;
     187                 :          5 :               involvedFeatures[layerFeature.layerId()].insert( layerFeature.feature().id() );
     188                 :          5 :               involvedFeatures[featurePool->layerId()].insert( fid );
     189                 :          5 :               error->setInvolvedFeatures( involvedFeatures );
     190                 :            : 
     191                 :          5 :               errors.append( error.release() );
     192                 :          5 :             }
     193                 :          6 :           }
     194                 :         48 :         }
     195                 :        132 :       }
     196                 :         20 :     }
     197                 :            :   }
     198                 :          6 : }
     199                 :            : 
     200                 :          5 : QgsRectangle QgsGeometryMissingVertexCheck::contextBoundingBox( const QgsCurvePolygon *polygon, const QgsVertexId &vertexId, const QgsPoint &point ) const
     201                 :            : {
     202                 :          5 :   QgsVertexId vertexBefore;
     203                 :          5 :   QgsVertexId vertexAfter;
     204                 :            : 
     205                 :          5 :   polygon->adjacentVertices( vertexId, vertexBefore, vertexAfter );
     206                 :            : 
     207                 :          5 :   QgsPoint ptBefore = polygon->vertexAt( vertexBefore );
     208                 :          5 :   QgsPoint ptAt = polygon->vertexAt( vertexId );
     209                 :          5 :   QgsPoint ptAfter = polygon->vertexAt( vertexAfter );
     210                 :            : 
     211                 :          5 :   double length = std::abs( ptAt.distance( ptBefore ) ) + std::abs( ptAt.distance( ptAfter ) );
     212                 :            : 
     213                 :          5 :   QgsRectangle rect( point.x() - length / 2, point.y() - length / 2, point.x() + length / 2, point.y() + length / 2 );
     214                 :            :   return rect;
     215                 :          5 : }
     216                 :            : 
     217                 :          0 : QString QgsGeometryMissingVertexCheck::id() const
     218                 :            : {
     219                 :          0 :   return factoryId();
     220                 :            : }
     221                 :            : 
     222                 :          1 : QList<QgsWkbTypes::GeometryType> QgsGeometryMissingVertexCheck::compatibleGeometryTypes() const
     223                 :            : {
     224                 :          1 :   return factoryCompatibleGeometryTypes();
     225                 :            : }
     226                 :            : 
     227                 :          0 : QgsGeometryCheck::Flags QgsGeometryMissingVertexCheck::flags() const
     228                 :            : {
     229                 :          0 :   return factoryFlags();
     230                 :            : }
     231                 :            : 
     232                 :          0 : QgsGeometryCheck::CheckType QgsGeometryMissingVertexCheck::checkType() const
     233                 :            : {
     234                 :          0 :   return factoryCheckType();
     235                 :            : }
     236                 :            : 
     237                 :            : ///@cond private
     238                 :          1 : QList<QgsWkbTypes::GeometryType> QgsGeometryMissingVertexCheck::factoryCompatibleGeometryTypes()
     239                 :            : {
     240                 :          1 :   return {QgsWkbTypes::PolygonGeometry};
     241                 :            : }
     242                 :            : 
     243                 :          0 : bool QgsGeometryMissingVertexCheck::factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP
     244                 :            : {
     245                 :          0 :   return factoryCompatibleGeometryTypes().contains( layer->geometryType() );
     246                 :          0 : }
     247                 :            : 
     248                 :          0 : QString QgsGeometryMissingVertexCheck::factoryDescription()
     249                 :            : {
     250                 :          0 :   return tr( "Missing Vertex" );
     251                 :            : }
     252                 :            : 
     253                 :          0 : QString QgsGeometryMissingVertexCheck::factoryId()
     254                 :            : {
     255                 :          0 :   return QStringLiteral( "QgsGeometryMissingVertexCheck" );
     256                 :            : }
     257                 :            : 
     258                 :          0 : QgsGeometryCheck::Flags QgsGeometryMissingVertexCheck::factoryFlags()
     259                 :            : {
     260                 :          0 :   return QgsGeometryCheck::AvailableInValidation;
     261                 :            : }
     262                 :            : 
     263                 :          0 : QgsGeometryCheck::CheckType QgsGeometryMissingVertexCheck::factoryCheckType()
     264                 :            : {
     265                 :          0 :   return QgsGeometryCheck::LayerCheck;
     266                 :            : }
     267                 :            : ///@endcond private
     268                 :            : 
     269                 :          5 : QgsGeometryMissingVertexCheckError::QgsGeometryMissingVertexCheckError( const QgsGeometryCheck *check, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, const QgsPointXY &errorLocation, QgsVertexId vidx, const QVariant &value, QgsGeometryCheckError::ValueType valueType )
     270                 :          5 :   : QgsGeometryCheckError( check, layerFeature, errorLocation, vidx, value, valueType )
     271                 :         10 : {
     272                 :          5 : }
     273                 :            : 
     274                 :          0 : QgsRectangle QgsGeometryMissingVertexCheckError::affectedAreaBBox() const
     275                 :            : {
     276                 :          0 :   return mAffectedAreaBBox;
     277                 :            : }
     278                 :            : 
     279                 :          5 : void QgsGeometryMissingVertexCheckError::setAffectedAreaBBox( const QgsRectangle &affectedAreaBBox )
     280                 :            : {
     281                 :          5 :   mAffectedAreaBBox = affectedAreaBBox;
     282                 :          5 : }
     283                 :            : 
     284                 :          0 : QMap<QString, QgsFeatureIds> QgsGeometryMissingVertexCheckError::involvedFeatures() const
     285                 :            : {
     286                 :          0 :   return mInvolvedFeatures;
     287                 :            : }
     288                 :            : 
     289                 :          5 : void QgsGeometryMissingVertexCheckError::setInvolvedFeatures( const QMap<QString, QgsFeatureIds> &involvedFeatures )
     290                 :            : {
     291                 :          5 :   mInvolvedFeatures = involvedFeatures;
     292                 :          5 : }
     293                 :            : 
     294                 :          0 : QIcon QgsGeometryMissingVertexCheckError::icon() const
     295                 :            : {
     296                 :            : 
     297                 :          0 :   if ( status() == QgsGeometryCheckError::StatusFixed )
     298                 :          0 :     return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmCheckGeometry.svg" ) );
     299                 :            :   else
     300                 :          0 :     return QgsApplication::getThemeIcon( QStringLiteral( "/checks/MissingVertex.svg" ) );
     301                 :          0 : }

Generated by: LCOV version 1.14