LCOV - code coverage report
Current view: top level - analysis/vector/geometry_checker - qgsgeometryareacheck.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 102 119 85.7 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :     qgsgeometryareacheck.cpp
       3                 :            :     ---------------------
       4                 :            :     begin                : September 2015
       5                 :            :     copyright            : (C) 2014 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 "qgsgeometryengine.h"
      18                 :            : #include "qgsgeometrycollection.h"
      19                 :            : #include "qgsgeometryareacheck.h"
      20                 :            : #include "qgsfeaturepool.h"
      21                 :            : #include "qgsgeometrycheckerror.h"
      22                 :            : 
      23                 :          2 : void QgsGeometryAreaCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
      24                 :            : {
      25                 :          2 :   Q_UNUSED( messages )
      26                 :          2 :   QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
      27                 :          2 :   QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext );
      28                 :         52 :   for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
      29                 :            :   {
      30                 :         50 :     const QgsAbstractGeometry *geom = layerFeature.geometry().constGet();
      31                 :         50 :     double layerToMapUnits = scaleFactor( layerFeature.layer() );
      32                 :        102 :     for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
      33                 :            :     {
      34                 :            :       double value;
      35                 :         52 :       const QgsAbstractGeometry *part = QgsGeometryCheckerUtils::getGeomPart( geom, iPart );
      36                 :         52 :       if ( checkThreshold( layerToMapUnits, part, value ) )
      37                 :            :       {
      38                 :         10 :         errors.append( new QgsGeometryCheckError( this, layerFeature, part->centroid(), QgsVertexId( iPart ), value * layerToMapUnits * layerToMapUnits, QgsGeometryCheckError::ValueArea ) );
      39                 :         10 :       }
      40                 :         52 :     }
      41                 :            :   }
      42                 :          2 : }
      43                 :            : 
      44                 :          4 : void QgsGeometryAreaCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const
      45                 :            : {
      46                 :          4 :   QgsFeaturePool *featurePool = featurePools[ error->layerId() ];
      47                 :          4 :   QgsFeature feature;
      48                 :          4 :   if ( !featurePool->getFeature( error->featureId(), feature ) )
      49                 :            :   {
      50                 :          0 :     error->setObsolete();
      51                 :          0 :     return;
      52                 :            :   }
      53                 :            : 
      54                 :          4 :   const QgsGeometry g = feature.geometry();
      55                 :          4 :   const QgsAbstractGeometry *geom = g.constGet();
      56                 :          4 :   QgsVertexId vidx = error->vidx();
      57                 :            : 
      58                 :          4 :   double layerToMapUnits = scaleFactor( featurePool->layer() );
      59                 :            : 
      60                 :            :   // Check if polygon still exists
      61                 :          4 :   if ( !vidx.isValid( geom ) )
      62                 :            :   {
      63                 :          0 :     error->setObsolete();
      64                 :          0 :     return;
      65                 :            :   }
      66                 :            : 
      67                 :            :   // Check if error still applies
      68                 :            :   double value;
      69                 :          4 :   if ( !checkThreshold( layerToMapUnits, QgsGeometryCheckerUtils::getGeomPart( geom, vidx.part ), value ) )
      70                 :            :   {
      71                 :          0 :     error->setObsolete();
      72                 :          0 :     return;
      73                 :            :   }
      74                 :            : 
      75                 :            :   // Fix with selected method
      76                 :          4 :   if ( method == NoChange )
      77                 :            :   {
      78                 :          0 :     error->setFixed( method );
      79                 :          0 :   }
      80                 :          4 :   else if ( method == Delete )
      81                 :            :   {
      82                 :          1 :     deleteFeatureGeometryPart( featurePools, error->layerId(), feature, vidx.part, changes );
      83                 :          1 :     error->setFixed( method );
      84                 :          1 :   }
      85                 :          3 :   else if ( method == MergeLongestEdge || method == MergeLargestArea || method == MergeIdenticalAttribute )
      86                 :            :   {
      87                 :          3 :     QString errMsg;
      88                 :          3 :     if ( mergeWithNeighbor( featurePools, error->layerId(), feature,  vidx.part, method, mergeAttributeIndices[error->layerId()], changes, errMsg ) )
      89                 :            :     {
      90                 :          3 :       error->setFixed( method );
      91                 :          3 :     }
      92                 :            :     else
      93                 :            :     {
      94                 :          0 :       error->setFixFailed( tr( "Failed to merge with neighbor: %1" ).arg( errMsg ) );
      95                 :            :     }
      96                 :          3 :   }
      97                 :            :   else
      98                 :            :   {
      99                 :          0 :     error->setFixFailed( tr( "Unknown method" ) );
     100                 :            :   }
     101                 :          4 : }
     102                 :            : 
     103                 :         30 : bool QgsGeometryAreaCheck::checkThreshold( double layerToMapUnits, const QgsAbstractGeometry *geom, double &value ) const
     104                 :            : {
     105                 :         30 :   value = geom->area();
     106                 :         30 :   double threshold = mAreaThreshold / ( layerToMapUnits * layerToMapUnits );
     107                 :         30 :   return value < threshold;
     108                 :            : }
     109                 :            : 
     110                 :          3 : bool QgsGeometryAreaCheck::mergeWithNeighbor( const QMap<QString, QgsFeaturePool *> &featurePools,
     111                 :            :     const QString &layerId, QgsFeature &feature,
     112                 :            :     int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const
     113                 :            : {
     114                 :          3 :   QgsFeaturePool *featurePool = featurePools[ layerId ];
     115                 :            : 
     116                 :          3 :   double maxVal = 0.;
     117                 :          3 :   QgsFeature mergeFeature;
     118                 :          3 :   int mergePartIdx = -1;
     119                 :          3 :   bool matchFound = false;
     120                 :          3 :   QgsGeometry featureGeometry = feature.geometry();
     121                 :          3 :   const QgsAbstractGeometry *geom = featureGeometry.constGet();
     122                 :            : 
     123                 :            :   // Search for touching neighboring geometries
     124                 :          3 :   const QgsFeatureIds intersects = featurePool->getIntersects( featureGeometry.boundingBox() );
     125                 :         13 :   for ( QgsFeatureId testId : intersects )
     126                 :            :   {
     127                 :         11 :     QgsFeature testFeature;
     128                 :         11 :     if ( !featurePool->getFeature( testId, testFeature ) )
     129                 :            :     {
     130                 :          0 :       continue;
     131                 :            :     }
     132                 :         11 :     QgsGeometry testFeatureGeom = testFeature.geometry();
     133                 :         11 :     const QgsAbstractGeometry *testGeom = testFeatureGeom.constGet();
     134                 :         21 :     for ( int testPartIdx = 0, nTestParts = testGeom->partCount(); testPartIdx < nTestParts; ++testPartIdx )
     135                 :            :     {
     136                 :         11 :       if ( testId == feature.id() && testPartIdx == partIdx )
     137                 :            :       {
     138                 :          2 :         continue;
     139                 :            :       }
     140                 :          9 :       double len = QgsGeometryCheckerUtils::sharedEdgeLength( QgsGeometryCheckerUtils::getGeomPart( geom, partIdx ), QgsGeometryCheckerUtils::getGeomPart( testGeom, testPartIdx ), mContext->reducedTolerance );
     141                 :          9 :       if ( len > 0. )
     142                 :            :       {
     143                 :          9 :         if ( method == MergeLongestEdge || method == MergeLargestArea )
     144                 :            :         {
     145                 :            :           double val;
     146                 :          6 :           if ( method == MergeLongestEdge )
     147                 :            :           {
     148                 :          3 :             val = len;
     149                 :          3 :           }
     150                 :            :           else
     151                 :            :           {
     152                 :          3 :             if ( dynamic_cast<const QgsGeometryCollection *>( testGeom ) )
     153                 :          3 :               val = static_cast<const QgsGeometryCollection *>( testGeom )->geometryN( testPartIdx )->area();
     154                 :            :             else
     155                 :          0 :               val = testGeom->area();
     156                 :            :           }
     157                 :          6 :           if ( val > maxVal )
     158                 :            :           {
     159                 :          5 :             maxVal = val;
     160                 :          5 :             mergeFeature = testFeature;
     161                 :          5 :             mergePartIdx = testPartIdx;
     162                 :          5 :           }
     163                 :          6 :         }
     164                 :          3 :         else if ( method == MergeIdenticalAttribute )
     165                 :            :         {
     166                 :          3 :           if ( testFeature.attribute( mergeAttributeIndex ) == feature.attribute( mergeAttributeIndex ) )
     167                 :            :           {
     168                 :          1 :             mergeFeature = testFeature;
     169                 :          1 :             mergePartIdx = testPartIdx;
     170                 :          1 :             matchFound = true;
     171                 :          1 :             break;
     172                 :            :           }
     173                 :          2 :         }
     174                 :          8 :       }
     175                 :          8 :     }
     176                 :         11 :     if ( matchFound )
     177                 :            :     {
     178                 :          1 :       break;
     179                 :            :     }
     180                 :         11 :   }
     181                 :            : 
     182                 :          3 :   if ( !matchFound && maxVal == 0. )
     183                 :            :   {
     184                 :          0 :     return method == MergeIdenticalAttribute;
     185                 :            :   }
     186                 :            : 
     187                 :            :   // Merge geometries
     188                 :          3 :   QgsGeometry mergeFeatureGeom = mergeFeature.geometry();
     189                 :          3 :   const QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.constGet();
     190                 :          3 :   std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometryCheckerUtils::createGeomEngine( QgsGeometryCheckerUtils::getGeomPart( mergeGeom, mergePartIdx ), mContext->reducedTolerance ) );
     191                 :          3 :   QgsAbstractGeometry *combinedGeom = geomEngine->combine( QgsGeometryCheckerUtils::getGeomPart( geom, partIdx ), &errMsg );
     192                 :          3 :   if ( !combinedGeom || combinedGeom->isEmpty() || !QgsWkbTypes::isSingleType( combinedGeom->wkbType() ) )
     193                 :            :   {
     194                 :          0 :     return false;
     195                 :            :   }
     196                 :            : 
     197                 :            :   // Replace polygon in merge geometry
     198                 :          3 :   if ( mergeFeature.id() == feature.id() && mergePartIdx > partIdx )
     199                 :            :   {
     200                 :          0 :     --mergePartIdx;
     201                 :          0 :   }
     202                 :          3 :   replaceFeatureGeometryPart( featurePools, layerId, mergeFeature, mergePartIdx, combinedGeom, changes );
     203                 :            :   // Remove polygon from source geometry
     204                 :          3 :   deleteFeatureGeometryPart( featurePools, layerId, feature, partIdx, changes );
     205                 :            : 
     206                 :          3 :   return true;
     207                 :          3 : }
     208                 :            : 
     209                 :          4 : QStringList QgsGeometryAreaCheck::resolutionMethods() const
     210                 :            : {
     211                 :          5 :   static QStringList methods = QStringList()
     212                 :          1 :                                << tr( "Merge with neighboring polygon with longest shared edge" )
     213                 :          1 :                                << tr( "Merge with neighboring polygon with largest area" )
     214                 :          1 :                                << tr( "Merge with neighboring polygon with identical attribute value, if any, or leave as is" )
     215                 :          1 :                                << tr( "Delete feature" )
     216                 :          1 :                                << tr( "No action" );
     217                 :          4 :   return methods;
     218                 :          0 : }

Generated by: LCOV version 1.14