Skip to content

Commit 4489508

Browse files
authored
Merge pull request #6525 from nyalldawson/segmentize
[FEATURE][processing] Add "segmentize" algorithms
2 parents 9edaf82 + 4232b93 commit 4489508

File tree

13 files changed

+444
-4
lines changed

13 files changed

+444
-4
lines changed

python/core/geometry/qgsgeometry.sip.in

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1423,10 +1423,13 @@ An empty geometry will be returned in the case of errors.
14231423
.. versionadded:: 3.0
14241424
%End
14251425

1426-
void convertToStraightSegment();
1426+
void convertToStraightSegment( double tolerance = M_PI / 180., QgsAbstractGeometry::SegmentationToleranceType toleranceType = QgsAbstractGeometry::MaximumAngle );
14271427
%Docstring
14281428
Converts the geometry to straight line segments, if it is a curved geometry type.
14291429

1430+
:param tolerance: segmentation tolerance
1431+
:param toleranceType: maximum segmentation angle or maximum difference between approximation and curve
1432+
14301433
.. versionadded:: 2.10
14311434

14321435
.. seealso:: :py:func:`requiresConversionToStraightSegments`
Binary file not shown.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ogr:FeatureCollection
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://ogr.maptools.org/ segmentize_by_angle.xsd"
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy>
8+
<gml:Box>
9+
<gml:coord><gml:X>-0.9875563570784498</gml:X><gml:Y>-3.019161406672679</gml:Y></gml:coord>
10+
<gml:coord><gml:X>9.807504202322207</gml:X><gml:Y>4.303079673657821</gml:Y></gml:coord>
11+
</gml:Box>
12+
</gml:boundedBy>
13+
14+
<gml:featureMember>
15+
<ogr:segmentize_by_angle fid="1">
16+
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>-0.98755635707845,-0.974075743913436 -0.696095751086827,0.344850591369106 -0.005888714481605,1.50594068665349 1.01349196457716,2.39215679681429 2.25929283346249,2.91416844211677 3.60593731102726,3.0193569100738 4.91768379222303,2.6971192154796 6.06230838593327,1.97993688007214</gml:coordinates></gml:LineString></ogr:geometryProperty>
17+
<ogr:id>1</ogr:id>
18+
</ogr:segmentize_by_angle>
19+
</gml:featureMember>
20+
<gml:featureMember>
21+
<ogr:segmentize_by_angle fid="2">
22+
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>6.91442741208296,-3.01916140667268 6.32475035835957,-2.4284524156428 5.95800750896124,-1.67868190262791 5.8536814658075,-0.850568253126039 6.0230037052269,-0.033264075350454 6.44774542579607,0.68524174270488 7.08218001471432,1.22759669304234 7.85800585857777,1.53541222183353 8.69169952127193,1.57554969254374 9.49350766456267,1.34368800721371 9.74057664392659,1.88754171095694 9.80750420232221,2.48112458938787 9.68773977279365,3.06633943289311 9.39300535646221,3.58590805730082 8.95214822603083,3.98897743369522 8.40831748509891,4.23609695307115 7.81474082890195,4.30307967365782 7.22951485804922,4.18336962947338 6.70991884580703,3.88868349864743</gml:coordinates></gml:LineString></ogr:geometryProperty>
23+
<ogr:id>2</ogr:id>
24+
</ogr:segmentize_by_angle>
25+
</gml:featureMember>
26+
</ogr:FeatureCollection>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
3+
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
4+
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
5+
<xs:complexType name="FeatureCollectionType">
6+
<xs:complexContent>
7+
<xs:extension base="gml:AbstractFeatureCollectionType">
8+
<xs:attribute name="lockId" type="xs:string" use="optional"/>
9+
<xs:attribute name="scope" type="xs:string" use="optional"/>
10+
</xs:extension>
11+
</xs:complexContent>
12+
</xs:complexType>
13+
<xs:element name="segmentize_by_angle" type="ogr:segmentize_by_angle_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="segmentize_by_angle_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="geometryProperty" type="gml:LineStringPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
19+
<xs:element name="id" nillable="true" minOccurs="0" maxOccurs="1">
20+
<xs:simpleType>
21+
<xs:restriction base="xs:integer">
22+
<xs:totalDigits value="10"/>
23+
</xs:restriction>
24+
</xs:simpleType>
25+
</xs:element>
26+
</xs:sequence>
27+
</xs:extension>
28+
</xs:complexContent>
29+
</xs:complexType>
30+
</xs:schema>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ogr:FeatureCollection
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://ogr.maptools.org/ segmentize_by_distance.xsd"
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy>
8+
<gml:Box>
9+
<gml:coord><gml:X>-0.9875563570784498</gml:X><gml:Y>-3.019161406672679</gml:Y></gml:coord>
10+
<gml:coord><gml:X>9.794917283570138</gml:X><gml:Y>4.303894025825194</gml:Y></gml:coord>
11+
</gml:Box>
12+
</gml:boundedBy>
13+
14+
<gml:featureMember>
15+
<ogr:segmentize_by_distance fid="1">
16+
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>-0.98755635707845,-0.974075743913436 -0.212652411335007,1.23725667259743 1.61554353037206,2.70291924637298 3.94247675733233,2.9783261066885 6.06230838593327,1.97993688007214</gml:coordinates></gml:LineString></ogr:geometryProperty>
17+
<ogr:id>1</ogr:id>
18+
</ogr:segmentize_by_distance>
19+
</gml:featureMember>
20+
<gml:featureMember>
21+
<ogr:segmentize_by_distance fid="2">
22+
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>6.91442741208296,-3.01916140667268 5.90638239076692,-1.47558750786245 6.20570477434748,0.34352800709968 7.65518199185988,1.48273428429481 9.49350766456267,1.34368800721371 9.79491728357014,2.63050854973142 9.18843697294715,3.80478981923308 7.96465191909257,4.30389402582519 6.70991884580703,3.88868349864743</gml:coordinates></gml:LineString></ogr:geometryProperty>
23+
<ogr:id>2</ogr:id>
24+
</ogr:segmentize_by_distance>
25+
</gml:featureMember>
26+
</ogr:FeatureCollection>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
3+
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
4+
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
5+
<xs:complexType name="FeatureCollectionType">
6+
<xs:complexContent>
7+
<xs:extension base="gml:AbstractFeatureCollectionType">
8+
<xs:attribute name="lockId" type="xs:string" use="optional"/>
9+
<xs:attribute name="scope" type="xs:string" use="optional"/>
10+
</xs:extension>
11+
</xs:complexContent>
12+
</xs:complexType>
13+
<xs:element name="segmentize_by_distance" type="ogr:segmentize_by_distance_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="segmentize_by_distance_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="geometryProperty" type="gml:LineStringPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
19+
<xs:element name="id" nillable="true" minOccurs="0" maxOccurs="1">
20+
<xs:simpleType>
21+
<xs:restriction base="xs:integer">
22+
<xs:totalDigits value="10"/>
23+
</xs:restriction>
24+
</xs:simpleType>
25+
</xs:element>
26+
</xs:sequence>
27+
</xs:extension>
28+
</xs:complexContent>
29+
</xs:complexType>
30+
</xs:schema>

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4818,3 +4818,27 @@ tests:
48184818
OUTPUT:
48194819
name: expected/multiring_buffer.gml
48204820
type: vector
4821+
4822+
- algorithm: native:segmentizebymaxangle
4823+
name: Segmentize by angle
4824+
params:
4825+
ANGLE: 20.0
4826+
INPUT:
4827+
name: custom/circular_strings.gpkg|layername=circular_strings
4828+
type: vector
4829+
results:
4830+
OUTPUT:
4831+
name: expected/segmentize_by_angle.gml
4832+
type: vector
4833+
4834+
- algorithm: native:segmentizebymaxdistance
4835+
name: Segmentize by distance
4836+
params:
4837+
DISTANCE: 0.2
4838+
INPUT:
4839+
name: custom/circular_strings.gpkg|layername=circular_strings
4840+
type: vector
4841+
results:
4842+
OUTPUT:
4843+
name: expected/segmentize_by_distance.gml
4844+
type: vector

