Skip to content

Commit

Permalink
[FEATURE][processing] Add a save features to file algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn committed Jul 22, 2020
1 parent 05be43d commit 8c61a80
Show file tree
Hide file tree
Showing 14 changed files with 283 additions and 20 deletions.
Expand Up @@ -662,7 +662,7 @@ Evaluates the parameter with matching ``name`` to a static boolean value.
%End

QgsFeatureSink *parameterAsSink( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context, QString &destinationIdentifier /Out/,
const QgsFields &fields, QgsWkbTypes::Type geometryType = QgsWkbTypes::NoGeometry, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem(), QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags() ) const throw( QgsProcessingException ) /Factory/;
const QgsFields &fields, QgsWkbTypes::Type geometryType = QgsWkbTypes::NoGeometry, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem(), QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags(), const QVariantMap &createOptions = QVariantMap(), const QStringList &datasourceOptions = QStringList(), const QStringList &layerOptions = QStringList() ) const throw( QgsProcessingException ) /Factory/;
%Docstring
Evaluates the parameter with matching ``name`` to a feature sink.

Expand All @@ -675,6 +675,10 @@ of the resulting feature sink.
The ``destinationIdentifier`` argument will be set to a string which can be used to retrieve the layer corresponding
to the sink, e.g. via calling :py:func:`QgsProcessingUtils.mapLayerFromString()`.

The ``createOptions`` argument is used to pass on creation options such as layer name.

The ``datasourceOptions`` and ``layerOptions`` arguments is used to pass on GDAL-specific format driver options.

This function creates a new object and the caller takes responsibility for deleting the returned object.

:raises :: py:class:`QgsProcessingException`
Expand Down
Expand Up @@ -837,7 +837,7 @@ Evaluates the parameter with matching ``definition`` and ``value`` to a static b

static QgsFeatureSink *parameterAsSink( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters,
const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs,
QgsProcessingContext &context, QString &destinationIdentifier /Out/, QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags() ) /Factory/;
QgsProcessingContext &context, QString &destinationIdentifier /Out/, QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags(), const QVariantMap &createOptions = QVariantMap(), const QStringList &datasourceOptions = QStringList(), const QStringList &layerOptions = QStringList() ) /Factory/;
%Docstring
Evaluates the parameter with matching ``definition`` to a feature sink.

Expand All @@ -849,12 +849,16 @@ providers and stored temporarily in the ``context``. The ``destinationIdentifier
argument will be set to a string which can be used to retrieve the layer corresponding
to the sink, e.g. via calling :py:func:`QgsProcessingUtils.mapLayerFromString()`.

The ``createOptions`` argument is used to pass on creation options such as layer name.

The ``datasourceOptions`` and ``layerOptions`` arguments is used to pass on GDAL-specific format driver options.

This function creates a new object and the caller takes responsibility for deleting the returned object.
%End

static QgsFeatureSink *parameterAsSink( const QgsProcessingParameterDefinition *definition, const QVariant &value,
const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs,
QgsProcessingContext &context, QString &destinationIdentifier /Out/, QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags() ) throw( QgsProcessingException ) /Factory/;
QgsProcessingContext &context, QString &destinationIdentifier /Out/, QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags(), const QVariantMap &createOptions = QVariantMap(), const QStringList &datasourceOptions = QStringList(), const QStringList &layerOptions = QStringList() ) throw( QgsProcessingException ) /Factory/;
%Docstring
Evaluates the parameter with matching ``definition`` and ``value`` to a feature sink.

Expand All @@ -866,6 +870,10 @@ providers and stored temporarily in the ``context``. The ``destinationIdentifier
argument will be set to a string which can be used to retrieve the layer corresponding
to the sink, e.g. via calling :py:func:`QgsProcessingUtils.mapLayerFromString()`.

The ``createOptions`` argument is used to pass on creation options such as layer name.

The ``datasourceOptions`` and ``layerOptions`` arguments is used to pass on GDAL-specific format driver options.

This function creates a new object and the caller takes responsibility for deleting the returned object.

