Skip to content
Permalink
Browse files

[FEATURE][processing] Add "segmentize" algorithms

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 4232b93539c2a0f03ad37fd48ab1170480c9a5ff
Binary file not shown.
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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
@@ -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
@@ -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


0 comments on commit 4232b93

Please sign in to comment.
You can’t perform that action at this time.