Skip to content

Commit

Permalink
Merge pull request #6501 from alexbruy/multi-ring
Browse files Browse the repository at this point in the history
[processing][FEATURE] add multi-ring buffer algorithm
  • Loading branch information
alexbruy committed Mar 2, 2018
2 parents b61882f + df61548 commit ae9bd06
Show file tree
Hide file tree
Showing 7 changed files with 541 additions and 0 deletions.
257 changes: 257 additions & 0 deletions python/plugins/processing/tests/testdata/expected/multiring_buffer.gml

Large diffs are not rendered by default.

@@ -0,0 +1,52 @@
<?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="multiring_buffer" type="ogr:multiring_buffer_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="multiring_buffer_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="geometryProperty" type="gml:PolygonPropertyType" 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:element name="id2" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:totalDigits value="10"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="ringId" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:totalDigits value="10"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="distance" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="6"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
13 changes: 13 additions & 0 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Expand Up @@ -4765,3 +4765,16 @@ tests:
OUTPUT:
name: expected/biggest_parts.gml
type: vector

- algorithm: native:multiringconstantbuffer
name: Multi-ring buffer with points
params:
DISTANCE: 0.05
INPUT:
name: points.gml
type: vector
RINGS: 3
results:
OUTPUT:
name: expected/multiring_buffer.gml
type: vector
1 change: 1 addition & 0 deletions src/analysis/CMakeLists.txt
Expand Up @@ -47,6 +47,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmmergevector.cpp
processing/qgsalgorithmminimumenclosingcircle.cpp
processing/qgsalgorithmmultiparttosinglepart.cpp
processing/qgsalgorithmmultiringconstantbuffer.cpp
processing/qgsalgorithmorderbyexpression.cpp
processing/qgsalgorithmorientedminimumboundingbox.cpp
processing/qgsalgorithmpackage.cpp
Expand Down
147 changes: 147 additions & 0 deletions src/analysis/processing/qgsalgorithmmultiringconstantbuffer.cpp
@@ -0,0 +1,147 @@
/***************************************************************************
qgsalgorithmnultitingconstantbuffer.cpp
--------------------------
begin : February 2018
copyright : (C) 2018 by Alexander Bruy
email : alexander dot bruy 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 "qgsalgorithmmultiringconstantbuffer.h"

///@cond PRIVATE

QString QgsMultiRingConstantBufferAlgorithm::name() const
{
return QStringLiteral( "multiringconstantbuffer" );
}

QString QgsMultiRingConstantBufferAlgorithm::displayName() const
{
return QObject::tr( "Multi-ring buffer (constant distance)" );
}

QStringList QgsMultiRingConstantBufferAlgorithm::tags() const
{
return QObject::tr( "buffer,grow,multiple,rings,distance,donut" ).split( ',' );
}

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

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

QString QgsMultiRingConstantBufferAlgorithm::outputName() const
{
return QObject::tr( "Multi-ring buffer (constant distance)" );
}

QString QgsMultiRingConstantBufferAlgorithm::shortHelpString() const
{
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." );
}

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

void QgsMultiRingConstantBufferAlgorithm::initParameters( const QVariantMap & )
{
std::unique_ptr< QgsProcessingParameterNumber> rings = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "RINGS" ),
QObject::tr( "Number of rings" ), QgsProcessingParameterNumber::Integer,
1, false, 0 );
rings->setIsDynamic( true );
rings->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "RINGS" ), QObject::tr( "Number of rings" ), QgsPropertyDefinition::IntegerPositive ) );
rings->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( rings.release() );

std::unique_ptr< QgsProcessingParameterNumber> distance = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "DISTANCE" ),
QObject::tr( "Distance between rings" ), QgsProcessingParameterNumber::Double,
1, false, 0 );
distance->setIsDynamic( true );
distance->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "DISTANCE" ), QObject::tr( "Distance between rings" ), QgsPropertyDefinition::DoublePositive ) );
distance->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( distance.release() );
}

bool QgsMultiRingConstantBufferAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
{
mRingsNumber = parameterAsInt( parameters, QStringLiteral( "RINGS" ), context );
mDynamicRingsNumber = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "RINGS" ) );
if ( mDynamicRingsNumber )
mRingsNumberProperty = parameters.value( QStringLiteral( "RINGS" ) ).value< QgsProperty >();

mDistance = parameterAsDouble( parameters, QStringLiteral( "DISTANCE" ), context );
mDynamicDistance = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "DISTANCE" ) );
if ( mDynamicDistance )
mDistanceProperty = parameters.value( QStringLiteral( "DISTANCE" ) ).value< QgsProperty >();

return true;
}

QgsFields QgsMultiRingConstantBufferAlgorithm::outputFields( const QgsFields &inputFields ) const
{
QgsFields fields = inputFields;
fields.append( QgsField( QStringLiteral( "ringId" ), QVariant::Int, QString(), 10, 0 ) );
fields.append( QgsField( QStringLiteral( "distance" ), QVariant::Double, QString(), 20, 6 ) );
return fields;
}

QgsFeatureList QgsMultiRingConstantBufferAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
double currentDistance = 0;
QgsGeometry outputGeometry, previousGeometry;

int rings = mRingsNumber;
if ( mDynamicRingsNumber )
rings = mRingsNumberProperty.valueAsInt( context.expressionContext(), rings );

double distance = mDistance;
if ( mDynamicDistance )
distance = mDistanceProperty.valueAsDouble( context.expressionContext(), distance );

QgsFeatureList outputs;

for ( int i = 1; i <= mRingsNumber; ++i )
{
QgsFeature out;
currentDistance = i * distance;
outputGeometry = feature.geometry().buffer( currentDistance, 40 );
if ( !outputGeometry )
{
feedback->reportError( QObject::tr( "Error calculating buffer for feature %1" ).arg( feature.id() ) );
continue;
}

if ( i == 1 )
{
out.setGeometry( outputGeometry );
}
else
{
out.setGeometry( outputGeometry.symDifference( previousGeometry ) );
}
previousGeometry = outputGeometry;
QgsAttributes attrs = feature.attributes();
attrs << i << currentDistance;
out.setAttributes( attrs );
outputs.append( out );
}
return outputs;
}

///@endcond
69 changes: 69 additions & 0 deletions src/analysis/processing/qgsalgorithmmultiringconstantbuffer.h
@@ -0,0 +1,69 @@
/***************************************************************************
qgsalgorithmmultiringconstantbuffer.h
-------------------------
begin : February 2018
copyright : (C) 2018 by Alexander Bruy
email : alexander dot bruy 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. *
* *
***************************************************************************/

