Skip to content

Commit

Permalink
[FEATURE][processing] Add "segmentize" algorithms
Browse files Browse the repository at this point in the history
Adds two new algorithms which expose QgsGeometry's methods
for segmentizing curved geometries.

"Segmentize by maximum distance":
The segmentization is performed by specifying the maximum
allowed offset distance between the original curve and the
segmentized representation.

"Segmentize by maximum angle":
The segmentization is performed by specifying the maximum
allowed radius angle between vertices on the straightened
geometry (e.g the angle of the arc created from the
original arc center to consective output vertices on the
linearized geometry).
  • Loading branch information
nyalldawson committed Mar 4, 2018
1 parent 6bd7600 commit 4232b93
Show file tree
Hide file tree
Showing 10 changed files with 435 additions and 0 deletions.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ segmentize_by_angle.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>-0.9875563570784498</gml:X><gml:Y>-3.019161406672679</gml:Y></gml:coord>
<gml:coord><gml:X>9.807504202322207</gml:X><gml:Y>4.303079673657821</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:segmentize_by_angle fid="1">
<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>
<ogr:id>1</ogr:id>
</ogr:segmentize_by_angle>
</gml:featureMember>
<gml:featureMember>
<ogr:segmentize_by_angle fid="2">
<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>
<ogr:id>2</ogr:id>
</ogr:segmentize_by_angle>
</gml:featureMember>
</ogr:FeatureCollection>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="segmentize_by_angle" type="ogr:segmentize_by_angle_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="segmentize_by_angle_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="geometryProperty" type="gml:LineStringPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
<xs:element name="id" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:totalDigits value="10"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ segmentize_by_distance.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>-0.9875563570784498</gml:X><gml:Y>-3.019161406672679</gml:Y></gml:coord>
<gml:coord><gml:X>9.794917283570138</gml:X><gml:Y>4.303894025825194</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:segmentize_by_distance fid="1">
<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>
<ogr:id>1</ogr:id>
</ogr:segmentize_by_distance>
</gml:featureMember>
<gml:featureMember>
<ogr:segmentize_by_distance fid="2">
<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>
<ogr:id>2</ogr:id>
</ogr:segmentize_by_distance>
</gml:featureMember>
</ogr:FeatureCollection>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="segmentize_by_distance" type="ogr:segmentize_by_distance_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="segmentize_by_distance_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="geometryProperty" type="gml:LineStringPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
<xs:element name="id" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:totalDigits value="10"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
24 changes: 24 additions & 0 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4794,3 +4794,27 @@ tests:
OUTPUT:
name: expected/multiring_buffer.gml
type: vector

- algorithm: native:segmentizebymaxangle
name: Segmentize by angle
params:
ANGLE: 20.0
INPUT:
name: custom/circular_strings.gpkg|layername=circular_strings
type: vector
results:
OUTPUT:
name: expected/segmentize_by_angle.gml
type: vector

