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

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :                          qgsalgorithmsplitwithlines.cpp
       3                 :            :                          ---------------------
       4                 :            :     begin                : April 2017
       5                 :            :     copyright            : (C) 2017 by Nyall Dawson
       6                 :            :     email                : nyall dot dawson at gmail dot com
       7                 :            :  ***************************************************************************/
       8                 :            : 
       9                 :            : /***************************************************************************
      10                 :            :  *                                                                         *
      11                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      12                 :            :  *   it under the terms of the GNU General Public License as published by  *
      13                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      14                 :            :  *   (at your option) any later version.                                   *
      15                 :            :  *                                                                         *
      16                 :            :  ***************************************************************************/
      17                 :            : 
      18                 :            : #include "qgsalgorithmsplitwithlines.h"
      19                 :            : #include "qgsgeometryengine.h"
      20                 :            : #include "qgsvectorlayer.h"
      21                 :            : ///@cond PRIVATE
      22                 :            : 
      23                 :          0 : QString QgsSplitWithLinesAlgorithm::name() const
      24                 :            : {
      25                 :          0 :   return QStringLiteral( "splitwithlines" );
      26                 :            : }
      27                 :            : 
      28                 :          0 : QString QgsSplitWithLinesAlgorithm::displayName() const
      29                 :            : {
      30                 :          0 :   return QObject::tr( "Split with lines" );
      31                 :            : }
      32                 :            : 
      33                 :          0 : QStringList QgsSplitWithLinesAlgorithm::tags() const
      34                 :            : {
      35                 :          0 :   return QObject::tr( "split,cut,lines" ).split( ',' );
      36                 :          0 : }
      37                 :            : 
      38                 :          0 : QString QgsSplitWithLinesAlgorithm::group() const
      39                 :            : {
      40                 :          0 :   return QObject::tr( "Vector overlay" );
      41                 :            : }
      42                 :            : 
      43                 :          0 : QString QgsSplitWithLinesAlgorithm::groupId() const
      44                 :            : {
      45                 :          0 :   return QStringLiteral( "vectoroverlay" );
      46                 :            : }
      47                 :            : 
      48                 :          0 : void QgsSplitWithLinesAlgorithm::initAlgorithm( const QVariantMap & )
      49                 :            : {
      50                 :          0 :   addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ),
      51                 :          0 :                 QObject::tr( "Input layer" ), QList< int >() << QgsProcessing::TypeVectorLine << QgsProcessing::TypeVectorPolygon ) );
      52                 :          0 :   addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "LINES" ),
      53                 :          0 :                 QObject::tr( "Split layer" ), QList< int >() << QgsProcessing::TypeVectorLine ) );
      54                 :          0 :   addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Split" ) ) );
      55                 :          0 : }
      56                 :            : 
      57                 :          0 : QString QgsSplitWithLinesAlgorithm::shortHelpString() const
      58                 :            : {
      59                 :          0 :   return QObject::tr( "This algorithm splits the lines or polygons in one layer using the lines in another layer to define the breaking points. "
      60                 :            :                       "Intersection between geometries in both layers are considered as split points." );
      61                 :            : }
      62                 :            : 
      63                 :          0 : QgsSplitWithLinesAlgorithm *QgsSplitWithLinesAlgorithm::createInstance() const
      64                 :            : {
      65                 :          0 :   return new QgsSplitWithLinesAlgorithm();
      66                 :            : }
      67                 :            : 
      68                 :          0 : QgsProcessingAlgorithm::Flags QgsSplitWithLinesAlgorithm::flags() const
      69                 :            : {
      70                 :          0 :   Flags f = QgsProcessingAlgorithm::flags();
      71                 :          0 :   f |= QgsProcessingAlgorithm::FlagSupportsInPlaceEdits;
      72                 :          0 :   return f;
      73                 :            : }
      74                 :            : 
      75                 :          0 : bool QgsSplitWithLinesAlgorithm::supportInPlaceEdit( const QgsMapLayer *l ) const
      76                 :            : {
      77                 :          0 :   const QgsVectorLayer *layer = qobject_cast< const QgsVectorLayer * >( l );
      78                 :          0 :   if ( !layer )
      79                 :          0 :     return false;
      80                 :            : 
      81                 :          0 :   if ( layer->geometryType() != QgsWkbTypes::LineGeometry && layer->geometryType() != QgsWkbTypes::PolygonGeometry )
      82                 :          0 :     return false;
      83                 :            : 
      84                 :          0 :   return true;
      85                 :          0 : }
      86                 :            : 
      87                 :          0 : QVariantMap QgsSplitWithLinesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
      88                 :            : {
      89                 :          0 :   std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
      90                 :          0 :   if ( !source )
      91                 :          0 :     throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
      92                 :            : 
      93                 :          0 :   std::unique_ptr< QgsFeatureSource > linesSource( parameterAsSource( parameters, QStringLiteral( "LINES" ), context ) );
      94                 :          0 :   if ( !linesSource )
      95                 :          0 :     throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "LINES" ) ) );
      96                 :            : 
      97                 :          0 :   bool sameLayer = parameters.value( QStringLiteral( "INPUT" ) ) == parameters.value( QStringLiteral( "LINES" ) );
      98                 :            : 
      99                 :          0 :   QString dest;
     100                 :          0 :   std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(),
     101                 :          0 :                                           QgsWkbTypes::multiType( source->wkbType() ),  source->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) );
     102                 :          0 :   if ( !sink )
     103                 :          0 :     throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
     104                 :            : 
     105                 :          0 :   QgsFeatureRequest request;
     106                 :          0 :   request.setNoAttributes();
     107                 :          0 :   request.setDestinationCrs( source->sourceCrs(), context.transformContext() );
     108                 :            : 
     109                 :          0 :   QgsFeatureIterator splitLines = linesSource->getFeatures( request );
     110                 :          0 :   QgsFeature aSplitFeature;
     111                 :            : 
     112                 :          0 :   const QgsSpatialIndex splitLinesIndex( splitLines, feedback, QgsSpatialIndex::FlagStoreFeatureGeometries );
     113                 :            : 
     114                 :          0 :   QgsFeature outFeat;
     115                 :          0 :   QgsFeatureIterator features = source->getFeatures();
     116                 :            : 
     117                 :          0 :   double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
     118                 :          0 :   int i = 0;
     119                 :          0 :   QgsFeature inFeatureA;
     120                 :          0 :   while ( features.nextFeature( inFeatureA ) )
     121                 :            :   {
     122                 :          0 :     i++;
     123                 :          0 :     if ( feedback->isCanceled() )
     124                 :            :     {
     125                 :          0 :       break;
     126                 :            :     }
     127                 :            : 
     128                 :          0 :     if ( !inFeatureA.hasGeometry() )
     129                 :            :     {
     130                 :          0 :       sink->addFeature( inFeatureA, QgsFeatureSink::FastInsert );
     131                 :          0 :       continue;
     132                 :            :     }
     133                 :            : 
     134                 :          0 :     const QgsGeometry originalGeometry = inFeatureA.geometry();
     135                 :          0 :     outFeat.setAttributes( inFeatureA.attributes() );
     136                 :            : 
     137                 :          0 :     QVector< QgsGeometry > inGeoms = originalGeometry.asGeometryCollection();
     138                 :            : 
     139                 :          0 :     const QgsFeatureIds splitLineCandidates = qgis::listToSet( splitLinesIndex.intersects( originalGeometry.boundingBox() ) );
     140                 :          0 :     if ( !splitLineCandidates.empty() ) // has intersection of bounding boxes
     141                 :            :     {
     142                 :          0 :       QVector< QgsGeometry > splittingLines;
     143                 :            : 
     144                 :            :       // use prepared geometries for faster intersection tests
     145                 :          0 :       std::unique_ptr< QgsGeometryEngine > originalGeometryEngine;
     146                 :            : 
     147                 :          0 :       for ( QgsFeatureId splitLineCandidateId : splitLineCandidates )
     148                 :            :       {
     149                 :            :         // check if trying to self-intersect
     150                 :          0 :         if ( sameLayer && inFeatureA.id() == splitLineCandidateId )
     151                 :          0 :           continue;
     152                 :            : 
     153                 :          0 :         const QgsGeometry splitLineCandidate = splitLinesIndex.geometry( splitLineCandidateId );
     154                 :          0 :         if ( !originalGeometryEngine )
     155                 :            :         {
     156                 :          0 :           originalGeometryEngine.reset( QgsGeometry::createGeometryEngine( originalGeometry.constGet() ) );
     157                 :          0 :           originalGeometryEngine->prepareGeometry();
     158                 :          0 :         }
     159                 :            : 
     160                 :          0 :         if ( originalGeometryEngine->intersects( splitLineCandidate.constGet() ) )
     161                 :            :         {
     162                 :          0 :           QVector< QgsGeometry > splitGeomParts = splitLineCandidate.asGeometryCollection();
     163                 :          0 :           splittingLines.append( splitGeomParts );
     164                 :          0 :         }
     165                 :          0 :       }
     166                 :            : 
     167                 :          0 :       if ( !splittingLines.empty() )
     168                 :            :       {
     169                 :          0 :         for ( const QgsGeometry &splitGeom : std::as_const( splittingLines ) )
     170                 :            :         {
     171                 :          0 :           QgsPointSequence splitterPList;
     172                 :          0 :           QVector< QgsGeometry > outGeoms;
     173                 :            : 
     174                 :            :           // use prepared geometries for faster intersection tests
     175                 :          0 :           std::unique_ptr< QgsGeometryEngine > splitGeomEngine( QgsGeometry::createGeometryEngine( splitGeom.constGet() ) );
     176                 :          0 :           splitGeomEngine->prepareGeometry();
     177                 :          0 :           while ( !inGeoms.empty() )
     178                 :            :           {
     179                 :          0 :             if ( feedback->isCanceled() )
     180                 :            :             {
     181                 :          0 :               break;
     182                 :            :             }
     183                 :            : 
     184                 :          0 :             QgsGeometry inGeom = inGeoms.takeFirst();
     185                 :          0 :             if ( inGeom.isNull() )
     186                 :          0 :               continue;
     187                 :            : 
     188                 :          0 :             if ( splitGeomEngine->intersects( inGeom.constGet() ) )
     189                 :            :             {
     190                 :          0 :               QgsGeometry before = inGeom;
     191                 :          0 :               if ( splitterPList.empty() )
     192                 :            :               {
     193                 :          0 :                 const QgsCoordinateSequence sequence = splitGeom.constGet()->coordinateSequence();
     194                 :          0 :                 for ( const QgsRingSequence &part : sequence )
     195                 :            :                 {
     196                 :          0 :                   for ( const QgsPointSequence &ring : part )
     197                 :            :                   {
     198                 :          0 :                     for ( const QgsPoint &pt : ring )
     199                 :            :                     {
     200                 :          0 :                       splitterPList << pt;
     201                 :            :                     }
     202                 :            :                   }
     203                 :            :                 }
     204                 :          0 :               }
     205                 :            : 
     206                 :          0 :               QVector< QgsGeometry > newGeometries;
     207                 :          0 :               QgsPointSequence topologyTestPoints;
     208                 :          0 :               QgsGeometry::OperationResult result = inGeom.splitGeometry( splitterPList, newGeometries, false, topologyTestPoints, true );
     209                 :            : 
     210                 :            :               // splitGeometry: If there are several intersections
     211                 :            :               // between geometry and splitLine, only the first one is considered.
     212                 :          0 :               if ( result == QgsGeometry::Success )
     213                 :            :               {
     214                 :            :                 // sometimes the resultant geometry has changed from the input, but only because of numerical precision issues.
     215                 :            :                 // and is effectively indistinguishable from the input. By testing the Hausdorff distance is less than this threshold
     216                 :            :                 // we are checking that the maximum "change" between the result and the input is actually significant enough to be meaningful...
     217                 :          0 :                 if ( inGeom.hausdorffDistance( before ) < 1e-12 )
     218                 :            :                 {
     219                 :            :                   // effectively no change!!
     220                 :          0 :                   outGeoms.append( inGeom );
     221                 :          0 :                 }
     222                 :            :                 else
     223                 :            :                 {
     224                 :          0 :                   inGeoms.append( inGeom );
     225                 :          0 :                   inGeoms.append( newGeometries );
     226                 :            :                 }
     227                 :          0 :               }
     228                 :            :               else
     229                 :            :               {
     230                 :          0 :                 outGeoms.append( inGeom );
     231                 :            :               }
     232                 :          0 :             }
     233                 :            :             else
     234                 :            :             {
     235                 :          0 :               outGeoms.append( inGeom );
     236                 :            :             }
     237                 :            : 
     238                 :          0 :           }
     239                 :          0 :           inGeoms = outGeoms;
     240                 :          0 :         }
     241                 :          0 :       }
     242                 :          0 :     }
     243                 :            : 
     244                 :          0 :     QVector< QgsGeometry > parts;
     245                 :          0 :     for ( const QgsGeometry &aGeom : std::as_const( inGeoms ) )
     246                 :            :     {
     247                 :          0 :       if ( feedback->isCanceled() )
     248                 :            :       {
     249                 :          0 :         break;
     250                 :            :       }
     251                 :            : 
     252                 :          0 :       bool passed = true;
     253                 :          0 :       if ( QgsWkbTypes::geometryType( aGeom.wkbType() ) == QgsWkbTypes::LineGeometry )
     254                 :            :       {
     255                 :          0 :         int numPoints = aGeom.constGet()->nCoordinates();
     256                 :            : 
     257                 :          0 :         if ( numPoints <= 2 )
     258                 :            :         {
     259                 :          0 :           if ( numPoints == 2 )
     260                 :          0 :             passed = !static_cast< const QgsCurve * >( aGeom.constGet() )->isClosed(); // tests if vertex 0 = vertex 1
     261                 :            :           else
     262                 :          0 :             passed = false; // sometimes splitting results in lines of zero length
     263                 :          0 :         }
     264                 :          0 :       }
     265                 :            : 
     266                 :          0 :       if ( passed )
     267                 :          0 :         parts.append( aGeom );
     268                 :            :     }
     269                 :            : 
     270                 :          0 :     for ( const QgsGeometry &g : parts )
     271                 :            :     {
     272                 :          0 :       outFeat.setGeometry( g );
     273                 :          0 :       sink->addFeature( outFeat, QgsFeatureSink::FastInsert );
     274                 :            :     }
     275                 :            : 
     276                 :          0 :     feedback->setProgress( i * step );
     277                 :          0 :   }
     278                 :            : 
     279                 :          0 :   QVariantMap outputs;
     280                 :          0 :   outputs.insert( QStringLiteral( "OUTPUT" ), dest );
     281                 :          0 :   return outputs;
     282                 :          0 : }
     283                 :            : 
     284                 :            : 
     285                 :            : 
     286                 :            : ///@endcond
     287                 :            : 
     288                 :            : 

Generated by: LCOV version 1.14