QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
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
23QString QgsSplitWithLinesAlgorithm::name() const
24{
25 return QStringLiteral( "splitwithlines" );
26}
27
28QString QgsSplitWithLinesAlgorithm::displayName() const
29{
30 return QObject::tr( "Split with lines" );
31}
32
33QStringList QgsSplitWithLinesAlgorithm::tags() const
34{
35 return QObject::tr( "split,cut,lines" ).split( ',' );
36}
37
38QString QgsSplitWithLinesAlgorithm::group() const
39{
40 return QObject::tr( "Vector overlay" );
41}
42
43QString QgsSplitWithLinesAlgorithm::groupId() const
44{
45 return QStringLiteral( "vectoroverlay" );
46}
47
48void 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 << QgsProcessing::TypeVectorPolygon ) );
54 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Split" ) ) );
55}
56
57QString QgsSplitWithLinesAlgorithm::shortHelpString() const
58{
59 return QObject::tr( "This algorithm splits the lines or polygons in one layer using the lines or polygon rings in another layer to define the breaking points. "
60 "Intersection between geometries in both layers are considered as split points." );
61}
62
63QgsSplitWithLinesAlgorithm *QgsSplitWithLinesAlgorithm::createInstance() const
64{
65 return new QgsSplitWithLinesAlgorithm();
66}
67
68QgsProcessingAlgorithm::Flags QgsSplitWithLinesAlgorithm::flags() const
69{
72 return f;
73}
74
75bool QgsSplitWithLinesAlgorithm::supportInPlaceEdit( const QgsMapLayer *l ) const
76{
77 const QgsVectorLayer *layer = qobject_cast< const QgsVectorLayer * >( l );
78 if ( !layer )
79 return false;
80
81 if ( layer->geometryType() != Qgis::GeometryType::Line && layer->geometryType() != Qgis::GeometryType::Polygon )
82 return false;
83
84 return true;
85}
86
87QVariantMap 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 splitFeatures = linesSource->getFeatures( request );
110 QgsFeature aSplitFeature;
111
112 const QgsSpatialIndex splitFeaturesIndex( splitFeatures, 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 if ( !sink->addFeature( inFeatureA, QgsFeatureSink::FastInsert ) )
131 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
132 continue;
133 }
134
135 const QgsGeometry originalGeometry = inFeatureA.geometry();
136 outFeat.setAttributes( inFeatureA.attributes() );
137
138 QVector< QgsGeometry > inGeoms = originalGeometry.asGeometryCollection();
139
140 const QgsFeatureIds splitFeatureCandidates = qgis::listToSet( splitFeaturesIndex.intersects( originalGeometry.boundingBox() ) );
141 if ( !splitFeatureCandidates.empty() ) // has intersection of bounding boxes
142 {
143 QVector< QgsGeometry > splittingLines;
144
145 // use prepared geometries for faster intersection tests
146 std::unique_ptr< QgsGeometryEngine > originalGeometryEngine;
147
148 for ( QgsFeatureId splitFeatureCandidateId : splitFeatureCandidates )
149 {
150 // check if trying to self-intersect
151 if ( sameLayer && inFeatureA.id() == splitFeatureCandidateId )
152 continue;
153
154 const QgsGeometry splitFeatureCandidate = splitFeaturesIndex.geometry( splitFeatureCandidateId );
155 if ( !originalGeometryEngine )
156 {
157 originalGeometryEngine.reset( QgsGeometry::createGeometryEngine( originalGeometry.constGet() ) );
158 originalGeometryEngine->prepareGeometry();
159 }
160
161 if ( originalGeometryEngine->intersects( splitFeatureCandidate.constGet() ) )
162 {
163
164 QVector< QgsGeometry > splitGeomParts = splitFeatureCandidate.convertToType( Qgis::GeometryType::Line, true ).asGeometryCollection();
165 splittingLines.append( splitGeomParts );
166 }
167 }
168
169 if ( !splittingLines.empty() )
170 {
171 for ( const QgsGeometry &splitGeom : std::as_const( splittingLines ) )
172 {
173 QgsPointSequence splitterPList;
174 QVector< QgsGeometry > outGeoms;
175
176 // use prepared geometries for faster intersection tests
177 std::unique_ptr< QgsGeometryEngine > splitGeomEngine( QgsGeometry::createGeometryEngine( splitGeom.constGet() ) );
178 splitGeomEngine->prepareGeometry();
179 while ( !inGeoms.empty() )
180 {
181 if ( feedback->isCanceled() )
182 {
183 break;
184 }
185
186 QgsGeometry inGeom = inGeoms.takeFirst();
187 if ( inGeom.isNull() )
188 continue;
189
190 if ( splitGeomEngine->intersects( inGeom.constGet() ) )
191 {
192 QgsGeometry before = inGeom;
193 if ( splitterPList.empty() )
194 {
195 const QgsCoordinateSequence sequence = splitGeom.constGet()->coordinateSequence();
196 for ( const QgsRingSequence &part : sequence )
197 {
198 for ( const QgsPointSequence &ring : part )
199 {
200 for ( const QgsPoint &pt : ring )
201 {
202 splitterPList << pt;
203 }
204 }
205 }
206 }
207
208 QVector< QgsGeometry > newGeometries;
209 QgsPointSequence topologyTestPoints;
210 Qgis::GeometryOperationResult result = inGeom.splitGeometry( splitterPList, newGeometries, false, topologyTestPoints, true );
211
212 // splitGeometry: If there are several intersections
213 // between geometry and splitLine, only the first one is considered.
215 {
216 // sometimes the resultant geometry has changed from the input, but only because of numerical precision issues.
217 // and is effectively indistinguishable from the input. By testing the Hausdorff distance is less than this threshold
218 // we are checking that the maximum "change" between the result and the input is actually significant enough to be meaningful...
219 if ( inGeom.hausdorffDistance( before ) < 1e-12 )
220 {
221 // effectively no change!!
222 outGeoms.append( inGeom );
223 }
224 else
225 {
226 outGeoms.append( inGeom );
227 outGeoms.append( newGeometries );
228 }
229 }
230 else
231 {
232 outGeoms.append( inGeom );
233 }
234 }
235 else
236 {
237 outGeoms.append( inGeom );
238 }
239
240 }
241 inGeoms = outGeoms;
242 }
243 }
244 }
245
246 QVector< QgsGeometry > parts;
247 for ( const QgsGeometry &aGeom : std::as_const( inGeoms ) )
248 {
249 if ( feedback->isCanceled() )
250 {
251 break;
252 }
253
254 bool passed = true;
255 if ( QgsWkbTypes::geometryType( aGeom.wkbType() ) == Qgis::GeometryType::Line )
256 {
257 int numPoints = aGeom.constGet()->nCoordinates();
258
259 if ( numPoints <= 2 )
260 {
261 if ( numPoints == 2 )
262 passed = !static_cast< const QgsCurve * >( aGeom.constGet() )->isClosed(); // tests if vertex 0 = vertex 1
263 else
264 passed = false; // sometimes splitting results in lines of zero length
265 }
266 }
267
268 if ( passed )
269 parts.append( aGeom );
270 }
271
272 for ( const QgsGeometry &g : parts )
273 {
274 outFeat.setGeometry( g );
275 if ( !sink->addFeature( outFeat, QgsFeatureSink::FastInsert ) )
276 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
277 }
278
279 feedback->setProgress( i * step );
280 }
281
282 QVariantMap outputs;
283 outputs.insert( QStringLiteral( "OUTPUT" ), dest );
284 return outputs;
285}
286
287
288
290
291
GeometryOperationResult
Success or failure of a geometry operation.
Definition: qgis.h:1266
@ Success
Operation succeeded.
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:160
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:233
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:170
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:164
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsGeometry convertToType(Qgis::GeometryType destType, bool destMultipart=false) const
Try to convert the geometry to the requested type.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
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.
Q_DECL_DEPRECATED Qgis::GeometryOperationResult splitGeometry(const QVector< QgsPointXY > &splitLine, QVector< QgsGeometry > &newGeometries, bool topological, QVector< QgsPointXY > &topologyTestPoints, bool splitFeature=true)
Splits this geometry according to a given line.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
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:51
@ TypeVectorPolygon
Vector polygon layers.
Definition: qgsprocessing.h:52
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 Qgis::GeometryType geometryType() const
Returns point, line or polygon.
static Qgis::GeometryType geometryType(Qgis::WkbType type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:865
static Qgis::WkbType multiType(Qgis::WkbType type) SIP_HOLDGIL
Returns the multi type for a WKB type.
Definition: qgswkbtypes.h:201
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