src/analysis/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ SET(QGIS_ANALYSIS_SRCS
5959
processing/qgsalgorithmremovenullgeometry.cpp
6060
processing/qgsalgorithmrenamelayer.cpp
6161
processing/qgsalgorithmsaveselectedfeatures.cpp
62+
processing/qgsalgorithmsegmentize.cpp
6263
processing/qgsalgorithmsimplify.cpp
6364
processing/qgsalgorithmsmooth.cpp
6465
processing/qgsalgorithmsnaptogrid.cpp
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
/***************************************************************************
2+
qgsalgorithmsegmentize.cpp
3+
---------------------
4+
begin : March 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 "qgsalgorithmsegmentize.h"
19+
20+
///@cond PRIVATE
21+
22+
QString QgsSegmentizeByMaximumDistanceAlgorithm::name() const
23+
{
24+
return QStringLiteral( "segmentizebymaxdistance" );
25+
}
26+
27+
QString QgsSegmentizeByMaximumDistanceAlgorithm::displayName() const
28+
{
29+
return QObject::tr( "Segmentize by maximum distance" );
30+
}
31+
32+
QStringList QgsSegmentizeByMaximumDistanceAlgorithm::tags() const
33+
{
34+
return QObject::tr( "straighten,linearize,densify,curves,curved,circular" ).split( ',' );
35+
}
36+
37+
QString QgsSegmentizeByMaximumDistanceAlgorithm::group() const
38+
{
39+
return QObject::tr( "Vector geometry" );
40+
}
41+
42+
QString QgsSegmentizeByMaximumDistanceAlgorithm::groupId() const
43+
{
44+
return QStringLiteral( "vectorgeometry" );
45+
}
46+
47+
QString QgsSegmentizeByMaximumDistanceAlgorithm::outputName() const
48+
{
49+
return QObject::tr( "Segmentized" );
50+
}
51+
52+
QString QgsSegmentizeByMaximumDistanceAlgorithm::shortHelpString() const
53+
{
54+
return QObject::tr( "This algorithm segmentizes a geometry by converting curved sections to linear sections.\n\n"
55+
"The segmentization is performed by specifying the maximum allowed offset distance between the original"
56+
"curve and the segmentized representation.\n\n"
57+
"Non-curved geometries will be retained without change." );
58+
}
59+
60+
QgsSegmentizeByMaximumDistanceAlgorithm *QgsSegmentizeByMaximumDistanceAlgorithm::createInstance() const
61+
{
62+
return new QgsSegmentizeByMaximumDistanceAlgorithm();
63+
}
64+
65+
QList<int> QgsSegmentizeByMaximumDistanceAlgorithm::inputLayerTypes() const
66+
{
67+
return QList<int>() << QgsProcessing::TypeVectorLine << QgsProcessing::TypeVectorPolygon;
68+
}
69+
70+
void QgsSegmentizeByMaximumDistanceAlgorithm::initParameters( const QVariantMap & )
71+
{
72+
std::unique_ptr< QgsProcessingParameterNumber > tolerance = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "DISTANCE" ),
73+
QObject::tr( "Maximum offset distance" ), QgsProcessingParameterNumber::Double,
74+
1.0, false, 0, 10000000.0 );
75+
tolerance->setIsDynamic( true );
76+
tolerance->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "DISTANCE" ), QObject::tr( "Maximum offset distance" ), QgsPropertyDefinition::DoublePositive ) );
77+
tolerance->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
78+
addParameter( tolerance.release() );
79+
}
80+
81+
bool QgsSegmentizeByMaximumDistanceAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
82+
{
83+
mTolerance = parameterAsDouble( parameters, QStringLiteral( "DISTANCE" ), context );
84+
mDynamicTolerance = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "DISTANCE" ) );
85+
if ( mDynamicTolerance )
86+
mToleranceProperty = parameters.value( QStringLiteral( "DISTANCE" ) ).value< QgsProperty >();
87+
88+
return true;
89+
}
90+
91+
QgsFeatureList QgsSegmentizeByMaximumDistanceAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback * )
92+
{
93+
QgsFeature f = feature;
94+
if ( f.hasGeometry() )
95+
{
96+
QgsGeometry geometry = f.geometry();
97+
double tolerance = mTolerance;
98+
if ( mDynamicTolerance )
99+
tolerance = mToleranceProperty.valueAsDouble( context.expressionContext(), tolerance );
100+
geometry.convertToStraightSegment( tolerance, QgsAbstractGeometry::MaximumDifference );
101+
f.setGeometry( geometry );
102+
}
103+
return QgsFeatureList() << f;
104+
}
105+
106+
107+
108+
109+
110+
QString QgsSegmentizeByMaximumAngleAlgorithm::name() const
111+
{
112+
return QStringLiteral( "segmentizebymaxangle" );
113+
}
114+
115+
QString QgsSegmentizeByMaximumAngleAlgorithm::displayName() const
116+
{
117+
return QObject::tr( "Segmentize by maximum angle" );
118+
}
119+
120+
QStringList QgsSegmentizeByMaximumAngleAlgorithm::tags() const
121+
{
122+
return QObject::tr( "straighten,linearize,densify,curves,curved,circular,angle" ).split( ',' );
123+
}
124+
125+
QString QgsSegmentizeByMaximumAngleAlgorithm::group() const
126+
{
127+
return QObject::tr( "Vector geometry" );
128+
}
129+
130+
QString QgsSegmentizeByMaximumAngleAlgorithm::groupId() const
131+
{
132+
return QStringLiteral( "vectorgeometry" );
133+
}
134+
135+
QString QgsSegmentizeByMaximumAngleAlgorithm::outputName() const
136+
{
137+
return QObject::tr( "Segmentized" );
138+
}
139+
140+
QString QgsSegmentizeByMaximumAngleAlgorithm::shortHelpString() const
141+
{
142+
return QObject::tr( "This algorithm segmentizes a geometry by converting curved sections to linear sections.\n\n"
143+
"The segmentization is performed by specifying the maximum allowed radius angle between vertices"
144+
"on the straightened geometry (e.g the angle of the arc created from the original arc center to consective"
145+
"output vertices on the linearized geometry).\n\n"
146+
"Non-curved geometries will be retained without change." );
147+
}
148+
149+
QgsSegmentizeByMaximumAngleAlgorithm *QgsSegmentizeByMaximumAngleAlgorithm::createInstance() const
150+
{
151+
return new QgsSegmentizeByMaximumAngleAlgorithm();
152+
}
153+
154+
QList<int> QgsSegmentizeByMaximumAngleAlgorithm::inputLayerTypes() const
155+
{
156+
return QList<int>() << QgsProcessing::TypeVectorLine << QgsProcessing::TypeVectorPolygon;
157+
}
158+
159+
void QgsSegmentizeByMaximumAngleAlgorithm::initParameters( const QVariantMap & )
160+
{
161+
std::unique_ptr< QgsProcessingParameterNumber > tolerance = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "ANGLE" ),
162+
QObject::tr( "Maximum angle between vertices (degrees)" ), QgsProcessingParameterNumber::Double,
163+
5.0, false, 0, 360.0 );
164+
tolerance->setIsDynamic( true );
165+
tolerance->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "ANGLE" ), QObject::tr( "Maximum angle between vertices (degrees)" ), QgsPropertyDefinition::DoublePositive ) );
166+
tolerance->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
167+
addParameter( tolerance.release() );
168+
}
169+
170+
bool QgsSegmentizeByMaximumAngleAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
171+
{
172+
mTolerance = parameterAsDouble( parameters, QStringLiteral( "ANGLE" ), context );
173+
mDynamicTolerance = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "ANGLE" ) );
174+
if ( mDynamicTolerance )
175+
mToleranceProperty = parameters.value( QStringLiteral( "ANGLE" ) ).value< QgsProperty >();
176+
177+
return true;
178+
}
179+
180+
QgsFeatureList QgsSegmentizeByMaximumAngleAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback * )
181+
{
182+
QgsFeature f = feature;
183+
if ( f.hasGeometry() )
184+
{
185+
QgsGeometry geometry = f.geometry();
186+
double tolerance = mTolerance;
187+
if ( mDynamicTolerance )
188+
tolerance = mToleranceProperty.valueAsDouble( context.expressionContext(), tolerance );
189+
geometry.convertToStraightSegment( M_PI * tolerance / 180.0, QgsAbstractGeometry::MaximumAngle );
190+
f.setGeometry( geometry );
191+
}
192+
return QgsFeatureList() << f;
193+
}
194+
195+
///@endcond
196+
197+

0 commit comments

Comments
 (0)