[processing] Change explicit encoding string parameters to more

flexible QVariantMap creatOptions parameters which include an
optional fileEncoding value

More flexible, allows sinks to be created using any creation
option which is passed to the underlying provider
This commit is contained in:
Nyall Dawson 2017-06-06 08:00:28 +10:00
parent ea2e477d91
commit d7aa3f5f7c
11 changed files with 50 additions and 39 deletions

View File

@ -80,9 +80,12 @@ class QgsProcessingFeatureSink
True if sink should be loaded into the current project when the algorithm completes.
%End
QString fileEncoding;
QVariantMap createOptions;
%Docstring
Encoding for destination file.
Map of optional sink creation options, which
are passed to the underlying provider when creating new layers. Known options also
include 'fileEncoding', which is used to specify a file encoding to use for created
files.
%End

View File

@ -90,19 +90,22 @@ class QgsProcessingUtils
static void createFeatureSinkPython(
QgsFeatureSink **sink /Out,TransferBack/,
QString &destination /In,Out/,
const QString &encoding,
QgsProcessingContext &context,
const QgsFields &fields,
QgsWkbTypes::Type geometryType,
const QgsCoordinateReferenceSystem &crs,
QgsProcessingContext &context ) /PyName=createFeatureSink/;
const QVariantMap &createOptions = QVariantMap() ) /PyName=createFeatureSink/;
%Docstring
Creates a feature sink ready for adding features. The ``destination`` specifies a destination
URI for the resultant layer. It may be updated in place to reflect the actual destination
for the layer.
Sink parameters such as desired ``encoding``, ``fields``, ``geometryType`` and ``crs`` must be specified.
Sink parameters such as desired ``fields``, ``geometryType`` and ``crs`` must be specified.
If the ``encoding`` is not specified, the default encoding from the ``context`` will be used.
The ``createOptions`` map can be used to specify additional sink creation options, which
are passed to the underlying provider when creating new layers. Known options also
include 'fileEncoding', which is used to specify a file encoding to use for created
files. If 'fileEncoding' is not specified, the default encoding from the ``context`` will be used.
If a layer is created for the feature sink, the layer will automatically be added to the ``context``'s
temporary layer store.

View File

@ -87,7 +87,7 @@ class VectorSplit(QgisAlgorithm):
for current, i in enumerate(uniqueValues):
fName = u'{0}_{1}.shp'.format(baseName, str(i).strip())
writer, dest = QgsProcessingUtils.createFeatureSink(fName, None, fields, geomType, crs, context)
writer, dest = QgsProcessingUtils.createFeatureSink(fName, context, fields, geomType, crs)
for f in QgsProcessingUtils.getFeatures(layer, context):
if f[fieldName] == i:
writer.addFeature(f)

View File

@ -10,8 +10,7 @@ from qgis.core import QgsFeature, QgsField, QgsProcessingUtils
layer = QgsProcessingUtils.mapLayerFromString(input, context)
fields = layer.fields()
fields.append(QgsField('UNIQ_COUNT', QVariant.Int))
writer, writer_dest = QgsProcessingUtils.createFeatureSink(N_unique_values, None, fields, layer.wkbType(), layer.crs(),
context)
writer, writer_dest = QgsProcessingUtils.createFeatureSink(N_unique_values, context, fields, layer.wkbType(), layer.crs())
class_field_index = layer.fields().lookupField(class_field)
value_field_index = layer.fields().lookupField(value_field)

View File

@ -369,7 +369,7 @@ class OutputVector(Output):
settings = QgsSettings()
self.encoding = settings.value('/Processing/encoding', 'System', str)
w, w_dest = QgsProcessingUtils.createFeatureSink(self.value, self.encoding, fields, geomType, crs, context)
w, w_dest = QgsProcessingUtils.createFeatureSink(self.value, context, fields, geomType, crs, {'fileEncoding': self.encoding})
self.value = w_dest
return w

View File

@ -8,8 +8,7 @@ from qgis.core import QgsWkbTypes, QgsProcessingUtils
layer = QgsProcessingUtils.mapLayerFromString(INPUT_LAYER, context)
fields = layer.fields()
writer, writer_dest = QgsProcessingUtils.createFeatureSink(OUTPUT_LAYER, 'utf-8', fields, QgsWkbTypes.Point, layer.crs(),
context)
writer, writer_dest = QgsProcessingUtils.createFeatureSink(OUTPUT_LAYER, context, fields, QgsWkbTypes.Point, layer.crs(), {'fileEncoding': 'utf-8'})
features = QgsProcessingUtils.getFeatures(layer, context)
count = QgsProcessingUtils.featureCount(layer, context)

View File