:raises :: py:class:`QgsProcessingException`
Expand Down
1 change: 1 addition & 0 deletions src/analysis/CMakeLists.txt
Expand Up @@ -147,6 +147,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmrotate.cpp
processing/qgsalgorithmroundrastervalues.cpp
processing/qgsalgorithmruggedness.cpp
processing/qgsalgorithmsavefeatures.cpp
processing/qgsalgorithmsavelog.cpp
processing/qgsalgorithmsaveselectedfeatures.cpp
processing/qgsalgorithmsegmentize.cpp
Expand Down
140 changes: 140 additions & 0 deletions src/analysis/processing/qgsalgorithmsavefeatures.cpp
@@ -0,0 +1,140 @@
/***************************************************************************
qgsalgorithmsavefeatures.cpp
---------------------
begin : July 2020
copyright : (C) 2020 by Mathieu Pellerin
email : nirvn dot asia 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 "qgsalgorithmsavefeatures.h"
#include "qgsvectorfilewriter.h"

///@cond PRIVATE

QString QgsSaveFeaturesAlgorithm::name() const
{
return QStringLiteral( "savefeatures" );
}

QString QgsSaveFeaturesAlgorithm::displayName() const
{
return QObject::tr( "Save vector features to file" );
}

QStringList QgsSaveFeaturesAlgorithm::tags() const
{
return QObject::tr( "save,write,export" ).split( ',' );
}

QString QgsSaveFeaturesAlgorithm::group() const
{
return QObject::tr( "Vector general" );
}

QString QgsSaveFeaturesAlgorithm::groupId() const
{
return QStringLiteral( "vectorgeneral" );
}

QString QgsSaveFeaturesAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm saves vector features to a specified file dataset.\n\n"
"For dataset formats supporting layers, an optional layer name parameter can be used to specify a custom string.\n\n"
"Optional GDAL-defined dataset and layer options can be specified. For more information on this, "
"read the online GDAL documentation." );
}

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

void QgsSaveFeaturesAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Vector features" ) ) );
addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Saved features" ), QgsVectorFileWriter::fileFilterString(), QVariant(), false ) );

std::unique_ptr< QgsProcessingParameterString > param = qgis::make_unique< QgsProcessingParameterString >( QStringLiteral( "LAYER_NAME" ), QObject::tr( "Layer name" ), QVariant(), false, true );
param->setFlags( param->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( param.release() );
param = qgis::make_unique< QgsProcessingParameterString >( QStringLiteral( "DATASOURCE_OPTIONS" ), QObject::tr( "GDAL dataset options (separate individual options with semicolons)" ), QVariant(), false, true );
param->setFlags( param->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( param.release() );
param = qgis::make_unique< QgsProcessingParameterString >( QStringLiteral( "LAYER_OPTIONS" ), QObject::tr( "GDAL layer options (separate individual options with semicolons)" ), QVariant(), false, true );
param->setFlags( param->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( param.release() );

addOutput( new QgsProcessingOutputString( QStringLiteral( "FILE_PATH" ), QObject::tr( "File name and path" ) ) );
addOutput( new QgsProcessingOutputString( QStringLiteral( "LAYER_NAME" ), QObject::tr( "Layer name" ) ) );
}

QVariantMap QgsSaveFeaturesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );

QString layerName = parameterAsString( parameters, QStringLiteral( "LAYER_NAME" ), context ).trimmed();
QVariantMap createOptions;
if ( !layerName.isEmpty() )
{
createOptions[QStringLiteral( "layerName" )] = layerName;
}

QStringList datasourceOptions = parameterAsString( parameters, QStringLiteral( "DATASOURCE_OPTIONS" ), context ).trimmed().split( ';', QString::SkipEmptyParts );
QStringList layerOptions = parameterAsString( parameters, QStringLiteral( "LAYER_OPTIONS" ), context ).trimmed().split( ';', QString::SkipEmptyParts );


QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(),
source->wkbType(), source->sourceCrs(), QgsFeatureSink::SinkFlags(), createOptions, datasourceOptions, layerOptions ) );
if ( !sink )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );

double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
long long i = 0;

QgsFeatureIterator features = source->getFeatures( QgsFeatureRequest(), QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks );
QgsFeature feat;
while ( features.nextFeature( feat ) )
{
i++;
if ( feedback->isCanceled() )
{
break;
}

feedback->setProgress( i * step );

sink->addFeature( feat, QgsFeatureSink::FastInsert );
}

QString filePath = dest;
layerName.clear(); // value of final layer name will be extracted from the destination string
int separatorIndex = dest.indexOf( '|' );
if ( separatorIndex > -1 )
{
QRegularExpression layerNameRx( QStringLiteral( "\\|layername=([^\\|]*)" ) );
QRegularExpressionMatch match = layerNameRx.match( dest );
if ( match.hasMatch() )
{
layerName = match.captured( 1 );
}
filePath = dest.mid( 0, separatorIndex );
}

QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
outputs.insert( QStringLiteral( "FILE_PATH" ), filePath );
outputs.insert( QStringLiteral( "LAYER_NAME" ), layerName );
return outputs;
}

///@endcond
53 changes: 53 additions & 0 deletions src/analysis/processing/qgsalgorithmsavefeatures.h
@@ -0,0 +1,53 @@
/***************************************************************************
qgsalgorithmsavefeatures.h
---------------------
begin : July 2020
copyright : (C) 2020 by Mathieu Pellerin
email : nirvn dot asia 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 QGSALGORITHMSAVEVECTOR_H
#define QGSALGORITHMSAVEVECTOR_H

#define SIP_NO_FILE

#include "qgis_sip.h"
#include "qgsprocessingalgorithm.h"

///@cond PRIVATE

/**
* Native save vector algorithm.
*/
class QgsSaveFeaturesAlgorithm : public QgsProcessingAlgorithm
{
public:
QgsSaveFeaturesAlgorithm() = 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;
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
QgsSaveFeaturesAlgorithm *createInstance() const override SIP_FACTORY;

protected:

QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;

};

