QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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 #include "qgsvectorlayer.h"
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 QgsProcessingAlgorithm::Flags QgsSplitWithLinesAlgorithm::flags() const
69 {
72  return f;
73 }
74 
75 bool QgsSplitWithLinesAlgorithm::supportInPlaceEdit( const QgsMapLayer *l ) const
76 {
77  const QgsVectorLayer *layer = qobject_cast< const QgsVectorLayer * >( l );
78  if ( !layer )
79  return false;
80 
82  return false;
83 
84  return true;
85 }
86 
87 QVariantMap QgsSplitWithLinesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
88 {
89  std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
90  if ( !source )
91  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
92 
93  std::unique_ptr< QgsFeatureSource > linesSource( parameterAsSource( parameters, QStringLiteral( "LINES" ), context ) );
94  if ( !linesSource )
95  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "LINES" ) ) );
96 
97  bool sameLayer = parameters.value( QStringLiteral( "INPUT" ) ) == parameters.value( QStringLiteral( "LINES" ) );
98 
99  QString dest;
100  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(),
101  QgsWkbTypes::multiType( source->wkbType() ), source->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) );
102  if ( !sink )
103  throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
104 
105  QgsSpatialIndex spatialIndex;
106  QMap< QgsFeatureId, QgsGeometry > splitGeoms;
107  QgsFeatureRequest request;
108  request.setNoAttributes();
109  request.setDestinationCrs( source->sourceCrs(), context.transformContext() );
110 
111  QgsFeatureIterator splitLines = linesSource->getFeatures( request );
112  QgsFeature aSplitFeature;
113  while ( splitLines.nextFeature( aSplitFeature ) )
114  {
115  if ( feedback->isCanceled() )
116  {
117  break;
118  }
119 
120  splitGeoms.insert( aSplitFeature.id(), aSplitFeature.geometry() );
121  spatialIndex.addFeature( aSplitFeature );
122  }
123 
124  QgsFeature outFeat;
125  QgsFeatureIterator features = source->getFeatures();
126 
127  double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
128  int i = 0;
129  QgsFeature inFeatureA;
130  while ( features.nextFeature( inFeatureA ) )
131  {
132  i++;
133  if ( feedback->isCanceled() )
134  {
135  break;
136  }
137 
138  if ( !inFeatureA.hasGeometry() )
139  {
140  sink->addFeature( inFeatureA, QgsFeatureSink::FastInsert );
141  continue;
142  }
143 
144  QgsGeometry inGeom = inFeatureA.geometry();
145  outFeat.setAttributes( inFeatureA.attributes() );
146 
147  QVector< QgsGeometry > inGeoms = inGeom.asGeometryCollection();
148 
149  const QgsFeatureIds lines = spatialIndex.intersects( inGeom.boundingBox() ).toSet();
150  if ( !lines.empty() ) // has intersection of bounding boxes
151  {
152  QVector< QgsGeometry > splittingLines;
153 
154  // use prepared geometries for faster intersection tests
155  std::unique_ptr< QgsGeometryEngine > engine;
156 
157  for ( QgsFeatureId line : lines )
158  {
159  // check if trying to self-intersect
160  if ( sameLayer && inFeatureA.id() == line )
161  continue;
162 
163  QgsGeometry splitGeom = splitGeoms.value( line );
164  if ( !engine )
165  {
166  engine.reset( QgsGeometry::createGeometryEngine( inGeom.constGet() ) );
167  engine->prepareGeometry();
168  }
169 
170  if ( engine->intersects( splitGeom.constGet() ) )
171  {
172  QVector< QgsGeometry > splitGeomParts = splitGeom.asGeometryCollection();
173  splittingLines.append( splitGeomParts );
174  }
175  }
176 
177  if ( !splittingLines.empty() )
178  {
179  for ( const QgsGeometry &splitGeom : qgis::as_const( splittingLines ) )
180  {
181  QVector<QgsPointXY> splitterPList;
182  QVector< QgsGeometry > outGeoms;
183 
184  // use prepared geometries for faster intersection tests
185  std::unique_ptr< QgsGeometryEngine > splitGeomEngine( QgsGeometry::createGeometryEngine( splitGeom.constGet() ) );
186  splitGeomEngine->prepareGeometry();
187  while ( !inGeoms.empty() )
188  {
189  if ( feedback->isCanceled() )
190  {
191  break;
192  }
193 
194  QgsGeometry inGeom = inGeoms.takeFirst();
195  if ( inGeom.isNull() )
196  continue;
197 
198  if ( splitGeomEngine->intersects( inGeom.constGet() ) )
199  {
200  QgsGeometry before = inGeom;
201  if ( splitterPList.empty() )
202  {
203  const QgsCoordinateSequence sequence = splitGeom.constGet()->coordinateSequence();
204  for ( const QgsRingSequence &part : sequence )
205  {
206  for ( const QgsPointSequence &ring : part )
207  {
208  for ( const QgsPoint &pt : ring )
209  {
210  splitterPList << QgsPointXY( pt );
211  }
212  }
213  }
214  }
215 
216  QVector< QgsGeometry > newGeometries;
217  QVector<QgsPointXY> topologyTestPoints;
218  QgsGeometry::OperationResult result = inGeom.splitGeometry( splitterPList, newGeometries, false, topologyTestPoints );
219 
220  // splitGeometry: If there are several intersections
221  // between geometry and splitLine, only the first one is considered.
222  if ( result == QgsGeometry::Success ) // split occurred
223  {
224  if ( inGeom.isGeosEqual( before ) )
225  {
226  // bug in splitGeometry: sometimes it returns 0 but
227  // the geometry is unchanged
228  outGeoms.append( inGeom );
229  }
230  else
231  {
232  inGeoms.append( inGeom );
233  inGeoms.append( newGeometries );
234  }
235  }
236  else
237  {
238  outGeoms.append( inGeom );
239  }
240  }
241  else
242  {
243  outGeoms.append( inGeom );
244  }
245 
246  }
247  inGeoms = outGeoms;
248  }
249  }
250  }
251 
252  QVector< QgsGeometry > parts;
253  for ( const QgsGeometry &aGeom : qgis::as_const( inGeoms ) )
254  {
255  if ( feedback->isCanceled() )
256  {
257  break;
258  }
259 
260  bool passed = true;
261  if ( QgsWkbTypes::geometryType( aGeom.wkbType() ) == QgsWkbTypes::LineGeometry )
262  {
263  int numPoints = aGeom.constGet()->nCoordinates();
264 
265  if ( numPoints <= 2 )
266  {
267  if ( numPoints == 2 )
268  passed = !static_cast< const QgsCurve * >( aGeom.constGet() )->isClosed(); // tests if vertex 0 = vertex 1
269  else
270  passed = false; // sometimes splitting results in lines of zero length
271  }
272  }
273 
274  if ( passed )
275  parts.append( aGeom );
276  }
277 
278  for ( const QgsGeometry &g : parts )
279  {
280  outFeat.setGeometry( g );
281  sink->addFeature( outFeat, QgsFeatureSink::FastInsert );
282  }
283 
284  feedback->setProgress( i * step );
285  }
286 
287  QVariantMap outputs;
288  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
289  return outputs;
290 }
291 
292 
293 
295 
296 
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature&#39;s geometries.
QgsFeatureId id
Definition: qgsfeature.h:64
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...
Base class for all map layer types.
Definition: qgsmaplayer.h:78
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
static Type multiType(Type type)
Returns the multi type for a WKB type.
Definition: qgswkbtypes.h:299
Base class for providing feedback from a processing algorithm.
QVector< QgsRingSequence > QgsCoordinateSequence
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
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
virtual Flags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users...
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:111
void setAttributes(const QgsAttributes &attrs)
Sets the feature&#39;s attributes.
Definition: qgsfeature.cpp:127
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
OperationResult
Success or failure of a geometry operation.
Definition: qgsgeometry.h:123
A feature sink output for processing algorithms.
bool isGeosEqual(const QgsGeometry &) const
Compares the geometry with another geometry using GEOS.
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
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:666
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
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
QgsGeometry geometry
Definition: qgsfeature.h:67
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=nullptr) override
Adds a feature to the index.
bool nextFeature(QgsFeature &f)
Operation succeeded.
Definition: qgsgeometry.h:125
Represents a vector layer which manages a vector based data sets.
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:65