[processing][feature] Port 'offset lines' to c++, support dynamic offset parameter

Adds data defined support for the offset line distance parameter.
This commit is contained in:
Nyall Dawson 2018-07-27 10:44:24 +10:00
parent 76c84f14d7
commit d1d6840a5f
8 changed files with 219 additions and 133 deletions

View File

@ -280,15 +280,6 @@ qgis:numberofuniquevaluesinclasses: >
The resulting layer contains the same features as the input layer, but with an additional attribute containing the count of unique values for that class.
qgis:offsetline: >
This algorithm offsets lines by a specified distance. Positive distances will offset lines to the left, and negative distances will offset to the right of lines.
The segments parameter controls the number of line segments to use to approximate a quarter circle when creating rounded offsets.
The join style parameter specifies whether round, miter or beveled joins should be used when offsetting corners in a line.
The miter limit parameter is only applicable for miter join styles, and controls the maximum distance from the offset curve to use when creating a mitered join.
qgis:minimalenclosingcircle: >
This algorithm takes a vector layer and generate a new one with the minimum enclosing circle that covers all the input features.

View File

@ -1,117 +0,0 @@
# -*- coding: utf-8 -*-
"""
***************************************************************************
OffsetLine.py
--------------
Date : July 2016
Copyright : (C) 2016 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. *
* *
***************************************************************************
"""
__author__ = 'Nyall Dawson'
__date__ = 'July 2016'
__copyright__ = '(C) 2016, Nyall Dawson'
# This will get replaced with a git SHA1 when you do a git archive323
__revision__ = '$Format:%H$'
import os
from qgis.core import (QgsFeatureSink,
QgsProcessing,
QgsProcessingException,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterNumber,
QgsProcessingParameterDistance,
QgsProcessingParameterEnum,
QgsProcessingParameterFeatureSink)
from processing.algs.qgis.QgisAlgorithm import QgisFeatureBasedAlgorithm
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
class OffsetLine(QgisFeatureBasedAlgorithm):
DISTANCE = 'DISTANCE'
SEGMENTS = 'SEGMENTS'
JOIN_STYLE = 'JOIN_STYLE'
MITER_LIMIT = 'MITER_LIMIT'
def group(self):
return self.tr('Vector geometry')
def groupId(self):
return 'vectorgeometry'
def __init__(self):
super().__init__()
self.distance = None
self.segments = None
self.join_style = None
self.miter_limit = None
def initParameters(self, config=None):
self.addParameter(QgsProcessingParameterDistance(self.DISTANCE,
self.tr('Distance'),
defaultValue=10.0,
parentParameterName='INPUT'))
self.addParameter(QgsProcessingParameterNumber(self.SEGMENTS,
self.tr('Segments'),
type=QgsProcessingParameterNumber.Integer,
minValue=1, defaultValue=8))
self.join_styles = [self.tr('Round'),
'Miter',
'Bevel']
self.addParameter(QgsProcessingParameterEnum(
self.JOIN_STYLE,
self.tr('Join style'),
options=self.join_styles))
self.addParameter(QgsProcessingParameterNumber(self.MITER_LIMIT,
self.tr('Miter limit'), type=QgsProcessingParameterNumber.Double,
minValue=1, defaultValue=2))
def name(self):
return 'offsetline'
def displayName(self):
return self.tr('Offset line')
def outputName(self):
return self.tr('Offset')
def inputLayerTypes(self):
return [QgsProcessing.TypeVectorLine]
def outputType(self):
return QgsProcessing.TypeVectorLine
def prepareAlgorithm(self, parameters, context, feedback):
self.distance = self.parameterAsDouble(parameters, self.DISTANCE, context)
self.segments = self.parameterAsInt(parameters, self.SEGMENTS, context)
self.join_style = self.parameterAsEnum(parameters, self.JOIN_STYLE, context) + 1
self.miter_limit = self.parameterAsDouble(parameters, self.MITER_LIMIT, context)
return True
def processFeature(self, feature, context, feedback):
input_geometry = feature.geometry()
if input_geometry:
output_geometry = input_geometry.offsetCurve(self.distance, self.segments, self.join_style, self.miter_limit)
if not output_geometry:
raise QgsProcessingException(
self.tr('Error calculating line offset'))
feature.setGeometry(output_geometry)
return [feature]

View File

