Skip to content

Commit ae9bd06

Browse files
authoredMar 2, 2018
Merge pull request #6501 from alexbruy/multi-ring
[processing][FEATURE] add multi-ring buffer algorithm
2 parents b61882f + df61548 commit ae9bd06

File tree

7 files changed

+541
-0
lines changed

7 files changed

+541
-0
lines changed
 

‎python/plugins/processing/tests/testdata/expected/multiring_buffer.gml

Lines changed: 257 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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="multiring_buffer" type="ogr:multiring_buffer_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="multiring_buffer_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="geometryProperty" type="gml:PolygonPropertyType" 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:element name="id2" nillable="true" minOccurs="0" maxOccurs="1">
27+
<xs:simpleType>
28+
<xs:restriction base="xs:integer">
29+
<xs:totalDigits value="10"/>
30+
</xs:restriction>
31+
</xs:simpleType>
32+
</xs:element>
33+
<xs:element name="ringId" nillable="true" minOccurs="0" maxOccurs="1">
34+
<xs:simpleType>
35+
<xs:restriction base="xs:integer">
36+
<xs:totalDigits value="10"/>
37+
</xs:restriction>
38+
</xs:simpleType>
39+
</xs:element>
40+
<xs:element name="distance" nillable="true" minOccurs="0" maxOccurs="1">
41+
<xs:simpleType>
42+
<xs:restriction base="xs:decimal">
43+
<xs:totalDigits value="21"/>
44+
<xs:fractionDigits value="6"/>
45+
</xs:restriction>
46+
</xs:simpleType>
47+
</xs:element>
48+
</xs:sequence>
49+
</xs:extension>
50+
</xs:complexContent>
51+
</xs:complexType>
52+
</xs:schema>

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4765,3 +4765,16 @@ tests:
47654765
OUTPUT:
47664766
name: expected/biggest_parts.gml
47674767
type: vector
4768+
4769+
- algorithm: native:multiringconstantbuffer
4770+
name: Multi-ring buffer with points
4771+
params:
4772+
DISTANCE: 0.05
4773+
INPUT:
4774+
name: points.gml
4775+
type: vector
4776+
RINGS: 3
4777+
results:
4778+
OUTPUT:
4779+
name: expected/multiring_buffer.gml
4780+
type: vector

