QGIS API Documentation 3.43.0-Master (e01d6d7c4c0)
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
57QString QgsSaveFeaturesAlgorithm::shortDescription() const
58{
59 return QObject::tr( "Saves vector features to a specified file dataset." );
60}
61
62QgsSaveFeaturesAlgorithm *QgsSaveFeaturesAlgorithm::createInstance() const
63{
64 return new QgsSaveFeaturesAlgorithm();
65}
66
67void QgsSaveFeaturesAlgorithm::initAlgorithm( const QVariantMap & )
68{
69 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Vector features" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::Vector ) ) );
70 addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Saved features" ), QgsVectorFileWriter::fileFilterString(), QVariant(), false ) );
71
72 auto param = std::make_unique<QgsProcessingParameterString>( QStringLiteral( "LAYER_NAME" ), QObject::tr( "Layer name" ), QVariant(), false, true );
73 param->setFlags( param->flags() | Qgis::ProcessingParameterFlag::Advanced );
74 addParameter( param.release() );
75 param = std::make_unique<QgsProcessingParameterString>( QStringLiteral( "DATASOURCE_OPTIONS" ), QObject::tr( "GDAL dataset options (separate individual options with semicolons)" ), QVariant(), false, true );
76 param->setFlags( param->flags() | Qgis::ProcessingParameterFlag::Advanced );
77 addParameter( param.release() );
78 param = std::make_unique<QgsProcessingParameterString>( QStringLiteral( "LAYER_OPTIONS" ), QObject::tr( "GDAL layer options (separate individual options with semicolons)" ), QVariant(), false, true );
79 param->setFlags( param->flags() | Qgis::ProcessingParameterFlag::Advanced );
80 addParameter( param.release() );
81
82 auto 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 );
83 paramEnum->setFlags( paramEnum->flags() | Qgis::ProcessingParameterFlag::Advanced );
84 addParameter( paramEnum.release() );
85
86 addOutput( new QgsProcessingOutputString( QStringLiteral( "FILE_PATH" ), QObject::tr( "File name and path" ) ) );
87 addOutput( new QgsProcessingOutputString( QStringLiteral( "LAYER_NAME" ), QObject::tr( "Layer name" ) ) );
88}
89
90QVariantMap QgsSaveFeaturesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
91{
92 std::unique_ptr<QgsProcessingFeatureSource> source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
93 if ( !source )
94 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
95
96 QString layerName = parameterAsString( parameters, QStringLiteral( "LAYER_NAME" ), context ).trimmed();
97 QVariantMap createOptions;
98 if ( !layerName.isEmpty() )
99 {
100 createOptions[QStringLiteral( "layerName" )] = layerName;
101 }
102
103 const QStringList datasourceOptions = parameterAsString( parameters, QStringLiteral( "DATASOURCE_OPTIONS" ), context ).trimmed().split( ';', Qt::SkipEmptyParts );
104 const QStringList layerOptions = parameterAsString( parameters, QStringLiteral( "LAYER_OPTIONS" ), context ).trimmed().split( ';', Qt::SkipEmptyParts );
105
106 QString destination = parameterAsString( parameters, QStringLiteral( "OUTPUT" ), context );
107 const QString format = QgsVectorFileWriter::driverForExtension( QFileInfo( destination ).completeSuffix() );
108
109 const QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile = static_cast<QgsVectorFileWriter::ActionOnExistingFile>( parameterAsInt( parameters, QStringLiteral( "ACTION_ON_EXISTING_FILE" ), context ) );
110
111 QString finalFileName;
112 QString finalLayerName;
114 saveOptions.fileEncoding = context.defaultEncoding().isEmpty() ? QStringLiteral( "system" ) : context.defaultEncoding();
115 saveOptions.layerName = layerName;
116 saveOptions.driverName = format;
117 saveOptions.datasourceOptions = datasourceOptions;
118 saveOptions.layerOptions = layerOptions;
120 saveOptions.actionOnExistingFile = actionOnExistingFile;
121
122 std::unique_ptr<QgsVectorFileWriter> writer( QgsVectorFileWriter::create( destination, source->fields(), source->wkbType(), source->sourceCrs(), context.transformContext(), saveOptions, QgsFeatureSink::SinkFlags(), &finalFileName, &finalLayerName ) );
123 if ( writer->hasError() )
124 {
125 throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, writer->errorMessage() ) );
126 }
127
128 if ( QgsProcessingFeedback *feedback = context.feedback() )
129 {
130 for ( const QgsField &field : source->fields() )
131 {
132 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldAliases ) )
133 feedback->pushWarning( QObject::tr( "%1: Aliases are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
134 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldComments ) )
135 feedback->pushWarning( QObject::tr( "%1: Comments are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
136 }
137 }
138
139 destination = finalFileName;
140 if ( !saveOptions.layerName.isEmpty() && !finalLayerName.isEmpty() )
141 destination += QStringLiteral( "|layername=%1" ).arg( finalLayerName );
142
143 std::unique_ptr<QgsFeatureSink> sink( new QgsProcessingFeatureSink( writer.release(), destination, context, true ) );
144 if ( !sink )
145 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
146
147 const double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
148 long long i = 0;
149
151 QgsFeature feat;
152 while ( features.nextFeature( feat ) )
153 {
154 i++;
155 if ( feedback->isCanceled() )
156 {
157 break;
158 }
159
160 feedback->setProgress( i * step );
161
162 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
163 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
164 }
165
166 finalFileName = destination;
167 finalLayerName.clear(); // value of final layer name will be extracted from the destination string
168 const int separatorIndex = destination.indexOf( '|' );
169 if ( separatorIndex > -1 )
170 {
171 const thread_local QRegularExpression layerNameRx( QStringLiteral( "\\|layername=([^\\|]*)" ) );
172 const QRegularExpressionMatch match = layerNameRx.match( destination );
173 if ( match.hasMatch() )
174 {
175 finalLayerName = match.captured( 1 );
176 }
177 finalFileName = destination.mid( 0, separatorIndex );
178 }
179
180 QVariantMap outputs;
181 outputs.insert( QStringLiteral( "OUTPUT" ), destination );
182 outputs.insert( QStringLiteral( "FILE_PATH" ), finalFileName );
183 outputs.insert( QStringLiteral( "LAYER_NAME" ), finalLayerName );
184 return outputs;
185}
186
@ 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.
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:58
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.
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 QgsVectorFileWriter::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.