QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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
20#include "qgsgeometryengine.h"
21#include "qgsspatialindex.h"
22#include "qgsvectorlayer.h"
23
25
26QString QgsSplitWithLinesAlgorithm::name() const
27{
28 return QStringLiteral( "splitwithlines" );
29}
30
31QString QgsSplitWithLinesAlgorithm::displayName() const
32{
33 return QObject::tr( "Split with lines" );
34}
35
36QStringList QgsSplitWithLinesAlgorithm::tags() const
37{
38 return QObject::tr( "split,cut,lines" ).split( ',' );
39}
40
41QString QgsSplitWithLinesAlgorithm::group() const
42{
43 return QObject::tr( "Vector overlay" );
44}
45
46QString QgsSplitWithLinesAlgorithm::groupId() const
47{
48 return QStringLiteral( "vectoroverlay" );
49}
50
51void QgsSplitWithLinesAlgorithm::initAlgorithm( const QVariantMap & )
52{
53 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) ) );
54 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "LINES" ), QObject::tr( "Split layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) ) );
55 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Split" ) ) );
56}
57
58QString QgsSplitWithLinesAlgorithm::shortHelpString() const
59{
60 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. "
61 "Intersection between geometries in both layers are considered as split points." );
62}
63
64QString QgsSplitWithLinesAlgorithm::shortDescription() const
65{
66 return QObject::tr( "Splits the lines or polygons in one layer using the lines or polygon rings in another layer to define the breaking points." );
67}
68
69Qgis::ProcessingAlgorithmDocumentationFlags QgsSplitWithLinesAlgorithm::documentationFlags() const
70{
72}
73
74QgsSplitWithLinesAlgorithm *QgsSplitWithLinesAlgorithm::createInstance() const
75{
76 return new QgsSplitWithLinesAlgorithm();
77}
78
79Qgis::ProcessingAlgorithmFlags QgsSplitWithLinesAlgorithm::flags() const
80{
83 return f;
84}
85
86bool QgsSplitWithLinesAlgorithm::supportInPlaceEdit( const QgsMapLayer *l ) const
87{
88 const QgsVectorLayer *layer = qobject_cast<const QgsVectorLayer *>( l );
89 if ( !layer )
90 return false;
91
93 return false;
94
95 return true;
96}
97
98QVariantMap QgsSplitWithLinesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
99{
100 std::unique_ptr<QgsFeatureSource> source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
101 if ( !source )
102 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
103
104 std::unique_ptr<QgsFeatureSource> linesSource( parameterAsSource( parameters, QStringLiteral( "LINES" ), context ) );
105 if ( !linesSource )
106 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "LINES" ) ) );
107
108 bool sameLayer = parameters.value( QStringLiteral( "INPUT" ) ) == parameters.value( QStringLiteral( "LINES" ) );
109
110 QString dest;
111 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(), QgsWkbTypes::multiType( source->wkbType() ), source->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) );
112 if ( !sink )
113 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
114
115 QgsFeatureRequest request;
116 request.setNoAttributes();
117 request.setDestinationCrs( source->sourceCrs(), context.transformContext() );
118
119 QgsFeatureIterator splitFeatures = linesSource->getFeatures( request );
120 QgsFeature aSplitFeature;
121
122 const QgsSpatialIndex splitFeaturesIndex( splitFeatures, feedback, QgsSpatialIndex::FlagStoreFeatureGeometries );
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 if ( !sink->addFeature( inFeatureA, QgsFeatureSink::FastInsert ) )
141 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
142 continue;
143 }
144
145 const QgsGeometry originalGeometry = inFeatureA.geometry();
146 outFeat.setAttributes( inFeatureA.attributes() );
147
148 QVector<QgsGeometry> inGeoms = originalGeometry.asGeometryCollection();
149
150 const QgsFeatureIds splitFeatureCandidates = qgis::listToSet( splitFeaturesIndex.intersects( originalGeometry.boundingBox() ) );
151 if ( !splitFeatureCandidates.empty() ) // has intersection of bounding boxes
152 {
153 QVector<QgsGeometry> splittingLines;
154
155 // use prepared geometries for faster intersection tests
156 std::unique_ptr<QgsGeometryEngine> originalGeometryEngine;
157
158 for ( QgsFeatureId splitFeatureCandidateId : splitFeatureCandidates )
159 {
160 // check if trying to self-intersect
161 if ( sameLayer && inFeatureA.id() == splitFeatureCandidateId )
162 continue;
163
164 const QgsGeometry splitFeatureCandidate = splitFeaturesIndex.geometry( splitFeatureCandidateId );
165 if ( !originalGeometryEngine )
166 {
167 originalGeometryEngine.reset( QgsGeometry::createGeometryEngine( originalGeometry.constGet() ) );
168 originalGeometryEngine->prepareGeometry();
169 }
170
171 if ( originalGeometryEngine->intersects( splitFeatureCandidate.constGet() ) )
172 {
173 QVector<QgsGeometry> splitGeomParts = splitFeatureCandidate.convertToType( Qgis::GeometryType::Line, true ).asGeometryCollection();
174 splittingLines.append( splitGeomParts );
175 }
176 }
177
178 if ( !splittingLines.empty() )
179 {
180 for ( const QgsGeometry &splitGeom : std::as_const( splittingLines ) )
181 {
182 QgsPointSequence splitterPList;
183 QVector<QgsGeometry> outGeoms;
184
185 // use prepared geometries for faster intersection tests
186 std::unique_ptr<QgsGeometryEngine> splitGeomEngine( QgsGeometry::createGeometryEngine( splitGeom.constGet() ) );
187 splitGeomEngine->prepareGeometry();
188 while ( !inGeoms.empty() )
189 {
190 if ( feedback->isCanceled() )
191 {
192 break;
193 }
194
195 QgsGeometry inGeom = inGeoms.takeFirst();
196 if ( inGeom.isNull() )
197 continue;
198
199 if ( splitGeomEngine->intersects( inGeom.constGet() ) )
200 {
201 QgsGeometry before = inGeom;
202 if ( splitterPList.empty() )
203 {
204 const QgsCoordinateSequence sequence = splitGeom.constGet()->coordinateSequence();
205 for ( const QgsRingSequence &part : sequence )
206 {
207 for ( const QgsPointSequence &ring : part )
208 {
209 for ( const QgsPoint &pt : ring )
210 {
211 splitterPList << pt;
212 }
213 }
214 }
215 }
216
217 QVector<QgsGeometry> newGeometries;
218 QgsPointSequence topologyTestPoints;
219 Qgis::GeometryOperationResult result = inGeom.splitGeometry( splitterPList, newGeometries, false, topologyTestPoints, true );
220
221 // splitGeometry: If there are several intersections
222 // between geometry and splitLine, only the first one is considered.
224 {
225 // sometimes the resultant geometry has changed from the input, but only because of numerical precision issues.
226 // and is effectively indistinguishable from the input. By testing the Hausdorff distance is less than this threshold
227 // we are checking that the maximum "change" between the result and the input is actually significant enough to be meaningful...
228 if ( inGeom.hausdorffDistance( before ) < 1e-12 )
229 {
230 // effectively no change!!
231 outGeoms.append( inGeom );
232 }
233 else
234 {
235 outGeoms.append( inGeom );
236 outGeoms.append( newGeometries );
237 }
238 }
239 else
240 {
241 outGeoms.append( inGeom );
242 }
243 }
244 else
245 {
246 outGeoms.append( inGeom );
247 }
248 }
249 inGeoms = outGeoms;
250 }
251 }
252 }
253
254 QVector<QgsGeometry> parts;
255 for ( const QgsGeometry &aGeom : std::as_const( inGeoms ) )
256 {
257 if ( feedback->isCanceled() )
258 {
259 break;
260 }
261
262 bool passed = true;
263 if ( QgsWkbTypes::geometryType( aGeom.wkbType() ) == Qgis::GeometryType::Line )
264 {
265 int numPoints = aGeom.constGet()->nCoordinates();
266
267 if ( numPoints <= 2 )
268 {
269 if ( numPoints == 2 )
270 passed = !static_cast<const QgsCurve *>( aGeom.constGet() )->isClosed(); // tests if vertex 0 = vertex 1
271 else
272 passed = false; // sometimes splitting results in lines of zero length
273 }
274 }
275
276 if ( passed )
277 parts.append( aGeom );
278 }
279
280 for ( const QgsGeometry &g : parts )
281 {
282 outFeat.setGeometry( g );
283 if ( !sink->addFeature( outFeat, QgsFeatureSink::FastInsert ) )
284 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
285 }
286
287 feedback->setProgress( i * step );
288 }
289
290 sink->finalize();
291
292 QVariantMap outputs;
293 outputs.insert( QStringLiteral( "OUTPUT" ), dest );
294 return outputs;
295}
296
297
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3536
@ VectorLine
Vector line layers.
Definition qgis.h:3535
GeometryOperationResult
Success or failure of a geometry operation.
Definition qgis.h:2042
@ Success
Operation succeeded.
Definition qgis.h:2043
@ Line
Lines.
Definition qgis.h:360
@ Polygon
Polygons.
Definition qgis.h:361
@ RegeneratesPrimaryKey
Algorithm always drops any existing primary keys or FID values and regenerates them in outputs.
Definition qgis.h:3619
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
Definition qgis.h:3609
QFlags< ProcessingAlgorithmDocumentationFlag > ProcessingAlgorithmDocumentationFlags
Flags describing algorithm behavior for documentation purposes.
Definition qgis.h:3630
@ SupportsInPlaceEdits
Algorithm supports in-place editing.
Definition qgis.h:3590
Abstract base class for curved geometry type.
Definition qgscurve.h:36
virtual bool isClosed() const
Returns true if the curve is closed.
Definition qgscurve.cpp:54
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
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:58
QgsAttributes attributes
Definition qgsfeature.h:67
QgsFeatureId id
Definition qgsfeature.h:66
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
A geometry is the spatial representation of a feature.
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.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
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.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
Base class for all map layer types.
Definition qgsmaplayer.h:80
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
virtual Qgis::ProcessingAlgorithmFlags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
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.
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.
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 dataset.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Qgis::WkbType multiType(Qgis::WkbType type)
Returns the multi type for a WKB type.
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features