Skip to content

Commit 607fed8

Browse files
committed
Restore ability to save outputs directly to Spatialite/PostGIS providers
1 parent 5e92c0d commit 607fed8

8 files changed

Lines changed: 80 additions & 32 deletions

File tree

‎python/core/processing/qgsprocessingparameters.sip‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,6 +1206,13 @@ class QgsProcessingParameterFeatureSink : QgsProcessingParameterDefinition
12061206
:rtype: QgsProcessingParameterDefinition.LayerType
12071207
%End
12081208
1209+
bool hasGeometry() const;
1210+
%Docstring
1211+
Returns true if sink is likely to include geometries. In cases were presence of geometry
1212+
cannot be reliably determined in advance, this method will default to returning true.
1213+
:rtype: bool
1214+
%End
1215+
12091216
void setDataType( QgsProcessingParameterDefinition::LayerType type );
12101217
%Docstring
12111218
Sets the layer ``type`` for the sinks associated with the parameter.

‎python/plugins/processing/gui/DestinationSelectionPanel.py‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from qgis.core import (QgsDataSourceUri,
3838
QgsCredentials,
3939
QgsSettings,
40-
QgsProcessingOutputVectorLayer,
40+
QgsProcessingParameterFeatureSink,
4141
QgsProcessingFeatureSinkDefinition)
4242
from processing.core.ProcessingConfig import ProcessingConfig
4343
from processing.core.outputs import OutputVector
@@ -67,7 +67,7 @@ def __init__(self, parameter, alg):
6767
self.encoding = settings.value('/Processing/encoding', 'System')
6868

6969
if hasattr(self.leText, 'setPlaceholderText'):
70-
if isinstance(self.parameter, QgsProcessingOutputVectorLayer) \
70+
if isinstance(self.parameter, QgsProcessingParameterFeatureSink) \
7171
and alg.provider().supportsNonFileBasedOutput():
7272
# use memory layers for temporary files if supported
7373
self.leText.setPlaceholderText(self.SAVE_TO_TEMP_LAYER)
@@ -82,7 +82,7 @@ def selectOutput(self):
8282
else:
8383
popupMenu = QMenu()
8484

85-
if isinstance(self.parameter, QgsProcessingOutputVectorLayer) \
85+
if isinstance(self.parameter, QgsProcessingParameterFeatureSink) \
8686
and self.alg.provider().supportsNonFileBasedOutput():
8787
# use memory layers for temporary layers if supported
8888
actionSaveToTemp = QAction(
@@ -103,7 +103,7 @@ def selectOutput(self):
103103
actionShowExpressionsBuilder.triggered.connect(self.showExpressionsBuilder)
104104
popupMenu.addAction(actionShowExpressionsBuilder)
105105

106-
if isinstance(self.parameter, QgsProcessingOutputVectorLayer) \
106+
if isinstance(self.parameter, QgsProcessingParameterFeatureSink) \
107107
and self.alg.provider().supportsNonFileBasedOutput():
108108
actionSaveToSpatialite = QAction(
109109
self.tr('Save to Spatialite table...'), self.btnSelect)
@@ -145,7 +145,7 @@ def saveToPostGIS(self):
145145
uri = QgsDataSourceUri()
146146
uri.setConnection(host, str(port), dbname, user, password)
147147
uri.setDataSource(dlg.schema, dlg.table,
148-
"the_geom" if self.parameter.hasGeometry() else None)
148+
"the_geom" if isinstance(self.parameter, QgsProcessingParameterFeatureSink) and self.parameter.hasGeometry() else None)
149149

150150
connInfo = uri.connectionInfo()
151151
(success, user, passwd) = QgsCredentials.instance().get(connInfo, None, None)
@@ -185,7 +185,7 @@ def saveToSpatialite(self):
185185
uri = QgsDataSourceUri()
186186
uri.setDatabase(fileName)
187187
uri.setDataSource('', self.parameter.name().lower(),
188-
'the_geom' if self.parameter.hasGeometry() else None)
188+
'the_geom' if isinstance(self.parameter, QgsProcessingParameterFeatureSink) and self.parameter.hasGeometry() else None)
189189
self.leText.setText("spatialite:" + uri.uri())
190190

191191
def selectFile(self):

‎python/plugins/processing/gui/Postprocessing.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def handleAlgorithmResults(alg, context, feedback=None, showResults=True):
5959
feedback.setProgress(100 * i / float(len(context.layersToLoadOnCompletion())))
6060
try:
6161
layer = QgsProcessingUtils.mapLayerFromString(l, context)
62-
if layer:
62+
if layer is not None:
6363
layer.setName(details.name)
6464
details.project.addMapLayer(context.temporaryLayerStore().takeMapLayer(layer))
6565
else:

‎src/core/processing/qgsnativealgorithms.cpp‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ QVariantMap QgsCentroidAlgorithm::processAlgorithm( const QVariantMap &parameter
8484

8585
QString dest;
8686
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT_LAYER" ), context, source->fields(), QgsWkbTypes::Point, source->sourceCrs(), dest ) );
87+
if ( !sink )
88+
return QVariantMap();
8789

8890
long count = source->featureCount();
8991
if ( count <= 0 )
@@ -157,6 +159,8 @@ QVariantMap QgsBufferAlgorithm::processAlgorithm( const QVariantMap &parameters,
157159

158160
QString dest;
159161
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT_LAYER" ), context, source->fields(), QgsWkbTypes::Polygon, source->sourceCrs(), dest ) );
162+
if ( !sink )
163+
return QVariantMap();
160164

161165
// fixed parameters
162166
//bool dissolve = QgsProcessingParameters::parameterAsBool( parameters, QStringLiteral( "DISSOLVE" ), context );

‎src/core/processing/qgsprocessingparameters.cpp‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,6 +1348,25 @@ QgsProcessingParameterDefinition::LayerType QgsProcessingParameterFeatureSink::d
13481348
return mDataType;
13491349
}
13501350

1351+
bool QgsProcessingParameterFeatureSink::hasGeometry() const
1352+
{
1353+
switch ( mDataType )
1354+
{
1355+
case TypeAny:
1356+
case TypeVectorAny:
1357+
case TypeVectorPoint:
1358+
case TypeVectorLine:
1359+
case TypeVectorPolygon:
1360+
case TypeTable:
1361+
return true;
1362+
1363+
case TypeRaster:
1364+
case TypeFile:
1365+
return false;
1366+
}
1367+
return true;
1368+
}
1369+
13511370
void QgsProcessingParameterFeatureSink::setDataType( QgsProcessingParameterDefinition::LayerType type )
13521371
{
13531372
mDataType = type;

‎src/core/processing/qgsprocessingparameters.h‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,6 +1202,12 @@ class CORE_EXPORT QgsProcessingParameterFeatureSink : public QgsProcessingParame
12021202
*/
12031203
QgsProcessingParameterDefinition::LayerType dataType() const;
12041204

1205+
/**
1206+
* Returns true if sink is likely to include geometries. In cases were presence of geometry
1207+
* cannot be reliably determined in advance, this method will default to returning true.
1208+
*/
1209+
bool hasGeometry() const;
1210+
12051211
/**
12061212
* Sets the layer \a type for the sinks associated with the parameter.
12071213
* \see dataType()

‎src/core/processing/qgsprocessingutils.cpp‎

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,6 @@ void parseDestinationString( QString &destination, QString &providerKey, QString
265265
QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, QgsProcessingContext &context, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &createOptions )
266266
{
267267
QVariantMap options = createOptions;
268-
QgsVectorLayer *layer = nullptr;
269268
if ( !options.contains( QStringLiteral( "fileEncoding" ) ) )
270269
{
271270
// no destination encoding specified, use default
@@ -275,7 +274,23 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, Qgs
275274
if ( destination.isEmpty() || destination.startsWith( QStringLiteral( "memory:" ) ) )
276275
{
277276
// memory provider cannot be used with QgsVectorLayerImport - so create layer manually
278-
layer = QgsMemoryProviderUtils::createMemoryLayer( destination, fields, geometryType, crs );
277+
std::unique_ptr< QgsVectorLayer > layer( QgsMemoryProviderUtils::createMemoryLayer( destination, fields, geometryType, crs ) );
278+
if ( !layer )
279+
return nullptr;
280+
281+
if ( !layer->isValid() )
282+
{
283+
return nullptr;
284+
}
285+
286+
// update destination to layer ID
287+
destination = layer->id();
288+
289+
// this is a factory, so we need to return a proxy
290+
std::unique_ptr< QgsProxyFeatureSink > sink( new QgsProxyFeatureSink( layer->dataProvider() ) );
291+
context.temporaryLayerStore()->addMapLayer( layer.release() );
292+
293+
return sink.release();
279294
}
280295
else
281296
{
@@ -297,33 +312,19 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, Qgs
297312
else
298313
{
299314
//create empty layer
300-
{
301-
QgsVectorLayerExporter import( uri, providerKey, fields, geometryType, crs, false, &options );
302-
if ( import.errorCode() )
303-
return nullptr;
304-
}
315+
std::unique_ptr< QgsVectorLayerExporter > exporter( new QgsVectorLayerExporter( uri, providerKey, fields, geometryType, crs, false, &options ) );
316+
if ( exporter->errorCode() )
317+
return nullptr;
305318

306319
// use destination string as layer name (eg "postgis:..." )
307-
layer = new QgsVectorLayer( uri, destination, providerKey );
320+
std::unique_ptr< QgsVectorLayer > layer( new QgsVectorLayer( uri, destination, providerKey ) );
321+
// update destination to layer ID
322+
destination = layer->id();
323+
context.temporaryLayerStore()->addMapLayer( layer.release() );
324+
return exporter.release();
308325
}
309326
}
310-
311-
if ( !layer )
312-
return nullptr;
313-
314-
if ( !layer->isValid() )
315-
{
316-
delete layer;
317-
return nullptr;
318-
}
319-
320-
// update destination to layer ID
321-
destination = layer->id();
322-
323-
context.temporaryLayerStore()->addMapLayer( layer );
324-
325-
// this is a factory, so we need to return a proxy
326-
return new QgsProxyFeatureSink( layer->dataProvider() );
327+
return nullptr;
327328
}
328329

329330
void QgsProcessingUtils::createFeatureSinkPython( QgsFeatureSink **sink, QString &destination, QgsProcessingContext &context, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &options )

‎tests/src/core/testqgsprocessing.cpp‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2286,6 +2286,17 @@ void TestQgsProcessing::parameterFeatureSink()
22862286
QVERIFY( def->checkValueIsAcceptable( "" ) );
22872287
QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
22882288
QVERIFY( def->checkValueIsAcceptable( QgsProcessingFeatureSinkDefinition( "layer1231123" ) ) );
2289+
2290+
// test hasGeometry
2291+
QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeAny ).hasGeometry() );
2292+
QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeVectorAny ).hasGeometry() );
2293+
QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeVectorPoint ).hasGeometry() );
2294+
QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeVectorLine ).hasGeometry() );
2295+
QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeVectorPolygon ).hasGeometry() );
2296+
QVERIFY( !QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeRaster ).hasGeometry() );
2297+
QVERIFY( !QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeFile ).hasGeometry() );
2298+
QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeTable ).hasGeometry() );
2299+
22892300
}
22902301

22912302
void TestQgsProcessing::checkParamValues()

0 commit comments

Comments
 (0)