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

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :     qgsvectorlayereditutils.cpp
       3                 :            :     ---------------------
       4                 :            :     begin                : Dezember 2012
       5                 :            :     copyright            : (C) 2012 by Martin Dobias
       6                 :            :     email                : wonder dot sk at gmail dot com
       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                 :            : #include "qgsvectorlayereditutils.h"
      16                 :            : 
      17                 :            : #include "qgsvectordataprovider.h"
      18                 :            : #include "qgsfeatureiterator.h"
      19                 :            : #include "qgsvectorlayereditbuffer.h"
      20                 :            : #include "qgslinestring.h"
      21                 :            : #include "qgslogger.h"
      22                 :            : #include "qgspoint.h"
      23                 :            : #include "qgsgeometryfactory.h"
      24                 :            : #include "qgis.h"
      25                 :            : #include "qgswkbtypes.h"
      26                 :            : #include "qgsvectorlayerutils.h"
      27                 :            : #include "qgsvectorlayer.h"
      28                 :            : #include "qgsgeometryoptions.h"
      29                 :            : #include "qgsabstractgeometry.h"
      30                 :            : 
      31                 :            : #include <limits>
      32                 :            : 
      33                 :            : 
      34                 :          0 : QgsVectorLayerEditUtils::QgsVectorLayerEditUtils( QgsVectorLayer *layer )
      35                 :          0 :   : mLayer( layer )
      36                 :            : {
      37                 :          0 : }
      38                 :            : 
      39                 :          0 : bool QgsVectorLayerEditUtils::insertVertex( double x, double y, QgsFeatureId atFeatureId, int beforeVertex )
      40                 :            : {
      41                 :          0 :   if ( !mLayer->isSpatial() )
      42                 :          0 :     return false;
      43                 :            : 
      44                 :          0 :   QgsFeature f;
      45                 :          0 :   if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setNoAttributes() ).nextFeature( f ) || !f.hasGeometry() )
      46                 :          0 :     return false; // geometry not found
      47                 :            : 
      48                 :          0 :   QgsGeometry geometry = f.geometry();
      49                 :            : 
      50                 :          0 :   geometry.insertVertex( x, y, beforeVertex );
      51                 :            : 
      52                 :          0 :   mLayer->changeGeometry( atFeatureId, geometry );
      53                 :          0 :   return true;
      54                 :          0 : }
      55                 :            : 
      56                 :          0 : bool QgsVectorLayerEditUtils::insertVertex( const QgsPoint &point, QgsFeatureId atFeatureId, int beforeVertex )
      57                 :            : {
      58                 :          0 :   if ( !mLayer->isSpatial() )
      59                 :          0 :     return false;
      60                 :            : 
      61                 :          0 :   QgsFeature f;
      62                 :          0 :   if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setNoAttributes() ).nextFeature( f ) || !f.hasGeometry() )
      63                 :          0 :     return false; // geometry not found
      64                 :            : 
      65                 :          0 :   QgsGeometry geometry = f.geometry();
      66                 :            : 
      67                 :          0 :   geometry.insertVertex( point, beforeVertex );
      68                 :            : 
      69                 :          0 :   mLayer->changeGeometry( atFeatureId, geometry );
      70                 :          0 :   return true;
      71                 :          0 : }
      72                 :            : 
      73                 :          0 : bool QgsVectorLayerEditUtils::moveVertex( double x, double y, QgsFeatureId atFeatureId, int atVertex )
      74                 :            : {
      75                 :          0 :   QgsPoint p( x, y );
      76                 :          0 :   return moveVertex( p, atFeatureId, atVertex );
      77                 :          0 : }
      78                 :            : 
      79                 :          0 : bool QgsVectorLayerEditUtils::moveVertex( const QgsPoint &p, QgsFeatureId atFeatureId, int atVertex )
      80                 :            : {
      81                 :          0 :   if ( !mLayer->isSpatial() )
      82                 :          0 :     return false;
      83                 :            : 
      84                 :          0 :   QgsFeature f;
      85                 :          0 :   if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setNoAttributes() ).nextFeature( f ) || !f.hasGeometry() )
      86                 :          0 :     return false; // geometry not found
      87                 :            : 
      88                 :          0 :   QgsGeometry geometry = f.geometry();
      89                 :            : 
      90                 :          0 :   geometry.moveVertex( p, atVertex );
      91                 :            : 
      92                 :          0 :   mLayer->changeGeometry( atFeatureId, geometry );
      93                 :          0 :   return true;
      94                 :          0 : }
      95                 :            : 
      96                 :            : 
      97                 :          0 : QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId featureId, int vertex )
      98                 :            : {
      99                 :          0 :   if ( !mLayer->isSpatial() )
     100                 :          0 :     return QgsVectorLayer::InvalidLayer;
     101                 :            : 
     102                 :          0 :   QgsFeature f;
     103                 :          0 :   if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setNoAttributes() ).nextFeature( f ) || !f.hasGeometry() )
     104                 :          0 :     return QgsVectorLayer::FetchFeatureFailed; // geometry not found
     105                 :            : 
     106                 :          0 :   QgsGeometry geometry = f.geometry();
     107                 :            : 
     108                 :          0 :   if ( !geometry.deleteVertex( vertex ) )
     109                 :          0 :     return QgsVectorLayer::EditFailed;
     110                 :            : 
     111                 :          0 :   if ( geometry.constGet() && geometry.constGet()->nCoordinates() == 0 )
     112                 :            :   {
     113                 :            :     //last vertex deleted, set geometry to null
     114                 :          0 :     geometry.set( nullptr );
     115                 :          0 :   }
     116                 :            : 
     117                 :          0 :   mLayer->changeGeometry( featureId, geometry );
     118                 :          0 :   return !geometry.isNull() ? QgsVectorLayer::Success : QgsVectorLayer::EmptyGeometry;
     119                 :          0 : }
     120                 :            : 
     121                 :          0 : QgsGeometry::OperationResult QgsVectorLayerEditUtils::addRing( const QVector<QgsPointXY> &ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId )
     122                 :            : {
     123                 :          0 :   QgsPointSequence l;
     124                 :          0 :   for ( QVector<QgsPointXY>::const_iterator it = ring.constBegin(); it != ring.constEnd(); ++it )
     125                 :            :   {
     126                 :          0 :     l <<  QgsPoint( *it );
     127                 :          0 :   }
     128                 :          0 :   return addRing( l, targetFeatureIds,  modifiedFeatureId );
     129                 :          0 : }
     130                 :            : 
     131                 :          0 : QgsGeometry::OperationResult QgsVectorLayerEditUtils::addRing( const QgsPointSequence &ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId )
     132                 :            : {
     133                 :          0 :   QgsLineString *ringLine = new QgsLineString( ring );
     134                 :          0 :   return addRing( ringLine, targetFeatureIds,  modifiedFeatureId );
     135                 :          0 : }
     136                 :            : 
     137                 :          0 : QgsGeometry::OperationResult QgsVectorLayerEditUtils::addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId )
     138                 :            : {
     139                 :          0 :   if ( !mLayer->isSpatial() )
     140                 :            :   {
     141                 :          0 :     delete ring;
     142                 :          0 :     return QgsGeometry::AddRingNotInExistingFeature;
     143                 :            :   }
     144                 :            : 
     145                 :          0 :   QgsGeometry::OperationResult addRingReturnCode = QgsGeometry::AddRingNotInExistingFeature; //default: return code for 'ring not inserted'
     146                 :          0 :   QgsFeature f;
     147                 :            : 
     148                 :          0 :   QgsFeatureIterator fit;
     149                 :          0 :   if ( !targetFeatureIds.isEmpty() )
     150                 :            :   {
     151                 :            :     //check only specified features
     152                 :          0 :     fit = mLayer->getFeatures( QgsFeatureRequest().setFilterFids( targetFeatureIds ) );
     153                 :          0 :   }
     154                 :            :   else
     155                 :            :   {
     156                 :            :     //check all intersecting features
     157                 :          0 :     QgsRectangle bBox = ring->boundingBox();
     158                 :          0 :     fit = mLayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
     159                 :            :   }
     160                 :            : 
     161                 :            :   //find first valid feature we can add the ring to
     162                 :          0 :   while ( fit.nextFeature( f ) )
     163                 :            :   {
     164                 :          0 :     if ( !f.hasGeometry() )
     165                 :          0 :       continue;
     166                 :            : 
     167                 :            :     //add ring takes ownership of ring, and deletes it if there's an error
     168                 :          0 :     QgsGeometry g = f.geometry();
     169                 :            : 
     170                 :          0 :     addRingReturnCode = g.addRing( static_cast< QgsCurve * >( ring->clone() ) );
     171                 :          0 :     if ( addRingReturnCode == 0 )
     172                 :          0 :       if ( addRingReturnCode == QgsGeometry::Success )
     173                 :            :       {
     174                 :          0 :         mLayer->changeGeometry( f.id(), g );
     175                 :          0 :         if ( modifiedFeatureId )
     176                 :          0 :           *modifiedFeatureId = f.id();
     177                 :            : 
     178                 :            :         //setModified( true, true );
     179                 :          0 :         break;
     180                 :            :       }
     181                 :          0 :   }
     182                 :            : 
     183                 :          0 :   delete ring;
     184                 :          0 :   return addRingReturnCode;
     185                 :          0 : }
     186                 :            : 
     187                 :          0 : QgsGeometry::OperationResult QgsVectorLayerEditUtils::addPart( const QVector<QgsPointXY> &points, QgsFeatureId featureId )
     188                 :            : {
     189                 :          0 :   QgsPointSequence l;
     190                 :          0 :   for ( QVector<QgsPointXY>::const_iterator it = points.constBegin(); it != points.constEnd(); ++it )
     191                 :            :   {
     192                 :          0 :     l <<  QgsPoint( *it );
     193                 :          0 :   }
     194                 :          0 :   return addPart( l, featureId );
     195                 :          0 : }
     196                 :            : 
     197                 :          0 : QgsGeometry::OperationResult QgsVectorLayerEditUtils::addPart( const QgsPointSequence &points, QgsFeatureId featureId )
     198                 :            : {
     199                 :          0 :   if ( !mLayer->isSpatial() )
     200                 :          0 :     return QgsGeometry::OperationResult::AddPartSelectedGeometryNotFound;
     201                 :            : 
     202                 :          0 :   QgsGeometry geometry;
     203                 :          0 :   bool firstPart = false;
     204                 :          0 :   QgsFeature f;
     205                 :          0 :   if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setNoAttributes() ).nextFeature( f ) )
     206                 :          0 :     return QgsGeometry::OperationResult::AddPartSelectedGeometryNotFound; //not found
     207                 :            : 
     208                 :          0 :   if ( !f.hasGeometry() )
     209                 :            :   {
     210                 :            :     //no existing geometry, so adding first part to null geometry
     211                 :          0 :     firstPart = true;
     212                 :          0 :   }
     213                 :            :   else
     214                 :            :   {
     215                 :          0 :     geometry = f.geometry();
     216                 :            :   }
     217                 :            : 
     218                 :          0 :   QgsGeometry::OperationResult errorCode = geometry.addPart( points,  mLayer->geometryType() );
     219                 :          0 :   if ( errorCode == QgsGeometry::Success )
     220                 :            :   {
     221                 :          0 :     if ( firstPart && QgsWkbTypes::isSingleType( mLayer->wkbType() )
     222                 :          0 :          && mLayer->dataProvider()->doesStrictFeatureTypeCheck() )
     223                 :            :     {
     224                 :            :       //convert back to single part if required by layer
     225                 :          0 :       geometry.convertToSingleType();
     226                 :          0 :     }
     227                 :          0 :     mLayer->changeGeometry( featureId, geometry );
     228                 :          0 :   }
     229                 :          0 :   return errorCode;
     230                 :          0 : }
     231                 :            : 
     232                 :          0 : QgsGeometry::OperationResult QgsVectorLayerEditUtils::addPart( QgsCurve *ring, QgsFeatureId featureId )
     233                 :            : {
     234                 :          0 :   if ( !mLayer->isSpatial() )
     235                 :          0 :     return QgsGeometry::AddPartSelectedGeometryNotFound;
     236                 :            : 
     237                 :          0 :   QgsGeometry geometry;
     238                 :          0 :   bool firstPart = false;
     239                 :          0 :   QgsFeature f;
     240                 :          0 :   if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setNoAttributes() ).nextFeature( f ) )
     241                 :          0 :     return QgsGeometry::AddPartSelectedGeometryNotFound;
     242                 :            : 
     243                 :          0 :   if ( !f.hasGeometry() )
     244                 :            :   {
     245                 :            :     //no existing geometry, so adding first part to null geometry
     246                 :          0 :     firstPart = true;
     247                 :          0 :   }
     248                 :            :   else
     249                 :            :   {
     250                 :          0 :     geometry = f.geometry();
     251                 :            :   }
     252                 :            : 
     253                 :          0 :   QgsGeometry::OperationResult errorCode = geometry.addPart( ring, mLayer->geometryType() );
     254                 :          0 :   if ( errorCode == QgsGeometry::Success )
     255                 :            :   {
     256                 :          0 :     if ( firstPart && QgsWkbTypes::isSingleType( mLayer->wkbType() )
     257                 :          0 :          && mLayer->dataProvider()->doesStrictFeatureTypeCheck() )
     258                 :            :     {
     259                 :            :       //convert back to single part if required by layer
     260                 :          0 :       geometry.convertToSingleType();
     261                 :          0 :     }
     262                 :          0 :     mLayer->changeGeometry( featureId, geometry );
     263                 :          0 :   }
     264                 :          0 :   return errorCode;
     265                 :          0 : }
     266                 :            : 
     267                 :            : 
     268                 :          0 : int QgsVectorLayerEditUtils::translateFeature( QgsFeatureId featureId, double dx, double dy )
     269                 :            : {
     270                 :          0 :   if ( !mLayer->isSpatial() )
     271                 :          0 :     return 1;
     272                 :            : 
     273                 :          0 :   QgsFeature f;
     274                 :          0 :   if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setNoAttributes() ).nextFeature( f ) || !f.hasGeometry() )
     275                 :          0 :     return 1; //geometry not found
     276                 :            : 
     277                 :          0 :   QgsGeometry geometry = f.geometry();
     278                 :            : 
     279                 :          0 :   int errorCode = geometry.translate( dx, dy );
     280                 :          0 :   if ( errorCode == 0 )
     281                 :            :   {
     282                 :          0 :     mLayer->changeGeometry( featureId, geometry );
     283                 :          0 :   }
     284                 :          0 :   return errorCode;
     285                 :          0 : }
     286                 :            : 
     287                 :          0 : QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitFeatures( const QVector<QgsPointXY> &splitLine, bool topologicalEditing )
     288                 :            : {
     289                 :          0 :   QgsPointSequence l;
     290                 :          0 :   for ( QVector<QgsPointXY>::const_iterator it = splitLine.constBegin(); it != splitLine.constEnd(); ++it )
     291                 :            :   {
     292                 :          0 :     l <<  QgsPoint( *it );
     293                 :          0 :   }
     294                 :          0 :   return splitFeatures( l, topologicalEditing );
     295                 :          0 : }
     296                 :            : 
     297                 :          0 : QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitFeatures( const QgsPointSequence &splitLine, bool topologicalEditing )
     298                 :            : {
     299                 :          0 :   QgsLineString lineString( splitLine );
     300                 :          0 :   QgsPointSequence topologyTestPoints;
     301                 :          0 :   bool preserveCircular = false;
     302                 :          0 :   return splitFeatures( &lineString, topologyTestPoints, preserveCircular, topologicalEditing );
     303                 :          0 : }
     304                 :            : 
     305                 :          0 : QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitFeatures( const QgsCurve *curve, QgsPointSequence &topologyTestPoints, bool preserveCircular, bool topologicalEditing )
     306                 :            : {
     307                 :          0 :   if ( !mLayer->isSpatial() )
     308                 :          0 :     return QgsGeometry::InvalidBaseGeometry;
     309                 :            : 
     310                 :          0 :   QgsRectangle bBox; //bounding box of the split line
     311                 :          0 :   QgsGeometry::OperationResult returnCode = QgsGeometry::OperationResult::Success;
     312                 :            :   QgsGeometry::OperationResult splitFunctionReturn; //return code of QgsGeometry::splitGeometry
     313                 :          0 :   int numberOfSplitFeatures = 0;
     314                 :            : 
     315                 :          0 :   QgsFeatureIterator features;
     316                 :          0 :   const QgsFeatureIds selectedIds = mLayer->selectedFeatureIds();
     317                 :            : 
     318                 :            :   // deactivate preserving circular if the curve contains only straight segments to avoid transforming Polygon to CurvePolygon
     319                 :          0 :   preserveCircular &= curve->hasCurvedSegments();
     320                 :            : 
     321                 :          0 :   if ( !selectedIds.isEmpty() ) //consider only the selected features if there is a selection
     322                 :            :   {
     323                 :          0 :     features = mLayer->getSelectedFeatures();
     324                 :          0 :   }
     325                 :            :   else //else consider all the feature that intersect the bounding box of the split line
     326                 :            :   {
     327                 :            : 
     328                 :          0 :     bBox = curve->boundingBox();
     329                 :            : 
     330                 :          0 :     if ( bBox.isEmpty() )
     331                 :            :     {
     332                 :            :       //if the bbox is a line, try to make a square out of it
     333                 :          0 :       if ( bBox.width() == 0.0 && bBox.height() > 0 )
     334                 :            :       {
     335                 :          0 :         bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 );
     336                 :          0 :         bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 );
     337                 :          0 :       }
     338                 :          0 :       else if ( bBox.height() == 0.0 && bBox.width() > 0 )
     339                 :            :       {
     340                 :          0 :         bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 );
     341                 :          0 :         bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 );
     342                 :          0 :       }
     343                 :            :       else
     344                 :            :       {
     345                 :            :         //If we have a single point, we still create a non-null box
     346                 :          0 :         double bufferDistance = 0.000001;
     347                 :          0 :         if ( mLayer->crs().isGeographic() )
     348                 :          0 :           bufferDistance = 0.00000001;
     349                 :          0 :         bBox.setXMinimum( bBox.xMinimum() - bufferDistance );
     350                 :          0 :         bBox.setXMaximum( bBox.xMaximum() + bufferDistance );
     351                 :          0 :         bBox.setYMinimum( bBox.yMinimum() - bufferDistance );
     352                 :          0 :         bBox.setYMaximum( bBox.yMaximum() + bufferDistance );
     353                 :            :       }
     354                 :          0 :     }
     355                 :            : 
     356                 :          0 :     features = mLayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
     357                 :            :   }
     358                 :            : 
     359                 :          0 :   QgsVectorLayerUtils::QgsFeaturesDataList featuresDataToAdd;
     360                 :            : 
     361                 :          0 :   QgsFeature feat;
     362                 :          0 :   while ( features.nextFeature( feat ) )
     363                 :            :   {
     364                 :          0 :     if ( !feat.hasGeometry() )
     365                 :            :     {
     366                 :          0 :       continue;
     367                 :            :     }
     368                 :          0 :     QVector<QgsGeometry> newGeometries;
     369                 :          0 :     QgsPointSequence featureTopologyTestPoints;
     370                 :          0 :     QgsGeometry featureGeom = feat.geometry();
     371                 :          0 :     splitFunctionReturn = featureGeom.splitGeometry( curve, newGeometries, preserveCircular, topologicalEditing, featureTopologyTestPoints );
     372                 :          0 :     topologyTestPoints.append( featureTopologyTestPoints );
     373                 :          0 :     if ( splitFunctionReturn == QgsGeometry::OperationResult::Success )
     374                 :            :     {
     375                 :            :       //change this geometry
     376                 :          0 :       mLayer->changeGeometry( feat.id(), featureGeom );
     377                 :            : 
     378                 :            :       //insert new features
     379                 :          0 :       QgsAttributeMap attributeMap = feat.attributes().toMap();
     380                 :          0 :       for ( const QgsGeometry &geom : std::as_const( newGeometries ) )
     381                 :            :       {
     382                 :          0 :         featuresDataToAdd << QgsVectorLayerUtils::QgsFeatureData( geom, attributeMap );
     383                 :            :       }
     384                 :            : 
     385                 :          0 :       if ( topologicalEditing )
     386                 :            :       {
     387                 :          0 :         QgsPointSequence::const_iterator topol_it = featureTopologyTestPoints.constBegin();
     388                 :          0 :         for ( ; topol_it != featureTopologyTestPoints.constEnd(); ++topol_it )
     389                 :            :         {
     390                 :          0 :           addTopologicalPoints( *topol_it );
     391                 :          0 :         }
     392                 :          0 :       }
     393                 :          0 :       ++numberOfSplitFeatures;
     394                 :          0 :     }
     395                 :          0 :     else if ( splitFunctionReturn != QgsGeometry::OperationResult::Success && splitFunctionReturn != QgsGeometry::NothingHappened ) // i.e. no split but no error occurred
     396                 :            :     {
     397                 :          0 :       returnCode = splitFunctionReturn;
     398                 :          0 :     }
     399                 :          0 :   }
     400                 :            : 
     401                 :          0 :   if ( !featuresDataToAdd.isEmpty() )
     402                 :            :   {
     403                 :            :     // finally create and add all bits of geometries cut off the original geometries
     404                 :            :     // (this is much faster than creating features one by one)
     405                 :          0 :     QgsFeatureList featuresListToAdd = QgsVectorLayerUtils::createFeatures( mLayer, featuresDataToAdd );
     406                 :          0 :     mLayer->addFeatures( featuresListToAdd );
     407                 :          0 :   }
     408                 :            : 
     409                 :          0 :   if ( numberOfSplitFeatures == 0 )
     410                 :            :   {
     411                 :          0 :     returnCode = QgsGeometry::OperationResult::NothingHappened;
     412                 :          0 :   }
     413                 :            : 
     414                 :          0 :   return returnCode;
     415                 :          0 : }
     416                 :            : 
     417                 :          0 : QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitParts( const QVector<QgsPointXY> &splitLine, bool topologicalEditing )
     418                 :            : {
     419                 :          0 :   QgsPointSequence l;
     420                 :          0 :   for ( QVector<QgsPointXY>::const_iterator it = splitLine.constBegin(); it != splitLine.constEnd(); ++it )
     421                 :            :   {
     422                 :          0 :     l <<  QgsPoint( *it );
     423                 :          0 :   }
     424                 :          0 :   return splitParts( l, topologicalEditing );
     425                 :          0 : }
     426                 :            : 
     427                 :          0 : QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitParts( const QgsPointSequence &splitLine, bool topologicalEditing )
     428                 :            : {
     429                 :          0 :   if ( !mLayer->isSpatial() )
     430                 :          0 :     return QgsGeometry::InvalidBaseGeometry;
     431                 :            : 
     432                 :            :   double xMin, yMin, xMax, yMax;
     433                 :          0 :   QgsRectangle bBox; //bounding box of the split line
     434                 :          0 :   QgsGeometry::OperationResult returnCode = QgsGeometry::OperationResult::Success;
     435                 :            :   QgsGeometry::OperationResult splitFunctionReturn; //return code of QgsGeometry::splitGeometry
     436                 :          0 :   int numberOfSplitParts = 0;
     437                 :            : 
     438                 :          0 :   QgsFeatureIterator fit;
     439                 :            : 
     440                 :          0 :   if ( mLayer->selectedFeatureCount() > 0 ) //consider only the selected features if there is a selection
     441                 :            :   {
     442                 :          0 :     fit = mLayer->getSelectedFeatures();
     443                 :          0 :   }
     444                 :            :   else //else consider all the feature that intersect the bounding box of the split line
     445                 :            :   {
     446                 :          0 :     if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) )
     447                 :            :     {
     448                 :          0 :       bBox.setXMinimum( xMin );
     449                 :          0 :       bBox.setYMinimum( yMin );
     450                 :          0 :       bBox.setXMaximum( xMax );
     451                 :          0 :       bBox.setYMaximum( yMax );
     452                 :          0 :     }
     453                 :            :     else
     454                 :            :     {
     455                 :          0 :       return QgsGeometry::OperationResult::InvalidInputGeometryType;
     456                 :            :     }
     457                 :            : 
     458                 :          0 :     if ( bBox.isEmpty() )
     459                 :            :     {
     460                 :            :       //if the bbox is a line, try to make a square out of it
     461                 :          0 :       if ( bBox.width() == 0.0 && bBox.height() > 0 )
     462                 :            :       {
     463                 :          0 :         bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 );
     464                 :          0 :         bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 );
     465                 :          0 :       }
     466                 :          0 :       else if ( bBox.height() == 0.0 && bBox.width() > 0 )
     467                 :            :       {
     468                 :          0 :         bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 );
     469                 :          0 :         bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 );
     470                 :          0 :       }
     471                 :            :       else
     472                 :            :       {
     473                 :            :         //If we have a single point, we still create a non-null box
     474                 :          0 :         double bufferDistance = 0.000001;
     475                 :          0 :         if ( mLayer->crs().isGeographic() )
     476                 :          0 :           bufferDistance = 0.00000001;
     477                 :          0 :         bBox.setXMinimum( bBox.xMinimum() - bufferDistance );
     478                 :          0 :         bBox.setXMaximum( bBox.xMaximum() + bufferDistance );
     479                 :          0 :         bBox.setYMinimum( bBox.yMinimum() - bufferDistance );
     480                 :          0 :         bBox.setYMaximum( bBox.yMaximum() + bufferDistance );
     481                 :            :       }
     482                 :          0 :     }
     483                 :            : 
     484                 :          0 :     fit = mLayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
     485                 :            :   }
     486                 :            : 
     487                 :          0 :   QgsFeature feat;
     488                 :          0 :   while ( fit.nextFeature( feat ) )
     489                 :            :   {
     490                 :          0 :     QVector<QgsGeometry> newGeometries;
     491                 :          0 :     QgsPointSequence topologyTestPoints;
     492                 :          0 :     QgsGeometry featureGeom = feat.geometry();
     493                 :          0 :     splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints, false );
     494                 :            : 
     495                 :          0 :     if ( splitFunctionReturn == QgsGeometry::OperationResult::Success && !newGeometries.isEmpty() )
     496                 :            :     {
     497                 :          0 :       QgsGeometry newGeom( newGeometries.at( 0 ) );
     498                 :          0 :       newGeom.convertToMultiType();
     499                 :            : 
     500                 :          0 :       for ( int i = 1; i < newGeometries.size(); ++i )
     501                 :            :       {
     502                 :          0 :         QgsGeometry part = newGeometries.at( i );
     503                 :          0 :         part.convertToSingleType();
     504                 :          0 :         newGeom.addPart( part );
     505                 :          0 :       }
     506                 :            : 
     507                 :          0 :       mLayer->changeGeometry( feat.id(), newGeom );
     508                 :            : 
     509                 :          0 :       if ( topologicalEditing )
     510                 :            :       {
     511                 :          0 :         QgsPointSequence::const_iterator topol_it = topologyTestPoints.constBegin();
     512                 :          0 :         for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
     513                 :            :         {
     514                 :          0 :           addTopologicalPoints( *topol_it );
     515                 :          0 :         }
     516                 :          0 :       }
     517                 :          0 :       ++numberOfSplitParts;
     518                 :          0 :     }
     519                 :          0 :     else if ( splitFunctionReturn != QgsGeometry::OperationResult::Success && splitFunctionReturn != QgsGeometry::OperationResult::NothingHappened )
     520                 :            :     {
     521                 :          0 :       returnCode = splitFunctionReturn;
     522                 :          0 :     }
     523                 :          0 :   }
     524                 :            : 
     525                 :          0 :   if ( numberOfSplitParts == 0 && mLayer->selectedFeatureCount() > 0  && returnCode == QgsGeometry::Success )
     526                 :            :   {
     527                 :            :     //There is a selection but no feature has been split.
     528                 :            :     //Maybe user forgot that only the selected features are split
     529                 :          0 :     returnCode = QgsGeometry::OperationResult::NothingHappened;
     530                 :          0 :   }
     531                 :            : 
     532                 :          0 :   return returnCode;
     533                 :          0 : }
     534                 :            : 
     535                 :            : 
     536                 :          0 : int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsGeometry &geom )
     537                 :            : {
     538                 :          0 :   if ( !mLayer->isSpatial() )
     539                 :          0 :     return 1;
     540                 :            : 
     541                 :          0 :   if ( geom.isNull() )
     542                 :            :   {
     543                 :          0 :     return 1;
     544                 :            :   }
     545                 :            : 
     546                 :          0 :   int returnVal = 0;
     547                 :            : 
     548                 :          0 :   QgsAbstractGeometry::vertex_iterator it = geom.vertices_begin();
     549                 :          0 :   while ( it != geom.vertices_end() )
     550                 :            :   {
     551                 :          0 :     if ( addTopologicalPoints( *it ) != 0 )
     552                 :            :     {
     553                 :          0 :       returnVal = 2;
     554                 :          0 :     }
     555                 :          0 :     ++it;
     556                 :            :   }
     557                 :            : 
     558                 :          0 :   return returnVal;
     559                 :          0 : }
     560                 :            : 
     561                 :          0 : int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPoint &p )
     562                 :            : {
     563                 :          0 :   if ( !mLayer->isSpatial() )
     564                 :          0 :     return 1;
     565                 :            : 
     566                 :          0 :   double segmentSearchEpsilon = mLayer->crs().isGeographic() ? 1e-12 : 1e-8;
     567                 :            : 
     568                 :            :   //work with a tolerance because coordinate projection may introduce some rounding
     569                 :          0 :   double threshold = mLayer->geometryOptions()->geometryPrecision();
     570                 :            : 
     571                 :          0 :   if ( qgsDoubleNear( threshold, 0.0 ) )
     572                 :            :   {
     573                 :          0 :     threshold = 0.0000001;
     574                 :            : 
     575                 :          0 :     if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceMeters )
     576                 :            :     {
     577                 :          0 :       threshold = 0.001;
     578                 :          0 :     }
     579                 :          0 :     else if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceFeet )
     580                 :            :     {
     581                 :          0 :       threshold = 0.0001;
     582                 :          0 :     }
     583                 :          0 :   }
     584                 :            : 
     585                 :          0 :   QgsRectangle searchRect( p.x() - threshold, p.y() - threshold,
     586                 :          0 :                            p.x() + threshold, p.y() + threshold );
     587                 :          0 :   double sqrSnappingTolerance = threshold * threshold;
     588                 :            : 
     589                 :          0 :   QgsFeature f;
     590                 :          0 :   QgsFeatureIterator fit = mLayer->getFeatures( QgsFeatureRequest()
     591                 :          0 :                            .setFilterRect( searchRect )
     592                 :          0 :                            .setFlags( QgsFeatureRequest::ExactIntersect )
     593                 :          0 :                            .setNoAttributes() );
     594                 :            : 
     595                 :          0 :   QMap<QgsFeatureId, QgsGeometry> features;
     596                 :          0 :   QMap<QgsFeatureId, int> segments;
     597                 :            : 
     598                 :          0 :   while ( fit.nextFeature( f ) )
     599                 :            :   {
     600                 :            :     int afterVertex;
     601                 :          0 :     QgsPointXY snappedPoint;
     602                 :          0 :     double sqrDistSegmentSnap = f.geometry().closestSegmentWithContext( p, snappedPoint, afterVertex, nullptr, segmentSearchEpsilon );
     603                 :          0 :     if ( sqrDistSegmentSnap < sqrSnappingTolerance )
     604                 :            :     {
     605                 :          0 :       segments[f.id()] = afterVertex;
     606                 :          0 :       features[f.id()] = f.geometry();
     607                 :          0 :     }
     608                 :            :   }
     609                 :            : 
     610                 :          0 :   if ( segments.isEmpty() )
     611                 :          0 :     return 2;
     612                 :            : 
     613                 :          0 :   bool pointsAdded = false;
     614                 :          0 :   for ( QMap<QgsFeatureId, int>::const_iterator it = segments.constBegin(); it != segments.constEnd(); ++it )
     615                 :            :   {
     616                 :          0 :     QgsFeatureId fid = it.key();
     617                 :          0 :     int segmentAfterVertex = it.value();
     618                 :          0 :     QgsGeometry geom = features[fid];
     619                 :            : 
     620                 :            :     int atVertex, beforeVertex, afterVertex;
     621                 :            :     double sqrDistVertexSnap;
     622                 :          0 :     geom.closestVertex( p, atVertex, beforeVertex, afterVertex, sqrDistVertexSnap );
     623                 :            : 
     624                 :          0 :     if ( sqrDistVertexSnap < sqrSnappingTolerance )
     625                 :          0 :       continue;  // the vertex already exists - do not insert it
     626                 :            : 
     627                 :          0 :     if ( !mLayer->insertVertex( p, fid, segmentAfterVertex ) )
     628                 :            :     {
     629                 :          0 :       QgsDebugMsg( QStringLiteral( "failed to insert topo point" ) );
     630                 :          0 :     }
     631                 :            :     else
     632                 :            :     {
     633                 :          0 :       pointsAdded = true;
     634                 :            :     }
     635                 :          0 :   }
     636                 :            : 
     637                 :          0 :   return pointsAdded ? 0 : 2;
     638                 :          0 : }
     639                 :            : 
     640                 :          0 : int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointSequence &ps )
     641                 :            : {
     642                 :          0 :   if ( !mLayer->isSpatial() )
     643                 :          0 :     return 1;
     644                 :            : 
     645                 :          0 :   if ( ps.isEmpty() )
     646                 :            :   {
     647                 :          0 :     return 1;
     648                 :            :   }
     649                 :            : 
     650                 :          0 :   bool pointsAdded = false;
     651                 :            : 
     652                 :          0 :   QgsPointSequence::const_iterator it = ps.constBegin();
     653                 :          0 :   while ( it != ps.constEnd() )
     654                 :            :   {
     655                 :          0 :     if ( addTopologicalPoints( *it ) == 0 )
     656                 :            :     {
     657                 :          0 :       pointsAdded = true;
     658                 :          0 :     }
     659                 :          0 :     ++it;
     660                 :            :   }
     661                 :            : 
     662                 :          0 :   return pointsAdded ? 0 : 2;
     663                 :          0 : }
     664                 :            : 
     665                 :          0 : int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p )
     666                 :            : {
     667                 :          0 :   return addTopologicalPoints( QgsPoint( p ) );
     668                 :          0 : }
     669                 :            : 
     670                 :            : 
     671                 :          0 : bool QgsVectorLayerEditUtils::boundingBoxFromPointList( const QgsPointSequence &list, double &xmin, double &ymin, double &xmax, double &ymax ) const
     672                 :            : {
     673                 :          0 :   if ( list.empty() )
     674                 :            :   {
     675                 :          0 :     return false;
     676                 :            :   }
     677                 :            : 
     678                 :          0 :   xmin = std::numeric_limits<double>::max();
     679                 :          0 :   xmax = -std::numeric_limits<double>::max();
     680                 :          0 :   ymin = std::numeric_limits<double>::max();
     681                 :          0 :   ymax = -std::numeric_limits<double>::max();
     682                 :            : 
     683                 :          0 :   for ( QgsPointSequence::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
     684                 :            :   {
     685                 :          0 :     if ( it->x() < xmin )
     686                 :            :     {
     687                 :          0 :       xmin = it->x();
     688                 :          0 :     }
     689                 :          0 :     if ( it->x() > xmax )
     690                 :            :     {
     691                 :          0 :       xmax = it->x();
     692                 :          0 :     }
     693                 :          0 :     if ( it->y() < ymin )
     694                 :            :     {
     695                 :          0 :       ymin = it->y();
     696                 :          0 :     }
     697                 :          0 :     if ( it->y() > ymax )
     698                 :            :     {
     699                 :          0 :       ymax = it->y();
     700                 :          0 :     }
     701                 :          0 :   }
     702                 :            : 
     703                 :          0 :   return true;
     704                 :          0 : }

Generated by: LCOV version 1.14