Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[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
  • Loading branch information
nyalldawson committed Jun 5, 2017
1 parent ea2e477 commit d7aa3f5
Show file tree
Hide file tree
Showing 11 changed files with 50 additions and 39 deletions.
7 changes: 5 additions & 2 deletions python/core/processing/qgsprocessingparameters.sip
Expand Up @@ -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


Expand Down
11 changes: 7 additions & 4 deletions python/core/processing/qgsprocessingutils.sip
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/VectorSplit.py
Expand Up @@ -87,7 +87,7 @@ def processAlgorithm(self, parameters, context, feedback):
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)
Expand Down
Expand Up @@ -10,8 +10,7 @@
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)
Expand Down
2 changes: 1 addition & 1 deletion python/plugins/processing/core/outputs.py
Expand Up @@ -369,7 +369,7 @@ def getVectorWriter(self, fields, geomType, crs, context):
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

Expand Down
Expand Up @@ -8,8 +8,7 @@
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)
Expand Down
6 changes: 3 additions & 3 deletions src/core/processing/qgsprocessingparameters.cpp
Expand Up @@ -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;
}

Expand All @@ -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 )
Expand Down
7 changes: 5 additions & 2 deletions src/core/processing/qgsprocessingparameters.h
Expand Up @@ -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.
Expand Down
17 changes: 7 additions & 10 deletions src/core/processing/qgsprocessingutils.cpp
Expand Up @@ -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:" ) ) )
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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 );
}


Expand Down
21 changes: 14 additions & 7 deletions src/core/processing/qgsprocessingutils.h
Expand Up @@ -30,6 +30,7 @@ class QgsProcessingContext;
class QgsMapLayerStore;

#include <QString>
#include <QVariant>

/**
* \class QgsProcessingUtils
Expand Down Expand Up @@ -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.
Expand All @@ -116,21 +120,24 @@ 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

/**
* Creates a feature sink ready for adding features. The \a 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 \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.
Expand All @@ -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
Expand Down
10 changes: 5 additions & 5 deletions tests/src/core/testqgsprocessing.cpp
Expand Up @@ -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 );
Expand All @@ -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 );
Expand All @@ -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 );
Expand All @@ -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 ))" ) ) );
Expand All @@ -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 ) );
Expand Down

0 comments on commit d7aa3f5

Please sign in to comment.