///@endcond PRIVATE

#endif // QGSALGORITHMSAVEVECTOR_H
2 changes: 2 additions & 0 deletions src/analysis/processing/qgsnativealgorithms.cpp
Expand Up @@ -142,6 +142,7 @@
#include "qgsalgorithmrotate.h"
#include "qgsalgorithmroundrastervalues.h"
#include "qgsalgorithmruggedness.h"
#include "qgsalgorithmsavefeatures.h"
#include "qgsalgorithmsavelog.h"
#include "qgsalgorithmsaveselectedfeatures.h"
#include "qgsalgorithmsegmentize.h"
Expand Down Expand Up @@ -377,6 +378,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsRotateFeaturesAlgorithm() );
addAlgorithm( new QgsRoundRasterValuesAlgorithm() );
addAlgorithm( new QgsRuggednessAlgorithm() );
addAlgorithm( new QgsSaveFeaturesAlgorithm() );
addAlgorithm( new QgsSaveLogToFileAlgorithm() );
addAlgorithm( new QgsSaveSelectedFeatures() );
addAlgorithm( new QgsSegmentizeByMaximumAngleAlgorithm() );
Expand Down
4 changes: 2 additions & 2 deletions src/core/processing/qgsprocessingalgorithm.cpp
Expand Up @@ -610,12 +610,12 @@ bool QgsProcessingAlgorithm::parameterAsBoolean( const QVariantMap &parameters,
return QgsProcessingParameters::parameterAsBool( parameterDefinition( name ), parameters, context );
}

QgsFeatureSink *QgsProcessingAlgorithm::parameterAsSink( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context, QString &destinationIdentifier, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, QgsFeatureSink::SinkFlags sinkFlags ) const
QgsFeatureSink *QgsProcessingAlgorithm::parameterAsSink( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context, QString &destinationIdentifier, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, QgsFeatureSink::SinkFlags sinkFlags, const QVariantMap &createOptions, const QStringList &datasourceOptions, const QStringList &layerOptions ) const
{
if ( !parameterDefinition( name ) )
throw QgsProcessingException( QObject::tr( "No parameter definition for the sink '%1'" ).arg( name ) );

return QgsProcessingParameters::parameterAsSink( parameterDefinition( name ), parameters, fields, geometryType, crs, context, destinationIdentifier, sinkFlags );
return QgsProcessingParameters::parameterAsSink( parameterDefinition( name ), parameters, fields, geometryType, crs, context, destinationIdentifier, sinkFlags, createOptions, datasourceOptions, layerOptions );
}

QgsProcessingFeatureSource *QgsProcessingAlgorithm::parameterAsSource( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context ) const
Expand Down
6 changes: 5 additions & 1 deletion src/core/processing/qgsprocessingalgorithm.h
Expand Up @@ -679,12 +679,16 @@ class CORE_EXPORT QgsProcessingAlgorithm
* The \a destinationIdentifier argument will be set to a string which can be used to retrieve the layer corresponding
* to the sink, e.g. via calling QgsProcessingUtils::mapLayerFromString().
*
* The \a createOptions argument is used to pass on creation options such as layer name.
*
* The \a datasourceOptions and \a layerOptions arguments is used to pass on GDAL-specific format driver options.
*
* This function creates a new object and the caller takes responsibility for deleting the returned object.
*
* \throws QgsProcessingException
*/
QgsFeatureSink *parameterAsSink( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context, QString &destinationIdentifier SIP_OUT,
const QgsFields &fields, QgsWkbTypes::Type geometryType = QgsWkbTypes::NoGeometry, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem(), QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags() ) const SIP_THROW( QgsProcessingException ) SIP_FACTORY;
const QgsFields &fields, QgsWkbTypes::Type geometryType = QgsWkbTypes::NoGeometry, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem(), QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags(), const QVariantMap &createOptions = QVariantMap(), const QStringList &datasourceOptions = QStringList(), const QStringList &layerOptions = QStringList() ) const SIP_THROW( QgsProcessingException ) SIP_FACTORY;

/**
* Evaluates the parameter with matching \a name to a feature source.
Expand Down

0 comments on commit 8c61a80

Please sign in to comment.