Skip to content

Commit

Permalink
Restore ability to save outputs directly to Spatialite/PostGIS providers
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Jun 6, 2017
1 parent 5e92c0d commit 607fed8
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 32 deletions.
7 changes: 7 additions & 0 deletions python/core/processing/qgsprocessingparameters.sip
Expand Up @@ -1206,6 +1206,13 @@ class QgsProcessingParameterFeatureSink : QgsProcessingParameterDefinition
:rtype: QgsProcessingParameterDefinition.LayerType
%End

bool hasGeometry() const;
%Docstring
Returns true if sink is likely to include geometries. In cases were presence of geometry
cannot be reliably determined in advance, this method will default to returning true.
:rtype: bool
%End

void setDataType( QgsProcessingParameterDefinition::LayerType type );
%Docstring
Sets the layer ``type`` for the sinks associated with the parameter.
Expand Down
12 changes: 6 additions & 6 deletions python/plugins/processing/gui/DestinationSelectionPanel.py
Expand Up @@ -37,7 +37,7 @@
from qgis.core import (QgsDataSourceUri,
QgsCredentials,
QgsSettings,
QgsProcessingOutputVectorLayer,
QgsProcessingParameterFeatureSink,
QgsProcessingFeatureSinkDefinition)
from processing.core.ProcessingConfig import ProcessingConfig
from processing.core.outputs import OutputVector
Expand Down Expand Up @@ -67,7 +67,7 @@ def __init__(self, parameter, alg):
self.encoding = settings.value('/Processing/encoding', 'System')

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

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

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

connInfo = uri.connectionInfo()
(success, user, passwd) = QgsCredentials.instance().get(connInfo, None, None)
Expand Down Expand Up @@ -185,7 +185,7 @@ def saveToSpatialite(self):
uri = QgsDataSourceUri()
uri.setDatabase(fileName)
uri.setDataSource('', self.parameter.name().lower(),
'the_geom' if self.parameter.hasGeometry() else None)
'the_geom' if isinstance(self.parameter, QgsProcessingParameterFeatureSink) and self.parameter.hasGeometry() else None)
self.leText.setText("spatialite:" + uri.uri())

def selectFile(self):
Expand Down
2 changes: 1 addition & 1 deletion python/plugins/processing/gui/Postprocessing.py
Expand Up @@ -59,7 +59,7 @@ def handleAlgorithmResults(alg, context, feedback=None, showResults=True):
feedback.setProgress(100 * i / float(len(context.layersToLoadOnCompletion())))
try:
layer = QgsProcessingUtils.mapLayerFromString(l, context)
if layer:
if layer is not None:
layer.setName(details.name)
details.project.addMapLayer(context.temporaryLayerStore().takeMapLayer(layer))
else:
Expand Down
4 changes: 4 additions & 0 deletions src/core/processing/qgsnativealgorithms.cpp
Expand Up @@ -84,6 +84,8 @@ QVariantMap QgsCentroidAlgorithm::processAlgorithm( const QVariantMap &parameter

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

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

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

// fixed parameters
//bool dissolve = QgsProcessingParameters::parameterAsBool( parameters, QStringLiteral( "DISSOLVE" ), context );
Expand Down
19 changes: 19 additions & 0 deletions src/core/processing/qgsprocessingparameters.cpp
Expand Up @@ -1348,6 +1348,25 @@ QgsProcessingParameterDefinition::LayerType QgsProcessingParameterFeatureSink::d
return mDataType;
}

bool QgsProcessingParameterFeatureSink::hasGeometry() const
{
switch ( mDataType )
{
case TypeAny:
case TypeVectorAny:
case TypeVectorPoint:
case TypeVectorLine:
case TypeVectorPolygon:
case TypeTable:
return true;

case TypeRaster:
case TypeFile:
return false;
}
return true;
}