@ -86,7 +86,6 @@ from .KeepNBiggestParts import KeepNBiggestParts
from .LinesToPolygons import LinesToPolygons
from .MinimumBoundingGeometry import MinimumBoundingGeometry
from .NearestNeighbourAnalysis import NearestNeighbourAnalysis
from .OffsetLine import OffsetLine
from .Orthogonalize import Orthogonalize
from .PointDistance import PointDistance
from .PointsAlongGeometry import PointsAlongGeometry
@ -201,7 +200,6 @@ class QgisAlgorithmProvider(QgsProcessingProvider):
LinesToPolygons(),
MinimumBoundingGeometry(),
NearestNeighbourAnalysis(),
OffsetLine(),
Orthogonalize(),
PointDistance(),
PointsAlongGeometry(),

View File

@ -984,7 +984,7 @@ tests:
name: expected/reverse_multiline.gml
type: vector
- algorithm: qgis:offsetline
- algorithm: native:offsetline
name: Offset line positive
params:
DISTANCE: 1.0
@ -1002,7 +1002,7 @@ tests:
geometry:
precision: 7
- algorithm: qgis:offsetline
- algorithm: native:offsetline
name: Offset line negative
params:
DISTANCE: -1.0
@ -1020,7 +1020,7 @@ tests:
geometry:
precision: 7
- algorithm: qgis:offsetline
- algorithm: native:offsetline
name: Offset line miter
params:
DISTANCE: 1.0
@ -1038,7 +1038,7 @@ tests:
geometry:
precision: 7
- algorithm: qgis:offsetline
- algorithm: native:offsetline
name: Offset line bevel
params:
DISTANCE: 1.0
@ -1056,7 +1056,7 @@ tests:
geometry:
precision: 7
- algorithm: qgis:offsetline
- algorithm: native:offsetline
name: Offset multilines
params:
DISTANCE: 1.0

View File

@ -58,6 +58,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmminimumenclosingcircle.cpp
processing/qgsalgorithmmultiparttosinglepart.cpp
processing/qgsalgorithmmultiringconstantbuffer.cpp
processing/qgsalgorithmoffsetlines.cpp
processing/qgsalgorithmorderbyexpression.cpp
processing/qgsalgorithmorientedminimumboundingbox.cpp
processing/qgsalgorithmpackage.cpp

View File

