Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[feature][processing] "Array of Offset Features" algorithm
This algorithm creates copies of features in a layer, by
creating multiple offset versions of the feature. Each copy is displaced
by a preset amount in the x/y/z/m axis.
  • Loading branch information
nyalldawson committed Jul 27, 2018
1 parent 07366e8 commit abe4532
Show file tree
Hide file tree
Showing 10 changed files with 326 additions and 0 deletions.
Binary file not shown.
@@ -0,0 +1 @@
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
@@ -0,0 +1 @@
GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]
Binary file not shown.
Binary file not shown.
17 changes: 17 additions & 0 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Expand Up @@ -5892,4 +5892,21 @@ tests:
name: expected/filter_by_z.shp
type: vector

- algorithm: native:arrayfeatures
name: Array of point features
params:
COUNT: 3
DELTA_M: -0.4
DELTA_X: 0.1
DELTA_Y: -0.2
DELTA_Z: 0.3
INPUT:
name: points.gml
type: vector
results:
OUTPUT:
name: expected/feature_array.shp
type: vector


# See ../README.md for a description of the file format
1 change: 1 addition & 0 deletions src/analysis/CMakeLists.txt
Expand Up @@ -20,6 +20,7 @@ SET(QGIS_ANALYSIS_SRCS
interpolation/Vector3D.cpp

processing/qgsalgorithmaddincrementalfield.cpp
processing/qgsalgorithmarrayfeatures.cpp
processing/qgsalgorithmassignprojection.cpp
processing/qgsalgorithmboundary.cpp
processing/qgsalgorithmboundingbox.cpp
Expand Down
221 changes: 221 additions & 0 deletions src/analysis/processing/qgsalgorithmarrayfeatures.cpp
@@ -0,0 +1,221 @@
/***************************************************************************
qgsalgorithmarrayfeatures.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 "qgsalgorithmarrayfeatures.h"

///@cond PRIVATE

QString QgsArrayFeaturesAlgorithm::name() const
{
return QStringLiteral( "arrayfeatures" );
}

QString QgsArrayFeaturesAlgorithm::displayName() const
{
return QObject::tr( "Array of offset features" );
}

QStringList QgsArrayFeaturesAlgorithm::tags() const
{
return QObject::tr( "translate,duplicate,grid,spaced,moved,copy,features,objects,step,repeat" ).split( ',' );
}

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

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

QString QgsArrayFeaturesAlgorithm::outputName() const
{
return QObject::tr( "Offset" );
}

QString QgsArrayFeaturesAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm creates copies of features in a layer, by creating multiple offset versions of each feature. "
"Each copy is displaced by a preset amount in the x/y/z/m axis." );
}

QString QgsArrayFeaturesAlgorithm::shortDescription() const
{
return QObject::tr( "Creates multiple offset copies of features in a layer." );
}

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

void QgsArrayFeaturesAlgorithm::initParameters( const QVariantMap & )
{
std::unique_ptr< QgsProcessingParameterNumber > count = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "COUNT" ),
QObject::tr( "Number of features to create" ), QgsProcessingParameterNumber::Integer,
10, true, 1 );
count->setIsDynamic( true );
count->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "COUNT" ), QObject::tr( "Number of features to create" ), QgsPropertyDefinition::IntegerPositiveGreaterZero ) );
count->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( count.release() );

std::unique_ptr< QgsProcessingParameterDistance > xOffset = qgis::make_unique< QgsProcessingParameterDistance >( QStringLiteral( "DELTA_X" ),
QObject::tr( "Step distance (x-axis)" ),
0.0, QStringLiteral( "INPUT" ) );
xOffset->setIsDynamic( true );
xOffset->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "DELTA_X" ), QObject::tr( "Offset distance (x-axis)" ), QgsPropertyDefinition::Double ) );
xOffset->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( xOffset.release() );

std::unique_ptr< QgsProcessingParameterDistance > yOffset = qgis::make_unique< QgsProcessingParameterDistance >( QStringLiteral( "DELTA_Y" ),
QObject::tr( "Step distance (y-axis)" ),
0.0, QStringLiteral( "INPUT" ) );
yOffset->setIsDynamic( true );
yOffset->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "DELTA_Y" ), QObject::tr( "Offset distance (y-axis)" ), QgsPropertyDefinition::Double ) );
yOffset->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( yOffset.release() );

std::unique_ptr< QgsProcessingParameterNumber > zOffset = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "DELTA_Z" ),
QObject::tr( "Step distance (z-axis)" ), QgsProcessingParameterNumber::Double,
0.0 );
zOffset->setIsDynamic( true );
zOffset->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "DELTA_Z" ), QObject::tr( "Offset distance (z-axis)" ), QgsPropertyDefinition::Double ) );
zOffset->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( zOffset.release() );

std::unique_ptr< QgsProcessingParameterNumber > mOffset = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "DELTA_M" ),
QObject::tr( "Step distance (m values)" ), QgsProcessingParameterNumber::Double,
0.0 );
mOffset->setIsDynamic( true );
mOffset->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "DELTA_M" ), QObject::tr( "Offset distance (m values)" ), QgsPropertyDefinition::Double ) );
mOffset->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( mOffset.release() );
}

bool QgsArrayFeaturesAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
{
mCount = parameterAsInt( parameters, QStringLiteral( "COUNT" ), context );
mDynamicCount = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "COUNT" ) );
if ( mDynamicCount )
mCountProperty = parameters.value( QStringLiteral( "COUNT" ) ).value< QgsProperty >();

mDeltaX = parameterAsDouble( parameters, QStringLiteral( "DELTA_X" ), context );
mDynamicDeltaX = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "DELTA_X" ) );
if ( mDynamicDeltaX )
mDeltaXProperty = parameters.value( QStringLiteral( "DELTA_X" ) ).value< QgsProperty >();

mDeltaY = parameterAsDouble( parameters, QStringLiteral( "DELTA_Y" ), context );
mDynamicDeltaY = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "DELTA_Y" ) );
if ( mDynamicDeltaY )
mDeltaYProperty = parameters.value( QStringLiteral( "DELTA_Y" ) ).value< QgsProperty >();

mDeltaZ = parameterAsDouble( parameters, QStringLiteral( "DELTA_Z" ), context );
mDynamicDeltaZ = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "DELTA_Z" ) );
if ( mDynamicDeltaZ )
mDeltaZProperty = parameters.value( QStringLiteral( "DELTA_Z" ) ).value< QgsProperty >();

mDeltaM = parameterAsDouble( parameters, QStringLiteral( "DELTA_M" ), context );
mDynamicDeltaM = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "DELTA_M" ) );
if ( mDynamicDeltaM )
mDeltaMProperty = parameters.value( QStringLiteral( "DELTA_M" ) ).value< QgsProperty >();

return true;
}

QgsFeatureList QgsArrayFeaturesAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback * )
{
QgsFeatureList result = QgsFeatureList();

if ( feature.hasGeometry() )
{
QgsGeometry geometry = feature.geometry();
const QgsAttributes attr = feature.attributes();

int count = mCount;
if ( mDynamicCount )
count = mCountProperty.valueAsInt( context.expressionContext(), count );
result.reserve( count + 1 );

double deltaX = mDeltaX;
if ( mDynamicDeltaX )
deltaX = mDeltaXProperty.valueAsDouble( context.expressionContext(), deltaX );
double deltaY = mDeltaY;
if ( mDynamicDeltaY )
deltaY = mDeltaYProperty.valueAsDouble( context.expressionContext(), deltaY );
double deltaZ = mDeltaZ;
if ( mDynamicDeltaZ )
deltaZ = mDeltaZProperty.valueAsDouble( context.expressionContext(), deltaZ );
double deltaM = mDeltaM;
if ( mDynamicDeltaM )
deltaM = mDeltaMProperty.valueAsDouble( context.expressionContext(), deltaM );

// we add the original feature only after adding initial z/m values if needed
if ( deltaZ != 0 && !geometry.constGet()->is3D() )
geometry.get()->addZValue( 0 );
if ( deltaM != 0 && !geometry.constGet()->isMeasure() )
geometry.get()->addMValue( 0 );

{
QgsFeature original = feature;
original.setGeometry( geometry );
QgsAttributes outAttrs = attr;
outAttrs << QVariant( 0 );
original.setAttributes( outAttrs );
result << original;
}

for ( int i = 0; i < count; ++i )
{
QgsFeature offsetFeature = feature;
geometry.translate( deltaX, deltaY, deltaZ, deltaM );
offsetFeature.setGeometry( geometry );
QgsAttributes outAttrs = attr;
outAttrs << QVariant( i + 1 );
offsetFeature.setAttributes( outAttrs );
result << offsetFeature;
}
}
else
{
result << feature;
}

return result;
}

QgsWkbTypes::Type QgsArrayFeaturesAlgorithm::outputWkbType( QgsWkbTypes::Type inputWkbType ) const
{
QgsWkbTypes::Type wkb = inputWkbType;
if ( mDeltaZ != 0 )
wkb = QgsWkbTypes::addZ( wkb );
if ( mDeltaM != 0 )
wkb = QgsWkbTypes::addM( wkb );
return wkb;
}

QgsFields QgsArrayFeaturesAlgorithm::outputFields( const QgsFields &inputFields ) const
{
QgsFields output = inputFields;
output.append( QgsField( QStringLiteral( "instance" ), QVariant::Int ) );
return output;
}

///@endcond


83 changes: 83 additions & 0 deletions src/analysis/processing/qgsalgorithmarrayfeatures.h
@@ -0,0 +1,83 @@
/***************************************************************************
qgsalgorithmarrayfeatures.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 QGSALGORITHMARRAYFEATURES_H
#define QGSALGORITHMARRAYFEATURES_H

#define SIP_NO_FILE

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

///@cond PRIVATE

/**
* Native create array of features algorithm.
*/
class QgsArrayFeaturesAlgorithm : public QgsProcessingFeatureBasedAlgorithm
{

public:

QgsArrayFeaturesAlgorithm() = 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;
QgsArrayFeaturesAlgorithm *createInstance() const override SIP_FACTORY;
void initParameters( const QVariantMap &configuration = QVariantMap() ) 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;
QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const override;
QgsFields outputFields( const QgsFields &inputFields ) const override;

private:

int mCount = 0;
bool mDynamicCount = false;
QgsProperty mCountProperty;

double mDeltaX = 0.0;
bool mDynamicDeltaX = false;
QgsProperty mDeltaXProperty;

double mDeltaY = 0.0;
bool mDynamicDeltaY = false;
QgsProperty mDeltaYProperty;

double mDeltaZ = 0.0;
bool mDynamicDeltaZ = false;
QgsProperty mDeltaZProperty;

double mDeltaM = 0.0;
bool mDynamicDeltaM = false;
QgsProperty mDeltaMProperty;

};


///@endcond PRIVATE

#endif // QGSALGORITHMARRAYFEATURES_H


2 changes: 2 additions & 0 deletions src/analysis/processing/qgsnativealgorithms.cpp
Expand Up @@ -17,6 +17,7 @@

#include "qgsnativealgorithms.h"
#include "qgsalgorithmaddincrementalfield.h"
#include "qgsalgorithmarrayfeatures.h"
#include "qgsalgorithmassignprojection.h"
#include "qgsalgorithmboundary.h"
#include "qgsalgorithmboundingbox.h"
Expand Down Expand Up @@ -129,6 +130,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
{
addAlgorithm( new QgsAddIncrementalFieldAlgorithm() );
addAlgorithm( new QgsAddUniqueValueIndexAlgorithm() );
addAlgorithm( new QgsArrayFeaturesAlgorithm() );
addAlgorithm( new QgsAssignProjectionAlgorithm() );
addAlgorithm( new QgsBoundaryAlgorithm() );
addAlgorithm( new QgsBoundingBoxAlgorithm() );
Expand Down

0 comments on commit abe4532

Please sign in to comment.