QGIS API Documentation 3.99.0-Master (357b655ed83)
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( "This algorithm saves vector features to a specified file dataset.\n\n"
57 "For dataset formats supporting layers, an optional layer name parameter can be used to specify a custom string.\n\n"
58 "Optional GDAL-defined dataset and layer options can be specified. For more information on this, "
59 "read the online GDAL documentation." );
60}
61
62QString QgsSaveFeaturesAlgorithm::shortDescription() const
63{
64 return QObject::tr( "Saves vector features to a specified file dataset." );
65}
66
67QgsSaveFeaturesAlgorithm *QgsSaveFeaturesAlgorithm::createInstance() const
68{
69 return new QgsSaveFeaturesAlgorithm();
70}
71
72void QgsSaveFeaturesAlgorithm::initAlgorithm( const QVariantMap & )
73{
74 addParameter( new QgsProcessingParameterFeatureSource( u"INPUT"_s, QObject::tr( "Vector features" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::Vector ) ) );
75 addParameter( new QgsProcessingParameterFileDestination( u"OUTPUT"_s, QObject::tr( "Saved features" ), QgsVectorFileWriter::fileFilterString(), QVariant(), false ) );
76
77 auto param = std::make_unique<QgsProcessingParameterString>( u"LAYER_NAME"_s, QObject::tr( "Layer name" ), QVariant(), false, true );
78 param->setFlags( param->flags() | Qgis::ProcessingParameterFlag::Advanced );
79 addParameter( param.release() );
80 param = std::make_unique<QgsProcessingParameterString>( u"DATASOURCE_OPTIONS"_s, QObject::tr( "GDAL dataset options (separate individual options with semicolons)" ), QVariant(), false, true );
81 param->setFlags( param->flags() | Qgis::ProcessingParameterFlag::Advanced );
82 addParameter( param.release() );
83 param = std::make_unique<QgsProcessingParameterString>( u"LAYER_OPTIONS"_s, QObject::tr( "GDAL layer options (separate individual options with semicolons)" ), QVariant(), false, true );
84 param->setFlags( param->flags() | Qgis::ProcessingParameterFlag::Advanced );
85 addParameter( param.release() );
86
87 auto paramEnum = std::make_unique<QgsProcessingParameterEnum>( u"ACTION_ON_EXISTING_FILE"_s, 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 );
88 paramEnum->setFlags( paramEnum->flags() | Qgis::ProcessingParameterFlag::Advanced );
89 addParameter( paramEnum.release() );
90
91 addOutput( new QgsProcessingOutputString( u"FILE_PATH"_s, QObject::tr( "File name and path" ) ) );
92 addOutput( new QgsProcessingOutputString( u"LAYER_NAME"_s, QObject::tr( "Layer name" ) ) );
93}
94
95QVariantMap QgsSaveFeaturesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
96{
97 std::unique_ptr<QgsProcessingFeatureSource> source( parameterAsSource( parameters, u"INPUT"_s, context ) );
98 if ( !source )
99 throw QgsProcessingException( invalidSourceError( parameters, u"INPUT"_s ) );
100
101 QString layerName = parameterAsString( parameters, u"LAYER_NAME"_s, context ).trimmed();
102 QVariantMap createOptions;
103 if ( !layerName.isEmpty() )
104 {
105 createOptions[u"layerName"_s] = layerName;
106 }
107
108 const QStringList datasourceOptions = parameterAsString( parameters, u"DATASOURCE_OPTIONS"_s, context ).trimmed().split( ';', Qt::SkipEmptyParts );
109 const QStringList layerOptions = parameterAsString( parameters, u"LAYER_OPTIONS"_s, context ).trimmed().split( ';', Qt::SkipEmptyParts );
110
111 QString destination = parameterAsString( parameters, u"OUTPUT"_s, context );
112 const QString format = QgsVectorFileWriter::driverForExtension( QFileInfo( destination ).completeSuffix() );
113
114 const QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile = static_cast<QgsVectorFileWriter::ActionOnExistingFile>( parameterAsInt( parameters, u"ACTION_ON_EXISTING_FILE"_s, context ) );
115
116 QString finalFileName;
117 QString finalLayerName;
119 saveOptions.fileEncoding = context.defaultEncoding().isEmpty() ? u"system"_s : context.defaultEncoding();
120 saveOptions.layerName = layerName;
121 saveOptions.driverName = format;
122 saveOptions.datasourceOptions = datasourceOptions;
123 saveOptions.layerOptions = layerOptions;
125 saveOptions.actionOnExistingFile = actionOnExistingFile;
126
127 std::unique_ptr<QgsVectorFileWriter> writer( QgsVectorFileWriter::create( destination, source->fields(), source->wkbType(), source->sourceCrs(), context.transformContext(), saveOptions, QgsFeatureSink::SinkFlags(), &finalFileName, &finalLayerName ) );
128 if ( writer->hasError() )
129 {
130 throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, writer->errorMessage() ) );
131 }
132
133 if ( QgsProcessingFeedback *feedback = context.feedback() )
134 {
135 for ( const QgsField &field : source->fields() )
136 {
137 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldAliases ) )
138 feedback->pushWarning( QObject::tr( "%1: Aliases are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
139 if ( !field.alias().isEmpty() && !( writer->capabilities() & Qgis::VectorFileWriterCapability::FieldComments ) )
140 feedback->pushWarning( QObject::tr( "%1: Comments are not supported by %2" ).arg( field.name(), writer->driverLongName() ) );
141 }
142 }
143
144 destination = finalFileName;
145 if ( !saveOptions.layerName.isEmpty() && !finalLayerName.isEmpty() )
146 destination += u"|layername=%1"_s.arg( finalLayerName );
147
148 std::unique_ptr<QgsFeatureSink> sink( new QgsProcessingFeatureSink( writer.release(), destination, context, true ) );
149 if ( !sink )
150 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
151
152 const double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
153 long long i = 0;
154
156 QgsFeature feat;
157 while ( features.nextFeature( feat ) )
158 {
159 i++;
160 if ( feedback->isCanceled() )
161 {
162 break;
163 }
164
165 feedback->setProgress( i * step );
166
167 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
168 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, u"OUTPUT"_s ) );
169 }
170
171 finalFileName = destination;
172 finalLayerName.clear(); // value of final layer name will be extracted from the destination string
173 const int separatorIndex = destination.indexOf( '|' );
174 if ( separatorIndex > -1 )
175 {
176 const thread_local QRegularExpression layerNameRx( u"\\|layername=([^\\|]*)"_s );
177 const QRegularExpressionMatch match = layerNameRx.match( destination );
178 if ( match.hasMatch() )
179 {
180 finalLayerName = match.captured( 1 );
181 }
182 finalFileName = destination.mid( 0, separatorIndex );
183 }
184
185 QVariantMap outputs;
186 outputs.insert( u"OUTPUT"_s, destination );
187 outputs.insert( u"FILE_PATH"_s, finalFileName );
188 outputs.insert( u"LAYER_NAME"_s, finalLayerName );
189 return outputs;
190}
191
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition qgis.h:3610
@ FieldComments
Writer can support field comments.
Definition qgis.h:1094
@ FieldAliases
Writer can support field aliases.
Definition qgis.h:1093
@ SkipGeometryValidityChecks
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
Definition qgis.h:3782
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3834
@ NoSymbology
Export only data.
Definition qgis.h:5859
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:55
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:63
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.