Skip to content

Commit 1942854

Browse files
committed
[processing] Port Explode Lines to c++
Aside from the performance benefits, the Python version of this algorithm occasionally fails on Travis with odd errors. Hopefully by porting to c++ it will fix these, or at least give useful debug information in the event of a fail. Also add support for curved input geometries.
1 parent fa051d5 commit 1942854

14 files changed

+279
-106
lines changed

python/plugins/processing/algs/help/qgis.yaml

-3
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,6 @@ qgis:eliminateselectedpolygons: >
157157
This algorithm combines selected polygons of the input layer with certain adjacent polygons by erasing their common boundary. The adjacent polygon can be either the one with the largest or smallest area or the one sharing the largest common boundary with the polygon to be eliminated. The selected features will always be eliminated whether the option "Use only selected features" is set or not.
158158
Eliminate is normally used to get rid of sliver polygons, i.e. tiny polygons that are a result of polygon intersection processes where boundaries of the inputs are similar but not identical.
159159

160-
qgis:explodelines: >
161-
This algorithm takes a lines layer and creates a new one in which each line is replaced by a set of lines representing the segments in the original line. Each line in the resulting layer contains only a start and an end point, with no intermediate nodes between them.
162-
163160
qgis:exportaddgeometrycolumns: >
164161
This algorithm computes geometric properties of the features in a vector layer. It generates a new vector layer with the same content as the input one, but with additional attributes in its attributes table, containing geometric measurements.
165162

python/plugins/processing/algs/qgis/Explode.py

-99
This file was deleted.

python/plugins/processing/algs/qgis/QgisAlgorithmProvider.py

-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@
6060
from .Difference import Difference
6161
from .EliminateSelection import EliminateSelection
6262
from .ExecuteSQL import ExecuteSQL
63-
from .Explode import Explode
6463
from .ExportGeometryInfo import ExportGeometryInfo
6564
from .ExtendLines import ExtendLines
6665
from .ExtentFromLayer import ExtentFromLayer
@@ -180,7 +179,6 @@ def getAlgs(self):
180179
Difference(),
181180
EliminateSelection(),
182181
ExecuteSQL(),
183-
Explode(),
184182
ExportGeometryInfo(),
185183
ExtendLines(),
186184
ExtentFromLayer(),
Binary file not shown.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]
Binary file not shown.
Binary file not shown.

python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml

+13-2
Original file line numberDiff line numberDiff line change
@@ -3477,7 +3477,7 @@ tests:
34773477
name: expected/voronoi_buffer.gml
34783478
type: vector
34793479

3480-
- algorithm: qgis:explodelines
3480+
- algorithm: native:explodelines
34813481
name: Explode lines
34823482
params:
34833483
INPUT:
@@ -3491,7 +3491,7 @@ tests:
34913491
fields:
34923492
fid: skip
34933493

3494-
- algorithm: qgis:explodelines
3494+
- algorithm: native:explodelines
34953495
name: Explode multilines
34963496
params:
34973497
INPUT:
@@ -3505,6 +3505,17 @@ tests:
35053505
fields:
35063506
fid: skip
35073507

3508+
- algorithm: native:explodelines
3509+
name: Explode compound curves
3510+
params:
3511+
INPUT:
3512+
name: custom/circular_strings.gpkg|layername=circular_strings_with_line
3513+
type: vector
3514+
results:
3515+
OUTPUT:
3516+
name: expected/explode_compound_curve.shp
3517+
type: vector
3518+
35083519
- algorithm: qgis:findprojection
35093520
name: Find projection
35103521
params:

src/analysis/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ SET(QGIS_ANALYSIS_SRCS
3030
processing/qgsalgorithmdissolve.cpp
3131
processing/qgsalgorithmdropgeometry.cpp
3232
processing/qgsalgorithmdropmzvalues.cpp
33+
processing/qgsalgorithmexplode.cpp
3334
processing/qgsalgorithmextenttolayer.cpp
3435
processing/qgsalgorithmextractbyattribute.cpp
3536
processing/qgsalgorithmextractbyexpression.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/***************************************************************************
2+
qgsalgorithmexplode.cpp
3+
---------------------
4+
begin : April 2018
5+
copyright : (C) 2018 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+
18+
#include "qgsalgorithmexplode.h"
19+
#include "qgscurve.h"
20+
#include "qgslinestring.h"
21+
#include "qgscircularstring.h"
22+
#include "qgscompoundcurve.h"
23+
#include "qgsgeometrycollection.h"
24+
25+
///@cond PRIVATE
26+
27+
QString QgsExplodeAlgorithm::name() const
28+
{
29+
return QStringLiteral( "explodelines" );
30+
}
31+
32+
QString QgsExplodeAlgorithm::displayName() const
33+
{
34+
return QObject::tr( "Explode lines" );
35+
}
36+
37+
QStringList QgsExplodeAlgorithm::tags() const
38+
{
39+
return QObject::tr( "segments,parts" ).split( ',' );
40+
}
41+
42+
QString QgsExplodeAlgorithm::group() const
43+
{
44+
return QObject::tr( "Vector geometry" );
45+
}
46+
47+
QString QgsExplodeAlgorithm::groupId() const
48+
{
49+
return QStringLiteral( "vectorgeometry" );
50+
}
51+
52+
QString QgsExplodeAlgorithm::shortHelpString() const
53+
{
54+
return QObject::tr( "This algorithm takes a lines layer and creates a new one in which each line is replaced by a set of "
55+
"lines representing the segments in the original line. Each line in the resulting layer contains only a "
56+
"start and an end point, with no intermediate nodes between them.\n\n"
57+
"If the input layer consists of CircularStrings or CompoundCurves, the output layer will be of the "
58+
"same type and contain only single curve segments." );
59+
}
60+
61+
QList<int> QgsExplodeAlgorithm::inputLayerTypes() const
62+
{
63+
return QList<int>() << QgsProcessing::TypeVectorLine;
64+
}
65+
66+
QgsProcessing::SourceType QgsExplodeAlgorithm::outputLayerType() const
67+
{
68+
return QgsProcessing::TypeVectorLine;
69+
}
70+
71+
QgsExplodeAlgorithm *QgsExplodeAlgorithm::createInstance() const
72+
{
73+
return new QgsExplodeAlgorithm();
74+
}
75+
76+
QString QgsExplodeAlgorithm::outputName() const
77+
{
78+
return QObject::tr( "Exploded" );
79+
}
80+
81+
QgsWkbTypes::Type QgsExplodeAlgorithm::outputWkbType( QgsWkbTypes::Type inputWkbType ) const
82+
{
83+
return QgsWkbTypes::singleType( inputWkbType );
84+
}
85+
86+
QgsFeatureList QgsExplodeAlgorithm::processFeature( const QgsFeature &f, QgsProcessingContext &, QgsProcessingFeedback * )
87+
{
88+
if ( !f.hasGeometry() )
89+
{
90+
return QgsFeatureList() << f;
91+
}
92+
else
93+
{
94+
const std::vector<QgsGeometry> parts = extractAsParts( f.geometry() );
95+
QgsFeature outputFeature;
96+
QgsFeatureList features;
97+
features.reserve( parts.size() );
98+
for ( const QgsGeometry &part : parts )
99+
{
100+
outputFeature.setAttributes( f.attributes() );
101+
outputFeature.setGeometry( part );
102+
features << outputFeature;
103+
}
104+
return features;
105+
}
106+
}
107+
108+
std::vector<QgsGeometry> QgsExplodeAlgorithm::extractAsParts( const QgsGeometry &geometry ) const
109+
{
110+
if ( geometry.isMultipart() )
111+
{
112+
std::vector<QgsGeometry> parts;
113+
const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geometry.constGet() );
114+
for ( int part = 0; part < collection->numGeometries(); ++part )
115+
{
116+
std::vector<QgsGeometry> segments = curveAsSingleSegments( qgsgeometry_cast< const QgsCurve * >( collection->geometryN( part ) ) );
117+
parts.reserve( parts.size() + segments.size() );
118+
std::move( std::begin( segments ), std::end( segments ), std::back_inserter( parts ) );
119+
}
120+
return parts;
121+
}
122+
else
123+
{
124+
return curveAsSingleSegments( qgsgeometry_cast< const QgsCurve * >( geometry.constGet() ) );
125+
}
126+
}
127+
128+
std::vector<QgsGeometry> QgsExplodeAlgorithm::curveAsSingleSegments( const QgsCurve *curve, bool useCompoundCurves ) const
129+
{
130+
std::vector<QgsGeometry> parts;
131+
switch ( QgsWkbTypes::flatType( curve->wkbType() ) )
132+
{
133+
case QgsWkbTypes::LineString:
134+
{
135+
const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( curve );
136+
for ( int i = 0; i < line->numPoints() - 1; ++i )
137+
{
138+
QgsPoint ptA = line->pointN( i );
139+
QgsPoint ptB = line->pointN( i + 1 );
140+
std::unique_ptr< QgsLineString > ls = qgis::make_unique< QgsLineString >( QVector< QgsPoint >() << ptA << ptB );
141+
if ( !useCompoundCurves )
142+
{
143+
parts.emplace_back( QgsGeometry( std::move( ls ) ) );
144+
}
145+
else
146+
{
147+
std::unique_ptr< QgsCompoundCurve > cc = qgis::make_unique< QgsCompoundCurve >();
148+
cc->addCurve( ls.release() );
149+
parts.emplace_back( QgsGeometry( std::move( cc ) ) );
150+
}
151+
}
152+
break;
153+
}
154+
155+
case QgsWkbTypes::CircularString:
156+
{
157+
const QgsCircularString *string = qgsgeometry_cast< const QgsCircularString * >( curve );
158+
for ( int i = 0; i < string->numPoints() - 2; i += 2 )
159+
{
160+
QgsPoint ptA = string->pointN( i );
161+
QgsPoint ptB = string->pointN( i + 1 );
162+
QgsPoint ptC = string->pointN( i + 2 );
163+
std::unique_ptr< QgsCircularString > cs = qgis::make_unique< QgsCircularString >();
164+
cs->setPoints( QgsPointSequence() << ptA << ptB << ptC );
165+
if ( !useCompoundCurves )
166+
{
167+
parts.emplace_back( QgsGeometry( std::move( cs ) ) );
168+
}
169+
else
170+
{
171+
std::unique_ptr< QgsCompoundCurve > cc = qgis::make_unique< QgsCompoundCurve >();
172+
cc->addCurve( cs.release() );
173+
parts.emplace_back( QgsGeometry( std::move( cc ) ) );
174+
}
175+
}
176+
break;
177+
}
178+
179+
case QgsWkbTypes::CompoundCurve:
180+
{
181+
const QgsCompoundCurve *compoundCurve = qgsgeometry_cast< QgsCompoundCurve * >( curve );
182+
for ( int i = 0; i < compoundCurve->nCurves(); ++i )
183+
{
184+
std::vector<QgsGeometry> segments = curveAsSingleSegments( compoundCurve->curveAt( i ), true );
185+
parts.reserve( parts.size() + segments.size() );
186+
std::move( std::begin( segments ), std::end( segments ), std::back_inserter( parts ) );
187+
}
188+
break;
189+
}
190+
191+
default:
192+
break;
193+
194+
}
195+
return parts;
196+
}
197+
198+
///@endcond
199+
200+
201+

0 commit comments

Comments
 (0)