@ -0,0 +1,139 @@
/***************************************************************************
qgsalgorithmoffsetlines.cpp
---------------------
begin : July 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 "qgsalgorithmoffsetlines.h"
///@cond PRIVATE
QString QgsOffsetLinesAlgorithm::name() const
{
return QStringLiteral( "offsetline" );
}
QString QgsOffsetLinesAlgorithm::displayName() const
{
return QObject::tr( "Offset lines" );
}
QStringList QgsOffsetLinesAlgorithm::tags() const
{
return QObject::tr( "offset,linestring" ).split( ',' );
}
QString QgsOffsetLinesAlgorithm::group() const
{
return QObject::tr( "Vector geometry" );
}
QString QgsOffsetLinesAlgorithm::groupId() const
{
return QStringLiteral( "vectorgeometry" );
}
QString QgsOffsetLinesAlgorithm::outputName() const
{
return QObject::tr( "Offset" );
}
QString QgsOffsetLinesAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm offsets lines by a specified distance. Positive distances will offset lines to the left, and negative distances "
"will offset to the right of lines.\n\n"
"The segments parameter controls the number of line segments to use to approximate a quarter circle when creating rounded offsets.\n\n"
"The join style parameter specifies whether round, miter or beveled joins should be used when offsetting corners in a line.\n\n"
"The miter limit parameter is only applicable for miter join styles, and controls the maximum distance from the offset curve to "
"use when creating a mitered join." );
}
QString QgsOffsetLinesAlgorithm::shortDescription() const
{
return QObject::tr( "Offsets lines by a specified distance." );
}
QgsOffsetLinesAlgorithm *QgsOffsetLinesAlgorithm::createInstance() const
{
return new QgsOffsetLinesAlgorithm();
}
void QgsOffsetLinesAlgorithm::initParameters( const QVariantMap & )
{
std::unique_ptr< QgsProcessingParameterDistance > offset = qgis::make_unique< QgsProcessingParameterDistance >( QStringLiteral( "DISTANCE" ),
QObject::tr( "Distance" ),
10.0, QStringLiteral( "INPUT" ) );
offset->setIsDynamic( true );
offset->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "DISTANCE" ), QObject::tr( "Distance" ), QgsPropertyDefinition::Double ) );
offset->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( offset.release() );
auto segmentParam = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "SEGMENTS" ), QObject::tr( "Segments" ), QgsProcessingParameterNumber::Integer, 8, false, 1 );
addParameter( segmentParam.release() );
auto joinStyleParam = qgis::make_unique< QgsProcessingParameterEnum>( QStringLiteral( "JOIN_STYLE" ), QObject::tr( "Join style" ), QStringList() << QObject::tr( "Round" ) << QObject::tr( "Miter" ) << QObject::tr( "Bevel" ), false, 0 );
addParameter( joinStyleParam.release() );
auto miterLimitParam = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "MITER_LIMIT" ), QObject::tr( "Miter limit" ), QgsProcessingParameterNumber::Double, 2, false, 1 );
addParameter( miterLimitParam.release() );
}
QList<int> QgsOffsetLinesAlgorithm::inputLayerTypes() const
{
return QList< int >() << QgsProcessing::TypeVectorLine;
}
QgsProcessing::SourceType QgsOffsetLinesAlgorithm::outputLayerType() const
{
return QgsProcessing::TypeVectorLine;
}
bool QgsOffsetLinesAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
{
mOffset = parameterAsDouble( parameters, QStringLiteral( "DISTANCE" ), context );
mDynamicOffset = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "DISTANCE" ) );
if ( mDynamicOffset )
mOffsetProperty = parameters.value( QStringLiteral( "DISTANCE" ) ).value< QgsProperty >();
mSegments = parameterAsInt( parameters, QStringLiteral( "SEGMENTS" ), context );
mJoinStyle = static_cast< QgsGeometry::JoinStyle>( 1 + parameterAsInt( parameters, QStringLiteral( "JOIN_STYLE" ), context ) );
mMiterLimit = parameterAsDouble( parameters, QStringLiteral( "MITER_LIMIT" ), context );
return true;
}
QgsFeatureList QgsOffsetLinesAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback * )
{
if ( feature.hasGeometry() )
{
QgsFeature f = feature;
const QgsGeometry geometry = feature.geometry();
double offset = mOffset;
if ( mDynamicOffset )
offset = mOffsetProperty.valueAsDouble( context.expressionContext(), offset );
const QgsGeometry offsetGeometry = geometry.offsetCurve( offset, mSegments, mJoinStyle, mMiterLimit );
f.setGeometry( offsetGeometry );
return QgsFeatureList() << f;
}
else
{
return QgsFeatureList() << feature;
}
}
///@endcond

View File

@ -0,0 +1,72 @@
/***************************************************************************
qgsalgorithmoffsetlines.h
---------------------
begin : July 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. *
* *
***************************************************************************/
#ifndef QGSALGORITHMOFFSETLINES_H
#define QGSALGORITHMOFFSETLINES_H
#define SIP_NO_FILE
#include "qgis.h"
#include "qgsprocessingalgorithm.h"
///@cond PRIVATE
/**
* Native offset lines algorithm.
*/
class QgsOffsetLinesAlgorithm : public QgsProcessingFeatureBasedAlgorithm
{
public:
QgsOffsetLinesAlgorithm() = 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;
QString shortDescription() const override;
QgsOffsetLinesAlgorithm *createInstance() const override SIP_FACTORY;
void initParameters( const QVariantMap &configuration = QVariantMap() ) override;
QList<int> inputLayerTypes() const override;
QgsProcessing::SourceType outputLayerType() const override;
protected:
QString outputName() const override;
bool prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QgsFeatureList processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
private:
double mOffset = 0.0;
bool mDynamicOffset = false;
QgsProperty mOffsetProperty;
int mSegments = 8;
QgsGeometry::JoinStyle mJoinStyle = QgsGeometry::JoinStyleRound;
double mMiterLimit = 2;
};
///@endcond PRIVATE
#endif // QGSALGORITHMOFFSETLINES_H

View File

@ -55,6 +55,7 @@
#include "qgsalgorithmminimumenclosingcircle.h"
#include "qgsalgorithmmultiparttosinglepart.h"
#include "qgsalgorithmmultiringconstantbuffer.h"
#include "qgsalgorithmoffsetlines.h"
#include "qgsalgorithmorderbyexpression.h"
#include "qgsalgorithmorientedminimumboundingbox.h"
#include "qgsalgorithmpackage.h"
@ -171,6 +172,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsMinimumEnclosingCircleAlgorithm() );
addAlgorithm( new QgsMultipartToSinglepartAlgorithm() );
addAlgorithm( new QgsMultiRingConstantBufferAlgorithm() );
addAlgorithm( new QgsOffsetLinesAlgorithm() );
addAlgorithm( new QgsOrderByExpressionAlgorithm() );
addAlgorithm( new QgsOrientedMinimumBoundingBoxAlgorithm() );
addAlgorithm( new QgsPackageAlgorithm() );