#ifndef QGSALGORITHMMULTIRINGCONSTANTBUFFER_H
#define QGSALGORITHMMULTIRINGCONSTANTBUFFER_H

#define SIP_NO_FILE

#include "qgis.h"
#include "qgsprocessingalgorithm.h"

///@cond PRIVATE

/**
* Native multiring buffer algorithm.
*/
class QgsMultiRingConstantBufferAlgorithm : public QgsProcessingFeatureBasedAlgorithm
{

public:

QgsMultiRingConstantBufferAlgorithm() = default;
QString name() const override;
QString displayName() const override;
QStringList tags() const override;
QString group() const override;
QString groupId() const override;
QString shortHelpString() const override;
QgsMultiRingConstantBufferAlgorithm *createInstance() const override SIP_FACTORY;
void initParameters( const QVariantMap &configuration = QVariantMap() ) override;

protected:

QString outputName() const override;
QgsFields outputFields( const QgsFields &inputFields ) const override;
QgsProcessing::SourceType outputLayerType() const override { return QgsProcessing::TypeVectorPolygon; }
QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const override { Q_UNUSED( inputWkbType ); return QgsWkbTypes::Polygon; }
bool prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QgsFeatureList processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;

private:
int mRingsNumber = 0;
bool mDynamicRingsNumber = false;
QgsProperty mRingsNumberProperty;

double mDistance = 0.0;
bool mDynamicDistance = false;
QgsProperty mDistanceProperty;
};

///@endcond PRIVATE

#endif // QGSALGORITHMMULTIRINGCONSTANTBUFFER_H


2 changes: 2 additions & 0 deletions src/analysis/processing/qgsnativealgorithms.cpp
Expand Up @@ -44,6 +44,7 @@
#include "qgsalgorithmmergevector.h"
#include "qgsalgorithmminimumenclosingcircle.h"
#include "qgsalgorithmmultiparttosinglepart.h"
#include "qgsalgorithmmultiringconstantbuffer.h"
#include "qgsalgorithmorderbyexpression.h"
#include "qgsalgorithmorientedminimumboundingbox.h"
#include "qgsalgorithmpackage.h"
Expand Down Expand Up @@ -134,6 +135,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsMergeVectorAlgorithm() );
addAlgorithm( new QgsMinimumEnclosingCircleAlgorithm() );
addAlgorithm( new QgsMultipartToSinglepartAlgorithm() );
addAlgorithm( new QgsMultiRingConstantBufferAlgorithm() );
addAlgorithm( new QgsOrderByExpressionAlgorithm() );
addAlgorithm( new QgsOrientedMinimumBoundingBoxAlgorithm() );
addAlgorithm( new QgsPackageAlgorithm() );
Expand Down

0 comments on commit ae9bd06

Please sign in to comment.