@ -214,13 +214,13 @@ QgsFeatureSink *QgsProcessingParameters::parameterAsSink( const QgsProcessingPar
}
bool loadIntoProject = false;
QString encoding;
QVariantMap createOptions;
if ( val.canConvert<QgsProcessingFeatureSink>() )
{
// input is a QgsProcessingFeatureSink - get extra properties from it
QgsProcessingFeatureSink fromVar = qvariant_cast<QgsProcessingFeatureSink>( val );
loadIntoProject = fromVar.loadIntoProject;
encoding = fromVar.fileEncoding;
createOptions = fromVar.createOptions;
val = fromVar.sink;
}
@ -239,7 +239,7 @@ QgsFeatureSink *QgsProcessingParameters::parameterAsSink( const QgsProcessingPar
dest = val.toString();
}
std::unique_ptr< QgsFeatureSink > sink( QgsProcessingUtils::createFeatureSink( dest, encoding, fields, geometryType, crs, context ) );
std::unique_ptr< QgsFeatureSink > sink( QgsProcessingUtils::createFeatureSink( dest, context, fields, geometryType, crs, createOptions ) );
destinationIdentifier = dest;
if ( loadIntoProject )

View File

@ -106,9 +106,12 @@ class CORE_EXPORT QgsProcessingFeatureSink
bool loadIntoProject;
/**
* Encoding for destination file.
* Map of optional sink creation options, which
* are passed to the underlying provider when creating new layers. Known options also
* include 'fileEncoding', which is used to specify a file encoding to use for created
* files.
*/
QString fileEncoding;
QVariantMap createOptions;
//! Allows direct construction of QVariants.

View File

