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

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :     qgsgeometrygapcheck.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 "qgsgeometrygapcheck.h"
      19                 :            : #include "qgsgeometrycollection.h"
      20                 :            : #include "qgsfeaturepool.h"
      21                 :            : #include "qgsvectorlayer.h"
      22                 :            : #include "qgsvectorlayerutils.h"
      23                 :            : #include "qgsfeedback.h"
      24                 :            : #include "qgsapplication.h"
      25                 :            : #include "qgsproject.h"
      26                 :            : #include "qgsexpressioncontextutils.h"
      27                 :            : #include "qgspolygon.h"
      28                 :            : #include "qgscurve.h"
      29                 :            : #include "qgssnappingutils.h"
      30                 :            : 
      31                 :            : #include "geos_c.h"
      32                 :            : 
      33                 :          6 : QgsGeometryGapCheck::QgsGeometryGapCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration )
      34                 :          3 :   : QgsGeometryCheck( context, configuration )
      35                 :          6 :   ,  mGapThresholdMapUnits( configuration.value( QStringLiteral( "gapThreshold" ) ).toDouble() )
      36                 :          6 : {
      37                 :          3 : }
      38                 :            : 
      39                 :          3 : void QgsGeometryGapCheck::prepare( const QgsGeometryCheckContext *context, const QVariantMap &configuration )
      40                 :            : {
      41                 :          6 :   if ( configuration.value( QStringLiteral( "allowedGapsEnabled" ) ).toBool() )
      42                 :            :   {
      43                 :          3 :     QgsVectorLayer *layer = context->project()->mapLayer<QgsVectorLayer *>( configuration.value( "allowedGapsLayer" ).toString() );
      44                 :          3 :     if ( layer )
      45                 :            :     {
      46                 :          3 :       mAllowedGapsLayer = layer;
      47                 :          3 :       mAllowedGapsSource = std::make_unique<QgsVectorLayerFeatureSource>( layer );
      48                 :            : 
      49                 :          6 :       mAllowedGapsBuffer = configuration.value( QStringLiteral( "allowedGapsBuffer" ) ).toDouble();
      50                 :          3 :     }
      51                 :          3 :   }
      52                 :            :   else
      53                 :            :   {
      54                 :          0 :     mAllowedGapsSource.reset();
      55                 :            :   }
      56                 :          3 : }
      57                 :            : 
      58                 :          5 : void QgsGeometryGapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const
      59                 :            : {
      60                 :          5 :   if ( feedback )
      61                 :          5 :     feedback->setProgress( feedback->progress() + 1.0 );
      62                 :            : 
      63                 :          5 :   std::unique_ptr<QgsAbstractGeometry> allowedGapsGeom;
      64                 :          5 :   std::unique_ptr<QgsGeometryEngine> allowedGapsGeomEngine;
      65                 :            : 
      66                 :          5 :   if ( mAllowedGapsSource )
      67                 :            :   {
      68                 :          3 :     QVector<QgsGeometry> allowedGaps;
      69                 :          3 :     QgsFeatureRequest request;
      70                 :          3 :     request.setSubsetOfAttributes( QgsAttributeList() );
      71                 :          3 :     QgsFeatureIterator iterator = mAllowedGapsSource->getFeatures( request );
      72                 :          3 :     QgsFeature feature;
      73                 :            : 
      74                 :          5 :     while ( iterator.nextFeature( feature ) )
      75                 :            :     {
      76                 :          2 :       QgsGeometry geom = feature.geometry();
      77                 :          2 :       QgsGeometry gg = geom.buffer( mAllowedGapsBuffer, 20 );
      78                 :          2 :       allowedGaps.append( gg );
      79                 :          2 :     }
      80                 :            : 
      81                 :          3 :     std::unique_ptr< QgsGeometryEngine > allowedGapsEngine = QgsGeometryCheckerUtils::createGeomEngine( nullptr, mContext->tolerance );
      82                 :            : 
      83                 :            :     // Create union of allowed gaps
      84                 :          3 :     QString errMsg;
      85                 :          3 :     allowedGapsGeom.reset( allowedGapsEngine->combine( allowedGaps, &errMsg ) );
      86                 :          3 :     allowedGapsGeomEngine = QgsGeometryCheckerUtils::createGeomEngine( allowedGapsGeom.get(), mContext->tolerance );
      87                 :          3 :     allowedGapsGeomEngine->prepareGeometry();
      88                 :          3 :   }
      89                 :            : 
      90                 :          5 :   QVector<QgsGeometry> geomList;
      91                 :          5 :   QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap();
      92                 :          5 :   const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), nullptr, mContext, true );
      93                 :         33 :   for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
      94                 :            :   {
      95                 :         28 :     geomList.append( layerFeature.geometry() );
      96                 :            : 
      97                 :         28 :     if ( feedback && feedback->isCanceled() )
      98                 :            :     {
      99                 :          0 :       geomList.clear();
     100                 :          0 :       break;
     101                 :            :     }
     102                 :            :   }
     103                 :            : 
     104                 :          5 :   if ( geomList.isEmpty() )
     105                 :            :   {
     106                 :          0 :     return;
     107                 :            :   }
     108                 :            : 
     109                 :          5 :   std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( nullptr, mContext->tolerance );
     110                 :            : 
     111                 :            :   // Create union of geometry
     112                 :          5 :   QString errMsg;
     113                 :          5 :   std::unique_ptr<QgsAbstractGeometry> unionGeom( geomEngine->combine( geomList, &errMsg ) );
     114                 :          5 :   if ( !unionGeom )
     115                 :            :   {
     116                 :          0 :     messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
     117                 :          0 :     return;
     118                 :            :   }
     119                 :            : 
     120                 :            :   // Get envelope of union
     121                 :          5 :   geomEngine = QgsGeometryCheckerUtils::createGeomEngine( unionGeom.get(), mContext->tolerance );
     122                 :          5 :   geomEngine->prepareGeometry();
     123                 :          5 :   std::unique_ptr<QgsAbstractGeometry> envelope( geomEngine->envelope( &errMsg ) );
     124                 :          5 :   if ( !envelope )
     125                 :            :   {
     126                 :          0 :     messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
     127                 :          0 :     return;
     128                 :            :   }
     129                 :            : 
     130                 :            :   // Buffer envelope
     131                 :          5 :   geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope.get(), mContext->tolerance );
     132                 :          5 :   geomEngine->prepareGeometry();
     133                 :          5 :   QgsAbstractGeometry *bufEnvelope = geomEngine->buffer( 2, 0, GEOSBUF_CAP_SQUARE, GEOSBUF_JOIN_MITRE, 4. );  //#spellok  //#spellok
     134                 :          5 :   envelope.reset( bufEnvelope );
     135                 :            : 
     136                 :            :   // Compute difference between envelope and union to obtain gap polygons
     137                 :          5 :   geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope.get(), mContext->tolerance );
     138                 :          5 :   geomEngine->prepareGeometry();
     139                 :          5 :   std::unique_ptr<QgsAbstractGeometry> diffGeom( geomEngine->difference( unionGeom.get(), &errMsg ) );
     140                 :          5 :   if ( !diffGeom )
     141                 :            :   {
     142                 :          0 :     messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
     143                 :          0 :     return;
     144                 :            :   }
     145                 :            : 
     146                 :            :   // For each gap polygon which does not lie on the boundary, get neighboring polygons and add error
     147                 :          5 :   QgsGeometryPartIterator parts = diffGeom->parts();
     148                 :         35 :   while ( parts.hasNext() )
     149                 :            :   {
     150                 :         33 :     const QgsAbstractGeometry *gapGeom = parts.next();
     151                 :            :     // Skip the gap between features and boundingbox
     152                 :         30 :     const double spacing = context()->tolerance;
     153                 :         30 :     if ( gapGeom->boundingBox().snappedToGrid( spacing ) == envelope->boundingBox().snappedToGrid( spacing ) )
     154                 :            :     {
     155                 :          5 :       continue;
     156                 :            :     }
     157                 :            : 
     158                 :            :     // Skip gaps above threshold
     159                 :         25 :     if ( ( mGapThresholdMapUnits > 0 && gapGeom->area() > mGapThresholdMapUnits ) || gapGeom->area() < mContext->reducedTolerance )
     160                 :            :     {
     161                 :          4 :       continue;
     162                 :            :     }
     163                 :            : 
     164                 :         21 :     QgsRectangle gapAreaBBox = gapGeom->boundingBox();
     165                 :            : 
     166                 :            :     // Get neighboring polygons
     167                 :         21 :     QMap<QString, QgsFeatureIds> neighboringIds;
     168                 :         21 :     const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds.keys(), gapAreaBBox, compatibleGeometryTypes(), mContext );
     169                 :         21 :     std::unique_ptr< QgsGeometryEngine > gapGeomEngine = QgsGeometryCheckerUtils::createGeomEngine( gapGeom, mContext->tolerance );
     170                 :         21 :     gapGeomEngine->prepareGeometry();
     171                 :         92 :     for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures )
     172                 :            :     {
     173                 :         71 :       const QgsGeometry geom = layerFeature.geometry();
     174                 :         71 :       if ( gapGeomEngine->distance( geom.constGet() ) < mContext->tolerance )
     175                 :            :       {
     176                 :         63 :         neighboringIds[layerFeature.layer()->id()].insert( layerFeature.feature().id() );
     177                 :         63 :         gapAreaBBox.combineExtentWith( geom.boundingBox() );
     178                 :         63 :       }
     179                 :         71 :     }
     180                 :            : 
     181                 :         21 :     if ( neighboringIds.isEmpty() )
     182                 :            :     {
     183                 :          0 :       continue;
     184                 :            :     }
     185                 :            : 
     186                 :         21 :     if ( allowedGapsGeomEngine && allowedGapsGeomEngine->contains( gapGeom ) )
     187                 :            :     {
     188                 :          2 :       continue;
     189                 :            :     }
     190                 :            : 
     191                 :            :     // Add error
     192                 :         19 :     double area = gapGeom->area();
     193                 :         19 :     QgsRectangle gapBbox = gapGeom->boundingBox();
     194                 :         19 :     errors.append( new QgsGeometryGapCheckError( this, QString(), QgsGeometry( gapGeom->clone() ), neighboringIds, area, gapBbox, gapAreaBBox ) );
     195                 :         21 :   }
     196                 :          5 : }
     197                 :            : 
     198                 :          3 : void QgsGeometryGapCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
     199                 :            : {
     200                 :          3 :   QMetaEnum metaEnum = QMetaEnum::fromType<QgsGeometryGapCheck::ResolutionMethod>();
     201                 :          3 :   if ( !metaEnum.isValid() || !metaEnum.valueToKey( method ) )
     202                 :            :   {
     203                 :          0 :     error->setFixFailed( tr( "Unknown method" ) );
     204                 :          0 :   }
     205                 :            :   else
     206                 :            :   {
     207                 :          3 :     ResolutionMethod methodValue = static_cast<ResolutionMethod>( method );
     208                 :          3 :     switch ( methodValue )
     209                 :            :     {
     210                 :            :       case NoChange:
     211                 :          0 :         error->setFixed( method );
     212                 :          0 :         break;
     213                 :            : 
     214                 :            :       case MergeLongestEdge:
     215                 :            :       {
     216                 :          2 :         QString errMsg;
     217                 :          2 :         if ( mergeWithNeighbor( featurePools, static_cast<QgsGeometryGapCheckError *>( error ), changes, errMsg, LongestSharedEdge ) )
     218                 :            :         {
     219                 :          2 :           error->setFixed( method );
     220                 :          2 :         }
     221                 :            :         else
     222                 :            :         {
     223                 :          0 :           error->setFixFailed( tr( "Failed to merge with neighbor: %1" ).arg( errMsg ) );
     224                 :            :         }
     225                 :            :         break;
     226                 :          2 :       }
     227                 :            : 
     228                 :            :       case AddToAllowedGaps:
     229                 :            :       {
     230                 :          1 :         QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( mAllowedGapsLayer.data() );
     231                 :          1 :         if ( layer )
     232                 :            :         {
     233                 :          1 :           if ( !layer->isEditable() && !layer->startEditing() )
     234                 :            :           {
     235                 :          0 :             error->setFixFailed( tr( "Could not start editing layer %1" ).arg( layer->name() ) );
     236                 :          0 :           }
     237                 :            :           else
     238                 :            :           {
     239                 :          1 :             QgsFeature feature = QgsVectorLayerUtils::createFeature( layer, error->geometry() );
     240                 :          1 :             QgsFeatureList features = QgsVectorLayerUtils::makeFeatureCompatible( feature, layer );
     241                 :          1 :             if ( !layer->addFeatures( features ) )
     242                 :            :             {
     243                 :          0 :               error->setFixFailed( tr( "Could not add feature to layer %1" ).arg( layer->name() ) );
     244                 :          0 :             }
     245                 :            :             else
     246                 :            :             {
     247                 :          1 :               error->setFixed( method );
     248                 :            :             }
     249                 :          1 :           }
     250                 :          1 :         }
     251                 :            :         else
     252                 :            :         {
     253                 :          0 :           error->setFixFailed( tr( "Allowed gaps layer could not be resolved" ) );
     254                 :            :         }
     255                 :          1 :         break;
     256                 :            :       }
     257                 :            : 
     258                 :            :       case CreateNewFeature:
     259                 :            :       {
     260                 :          0 :         QgsGeometryGapCheckError *gapCheckError = static_cast<QgsGeometryGapCheckError *>( error );
     261                 :          0 :         QgsProject *project = QgsProject::instance();
     262                 :          0 :         QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( project->mapLayer( gapCheckError->neighbors().keys().first() ) );
     263                 :          0 :         if ( layer )
     264                 :            :         {
     265                 :          0 :           const QgsGeometry geometry = error->geometry();
     266                 :          0 :           QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
     267                 :          0 :           QgsFeature feature = QgsVectorLayerUtils::createFeature( layer, geometry, QgsAttributeMap(), &context );
     268                 :          0 :           if ( !layer->addFeature( feature ) )
     269                 :            :           {
     270                 :          0 :             error->setFixFailed( tr( "Could not add feature" ) );
     271                 :          0 :           }
     272                 :            :           else
     273                 :            :           {
     274                 :          0 :             error->setFixed( method );
     275                 :            :           }
     276                 :          0 :         }
     277                 :            :         else
     278                 :            :         {
     279                 :          0 :           error->setFixFailed( tr( "Could not resolve target layer %1 to add feature" ).arg( error->layerId() ) );
     280                 :            :         }
     281                 :          0 :         break;
     282                 :            :       }
     283                 :            : 
     284                 :            :       case MergeLargestArea:
     285                 :            :       {
     286                 :          0 :         QString errMsg;
     287                 :          0 :         if ( mergeWithNeighbor( featurePools, static_cast<QgsGeometryGapCheckError *>( error ), changes, errMsg, LargestArea ) )
     288                 :            :         {
     289                 :          0 :           error->setFixed( method );
     290                 :          0 :         }
     291                 :            :         else
     292                 :            :         {
     293                 :          0 :           error->setFixFailed( tr( "Failed to merge with neighbor: %1" ).arg( errMsg ) );
     294                 :            :         }
     295                 :            :         break;
     296                 :          0 :       }
     297                 :            :     }
     298                 :            :   }
     299                 :          3 : }
     300                 :            : 
     301                 :          2 : bool QgsGeometryGapCheck::mergeWithNeighbor( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryGapCheckError *err, Changes &changes, QString &errMsg, Condition condition ) const
     302                 :            : {
     303                 :          2 :   double maxVal = 0.;
     304                 :          2 :   QString mergeLayerId;
     305                 :          2 :   QgsFeature mergeFeature;
     306                 :          2 :   int mergePartIdx = -1;
     307                 :            : 
     308                 :          2 :   const QgsGeometry geometry = err->geometry();
     309                 :          2 :   const QgsAbstractGeometry *errGeometry = QgsGeometryCheckerUtils::getGeomPart( geometry.constGet(), 0 );
     310                 :            : 
     311                 :          2 :   const auto layerIds = err->neighbors().keys();
     312                 :          2 :   QList<QgsFeature> neighbours;
     313                 :            : 
     314                 :            :   // Search for touching neighboring geometries
     315                 :          4 :   for ( const QString &layerId : layerIds )
     316                 :            :   {
     317                 :          2 :     QgsFeaturePool *featurePool = featurePools.value( layerId );
     318                 :          2 :     std::unique_ptr<QgsAbstractGeometry> errLayerGeom( errGeometry->clone() );
     319                 :          2 :     QgsCoordinateTransform ct( featurePool->crs(), mContext->mapCrs, mContext->transformContext );
     320                 :          2 :     errLayerGeom->transform( ct, QgsCoordinateTransform::ReverseTransform );
     321                 :            : 
     322                 :          2 :     const auto featureIds = err->neighbors().value( layerId );
     323                 :            : 
     324                 :          8 :     for ( QgsFeatureId testId : featureIds )
     325                 :            :     {
     326                 :          6 :       QgsFeature feature;
     327                 :          6 :       if ( !featurePool->getFeature( testId, feature ) )
     328                 :            :       {
     329                 :          0 :         continue;
     330                 :            :       }
     331                 :            : 
     332                 :          6 :       QgsGeometry transformedGeometry = feature.geometry();
     333                 :          6 :       transformedGeometry.transform( ct );
     334                 :          6 :       feature.setGeometry( transformedGeometry );
     335                 :          6 :       neighbours.append( feature );
     336                 :          6 :     }
     337                 :            : 
     338                 :          8 :     for ( const QgsFeature &testFeature : neighbours )
     339                 :            :     {
     340                 :          6 :       const QgsGeometry featureGeom = testFeature.geometry();
     341                 :          6 :       const QgsAbstractGeometry *testGeom = featureGeom.constGet();
     342                 :         12 :       for ( int iPart = 0, nParts = testGeom->partCount(); iPart < nParts; ++iPart )
     343                 :            :       {
     344                 :          6 :         double val = 0;
     345                 :          6 :         switch ( condition )
     346                 :            :         {
     347                 :            :           case LongestSharedEdge:
     348                 :          6 :             val = QgsGeometryCheckerUtils::sharedEdgeLength( errLayerGeom.get(), QgsGeometryCheckerUtils::getGeomPart( testGeom, iPart ), mContext->reducedTolerance );
     349                 :          6 :             break;
     350                 :            : 
     351                 :            :           case LargestArea:
     352                 :            :             // We might get a neighbour where we touch only a corner
     353                 :          0 :             if ( QgsGeometryCheckerUtils::sharedEdgeLength( errLayerGeom.get(), QgsGeometryCheckerUtils::getGeomPart( testGeom, iPart ), mContext->reducedTolerance ) > 0 )
     354                 :          0 :               val = QgsGeometryCheckerUtils::getGeomPart( testGeom, iPart )->area();
     355                 :          0 :             break;
     356                 :            :         }
     357                 :            : 
     358                 :          6 :         if ( val > maxVal )
     359                 :            :         {
     360                 :          3 :           maxVal = val;
     361                 :          3 :           mergeFeature = testFeature;
     362                 :          3 :           mergePartIdx = iPart;
     363                 :          3 :           mergeLayerId = layerId;
     364                 :          3 :         }
     365                 :          6 :       }
     366                 :          6 :     }
     367                 :          2 :   }
     368                 :            : 
     369                 :          2 :   if ( maxVal == 0. )
     370                 :            :   {
     371                 :          0 :     return false;
     372                 :            :   }
     373                 :            : 
     374                 :            :   // Create an index of all neighbouring vertices
     375                 :          2 :   QgsSpatialIndex neighbourVerticesIndex( QgsSpatialIndex::Flag::FlagStoreFeatureGeometries );
     376                 :          2 :   int id = 0;
     377                 :          8 :   for ( const QgsFeature &neighbour : neighbours )
     378                 :            :   {
     379                 :          6 :     QgsVertexIterator vit = neighbour.geometry().vertices();
     380                 :         43 :     while ( vit.hasNext() )
     381                 :            :     {
     382                 :         37 :       QgsPoint pt = vit.next();
     383                 :         37 :       QgsFeature f;
     384                 :         37 :       f.setId( id ); // required for SpatialIndex to return the correct result
     385                 :         37 :       f.setGeometry( QgsGeometry( pt.clone() ) );
     386                 :         37 :       neighbourVerticesIndex.addFeature( f );
     387                 :         37 :       id++;
     388                 :         37 :     }
     389                 :            :   }
     390                 :            : 
     391                 :            :   // Snap to the closest vertex
     392                 :          2 :   QgsPolyline snappedRing;
     393                 :          2 :   QgsVertexIterator iterator = errGeometry->vertices();
     394                 :         10 :   while ( iterator.hasNext() )
     395                 :            :   {
     396                 :          8 :     QgsPoint pt = iterator.next();
     397                 :          8 :     QgsGeometry closestGeom = neighbourVerticesIndex.geometry( neighbourVerticesIndex.nearestNeighbor( QgsPointXY( pt ) ).first() );
     398                 :          8 :     if ( !closestGeom.isEmpty() )
     399                 :            :     {
     400                 :          8 :       snappedRing.append( QgsPoint( closestGeom.vertexAt( 0 ) ) );
     401                 :          8 :     }
     402                 :          8 :   }
     403                 :            : 
     404                 :          2 :   std::unique_ptr<QgsPolygon> snappedErrGeom = std::make_unique<QgsPolygon>();
     405                 :          2 :   snappedErrGeom->setExteriorRing( new QgsLineString( snappedRing ) );
     406                 :            : 
     407                 :            :   // Merge geometries
     408                 :          2 :   QgsFeaturePool *featurePool = featurePools[ mergeLayerId ];
     409                 :          2 :   std::unique_ptr<QgsAbstractGeometry> errLayerGeom( snappedErrGeom->clone() );
     410                 :          2 :   QgsCoordinateTransform ct( featurePool->crs(), mContext->mapCrs, mContext->transformContext );
     411                 :          2 :   errLayerGeom->transform( ct, QgsCoordinateTransform::ReverseTransform );
     412                 :          2 :   const QgsGeometry mergeFeatureGeom = mergeFeature.geometry();
     413                 :          2 :   const QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.constGet();
     414                 :          2 :   std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( errLayerGeom.get(), 0 );
     415                 :          2 :   std::unique_ptr<QgsAbstractGeometry> combinedGeom( geomEngine->combine( QgsGeometryCheckerUtils::getGeomPart( mergeGeom, mergePartIdx ), &errMsg ) );
     416                 :          2 :   if ( !combinedGeom || combinedGeom->isEmpty() || !QgsWkbTypes::isSingleType( combinedGeom->wkbType() ) )
     417                 :            :   {
     418                 :          0 :     return false;
     419                 :            :   }
     420                 :            : 
     421                 :            :   // Add merged polygon to destination geometry
     422                 :          2 :   replaceFeatureGeometryPart( featurePools, mergeLayerId, mergeFeature, mergePartIdx, combinedGeom.release(), changes );
     423                 :            : 
     424                 :          2 :   return true;
     425                 :          2 : }
     426                 :            : 
     427                 :            : 
     428                 :          0 : QStringList QgsGeometryGapCheck::resolutionMethods() const
     429                 :            : {
     430                 :          0 :   QStringList methods = QStringList()
     431                 :          0 :                         << tr( "Add gap area to neighboring polygon with longest shared edge" )
     432                 :          0 :                         << tr( "No action" );
     433                 :          0 :   if ( mAllowedGapsSource )
     434                 :          0 :     methods << tr( "Add gap to allowed exceptions" );
     435                 :            : 
     436                 :          0 :   return methods;
     437                 :          0 : }
     438                 :            : 
     439                 :          3 : QList<QgsGeometryCheckResolutionMethod> QgsGeometryGapCheck::availableResolutionMethods() const
     440                 :            : {
     441                 :         12 :   QList<QgsGeometryCheckResolutionMethod> fixes
     442                 :         12 :   {
     443                 :          3 :     QgsGeometryCheckResolutionMethod( MergeLongestEdge, tr( "Add to longest shared edge" ), tr( "Add the gap area to the neighbouring polygon with the longest shared edge." ), false ),
     444                 :          3 :     QgsGeometryCheckResolutionMethod( CreateNewFeature, tr( "Create new feature" ), tr( "Create a new feature from the gap area." ), false ),
     445                 :          3 :     QgsGeometryCheckResolutionMethod( MergeLargestArea, tr( "Add to largest neighbouring area" ), tr( "Add the gap area to the neighbouring polygon with the largest area." ), false )
     446                 :            :   };
     447                 :            : 
     448                 :          3 :   if ( mAllowedGapsSource )
     449                 :          1 :     fixes << QgsGeometryCheckResolutionMethod( AddToAllowedGaps, tr( "Add Gap to Allowed Exceptions" ), tr( "Create a new feature from the gap geometry on the allowed exceptions layer." ), true );
     450                 :            : 
     451                 :          3 :   fixes << QgsGeometryCheckResolutionMethod( NoChange, tr( "No action" ), tr( "Do not perform any action and mark this error as fixed." ), false );
     452                 :            : 
     453                 :          3 :   return fixes;
     454                 :          3 : }
     455                 :            : 
     456                 :          0 : QString QgsGeometryGapCheck::description() const
     457                 :            : {
     458                 :          0 :   return factoryDescription();
     459                 :            : }
     460                 :            : 
     461                 :          0 : QString QgsGeometryGapCheck::id() const
     462                 :            : {
     463                 :          0 :   return factoryId();
     464                 :            : }
     465                 :            : 
     466                 :          0 : QgsGeometryCheck::Flags QgsGeometryGapCheck::flags() const
     467                 :            : {
     468                 :          0 :   return factoryFlags();
     469                 :            : }
     470                 :            : 
     471                 :            : ///@cond private
     472                 :          0 : QString QgsGeometryGapCheck::factoryDescription()
     473                 :            : {
     474                 :          0 :   return tr( "Gap" );
     475                 :            : }
     476                 :            : 
     477                 :          0 : QString QgsGeometryGapCheck::factoryId()
     478                 :            : {
     479                 :          0 :   return QStringLiteral( "QgsGeometryGapCheck" );
     480                 :            : }
     481                 :            : 
     482                 :          0 : QgsGeometryCheck::Flags QgsGeometryGapCheck::factoryFlags()
     483                 :            : {
     484                 :          0 :   return QgsGeometryCheck::AvailableInValidation;
     485                 :            : }
     486                 :            : 
     487                 :         26 : QList<QgsWkbTypes::GeometryType> QgsGeometryGapCheck::factoryCompatibleGeometryTypes()
     488                 :            : {
     489                 :         26 :   return {QgsWkbTypes::PolygonGeometry};
     490                 :            : }
     491                 :            : 
     492                 :          0 : bool QgsGeometryGapCheck::factoryIsCompatible( QgsVectorLayer *layer ) SIP_SKIP
     493                 :            : {
     494                 :          0 :   return factoryCompatibleGeometryTypes().contains( layer->geometryType() );
     495                 :          0 : }
     496                 :            : 
     497                 :          0 : QgsGeometryCheck::CheckType QgsGeometryGapCheck::factoryCheckType()
     498                 :            : {
     499                 :          0 :   return QgsGeometryCheck::LayerCheck;
     500                 :            : }
     501                 :            : ///@endcond private
     502                 :            : 
     503                 :          2 : QgsRectangle QgsGeometryGapCheckError::contextBoundingBox() const
     504                 :            : {
     505                 :          2 :   return mContextBoundingBox;
     506                 :            : }
     507                 :            : 
     508                 :          0 : bool QgsGeometryGapCheckError::isEqual( QgsGeometryCheckError *other ) const
     509                 :            : {
     510                 :          0 :   QgsGeometryGapCheckError *err = dynamic_cast<QgsGeometryGapCheckError *>( other );
     511                 :          0 :   return err && QgsGeometryCheckerUtils::pointsFuzzyEqual( err->location(), location(), mCheck->context()->reducedTolerance ) && err->neighbors() == neighbors();
     512                 :            : }
     513                 :            : 
     514                 :          0 : bool QgsGeometryGapCheckError::closeMatch( QgsGeometryCheckError *other ) const
     515                 :            : {
     516                 :          0 :   QgsGeometryGapCheckError *err = dynamic_cast<QgsGeometryGapCheckError *>( other );
     517                 :          0 :   return err && err->layerId() == layerId() && err->neighbors() == neighbors();
     518                 :            : }
     519                 :            : 
     520                 :          0 : void QgsGeometryGapCheckError::update( const QgsGeometryCheckError *other )
     521                 :            : {
     522                 :          0 :   QgsGeometryCheckError::update( other );
     523                 :            :   // Static cast since this should only get called if isEqual == true
     524                 :          0 :   const QgsGeometryGapCheckError *err = static_cast<const QgsGeometryGapCheckError *>( other );
     525                 :          0 :   mNeighbors = err->mNeighbors;
     526                 :          0 :   mGapAreaBBox = err->mGapAreaBBox;
     527                 :          0 : }
     528                 :            : 
     529                 :          0 : bool QgsGeometryGapCheckError::handleChanges( const QgsGeometryCheck::Changes & )
     530                 :            : {
     531                 :          0 :   return true;
     532                 :            : }
     533                 :            : 
     534                 :          2 : QgsRectangle QgsGeometryGapCheckError::affectedAreaBBox() const
     535                 :            : {
     536                 :          2 :   return mGapAreaBBox;
     537                 :            : }
     538                 :            : 
     539                 :          0 : QMap<QString, QgsFeatureIds> QgsGeometryGapCheckError::involvedFeatures() const
     540                 :            : {
     541                 :          0 :   return mNeighbors;
     542                 :            : }
     543                 :            : 
     544                 :          0 : QIcon QgsGeometryGapCheckError::icon() const
     545                 :            : {
     546                 :            : 
     547                 :          0 :   if ( status() == QgsGeometryCheckError::StatusFixed )
     548                 :          0 :     return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmCheckGeometry.svg" ) );
     549                 :            :   else
     550                 :          0 :     return QgsApplication::getThemeIcon( QStringLiteral( "/checks/SliverOrGap.svg" ) );
     551                 :          0 : }

Generated by: LCOV version 1.14