- algorithm: native:segmentizebymaxdistance
name: Segmentize by distance
params:
DISTANCE: 0.2
INPUT:
name: custom/circular_strings.gpkg|layername=circular_strings
type: vector
results:
OUTPUT:
name: expected/segmentize_by_distance.gml
type: vector
1 change: 1 addition & 0 deletions src/analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmremovenullgeometry.cpp
processing/qgsalgorithmrenamelayer.cpp
processing/qgsalgorithmsaveselectedfeatures.cpp
processing/qgsalgorithmsegmentize.cpp
processing/qgsalgorithmsimplify.cpp
processing/qgsalgorithmsmooth.cpp
processing/qgsalgorithmsnaptogrid.cpp
Expand Down
197 changes: 197 additions & 0 deletions src/analysis/processing/qgsalgorithmsegmentize.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/***************************************************************************
qgsalgorithmsegmentize.cpp
---------------------
begin : March 2018
copyright : (C) 2018 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsalgorithmsegmentize.h"

///@cond PRIVATE

QString QgsSegmentizeByMaximumDistanceAlgorithm::name() const
{
return QStringLiteral( "segmentizebymaxdistance" );
}

QString QgsSegmentizeByMaximumDistanceAlgorithm::displayName() const
{
return QObject::tr( "Segmentize by maximum distance" );
}

QStringList QgsSegmentizeByMaximumDistanceAlgorithm::tags() const
{
return QObject::tr( "straighten,linearize,densify,curves,curved,circular" ).split( ',' );
}

QString QgsSegmentizeByMaximumDistanceAlgorithm::group() const
{
return QObject::tr( "Vector geometry" );
}

QString QgsSegmentizeByMaximumDistanceAlgorithm::groupId() const
{
return QStringLiteral( "vectorgeometry" );
}

QString QgsSegmentizeByMaximumDistanceAlgorithm::outputName() const
{
return QObject::tr( "Segmentized" );
}

QString QgsSegmentizeByMaximumDistanceAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm segmentizes a geometry by converting curved sections to linear sections.\n\n"
"The segmentization is performed by specifying the maximum allowed offset distance between the original"
"curve and the segmentized representation.\n\n"
"Non-curved geometries will be retained without change." );
}

QgsSegmentizeByMaximumDistanceAlgorithm *QgsSegmentizeByMaximumDistanceAlgorithm::createInstance() const
{
return new QgsSegmentizeByMaximumDistanceAlgorithm();
}

QList<int> QgsSegmentizeByMaximumDistanceAlgorithm::inputLayerTypes() const
{
return QList<int>() << QgsProcessing::TypeVectorLine << QgsProcessing::TypeVectorPolygon;
}

void QgsSegmentizeByMaximumDistanceAlgorithm::initParameters( const QVariantMap & )
{
std::unique_ptr< QgsProcessingParameterNumber > tolerance = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "DISTANCE" ),
QObject::tr( "Maximum offset distance" ), QgsProcessingParameterNumber::Double,
1.0, false, 0, 10000000.0 );
tolerance->setIsDynamic( true );
tolerance->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "DISTANCE" ), QObject::tr( "Maximum offset distance" ), QgsPropertyDefinition::DoublePositive ) );
tolerance->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( tolerance.release() );
}

bool QgsSegmentizeByMaximumDistanceAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
{
mTolerance = parameterAsDouble( parameters, QStringLiteral( "DISTANCE" ), context );
mDynamicTolerance = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "DISTANCE" ) );
if ( mDynamicTolerance )
mToleranceProperty = parameters.value( QStringLiteral( "DISTANCE" ) ).value< QgsProperty >();

return true;
}

QgsFeatureList QgsSegmentizeByMaximumDistanceAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback * )
{
QgsFeature f = feature;
if ( f.hasGeometry() )
{
QgsGeometry geometry = f.geometry();
double tolerance = mTolerance;
if ( mDynamicTolerance )
tolerance = mToleranceProperty.valueAsDouble( context.expressionContext(), tolerance );
geometry.convertToStraightSegment( tolerance, QgsAbstractGeometry::MaximumDifference );
f.setGeometry( geometry );
}
return QgsFeatureList() << f;
}





QString QgsSegmentizeByMaximumAngleAlgorithm::name() const
{
return QStringLiteral( "segmentizebymaxangle" );
}

QString QgsSegmentizeByMaximumAngleAlgorithm::displayName() const
{
return QObject::tr( "Segmentize by maximum angle" );
}

QStringList QgsSegmentizeByMaximumAngleAlgorithm::tags() const
{
return QObject::tr( "straighten,linearize,densify,curves,curved,circular,angle" ).split( ',' );
}

QString QgsSegmentizeByMaximumAngleAlgorithm::group() const
{
return QObject::tr( "Vector geometry" );
}

QString QgsSegmentizeByMaximumAngleAlgorithm::groupId() const
{
return QStringLiteral( "vectorgeometry" );
}

QString QgsSegmentizeByMaximumAngleAlgorithm::outputName() const
{
return QObject::tr( "Segmentized" );
}

QString QgsSegmentizeByMaximumAngleAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm segmentizes a geometry by converting curved sections to linear sections.\n\n"
"The segmentization is performed by specifying the maximum allowed radius angle between vertices"
"on the straightened geometry (e.g the angle of the arc created from the original arc center to consective"
"output vertices on the linearized geometry).\n\n"
"Non-curved geometries will be retained without change." );
}

QgsSegmentizeByMaximumAngleAlgorithm *QgsSegmentizeByMaximumAngleAlgorithm::createInstance() const
{
return new QgsSegmentizeByMaximumAngleAlgorithm();
}

QList<int> QgsSegmentizeByMaximumAngleAlgorithm::inputLayerTypes() const
{
return QList<int>() << QgsProcessing::TypeVectorLine << QgsProcessing::TypeVectorPolygon;
}

void QgsSegmentizeByMaximumAngleAlgorithm::initParameters( const QVariantMap & )
{
std::unique_ptr< QgsProcessingParameterNumber > tolerance = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "ANGLE" ),
QObject::tr( "Maximum angle between vertices (degrees)" ), QgsProcessingParameterNumber::Double,
5.0, false, 0, 360.0 );
tolerance->setIsDynamic( true );
tolerance->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "ANGLE" ), QObject::tr( "Maximum angle between vertices (degrees)" ), QgsPropertyDefinition::DoublePositive ) );
tolerance->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( tolerance.release() );
}

bool QgsSegmentizeByMaximumAngleAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
{
mTolerance = parameterAsDouble( parameters, QStringLiteral( "ANGLE" ), context );
mDynamicTolerance = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "ANGLE" ) );
if ( mDynamicTolerance )
mToleranceProperty = parameters.value( QStringLiteral( "ANGLE" ) ).value< QgsProperty >();

return true;
}

QgsFeatureList QgsSegmentizeByMaximumAngleAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback * )
{
QgsFeature f = feature;
if ( f.hasGeometry() )
{
QgsGeometry geometry = f.geometry();
double tolerance = mTolerance;
if ( mDynamicTolerance )
tolerance = mToleranceProperty.valueAsDouble( context.expressionContext(), tolerance );
geometry.convertToStraightSegment( M_PI * tolerance / 180.0, QgsAbstractGeometry::MaximumAngle );
f.setGeometry( geometry );
}
return QgsFeatureList() << f;
}

///@endcond


Loading

0 comments on commit 4232b93

Please sign in to comment.