QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsalgorithmsavefeatures.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmsavefeatures.cpp
3 ---------------------
4 begin : July 2020
5 copyright : (C) 2020 by Mathieu Pellerin
6 email : nirvn dot asia 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
19#include "qgsvectorfilewriter.h"
20#include <QRegularExpression>
21
23
24QString QgsSaveFeaturesAlgorithm::name() const
25{
26 return QStringLiteral( "savefeatures" );
27}
28
29QString QgsSaveFeaturesAlgorithm::displayName() const
30{
31 return QObject::tr( "Save vector features to file" );
32}
33
34QStringList QgsSaveFeaturesAlgorithm::tags() const
35{
36 return QObject::tr( "save,write,export" ).split( ',' );
37}
38
39QString QgsSaveFeaturesAlgorithm::group() const
40{
41 return QObject::tr( "Vector general" );
42}
43
44QString QgsSaveFeaturesAlgorithm::groupId() const
45{
46 return QStringLiteral( "vectorgeneral" );
47}
48
49QString QgsSaveFeaturesAlgorithm::shortHelpString() const
50{
51 return QObject::tr( "This algorithm saves vector features to a specified file dataset.\n\n"
52 "For dataset formats supporting layers, an optional layer name parameter can be used to specify a custom string.\n\n"
53 "Optional GDAL-defined dataset and layer options can be specified. For more information on this, "
54 "read the online GDAL documentation." );
55}
56
57QgsSaveFeaturesAlgorithm *QgsSaveFeaturesAlgorithm::createInstance() const
58{
59 return new QgsSaveFeaturesAlgorithm();
60}
61
62void QgsSaveFeaturesAlgorithm::initAlgorithm( const QVariantMap & )
63{
64 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Vector features" ), QList<int>() << static_cast< int >( Qgis::ProcessingSourceType::Vector ) ) );
65 addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Saved features" ), QgsVectorFileWriter::fileFilterString(), QVariant(), false ) );
66
67 std::unique_ptr< QgsProcessingParameterString > param = std::make_unique< QgsProcessingParameterString >( QStringLiteral( "LAYER_NAME" ), QObject::tr( "Layer name" ), QVariant(), false, true );
68 param->setFlags( param->flags() | Qgis::ProcessingParameterFlag::Advanced );
69 addParameter( param.release() );
70 param = std::make_unique< QgsProcessingParameterString >( QStringLiteral( "DATASOURCE_OPTIONS" ), QObject::tr( "GDAL dataset options (separate individual options with semicolons)" ), QVariant(), false, true );
71 param->setFlags( param->flags() | Qgis::ProcessingParameterFlag::Advanced );
72 addParameter( param.release() );
73 param = std::make_unique< QgsProcessingParameterString >( QStringLiteral( "LAYER_OPTIONS" ), QObject::tr( "GDAL layer options (separate individual options with semicolons)" ), QVariant(), false, true );
74 param->setFlags( param->flags() | Qgis::ProcessingParameterFlag::Advanced );
75 addParameter( param.release() );
76
77 std::unique_ptr< QgsProcessingParameterEnum > paramEnum = std::make_unique< QgsProcessingParameterEnum >( QStringLiteral( "ACTION_ON_EXISTING_FILE" ), QObject::tr( "Action to take on pre-existing file" ), QStringList() << QObject::tr( "Create or overwrite file" ) << QObject::tr( "Create or overwrite layer" ) << QObject::tr( "Append features to existing layer, but do not create new fields" ) << QObject::tr( "Append features to existing layer, and create new fields if needed" ), false, 0 );
78 paramEnum->setFlags( paramEnum->flags() | Qgis::ProcessingParameterFlag::Advanced );
79 addParameter( paramEnum.release() );
80
81 addOutput( new QgsProcessingOutputString( QStringLiteral( "FILE_PATH" ), QObject::tr( "File name and path" ) ) );
82 addOutput( new QgsProcessingOutputString( QStringLiteral( "LAYER_NAME" ), QObject::tr( "Layer name" ) ) );
83}
84
85QVariantMap QgsSaveFeaturesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
86{
87 std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
88
89 QString layerName = parameterAsString( parameters, QStringLiteral( "LAYER_NAME" ), context ).trimmed();
90 QVariantMap createOptions;
91 if ( !layerName.isEmpty() )
92 {
93 createOptions[QStringLiteral( "layerName" )] = layerName;
94 }
95
96 const QStringList datasourceOptions = parameterAsString( parameters, QStringLiteral( "DATASOURCE_OPTIONS" ), context ).trimmed().split( ';', Qt::SkipEmptyParts );
97 const QStringList layerOptions = parameterAsString( parameters, QStringLiteral( "LAYER_OPTIONS" ), context ).trimmed().split( ';', Qt::SkipEmptyParts );
98
99 QString destination = parameterAsString( parameters, QStringLiteral( "OUTPUT" ), context );
100 const QString format = QgsVectorFileWriter::driverForExtension( QFileInfo( destination ).completeSuffix() );
101
102 const QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile = static_cast< QgsVectorFileWriter::ActionOnExistingFile >( parameterAsInt( parameters, QStringLiteral( "ACTION_ON_EXISTING_FILE" ), context ) );
103
104 QString finalFileName;
105 QString finalLayerName;
107 saveOptions.fileEncoding = context.defaultEncoding().isEmpty() ? QStringLiteral( "system" ) : context.defaultEncoding();
108 saveOptions.layerName = layerName;
109 saveOptions.driverName = format;
110 saveOptions.datasourceOptions = datasourceOptions;
111 saveOptions.layerOptions = layerOptions;
113 saveOptions.actionOnExistingFile = actionOnExistingFile;
114
115 std::unique_ptr< QgsVectorFileWriter > writer( QgsVectorFileWriter::create( destination, source->fields(), source->wkbType(), source->sourceCrs(), context.transformContext(), saveOptions, QgsFeatureSink::SinkFlags(), &finalFileName, &finalLayerName ) );
116 if ( writer->hasError() )
117 {
118 throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, writer->errorMessage() ) );
119 }
120
121 if ( QgsProcessingFeedback *feedback = context.feedback() )
122 {
123 for ( const QgsField &field : source->fields() )
124 {
125 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldAliases ) )
126 feedback->pushWarning( QObject::tr( "%1: Aliases are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
127 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldComments ) )
128 feedback->pushWarning( QObject::tr( "%1: Comments are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
129 }
130 }
131
132 destination = finalFileName;
133 if ( !saveOptions.layerName.isEmpty() && !finalLayerName.isEmpty() )
134 destination += QStringLiteral( "|layername=%1" ).arg( finalLayerName );
135
136 std::unique_ptr< QgsFeatureSink > sink( new QgsProcessingFeatureSink( writer.release(), destination, context, true ) );
137 if ( !sink )
138 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
139
140 const double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
141 long long i = 0;
142
144 QgsFeature feat;
145 while ( features.nextFeature( feat ) )
146 {
147 i++;
148 if ( feedback->isCanceled() )
149 {
150 break;
151 }
152
153 feedback->setProgress( i * step );
154
155 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
156 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
157 }
158
159 finalFileName = destination;
160 finalLayerName.clear(); // value of final layer name will be extracted from the destination string
161 const int separatorIndex = destination.indexOf( '|' );
162 if ( separatorIndex > -1 )
163 {
164 const thread_local QRegularExpression layerNameRx( QStringLiteral( "\\|layername=([^\\|]*)" ) );
165 const QRegularExpressionMatch match = layerNameRx.match( destination );
166 if ( match.hasMatch() )
167 {
168 finalLayerName = match.captured( 1 );
169 }
170 finalFileName = destination.mid( 0, separatorIndex );
171 }
172
173 QVariantMap outputs;
174 outputs.insert( QStringLiteral( "OUTPUT" ), destination );
175 outputs.insert( QStringLiteral( "FILE_PATH" ), finalFileName );
176 outputs.insert( QStringLiteral( "LAYER_NAME" ), finalLayerName );
177 return outputs;
178}
179
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
@ FieldComments
Writer can support field comments.
@ FieldAliases
Writer can support field aliases.
@ SkipGeometryValidityChecks
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
@ NoSymbology
Export only data.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QFlags< SinkFlag > SinkFlags
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:61
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:53
Contains information about the context in which a processing algorithm is executed.
QString defaultEncoding() const
Returns the default encoding to use for newly created files.
QgsProcessingFeedback * feedback()
Returns the associated feedback object.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:83
QgsProxyFeatureSink subclass which reports feature addition errors to a QgsProcessingContext.
Base class for providing feedback from a processing algorithm.
virtual void pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
A string output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
A generic file based destination parameter, for specifying the destination path for a file (non-map l...
Options to pass to writeAsVectorFormat()
QString layerName
Layer name. If let empty, it will be derived from the filename.
QStringList layerOptions
List of OGR layer creation options.
Qgis::FeatureSymbologyExport symbologyExport
Symbology to export.
QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile
Action on existing file.
QStringList datasourceOptions
List of OGR data source creation options.
static QString driverForExtension(const QString &extension)
Returns the OGR driver name for a specified file extension.
static QgsVectorFileWriter * create(const QString &fileName, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &srs, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QString *newFilename=nullptr, QString *newLayer=nullptr)
Create a new vector file writer.
static QString fileFilterString(VectorFormatOptions options=SortRecommended)
Returns filter string that can be used for dialogs.
ActionOnExistingFile
Enumeration to describe how to handle existing files.