QGIS API Documentation  3.20.0-Odense (decaadbb31)
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  QgsFeatureRequest request;
106  request.setNoAttributes();
107  request.setDestinationCrs( source->sourceCrs(), context.transformContext() );
108 
109  QgsFeatureIterator splitLines = linesSource->getFeatures( request );
110  QgsFeature aSplitFeature;
111 
112  const QgsSpatialIndex splitLinesIndex( splitLines, feedback, QgsSpatialIndex::FlagStoreFeatureGeometries );
113 
114  QgsFeature outFeat;
115  QgsFeatureIterator features = source->getFeatures();
116 
117  double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
118  int i = 0;
119  QgsFeature inFeatureA;
120  while ( features.nextFeature( inFeatureA ) )
121  {
122  i++;
123  if ( feedback->isCanceled() )
124  {
125  break;
126  }
127 
128  if ( !inFeatureA.hasGeometry() )
129  {
130  sink->addFeature( inFeatureA, QgsFeatureSink::FastInsert );
131  continue;
132  }
133 
134  const QgsGeometry originalGeometry = inFeatureA.geometry();
135  outFeat.setAttributes( inFeatureA.attributes() );
136 
137  QVector< QgsGeometry > inGeoms = originalGeometry.asGeometryCollection();
138 
139  const QgsFeatureIds splitLineCandidates = qgis::listToSet( splitLinesIndex.intersects( originalGeometry.boundingBox() ) );
140  if ( !splitLineCandidates.empty() ) // has intersection of bounding boxes
141  {
142  QVector< QgsGeometry > splittingLines;
143 
144  // use prepared geometries for faster intersection tests
145  std::unique_ptr< QgsGeometryEngine > originalGeometryEngine;
146 
147  for ( QgsFeatureId splitLineCandidateId : splitLineCandidates )
148  {
149  // check if trying to self-intersect
150  if ( sameLayer && inFeatureA.id() == splitLineCandidateId )
151  continue;
152 
153  const QgsGeometry splitLineCandidate = splitLinesIndex.geometry( splitLineCandidateId );
154  if ( !originalGeometryEngine )
155  {
156  originalGeometryEngine.reset( QgsGeometry::createGeometryEngine( originalGeometry.constGet() ) );
157  originalGeometryEngine->prepareGeometry();
158  }
159 
160  if ( originalGeometryEngine->intersects( splitLineCandidate.constGet() ) )
161  {
162  QVector< QgsGeometry > splitGeomParts = splitLineCandidate.asGeometryCollection();
163  splittingLines.append( splitGeomParts );
164  }
165  }
166 
167  if ( !splittingLines.empty() )
168  {
169  for ( const QgsGeometry &splitGeom : std::as_const( splittingLines ) )
170  {
171  QgsPointSequence splitterPList;
172  QVector< QgsGeometry > outGeoms;
173 
174  // use prepared geometries for faster intersection tests
175  std::unique_ptr< QgsGeometryEngine > splitGeomEngine( QgsGeometry::createGeometryEngine( splitGeom.constGet() ) );
176  splitGeomEngine->prepareGeometry();
177  while ( !inGeoms.empty() )
178  {
179  if ( feedback->isCanceled() )
180  {
181  break;
182  }
183 
184  QgsGeometry inGeom = inGeoms.takeFirst();
185  if ( inGeom.isNull() )
186  continue;
187 
188  if ( splitGeomEngine->intersects( inGeom.constGet() ) )
189  {
190  QgsGeometry before = inGeom;
191  if ( splitterPList.empty() )
192  {
193  const QgsCoordinateSequence sequence = splitGeom.constGet()->coordinateSequence();
194  for ( const QgsRingSequence &part : sequence )
195  {
196  for ( const QgsPointSequence &ring : part )
197  {
198  for ( const QgsPoint &pt : ring )
199  {
200  splitterPList << pt;
201  }
202  }
203  }
204  }
205 
206  QVector< QgsGeometry > newGeometries;
207  QgsPointSequence topologyTestPoints;
208  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  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  if ( inGeom.hausdorffDistance( before ) < 1e-12 )
218  {
219  // effectively no change!!
220  outGeoms.append( inGeom );
221  }
222  else
223  {
224  inGeoms.append( inGeom );
225  inGeoms.append( newGeometries );
226  }
227  }
228  else
229  {
230  outGeoms.append( inGeom );
231  }
232  }
233  else
234  {
235  outGeoms.append( inGeom );
236  }
237 
238  }
239  inGeoms = outGeoms;
240  }
241  }
242  }
243 
244  QVector< QgsGeometry > parts;
245  for ( const QgsGeometry &aGeom : std::as_const( inGeoms ) )
246  {
247  if ( feedback->isCanceled() )
248  {
249  break;
250  }
251 
252  bool passed = true;
253  if ( QgsWkbTypes::geometryType( aGeom.wkbType() ) == QgsWkbTypes::LineGeometry )
254  {
255  int numPoints = aGeom.constGet()->nCoordinates();
256 
257  if ( numPoints <= 2 )
258  {
259  if ( numPoints == 2 )
260  passed = !static_cast< const QgsCurve * >( aGeom.constGet() )->isClosed(); // tests if vertex 0 = vertex 1
261  else
262  passed = false; // sometimes splitting results in lines of zero length
263  }
264  }
265 
266  if ( passed )
267  parts.append( aGeom );
268  }
269 
270  for ( const QgsGeometry &g : parts )
271  {
272  outFeat.setGeometry( g );
273  sink->addFeature( outFeat, QgsFeatureSink::FastInsert );
274  }
275 
276  feedback->setProgress( i * step );
277  }
278 
279  QVariantMap outputs;
280  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
281  return outputs;
282 }
283 
284 
285 
287 
288 
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature's geometries.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
@ RegeneratePrimaryKey
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsAttributes attributes
Definition: qgsfeature.h:65
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:135
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:205
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:145
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
Q_GADGET bool isNull
Definition: qgsgeometry.h:126
Q_DECL_DEPRECATED OperationResult splitGeometry(const QVector< QgsPointXY > &splitLine, QVector< QgsGeometry > &newGeometries, bool topological, QVector< QgsPointXY > &topologyTestPoints, bool splitFeature=true)
Splits this geometry according to a given line.
OperationResult
Success or failure of a geometry operation.
Definition: qgsgeometry.h:136
@ Success
Operation succeeded.
Definition: qgsgeometry.h:137
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Base class for all map layer types.
Definition: qgsmaplayer.h:70
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
virtual Flags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
@ FlagSupportsInPlaceEdits
Algorithm supports in-place editing.
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:83
Base class for providing feedback from a processing algorithm.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
@ TypeVectorLine
Vector line layers.
Definition: qgsprocessing.h:50
@ TypeVectorPolygon
Vector polygon layers.
Definition: qgsprocessing.h:51
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
static GeometryType geometryType(Type type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:938
static Type multiType(Type type) SIP_HOLDGIL
Returns the multi type for a WKB type.
Definition: qgswkbtypes.h:302
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28