Skip to content
Permalink
Browse files

Merge pull request #6525 from nyalldawson/segmentize

[FEATURE][processing] Add "segmentize" algorithms
  • Loading branch information
alexbruy committed Mar 6, 2018
2 parents 9edaf82 + 4232b93 commit 44895084d908fa118eed12ca2045f7f490274877
@@ -1423,10 +1423,13 @@ An empty geometry will be returned in the case of errors.
.. versionadded:: 3.0
%End

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

:param tolerance: segmentation tolerance
:param toleranceType: maximum segmentation angle or maximum difference between approximation and curve

.. versionadded:: 2.10

.. seealso:: :py:func:`requiresConversionToStraightSegments`
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>
@@ -4818,3 +4818,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 4489508

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