QGIS API Documentation 4.3.0-Master (45633be667c)
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 QGS_MARK_ALGORITHM_SOURCE
110
111 std::unique_ptr<QgsProcessingFeatureSource> source( parameterAsSource( parameters, u"INPUT"_s, context ) );
112 if ( !source )
113 throw QgsProcessingException( invalidSourceError( parameters, u"INPUT"_s ) );
114
115 QString layerName = parameterAsString( parameters, u"LAYER_NAME"_s, context ).trimmed();
116 QVariantMap createOptions;
117 if ( !layerName.isEmpty() )
118 {
119 createOptions[u"layerName"_s] = layerName;
120 }
121
122 const QStringList datasourceOptions = parameterAsString( parameters, u"DATASOURCE_OPTIONS"_s, context ).trimmed().split( ';', Qt::SkipEmptyParts );
123 const QStringList layerOptions = parameterAsString( parameters, u"LAYER_OPTIONS"_s, context ).trimmed().split( ';', Qt::SkipEmptyParts );
124
125 QString destination = parameterAsString( parameters, u"OUTPUT"_s, context );
126 const QString format = QgsVectorFileWriter::driverForExtension( QFileInfo( destination ).completeSuffix() );
127
128 const QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile = static_cast<QgsVectorFileWriter::ActionOnExistingFile>( parameterAsInt( parameters, u"ACTION_ON_EXISTING_FILE"_s, context ) );
129
130 QString finalFileName;
131 QString finalLayerName;
133 saveOptions.fileEncoding = context.defaultEncoding().isEmpty() ? u"system"_s : context.defaultEncoding();
134 saveOptions.layerName = layerName;
135 saveOptions.driverName = format;
136 saveOptions.datasourceOptions = datasourceOptions;
137 saveOptions.layerOptions = layerOptions;
139 saveOptions.actionOnExistingFile = actionOnExistingFile;
140
141 std::unique_ptr<QgsVectorFileWriter> writer(
142 QgsVectorFileWriter::create( destination, source->fields(), source->wkbType(), source->sourceCrs(), context.transformContext(), saveOptions, QgsFeatureSink::SinkFlags(), &finalFileName, &finalLayerName )
143 );
144 if ( writer->hasError() )
145 {
146 throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, writer->errorMessage() ) );
147 }
148
149 if ( QgsProcessingFeedback *feedback = context.feedback() )
150 {
151 for ( const QgsField &field : source->fields() )
152 {
153 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldAliases ) )
154 feedback->pushWarning( QObject::tr( "%1: Aliases are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
155 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldComments ) )
156 feedback->pushWarning( QObject::tr( "%1: Comments are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
157 }
158 }
159
160 destination = finalFileName;
161 if ( !saveOptions.layerName.isEmpty() && !finalLayerName.isEmpty() )
162 destination += u"|layername=%1"_s.arg( finalLayerName );
163
164 std::unique_ptr<QgsFeatureSink> sink( new QgsProcessingFeatureSink( writer.release(), destination, context, true ) );
165 if ( !sink )
166 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
167
168 const double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
169 long long i = 0;
170
172 QgsFeature feat;
173 while ( features.nextFeature( feat ) )
174 {
175 i++;
176 if ( feedback->isCanceled() )
177 {
178 break;
179 }
180
181 feedback->setProgress( i * step );
182
183 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
184 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, u"OUTPUT"_s ) );
185 }
186
187 finalFileName = destination;
188 finalLayerName.clear(); // value of final layer name will be extracted from the destination string
189 const int separatorIndex = destination.indexOf( '|' );
190 if ( separatorIndex > -1 )
191 {
192 const thread_local QRegularExpression layerNameRx( u"\\|layername=([^\\|]*)"_s );
193 const QRegularExpressionMatch match = layerNameRx.match( destination );
194 if ( match.hasMatch() )
195 {
196 finalLayerName = match.captured( 1 );
197 }
198 finalFileName = destination.mid( 0, separatorIndex );
199 }
200
201 QVariantMap outputs;
202 outputs.insert( u"OUTPUT"_s, destination );
203 outputs.insert( u"FILE_PATH"_s, finalFileName );
204 outputs.insert( u"LAYER_NAME"_s, finalLayerName );
205 return outputs;
206}
207
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition qgis.h:3757
@ FieldComments
Writer can support field comments.
Definition qgis.h:1156
@ FieldAliases
Writer can support field aliases.
Definition qgis.h:1155
@ SkipGeometryValidityChecks
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
Definition qgis.h:3954
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:4006
@ NoSymbology
Export only data.
Definition qgis.h:6346
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.