‎src/analysis/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ SET(QGIS_ANALYSIS_SRCS
4747
processing/qgsalgorithmmergevector.cpp
4848
processing/qgsalgorithmminimumenclosingcircle.cpp
4949
processing/qgsalgorithmmultiparttosinglepart.cpp
50+
processing/qgsalgorithmmultiringconstantbuffer.cpp
5051
processing/qgsalgorithmorderbyexpression.cpp
5152
processing/qgsalgorithmorientedminimumboundingbox.cpp
5253
processing/qgsalgorithmpackage.cpp
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/***************************************************************************
2+
qgsalgorithmnultitingconstantbuffer.cpp
3+
--------------------------
4+
begin : February 2018
5+
copyright : (C) 2018 by Alexander Bruy
6+
email : alexander dot bruy 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 "qgsalgorithmmultiringconstantbuffer.h"
19+
20+
///@cond PRIVATE
21+
22+
QString QgsMultiRingConstantBufferAlgorithm::name() const
23+
{
24+
return QStringLiteral( "multiringconstantbuffer" );
25+
}
26+
27+
QString QgsMultiRingConstantBufferAlgorithm::displayName() const
28+
{
29+
return QObject::tr( "Multi-ring buffer (constant distance)" );
30+
}
31+
32+
QStringList QgsMultiRingConstantBufferAlgorithm::tags() const
33+
{
34+
return QObject::tr( "buffer,grow,multiple,rings,distance,donut" ).split( ',' );
35+
}
36+
37+
QString QgsMultiRingConstantBufferAlgorithm::group() const
38+
{
39+
return QObject::tr( "Vector geometry" );
40+
}
41+
42+
QString QgsMultiRingConstantBufferAlgorithm::groupId() const
43+
{
44+
return QStringLiteral( "vectorgeometry" );
45+
}
46+
47+
QString QgsMultiRingConstantBufferAlgorithm::outputName() const
48+
{
49+
return QObject::tr( "Multi-ring buffer (constant distance)" );
50+
}
51+
52+
QString QgsMultiRingConstantBufferAlgorithm::shortHelpString() const
53+
{
54+
return QObject::tr( "This algorithm computes multi-ring ('donuts') buffer for all the features in an input layer, using a fixed or dynamic distance and rings number." );
55+
}
56+
57+
QgsMultiRingConstantBufferAlgorithm *QgsMultiRingConstantBufferAlgorithm::createInstance() const
58+
{
59+
return new QgsMultiRingConstantBufferAlgorithm();
60+
}
61+
62+
void QgsMultiRingConstantBufferAlgorithm::initParameters( const QVariantMap & )
63+
{
64+
std::unique_ptr< QgsProcessingParameterNumber> rings = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "RINGS" ),
65+
QObject::tr( "Number of rings" ), QgsProcessingParameterNumber::Integer,
66+
1, false, 0 );
67+
rings->setIsDynamic( true );
68+
rings->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "RINGS" ), QObject::tr( "Number of rings" ), QgsPropertyDefinition::IntegerPositive ) );
69+
rings->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
70+
addParameter( rings.release() );
71+
72+
std::unique_ptr< QgsProcessingParameterNumber> distance = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "DISTANCE" ),
73+
QObject::tr( "Distance between rings" ), QgsProcessingParameterNumber::Double,
74+
1, false, 0 );
75+
distance->setIsDynamic( true );
76+
distance->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "DISTANCE" ), QObject::tr( "Distance between rings" ), QgsPropertyDefinition::DoublePositive ) );
77+
distance->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
78+
addParameter( distance.release() );
79+
}
80+
81+
bool QgsMultiRingConstantBufferAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
82+
{
83+
mRingsNumber = parameterAsInt( parameters, QStringLiteral( "RINGS" ), context );
84+
mDynamicRingsNumber = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "RINGS" ) );
85+
if ( mDynamicRingsNumber )
86+
mRingsNumberProperty = parameters.value( QStringLiteral( "RINGS" ) ).value< QgsProperty >();
87+
88+
mDistance = parameterAsDouble( parameters, QStringLiteral( "DISTANCE" ), context );
89+
mDynamicDistance = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "DISTANCE" ) );
90+
if ( mDynamicDistance )
91+
mDistanceProperty = parameters.value( QStringLiteral( "DISTANCE" ) ).value< QgsProperty >();
92+
93+
return true;
94+
}
95+
96+
QgsFields QgsMultiRingConstantBufferAlgorithm::outputFields( const QgsFields &inputFields ) const
97+
{
98+
QgsFields fields = inputFields;
99+
fields.append( QgsField( QStringLiteral( "ringId" ), QVariant::Int, QString(), 10, 0 ) );
100+
fields.append( QgsField( QStringLiteral( "distance" ), QVariant::Double, QString(), 20, 6 ) );
101+
return fields;
102+
}
103+
104+
QgsFeatureList QgsMultiRingConstantBufferAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
105+
{
106+
double currentDistance = 0;
107+
QgsGeometry outputGeometry, previousGeometry;
108+
109+
int rings = mRingsNumber;
110+
if ( mDynamicRingsNumber )
111+
rings = mRingsNumberProperty.valueAsInt( context.expressionContext(), rings );
112+
113+
double distance = mDistance;
114+
if ( mDynamicDistance )
115+
distance = mDistanceProperty.valueAsDouble( context.expressionContext(), distance );
116+
117+
QgsFeatureList outputs;
118+
119+
for ( int i = 1; i <= mRingsNumber; ++i )
120+
{
121+
QgsFeature out;
122+
currentDistance = i * distance;
123+
outputGeometry = feature.geometry().buffer( currentDistance, 40 );
124+
if ( !outputGeometry )
125+
{
126+
feedback->reportError( QObject::tr( "Error calculating buffer for feature %1" ).arg( feature.id() ) );
127+
continue;
128+
}
129+
130+
if ( i == 1 )
131+
{
132+
out.setGeometry( outputGeometry );
133+
}
134+
else
135+
{
136+
out.setGeometry( outputGeometry.symDifference( previousGeometry ) );
137+
}
138+
previousGeometry = outputGeometry;
139+
QgsAttributes attrs = feature.attributes();
140+
attrs << i << currentDistance;
141+
out.setAttributes( attrs );
142+
outputs.append( out );
143+
}
144+
return outputs;
145+
}
146+
147+
///@endcond
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/***************************************************************************
2+
qgsalgorithmmultiringconstantbuffer.h
3+
-------------------------
4+
begin : February 2018
5+
copyright : (C) 2018 by Alexander Bruy
6+
email : alexander dot bruy 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+
#ifndef QGSALGORITHMMULTIRINGCONSTANTBUFFER_H
19+
#define QGSALGORITHMMULTIRINGCONSTANTBUFFER_H
20+
21+
#define SIP_NO_FILE
22+
23+
#include "qgis.h"
24+
#include "qgsprocessingalgorithm.h"
25+
26+
///@cond PRIVATE
27+
28+
/**
29+
* Native multiring buffer algorithm.
30+
*/
31+
class QgsMultiRingConstantBufferAlgorithm : public QgsProcessingFeatureBasedAlgorithm
32+
{
33+
34+
public:
35+
36+
QgsMultiRingConstantBufferAlgorithm() = default;
37+
QString name() const override;
38+
QString displayName() const override;
39+
QStringList tags() const override;
40+
QString group() const override;
41+
QString groupId() const override;
42+
QString shortHelpString() const override;
43+
QgsMultiRingConstantBufferAlgorithm *createInstance() const override SIP_FACTORY;
44+
void initParameters( const QVariantMap &configuration = QVariantMap() ) override;
45+
46+
protected:
47+
48+
QString outputName() const override;
49+
QgsFields outputFields( const QgsFields &inputFields ) const override;
50+
QgsProcessing::SourceType outputLayerType() const override { return QgsProcessing::TypeVectorPolygon; }
51+
QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const override { Q_UNUSED( inputWkbType ); return QgsWkbTypes::Polygon; }
52+
bool prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
53+
QgsFeatureList processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
54+
55+
private:
56+
int mRingsNumber = 0;
57+
bool mDynamicRingsNumber = false;
58+
QgsProperty mRingsNumberProperty;
59+
60+
double mDistance = 0.0;
61+
bool mDynamicDistance = false;
62+
QgsProperty mDistanceProperty;
63+
};
64+
65+
///@endcond PRIVATE
66+
67+
#endif // QGSALGORITHMMULTIRINGCONSTANTBUFFER_H
68+
69+

‎src/analysis/processing/qgsnativealgorithms.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "qgsalgorithmmergevector.h"
4545
#include "qgsalgorithmminimumenclosingcircle.h"
4646
#include "qgsalgorithmmultiparttosinglepart.h"
47+
#include "qgsalgorithmmultiringconstantbuffer.h"
4748
#include "qgsalgorithmorderbyexpression.h"
4849
#include "qgsalgorithmorientedminimumboundingbox.h"
4950
#include "qgsalgorithmpackage.h"
@@ -134,6 +135,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
134135
addAlgorithm( new QgsMergeVectorAlgorithm() );
135136
addAlgorithm( new QgsMinimumEnclosingCircleAlgorithm() );
136137
addAlgorithm( new QgsMultipartToSinglepartAlgorithm() );
138+
addAlgorithm( new QgsMultiRingConstantBufferAlgorithm() );
137139
addAlgorithm( new QgsOrderByExpressionAlgorithm() );
138140
addAlgorithm( new QgsOrientedMinimumBoundingBoxAlgorithm() );
139141
addAlgorithm( new QgsPackageAlgorithm() );

0 commit comments

Comments
 (0)
Please sign in to comment.