QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
Loading...
Searching...
No Matches
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
20#include "qgsvectorfilewriter.h"
21
22#include <QRegularExpression>
23#include <QString>
24
25using namespace Qt::StringLiterals;
26
28
29QString QgsSaveFeaturesAlgorithm::name() const
30{
31 return u"savefeatures"_s;
32}
33
34QString QgsSaveFeaturesAlgorithm::displayName() const
35{
36 return QObject::tr( "Save vector features to file" );
37}
38
39QStringList QgsSaveFeaturesAlgorithm::tags() const
40{
41 return QObject::tr( "save,write,export" ).split( ',' );
42}
43
44QString QgsSaveFeaturesAlgorithm::group() const
45{
46 return QObject::tr( "Vector general" );
47}
48
49QString QgsSaveFeaturesAlgorithm::groupId() const
50{
51 return u"vectorgeneral"_s;
52}
53
54QString QgsSaveFeaturesAlgorithm::shortHelpString() const
55{
56 return QObject::tr(
57 "This algorithm saves vector features to a specified file dataset.\n\n"
58 "For dataset formats supporting layers, an optional layer name parameter can be used to specify a custom string.\n\n"
59 "Optional GDAL-defined dataset and layer options can be specified. For more information on this, "
60 "read the online GDAL documentation."
61 );
62}
63
64QString QgsSaveFeaturesAlgorithm::shortDescription() const
65{
66 return QObject::tr( "Saves vector features to a specified file dataset." );
67}
68
69QgsSaveFeaturesAlgorithm *QgsSaveFeaturesAlgorithm::createInstance() const
70{
71 return new QgsSaveFeaturesAlgorithm();
72}
73
74void QgsSaveFeaturesAlgorithm::initAlgorithm( const QVariantMap & )
75{
76 addParameter( new QgsProcessingParameterFeatureSource( u"INPUT"_s, QObject::tr( "Vector features" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::Vector ) ) );
77 addParameter( new QgsProcessingParameterFileDestination( u"OUTPUT"_s, QObject::tr( "Saved features" ), QgsVectorFileWriter::fileFilterString(), QVariant(), false ) );
78
79 auto param = std::make_unique<QgsProcessingParameterString>( u"LAYER_NAME"_s, QObject::tr( "Layer name" ), QVariant(), false, true );
80 param->setFlags( param->flags() | Qgis::ProcessingParameterFlag::Advanced );
81 addParameter( param.release() );
82 param = std::make_unique<QgsProcessingParameterString>( u"DATASOURCE_OPTIONS"_s, QObject::tr( "GDAL dataset options (separate individual options with semicolons)" ), QVariant(), false, true );
83 param->setFlags( param->flags() | Qgis::ProcessingParameterFlag::Advanced );
84 addParameter( param.release() );
85 param = std::make_unique<QgsProcessingParameterString>( u"LAYER_OPTIONS"_s, QObject::tr( "GDAL layer options (separate individual options with semicolons)" ), QVariant(), false, true );
86 param->setFlags( param->flags() | Qgis::ProcessingParameterFlag::Advanced );
87 addParameter( param.release() );
88
89 auto paramEnum = std::make_unique<QgsProcessingParameterEnum>(
90 u"ACTION_ON_EXISTING_FILE"_s,
91 QObject::tr( "Action to take on pre-existing file" ),
92 QStringList()
93 << QObject::tr( "Create or overwrite file" )
94 << QObject::tr( "Create or overwrite layer" )
95 << QObject::tr( "Append features to existing layer, but do not create new fields" )
96 << QObject::tr( "Append features to existing layer, and create new fields if needed" ),
97 false,
98 0
99 );
100 paramEnum->setFlags( paramEnum->flags() | Qgis::ProcessingParameterFlag::Advanced );
101 addParameter( paramEnum.release() );
102
103 addOutput( new QgsProcessingOutputString( u"FILE_PATH"_s, QObject::tr( "File name and path" ) ) );
104 addOutput( new QgsProcessingOutputString( u"LAYER_NAME"_s, QObject::tr( "Layer name" ) ) );
105}
106
107QVariantMap QgsSaveFeaturesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
108{
109 std::unique_ptr<QgsProcessingFeatureSource> source( parameterAsSource( parameters, u"INPUT"_s, context ) );
110 if ( !source )
111 throw QgsProcessingException( invalidSourceError( parameters, u"INPUT"_s ) );
112
113 QString layerName = parameterAsString( parameters, u"LAYER_NAME"_s, context ).trimmed();
114 QVariantMap createOptions;
115 if ( !layerName.isEmpty() )
116 {
117 createOptions[u"layerName"_s] = layerName;
118 }
119
120 const QStringList datasourceOptions = parameterAsString( parameters, u"DATASOURCE_OPTIONS"_s, context ).trimmed().split( ';', Qt::SkipEmptyParts );
121 const QStringList layerOptions = parameterAsString( parameters, u"LAYER_OPTIONS"_s, context ).trimmed().split( ';', Qt::SkipEmptyParts );
122
123 QString destination = parameterAsString( parameters, u"OUTPUT"_s, context );
124 const QString format = QgsVectorFileWriter::driverForExtension( QFileInfo( destination ).completeSuffix() );
125
126 const QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile = static_cast<QgsVectorFileWriter::ActionOnExistingFile>( parameterAsInt( parameters, u"ACTION_ON_EXISTING_FILE"_s, context ) );
127
128 QString finalFileName;
129 QString finalLayerName;
131 saveOptions.fileEncoding = context.defaultEncoding().isEmpty() ? u"system"_s : context.defaultEncoding();
132 saveOptions.layerName = layerName;
133 saveOptions.driverName = format;
134 saveOptions.datasourceOptions = datasourceOptions;
135 saveOptions.layerOptions = layerOptions;
137 saveOptions.actionOnExistingFile = actionOnExistingFile;
138
139 std::unique_ptr<QgsVectorFileWriter> writer(
140 QgsVectorFileWriter::create( destination, source->fields(), source->wkbType(), source->sourceCrs(), context.transformContext(), saveOptions, QgsFeatureSink::SinkFlags(), &finalFileName, &finalLayerName )
141 );
142 if ( writer->hasError() )
143 {
144 throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, writer->errorMessage() ) );
145 }
146
147 if ( QgsProcessingFeedback *feedback = context.feedback() )
148 {
149 for ( const QgsField &field : source->fields() )
150 {
151 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldAliases ) )
152 feedback->pushWarning( QObject::tr( "%1: Aliases are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
153 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldComments ) )
154 feedback->pushWarning( QObject::tr( "%1: Comments are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
155 }
156 }
157
158 destination = finalFileName;
159 if ( !saveOptions.layerName.isEmpty() && !finalLayerName.isEmpty() )
160 destination += u"|layername=%1"_s.arg( finalLayerName );
161
162 std::unique_ptr<QgsFeatureSink> sink( new QgsProcessingFeatureSink( writer.release(), destination, context, true ) );
163 if ( !sink )
164 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
165
166 const double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
167 long long i = 0;
168
170 QgsFeature feat;
171 while ( features.nextFeature( feat ) )
172 {
173 i++;
174 if ( feedback->isCanceled() )
175 {
176 break;
177 }
178
179 feedback->setProgress( i * step );
180
181 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
182 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, u"OUTPUT"_s ) );
183 }
184
185 finalFileName = destination;
186 finalLayerName.clear(); // value of final layer name will be extracted from the destination string
187 const int separatorIndex = destination.indexOf( '|' );
188 if ( separatorIndex > -1 )
189 {
190 const thread_local QRegularExpression layerNameRx( u"\\|layername=([^\\|]*)"_s );
191 const QRegularExpressionMatch match = layerNameRx.match( destination );
192 if ( match.hasMatch() )
193 {
194 finalLayerName = match.captured( 1 );
195 }
196 finalFileName = destination.mid( 0, separatorIndex );
197 }
198
199 QVariantMap outputs;
200 outputs.insert( u"OUTPUT"_s, destination );
201 outputs.insert( u"FILE_PATH"_s, finalFileName );
202 outputs.insert( u"LAYER_NAME"_s, finalLayerName );
203 return outputs;
204}
205
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition qgis.h:3653
@ FieldComments
Writer can support field comments.
Definition qgis.h:1101
@ FieldAliases
Writer can support field aliases.
Definition qgis.h:1100
@ SkipGeometryValidityChecks
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
Definition qgis.h:3828
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3880
@ NoSymbology
Export only data.
Definition qgis.h:5914
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:60
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:65
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
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.