Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Merge pull request #6525 from nyalldawson/segmentize
[FEATURE][processing] Add "segmentize" algorithms
- Loading branch information
Showing
with
444 additions
and 4 deletions.
- +4 −1 python/core/geometry/qgsgeometry.sip.in
- BIN python/plugins/processing/tests/testdata/custom/circular_strings.gpkg
- +26 −0 python/plugins/processing/tests/testdata/expected/segmentize_by_angle.gml
- +30 −0 python/plugins/processing/tests/testdata/expected/segmentize_by_angle.xsd
- +26 −0 python/plugins/processing/tests/testdata/expected/segmentize_by_distance.gml
- +30 −0 python/plugins/processing/tests/testdata/expected/segmentize_by_distance.xsd
- +24 −0 python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
- +1 −0 src/analysis/CMakeLists.txt
- +197 −0 src/analysis/processing/qgsalgorithmsegmentize.cpp
- +98 −0 src/analysis/processing/qgsalgorithmsegmentize.h
- +3 −0 src/analysis/processing/qgsnativealgorithms.cpp
- +2 −2 src/core/geometry/qgsgeometry.cpp
- +3 −1 src/core/geometry/qgsgeometry.h
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> |
@@ -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 ¶meters, 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 ¶meters, 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 | ||
|
||
|
Oops, something went wrong.