QGIS API Documentation  3.2.0-Bonn (bc43194)
qgsalgorithmsplitwithlines.cpp
Go to the documentation of this file.
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 
19 #include "qgsgeometryengine.h"
20 
22 
23 QString QgsSplitWithLinesAlgorithm::name() const
24 {
25  return QStringLiteral( "splitwithlines" );
26 }
27 
28 QString QgsSplitWithLinesAlgorithm::displayName() const
29 {
30  return QObject::tr( "Split with lines" );
31 }
32 
33 QStringList QgsSplitWithLinesAlgorithm::tags() const
34 {
35  return QObject::tr( "split,cut,lines" ).split( ',' );
36 }
37 
38 QString QgsSplitWithLinesAlgorithm::group() const
39 {
40  return QObject::tr( "Vector overlay" );
41 }
42 
43 QString QgsSplitWithLinesAlgorithm::groupId() const
44 {
45  return QStringLiteral( "vectoroverlay" );
46 }
47 
48 void QgsSplitWithLinesAlgorithm::initAlgorithm( const QVariantMap & )
49 {
50  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ),
51  QObject::tr( "Input layer" ), QList< int >() << QgsProcessing::TypeVectorLine << QgsProcessing::TypeVectorPolygon ) );
52  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "LINES" ),
53  QObject::tr( "Split layer" ), QList< int >() << QgsProcessing::TypeVectorLine ) );
54  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Split" ) ) );
55 }
56 
57 QString QgsSplitWithLinesAlgorithm::shortHelpString() const
58 {
59  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 QgsSplitWithLinesAlgorithm *QgsSplitWithLinesAlgorithm::createInstance() const
64 {
65  return new QgsSplitWithLinesAlgorithm();
66 }
67 
68 QVariantMap QgsSplitWithLinesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
69 {
70  std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
71  if ( !source )
72  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
73 
74  std::unique_ptr< QgsFeatureSource > linesSource( parameterAsSource( parameters, QStringLiteral( "LINES" ), context ) );
75  if ( !linesSource )
76  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "LINES" ) ) );
77 
78  bool sameLayer = parameters.value( QStringLiteral( "INPUT" ) ) == parameters.value( QStringLiteral( "LINES" ) );
79 
80  QString dest;
81  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(),
82  QgsWkbTypes::multiType( source->wkbType() ), source->sourceCrs() ) );
83  if ( !sink )
84  throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
85 
86  QgsSpatialIndex spatialIndex;
87  QMap< QgsFeatureId, QgsGeometry > splitGeoms;
88  QgsFeatureRequest request;
90  request.setDestinationCrs( source->sourceCrs(), context.transformContext() );
91 
92  QgsFeatureIterator splitLines = linesSource->getFeatures( request );
93  QgsFeature aSplitFeature;
94  while ( splitLines.nextFeature( aSplitFeature ) )
95  {
96  if ( feedback->isCanceled() )
97  {
98  break;
99  }
100 
101  splitGeoms.insert( aSplitFeature.id(), aSplitFeature.geometry() );
102  spatialIndex.insertFeature( aSplitFeature );
103  }
104 
105  QgsFeature outFeat;
106  QgsFeatureIterator features = source->getFeatures();
107 
108  double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
109  int i = 0;
110  QgsFeature inFeatureA;
111  while ( features.nextFeature( inFeatureA ) )
112  {
113  i++;
114  if ( feedback->isCanceled() )
115  {
116  break;
117  }
118 
119  if ( !inFeatureA.hasGeometry() )
120  {
121  sink->addFeature( inFeatureA, QgsFeatureSink::FastInsert );
122  continue;
123  }
124 
125  QgsGeometry inGeom = inFeatureA.geometry();
126  outFeat.setAttributes( inFeatureA.attributes() );
127 
128  QVector< QgsGeometry > inGeoms = inGeom.asGeometryCollection();
129 
130  const QgsFeatureIds lines = spatialIndex.intersects( inGeom.boundingBox() ).toSet();
131  if ( !lines.empty() ) // has intersection of bounding boxes
132  {
133  QVector< QgsGeometry > splittingLines;
134 
135  // use prepared geometries for faster intersection tests
136  std::unique_ptr< QgsGeometryEngine > engine;
137 
138  for ( QgsFeatureId line : lines )
139  {
140  // check if trying to self-intersect
141  if ( sameLayer && inFeatureA.id() == line )
142  continue;
143 
144  QgsGeometry splitGeom = splitGeoms.value( line );
145  if ( !engine )
146  {
147  engine.reset( QgsGeometry::createGeometryEngine( inGeom.constGet() ) );
148  engine->prepareGeometry();
149  }
150 
151  if ( engine->intersects( splitGeom.constGet() ) )
152  {
153  QVector< QgsGeometry > splitGeomParts = splitGeom.asGeometryCollection();
154  splittingLines.append( splitGeomParts );
155  }
156  }
157 
158  if ( !splittingLines.empty() )
159  {
160  for ( const QgsGeometry &splitGeom : qgis::as_const( splittingLines ) )
161  {
162  QVector<QgsPointXY> splitterPList;
163  QVector< QgsGeometry > outGeoms;
164 
165  // use prepared geometries for faster intersection tests
166  std::unique_ptr< QgsGeometryEngine > splitGeomEngine( QgsGeometry::createGeometryEngine( splitGeom.constGet() ) );
167  splitGeomEngine->prepareGeometry();
168  while ( !inGeoms.empty() )
169  {
170  if ( feedback->isCanceled() )
171  {
172  break;
173  }
174 
175  QgsGeometry inGeom = inGeoms.takeFirst();
176  if ( !inGeom )
177  continue;
178 
179  if ( splitGeomEngine->intersects( inGeom.constGet() ) )
180  {
181  QgsGeometry before = inGeom;
182  if ( splitterPList.empty() )
183  {
184  const QgsCoordinateSequence sequence = splitGeom.constGet()->coordinateSequence();
185  for ( const QgsRingSequence &part : sequence )
186  {
187  for ( const QgsPointSequence &ring : part )
188  {
189  for ( const QgsPoint &pt : ring )
190  {
191  splitterPList << QgsPointXY( pt );
192  }
193  }
194  }
195  }
196 
197  QVector< QgsGeometry > newGeometries;
198  QVector<QgsPointXY> topologyTestPoints;
199  QgsGeometry::OperationResult result = inGeom.splitGeometry( splitterPList, newGeometries, false, topologyTestPoints );
200 
201  // splitGeometry: If there are several intersections
202  // between geometry and splitLine, only the first one is considered.
203  if ( result == QgsGeometry::Success ) // split occurred
204  {
205  if ( inGeom.isGeosEqual( before ) )
206  {
207  // bug in splitGeometry: sometimes it returns 0 but
208  // the geometry is unchanged
209  outGeoms.append( inGeom );
210  }
211  else
212  {
213  inGeoms.append( inGeom );
214  inGeoms.append( newGeometries );
215  }
216  }
217  else
218  {
219  outGeoms.append( inGeom );
220  }
221  }
222  else
223  {
224  outGeoms.append( inGeom );
225  }
226 
227  }
228  inGeoms = outGeoms;
229  }
230  }
231  }
232 
233  QVector< QgsGeometry > parts;
234  for ( const QgsGeometry &aGeom : qgis::as_const( inGeoms ) )
235  {
236  if ( feedback->isCanceled() )
237  {
238  break;
239  }
240 
241  bool passed = true;
242  if ( QgsWkbTypes::geometryType( aGeom.wkbType() ) == QgsWkbTypes::LineGeometry )
243  {
244  int numPoints = aGeom.constGet()->nCoordinates();
245 
246  if ( numPoints <= 2 )
247  {
248  if ( numPoints == 2 )
249  passed = !static_cast< const QgsCurve * >( aGeom.constGet() )->isClosed(); // tests if vertex 0 = vertex 1
250  else
251  passed = false; // sometimes splitting results in lines of zero length
252  }
253  }
254 
255  if ( passed )
256  parts.append( aGeom );
257  }
258 
259  for ( const QgsGeometry &g : parts )
260  {
261  outFeat.setGeometry( g );
262  sink->addFeature( outFeat, QgsFeatureSink::FastInsert );
263  }
264 
265  feedback->setProgress( i * step );
266  }
267 
268  QVariantMap outputs;
269  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
270  return outputs;
271 }
272 
273 
274 
276 
277 
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature&#39;s geometries.
QgsFeatureId id
Definition: qgsfeature.h:71
Wrapper for iterator of features from vector data provider or vector layer.
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
static Type multiType(Type type)
Returns the multi type for a WKB type.
Definition: qgswkbtypes.h:296
Base class for providing feedback from a processing algorithm.
QVector< QgsRingSequence > QgsCoordinateSequence
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:544
A class to represent a 2D point.
Definition: qgspointxy.h:43
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:104
void setAttributes(const QgsAttributes &attrs)
Sets the feature&#39;s attributes.
Definition: qgsfeature.cpp:127
bool insertFeature(const QgsFeature &feature)
Adds a feature to the index.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:190
OperationResult
Success or failure of a geometry operation.
Definition: qgsgeometry.h:113
A feature sink output for processing algorithms.
bool isGeosEqual(const QgsGeometry &) const
Compares the geometry with another geometry using GEOS.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:663
This class wraps a request for features to a vector layer (or directly its vector data provider)...
Custom exception class for processing related exceptions.
Definition: qgsexception.h:82
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Vector polygon layers.
Definition: qgsprocessing.h:50
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QVector< QgsPoint > QgsPointSequence
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
QVector< QgsPointSequence > QgsRingSequence
A spatial index for QgsFeature objects.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
OperationResult splitGeometry(const QVector< QgsPointXY > &splitLine, QVector< QgsGeometry > &newGeometries, bool topological, QVector< QgsPointXY > &topologyTestPoints)
Splits this geometry according to a given line.
An input feature source (such as vector layers) parameter for processing algorithms.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Vector line layers.
Definition: qgsprocessing.h:49
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
qint64 QgsFeatureId
Definition: qgsfeature.h:37
QList< int > QgsAttributeList
Definition: qgsfield.h:27
bool nextFeature(QgsFeature &f)
Operation succeeded.
Definition: qgsgeometry.h:115
Contains information about the context in which a processing algorithm is executed.
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
QgsAttributes attributes
Definition: qgsfeature.h:72