[processing] Cleanup some layer/writer related handling

Ensure that layers created by QgsProcessingUtils::createFeatureSink
can always be retrieved using QgsProcessingUtils::mapLayerFromString
This commit is contained in:
Nyall Dawson 2017-05-09 15:03:51 +10:00
parent cb23ebed65
commit 6aa10c6817
9 changed files with 33 additions and 40 deletions

View File

@ -130,8 +130,7 @@ class QgsProcessingUtils
const QgsFields &fields, const QgsFields &fields,
QgsWkbTypes::Type geometryType, QgsWkbTypes::Type geometryType,
const QgsCoordinateReferenceSystem &crs, const QgsCoordinateReferenceSystem &crs,
QgsProcessingContext &context, QgsProcessingContext &context ) /PyName=createFeatureSink/;
QgsVectorLayer **outputLayer /Out/ ) /PyName=createFeatureSink/;
%Docstring %Docstring
Creates a feature sink ready for adding features. The ``destination`` specifies a destination 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 URI for the resultant layer. It may be updated in place to reflect the actual destination
@ -142,7 +141,7 @@ class QgsProcessingUtils
If the ``encoding`` is not specified, the default encoding from the ``context`` will be used. If the ``encoding`` 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 If a layer is created for the feature sink, the layer will automatically be added to the ``context``'s
temporary layer store, and the ``outputLayer`` argument updated to point at this newly created layer. temporary layer store.
.. note:: .. note::

View File

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

View File

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

View File

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

View File

@ -61,11 +61,10 @@ def handleAlgorithmResults(alg, context, feedback=None, showResults=True):
continue continue
if isinstance(out, (OutputRaster, OutputVector, OutputTable)): if isinstance(out, (OutputRaster, OutputVector, OutputTable)):
try: try:
if hasattr(out, "layer") and out.layer is not None: layer = QgsProcessingUtils.mapLayerFromString(out.value, context)
out.layer.setName(out.description) if layer:
QgsProject.instance().addMapLayer(context.temporaryLayerStore().takeMapLayer(out.layer)) layer.setName(out.description)
# temporary hack to work around mutable outputs QgsProject.instance().addMapLayer(context.temporaryLayerStore().takeMapLayer(layer))
out.layer = None
else: else:
if ProcessingConfig.getSetting( if ProcessingConfig.getSetting(
ProcessingConfig.USE_FILENAME_AS_LAYER_NAME): ProcessingConfig.USE_FILENAME_AS_LAYER_NAME):

View File

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

View File

@ -328,12 +328,10 @@ 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, QgsVectorLayer *&outputLayer ) QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, const QString &encoding, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, QgsProcessingContext &context )
{ {
outputLayer = nullptr;
QgsVectorLayer *layer = nullptr;
QString destEncoding = encoding; QString destEncoding = encoding;
QgsVectorLayer *layer = nullptr;
if ( destEncoding.isEmpty() ) if ( destEncoding.isEmpty() )
{ {
// no destination encoding specified, use default // no destination encoding specified, use default
@ -344,8 +342,6 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, con
{ {
// memory provider cannot be used with QgsVectorLayerImport - so create layer manually // memory provider cannot be used with QgsVectorLayerImport - so create layer manually
layer = QgsMemoryProviderUtils::createMemoryLayer( destination, fields, geometryType, crs ); layer = QgsMemoryProviderUtils::createMemoryLayer( destination, fields, geometryType, crs );
if ( layer && layer->isValid() )
destination = layer->id();
} }
else else
{ {
@ -376,6 +372,7 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, con
return nullptr; return nullptr;
} }
// use destination string as layer name (eg "postgis:..." )
layer = new QgsVectorLayer( uri, destination, providerKey ); layer = new QgsVectorLayer( uri, destination, providerKey );
} }
} }
@ -389,19 +386,18 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, con
return nullptr; return nullptr;
} }
// update destination to layer ID
destination = layer->id();
context.temporaryLayerStore()->addMapLayer( layer ); context.temporaryLayerStore()->addMapLayer( layer );
outputLayer = layer;
// this is a factory, so we need to return a proxy // this is a factory, so we need to return a proxy
return new QgsProxyFeatureSink( layer->dataProvider() ); 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, QgsVectorLayer **outputLayer ) void QgsProcessingUtils::createFeatureSinkPython( QgsFeatureSink **sink, QString &destination, const QString &encoding, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, QgsProcessingContext &context )
{ {
QgsVectorLayer *layer = nullptr; *sink = createFeatureSink( destination, encoding, fields, geometryType, crs, context );
*sink = createFeatureSink( destination, encoding, fields, geometryType, crs, context, layer );
if ( outputLayer )
*outputLayer = layer;
} }