@ -262,14 +262,14 @@ void parseDestinationString( QString &destination, QString &providerKey, QString
}
}
QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, const QString &encoding, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, QgsProcessingContext &context )
QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, QgsProcessingContext &context, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &createOptions )
{
QString destEncoding = encoding;
QVariantMap options = createOptions;
QgsVectorLayer *layer = nullptr;
if ( destEncoding.isEmpty() )
if ( !options.contains( QStringLiteral( "fileEncoding" ) ) )
{
// no destination encoding specified, use default
destEncoding = context.defaultEncoding().isEmpty() ? QStringLiteral( "system" ) : context.defaultEncoding();
options.insert( QStringLiteral( "fileEncoding" ), context.defaultEncoding().isEmpty() ? QStringLiteral( "system" ) : context.defaultEncoding() );
}
if ( destination.isEmpty() || destination.startsWith( QStringLiteral( "memory:" ) ) )
@ -279,9 +279,6 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, con
}
else
{
QMap<QString, QVariant> options;
options.insert( QStringLiteral( "fileEncoding" ), destEncoding );
QString providerKey;
QString uri;
QString format;
@ -292,7 +289,7 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, con
// use QgsVectorFileWriter for OGR destinations instead of QgsVectorLayerImport, as that allows
// us to use any OGR format which supports feature addition
QString finalFileName;
QgsVectorFileWriter *writer = new QgsVectorFileWriter( destination, destEncoding, fields, geometryType, crs, format, QgsVectorFileWriter::defaultDatasetOptions( format ),
QgsVectorFileWriter *writer = new QgsVectorFileWriter( destination, options.value( QStringLiteral( "fileEncoding" ) ).toString(), fields, geometryType, crs, format, QgsVectorFileWriter::defaultDatasetOptions( format ),
QgsVectorFileWriter::defaultLayerOptions( format ), &finalFileName );
destination = finalFileName;
return writer;
@ -329,9 +326,9 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, con
return new QgsProxyFeatureSink( layer->dataProvider() );
}
void QgsProcessingUtils::createFeatureSinkPython( QgsFeatureSink **sink, QString &destination, const QString &encoding, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, QgsProcessingContext &context )
void QgsProcessingUtils::createFeatureSinkPython( QgsFeatureSink **sink, QString &destination, QgsProcessingContext &context, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &options )
{
*sink = createFeatureSink( destination, encoding, fields, geometryType, crs, context );
*sink = createFeatureSink( destination, context, fields, geometryType, crs, options );
}

View File

@ -30,6 +30,7 @@ class QgsProcessingContext;
class QgsMapLayerStore;
#include <QString>
#include <QVariant>
/**
* \class QgsProcessingUtils
@ -106,7 +107,10 @@ class CORE_EXPORT QgsProcessingUtils
*
* Sink parameters such as desired \a encoding, \a fields, \a geometryType and \a crs must be specified.
*
* If the \a encoding is not specified, the default encoding from the \a context will be used.
* The \a createOptions map can be used to specify additional sink creation options, which
* are passed to the underlying provider when creating new layers. Known options also
* include 'fileEncoding', which is used to specify a file encoding to use for created
* files. If 'fileEncoding' is not specified, the default encoding from the \a context will be used.
*
* If a layer is created for the feature sink, the layer will automatically be added to the \a context's
* temporary layer store.
@ -116,11 +120,11 @@ class CORE_EXPORT QgsProcessingUtils
#ifndef SIP_RUN
static QgsFeatureSink *createFeatureSink(
QString &destination,
const QString &encoding,
QgsProcessingContext &context,
const QgsFields &fields,
QgsWkbTypes::Type geometryType,
const QgsCoordinateReferenceSystem &crs,
QgsProcessingContext &context ) SIP_FACTORY;
const QVariantMap &createOptions = QVariantMap() ) SIP_FACTORY;
#endif
/**
@ -128,9 +132,12 @@ class CORE_EXPORT QgsProcessingUtils
* URI for the resultant layer. It may be updated in place to reflect the actual destination
* for the layer.
*
* Sink parameters such as desired \a encoding, \a fields, \a geometryType and \a crs must be specified.
* Sink parameters such as desired \a fields, \a geometryType and \a crs must be specified.
*
* If the \a encoding is not specified, the default encoding from the \a context will be used.
* The \a createOptions map can be used to specify additional sink creation options, which
* are passed to the underlying provider when creating new layers. Known options also
* include 'fileEncoding', which is used to specify a file encoding to use for created
* files. If 'fileEncoding' is not specified, the default encoding from the \a context will be used.
*
* If a layer is created for the feature sink, the layer will automatically be added to the \a context's
* temporary layer store.
@ -142,11 +149,11 @@ class CORE_EXPORT QgsProcessingUtils
static void createFeatureSinkPython(
QgsFeatureSink **sink SIP_OUT SIP_TRANSFERBACK,
QString &destination SIP_INOUT,
const QString &encoding,
QgsProcessingContext &context,
const QgsFields &fields,
QgsWkbTypes::Type geometryType,
const QgsCoordinateReferenceSystem &crs,
QgsProcessingContext &context ) SIP_PYNAME( createFeatureSink );
const QVariantMap &createOptions = QVariantMap() ) SIP_PYNAME( createFeatureSink );
/**
* Combines the extent of several map \a layers. If specified, the target \a crs

View File

@ -921,7 +921,7 @@ void TestQgsProcessing::createFeatureSink()
QgsVectorLayer *layer = nullptr;
// should create a memory layer
std::unique_ptr< QgsFeatureSink > sink( QgsProcessingUtils::createFeatureSink( destination, QString(), QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem(), context ) );
std::unique_ptr< QgsFeatureSink > sink( QgsProcessingUtils::createFeatureSink( destination, context, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem() ) );
QVERIFY( sink.get() );
layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, false ) );
QVERIFY( layer );
@ -938,7 +938,7 @@ void TestQgsProcessing::createFeatureSink()
// specific memory layer output
destination = QStringLiteral( "memory:mylayer" );
sink.reset( QgsProcessingUtils::createFeatureSink( destination, QString(), QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem(), context ) );
sink.reset( QgsProcessingUtils::createFeatureSink( destination, context, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem() ) );
QVERIFY( sink.get() );
layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, false ) );
QVERIFY( layer );
@ -957,7 +957,7 @@ void TestQgsProcessing::createFeatureSink()
destination = QStringLiteral( "memory:mylayer" );
QgsFields fields;
fields.append( QgsField( QStringLiteral( "my_field" ), QVariant::String, QString(), 100 ) );
sink.reset( QgsProcessingUtils::createFeatureSink( destination, QString(), fields, QgsWkbTypes::PointZM, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ), context ) );
sink.reset( QgsProcessingUtils::createFeatureSink( destination, context, fields, QgsWkbTypes::PointZM, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ) ) );
QVERIFY( sink.get() );
layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, false ) );
QVERIFY( layer );
@ -980,7 +980,7 @@ void TestQgsProcessing::createFeatureSink()
// non memory layer output
destination = QDir::tempPath() + "/create_feature_sink.tab";
QString prevDest = destination;
sink.reset( QgsProcessingUtils::createFeatureSink( destination, QString(), fields, QgsWkbTypes::Polygon, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ), context ) );
sink.reset( QgsProcessingUtils::createFeatureSink( destination, context, fields, QgsWkbTypes::Polygon, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ) ) );
QVERIFY( sink.get() );
f = QgsFeature( fields );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((0 0, 0 1, 1 1, 1 0, 0 0 ))" ) ) );
@ -1001,7 +1001,7 @@ void TestQgsProcessing::createFeatureSink()
// no extension, should default to shp
destination = QDir::tempPath() + "/create_feature_sink2";
prevDest = QDir::tempPath() + "/create_feature_sink2.shp";
sink.reset( QgsProcessingUtils::createFeatureSink( destination, QString(), fields, QgsWkbTypes::Point25D, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ), context ) );
sink.reset( QgsProcessingUtils::createFeatureSink( destination, context, fields, QgsWkbTypes::Point25D, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ) ) );
QVERIFY( sink.get() );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "PointZ(1 2 3)" ) ) );
QVERIFY( sink->addFeature( f ) );