void QgsProcessingParameterFeatureSink::setDataType( QgsProcessingParameterDefinition::LayerType type )
{
mDataType = type;
Expand Down
6 changes: 6 additions & 0 deletions src/core/processing/qgsprocessingparameters.h
Expand Up @@ -1202,6 +1202,12 @@ class CORE_EXPORT QgsProcessingParameterFeatureSink : public QgsProcessingParame
*/
QgsProcessingParameterDefinition::LayerType dataType() const;

/**
* Returns true if sink is likely to include geometries. In cases were presence of geometry
* cannot be reliably determined in advance, this method will default to returning true.
*/
bool hasGeometry() const;

/**
* Sets the layer \a type for the sinks associated with the parameter.
* \see dataType()
Expand Down
51 changes: 26 additions & 25 deletions src/core/processing/qgsprocessingutils.cpp
Expand Up @@ -265,7 +265,6 @@ void parseDestinationString( QString &destination, QString &providerKey, QString
QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, QgsProcessingContext &context, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &createOptions )
{
QVariantMap options = createOptions;
QgsVectorLayer *layer = nullptr;
if ( !options.contains( QStringLiteral( "fileEncoding" ) ) )
{
// no destination encoding specified, use default
Expand All @@ -275,7 +274,23 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, Qgs
if ( destination.isEmpty() || destination.startsWith( QStringLiteral( "memory:" ) ) )
{
// memory provider cannot be used with QgsVectorLayerImport - so create layer manually
layer = QgsMemoryProviderUtils::createMemoryLayer( destination, fields, geometryType, crs );
std::unique_ptr< QgsVectorLayer > layer( QgsMemoryProviderUtils::createMemoryLayer( destination, fields, geometryType, crs ) );
if ( !layer )
return nullptr;

if ( !layer->isValid() )
{
return nullptr;
}

// update destination to layer ID
destination = layer->id();

// this is a factory, so we need to return a proxy
std::unique_ptr< QgsProxyFeatureSink > sink( new QgsProxyFeatureSink( layer->dataProvider() ) );
context.temporaryLayerStore()->addMapLayer( layer.release() );

return sink.release();
}
else
{
Expand All @@ -297,33 +312,19 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, Qgs
else
{
//create empty layer
{
QgsVectorLayerExporter import( uri, providerKey, fields, geometryType, crs, false, &options );
if ( import.errorCode() )
return nullptr;
}
std::unique_ptr< QgsVectorLayerExporter > exporter( new QgsVectorLayerExporter( uri, providerKey, fields, geometryType, crs, false, &options ) );
if ( exporter->errorCode() )
return nullptr;

// use destination string as layer name (eg "postgis:..." )
layer = new QgsVectorLayer( uri, destination, providerKey );
std::unique_ptr< QgsVectorLayer > layer( new QgsVectorLayer( uri, destination, providerKey ) );
// update destination to layer ID
destination = layer->id();
context.temporaryLayerStore()->addMapLayer( layer.release() );
return exporter.release();
}
}

if ( !layer )
return nullptr;

if ( !layer->isValid() )
{
delete layer;
return nullptr;
}

// update destination to layer ID
destination = layer->id();

context.temporaryLayerStore()->addMapLayer( layer );

// this is a factory, so we need to return a proxy
return new QgsProxyFeatureSink( layer->dataProvider() );
return nullptr;
}

void QgsProcessingUtils::createFeatureSinkPython( QgsFeatureSink **sink, QString &destination, QgsProcessingContext &context, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &options )
Expand Down
11 changes: 11 additions & 0 deletions tests/src/core/testqgsprocessing.cpp
Expand Up @@ -2286,6 +2286,17 @@ void TestQgsProcessing::parameterFeatureSink()
QVERIFY( def->checkValueIsAcceptable( "" ) );
QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
QVERIFY( def->checkValueIsAcceptable( QgsProcessingFeatureSinkDefinition( "layer1231123" ) ) );

// test hasGeometry
QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeAny ).hasGeometry() );
QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeVectorAny ).hasGeometry() );
QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeVectorPoint ).hasGeometry() );
QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeVectorLine ).hasGeometry() );
QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeVectorPolygon ).hasGeometry() );
QVERIFY( !QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeRaster ).hasGeometry() );
QVERIFY( !QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeFile ).hasGeometry() );
QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessingParameterDefinition::TypeTable ).hasGeometry() );

}

void TestQgsProcessing::checkParamValues()
Expand Down

0 comments on commit 607fed8

Please sign in to comment.