View File

@ -141,7 +141,7 @@ class CORE_EXPORT QgsProcessingUtils
* If the \a encoding is not specified, the default encoding from the \a context will be used. * If the \a encoding 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 * If a layer is created for the feature sink, the layer will automatically be added to the \a context's
* temporary layer store, and the \a outputLayer argument updated to point at this newly created layer. * temporary layer store.
* *
* The caller takes responsibility for deleting the returned sink. * The caller takes responsibility for deleting the returned sink.
*/ */
@ -152,8 +152,7 @@ class CORE_EXPORT QgsProcessingUtils
const QgsFields &fields, const QgsFields &fields,
QgsWkbTypes::Type geometryType, QgsWkbTypes::Type geometryType,
const QgsCoordinateReferenceSystem &crs, const QgsCoordinateReferenceSystem &crs,
QgsProcessingContext &context, QgsProcessingContext &context ) SIP_FACTORY;
QgsVectorLayer *&outputLayer ) SIP_FACTORY;
#endif #endif
/** /**
@ -166,7 +165,7 @@ class CORE_EXPORT QgsProcessingUtils
* If the \a encoding is not specified, the default encoding from the \a context will be used. * If the \a encoding 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 * If a layer is created for the feature sink, the layer will automatically be added to the \a context's
* temporary layer store, and the \a outputLayer argument updated to point at this newly created layer. * temporary layer store.
* *
* \note this version of the createFeatureSink() function has an API designed around use from the * \note this version of the createFeatureSink() function has an API designed around use from the
* SIP bindings. c++ code should call the other createFeatureSink() version. * SIP bindings. c++ code should call the other createFeatureSink() version.
@ -179,8 +178,7 @@ class CORE_EXPORT QgsProcessingUtils
const QgsFields &fields, const QgsFields &fields,
QgsWkbTypes::Type geometryType, QgsWkbTypes::Type geometryType,
const QgsCoordinateReferenceSystem &crs, const QgsCoordinateReferenceSystem &crs,
QgsProcessingContext &context, QgsProcessingContext &context ) SIP_PYNAME( createFeatureSink );
QgsVectorLayer **outputLayer SIP_OUT ) SIP_PYNAME( createFeatureSink );
private: private:

View File

@ -748,8 +748,9 @@ void TestQgsProcessing::createFeatureSink()
QgsVectorLayer *layer = nullptr; QgsVectorLayer *layer = nullptr;
// should create a memory layer // should create a memory layer
QgsFeatureSink *sink = QgsProcessingUtils::createFeatureSink( destination, QString(), QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem(), context, layer ); QgsFeatureSink *sink = QgsProcessingUtils::createFeatureSink( destination, QString(), QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem(), context );
QVERIFY( sink ); QVERIFY( sink );
layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, false ) );
QVERIFY( layer ); QVERIFY( layer );
QCOMPARE( static_cast< QgsProxyFeatureSink *>( sink )->destinationSink(), layer->dataProvider() ); QCOMPARE( static_cast< QgsProxyFeatureSink *>( sink )->destinationSink(), layer->dataProvider() );
QCOMPARE( layer->dataProvider()->name(), QStringLiteral( "memory" ) ); QCOMPARE( layer->dataProvider()->name(), QStringLiteral( "memory" ) );
@ -765,8 +766,9 @@ void TestQgsProcessing::createFeatureSink()
// specific memory layer output // specific memory layer output
destination = QStringLiteral( "memory:mylayer" ); destination = QStringLiteral( "memory:mylayer" );
sink = QgsProcessingUtils::createFeatureSink( destination, QString(), QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem(), context, layer ); sink = QgsProcessingUtils::createFeatureSink( destination, QString(), QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem(), context );
QVERIFY( sink ); QVERIFY( sink );
layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, false ) );
QVERIFY( layer ); QVERIFY( layer );
QCOMPARE( static_cast< QgsProxyFeatureSink *>( sink )->destinationSink(), layer->dataProvider() ); QCOMPARE( static_cast< QgsProxyFeatureSink *>( sink )->destinationSink(), layer->dataProvider() );
QCOMPARE( layer->dataProvider()->name(), QStringLiteral( "memory" ) ); QCOMPARE( layer->dataProvider()->name(), QStringLiteral( "memory" ) );
@ -784,8 +786,9 @@ void TestQgsProcessing::createFeatureSink()
destination = QStringLiteral( "memory:mylayer" ); destination = QStringLiteral( "memory:mylayer" );
QgsFields fields; QgsFields fields;
fields.append( QgsField( QStringLiteral( "my_field" ), QVariant::String, QString(), 100 ) ); fields.append( QgsField( QStringLiteral( "my_field" ), QVariant::String, QString(), 100 ) );
sink = QgsProcessingUtils::createFeatureSink( destination, QString(), fields, QgsWkbTypes::PointZM, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ), context, layer ); sink = QgsProcessingUtils::createFeatureSink( destination, QString(), fields, QgsWkbTypes::PointZM, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ), context );
QVERIFY( sink ); QVERIFY( sink );
layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, false ) );
QVERIFY( layer ); QVERIFY( layer );
QCOMPARE( static_cast< QgsProxyFeatureSink *>( sink )->destinationSink(), layer->dataProvider() ); QCOMPARE( static_cast< QgsProxyFeatureSink *>( sink )->destinationSink(), layer->dataProvider() );
QCOMPARE( layer->dataProvider()->name(), QStringLiteral( "memory" ) ); QCOMPARE( layer->dataProvider()->name(), QStringLiteral( "memory" ) );
@ -807,16 +810,15 @@ void TestQgsProcessing::createFeatureSink()
// non memory layer output // non memory layer output
destination = QDir::tempPath() + "/create_feature_sink.tab"; destination = QDir::tempPath() + "/create_feature_sink.tab";
QString prevDest = destination; QString prevDest = destination;
sink = QgsProcessingUtils::createFeatureSink( destination, QString(), fields, QgsWkbTypes::Polygon, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ), context, layer ); sink = QgsProcessingUtils::createFeatureSink( destination, QString(), fields, QgsWkbTypes::Polygon, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ), context );
QVERIFY( sink ); QVERIFY( sink );
f = QgsFeature( fields ); f = QgsFeature( fields );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((0 0, 0 1, 1 1, 1 0, 0 0 ))" ) ) ); f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((0 0, 0 1, 1 1, 1 0, 0 0 ))" ) ) );
f.setAttributes( QgsAttributes() << "val" ); f.setAttributes( QgsAttributes() << "val" );
QVERIFY( sink->addFeature( f ) ); QVERIFY( sink->addFeature( f ) );
QVERIFY( !layer );
QCOMPARE( destination, prevDest ); QCOMPARE( destination, prevDest );
delete sink; delete sink;
layer = new QgsVectorLayer( destination, "test_layer", "ogr" ); layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, true ) );
QVERIFY( layer->isValid() ); QVERIFY( layer->isValid() );
QCOMPARE( layer->crs().authid(), QStringLiteral( "EPSG:3111" ) ); QCOMPARE( layer->crs().authid(), QStringLiteral( "EPSG:3111" ) );
QCOMPARE( layer->fields().size(), 1 ); QCOMPARE( layer->fields().size(), 1 );
@ -829,14 +831,14 @@ void TestQgsProcessing::createFeatureSink()
// no extension, should default to shp // no extension, should default to shp
destination = QDir::tempPath() + "/create_feature_sink2"; destination = QDir::tempPath() + "/create_feature_sink2";
prevDest = QDir::tempPath() + "/create_feature_sink2.shp"; prevDest = QDir::tempPath() + "/create_feature_sink2.shp";
sink = QgsProcessingUtils::createFeatureSink( destination, QString(), fields, QgsWkbTypes::Point25D, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ), context, layer ); sink = QgsProcessingUtils::createFeatureSink( destination, QString(), fields, QgsWkbTypes::Point25D, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ), context );
QVERIFY( sink ); QVERIFY( sink );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "PointZ(1 2 3)" ) ) ); f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "PointZ(1 2 3)" ) ) );
QVERIFY( sink->addFeature( f ) ); QVERIFY( sink->addFeature( f ) );
QVERIFY( !layer ); QVERIFY( !layer );
QCOMPARE( destination, prevDest ); QCOMPARE( destination, prevDest );
delete sink; delete sink;
layer = new QgsVectorLayer( destination, "test_layer", "ogr" ); layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, true ) );
QCOMPARE( layer->wkbType(), QgsWkbTypes::Point25D ); QCOMPARE( layer->wkbType(), QgsWkbTypes::Point25D );
QCOMPARE( layer->crs().authid(), QStringLiteral( "EPSG:3111" ) ); QCOMPARE( layer->crs().authid(), QStringLiteral( "EPSG:3111" ) );
QCOMPARE( layer->fields().size(), 1 ); QCOMPARE( layer->fields().size(), 1 );