Skip to content
Permalink
Browse files

Merge pull request #5689 from nyalldawson/gpkg_alg

[processing] Fixes to non flat-file exports
  • Loading branch information
nyalldawson committed Nov 24, 2017
2 parents 6278245 + 5b66ea7 commit 2b5aca55187673e926a51c0d3a0c6f6bad1657af
@@ -94,11 +94,7 @@ def prepareAlgorithm(self, parameters, context, feedback):
return False

self.expression_context = self.createExpressionContext(parameters, context)

if not self.expression.prepare(self.expression_context):
feedback.reportErro(
self.tr('Evaluation error: {0}').format(self.expression.evalErrorString()))
return False
self.expression.prepare(self.expression_context)

return True

@@ -31,7 +31,7 @@

from qgis.PyQt import uic
from qgis.PyQt.QtCore import QCoreApplication, QDir
from qgis.PyQt.QtWidgets import QDialog, QMenu, QAction, QFileDialog
from qgis.PyQt.QtWidgets import QDialog, QMenu, QAction, QFileDialog, QInputDialog
from qgis.PyQt.QtGui import QCursor
from qgis.gui import QgsEncodingSelectionDialog
from qgis.core import (QgsDataSourceUri,
@@ -125,10 +125,10 @@ def selectOutput(self):

if isinstance(self.parameter, QgsProcessingParameterFeatureSink) \
and self.alg.provider().supportsNonFileBasedOutput():
actionSaveToSpatialite = QAction(
self.tr('Save to SpatiaLite table...'), self.btnSelect)
actionSaveToSpatialite.triggered.connect(self.saveToSpatialite)
popupMenu.addAction(actionSaveToSpatialite)
actionSaveToGpkg = QAction(
self.tr('Save to GeoPackage...'), self.btnSelect)
actionSaveToGpkg.triggered.connect(self.saveToGeopackage)
popupMenu.addAction(actionSaveToGpkg)
actionSaveToPostGIS = QAction(
self.tr('Save to PostGIS table...'), self.btnSelect)
actionSaveToPostGIS.triggered.connect(self.saveToPostGIS)
@@ -177,30 +177,34 @@ def saveToPostGIS(self):
QgsCredentials.instance().put(connInfo, user, passwd)
self.leText.setText("postgis:" + uri.uri())

def saveToSpatialite(self):
file_filter = self.tr('SpatiaLite files (*.sqlite)', 'OutputFile')
def saveToGeopackage(self):
file_filter = self.tr('GeoPackage files (*.gpkg);;All files (*.*)', 'OutputFile')

settings = QgsSettings()
if settings.contains('/Processing/LastOutputPath'):
path = settings.value('/Processing/LastOutputPath')
else:
path = ProcessingConfig.getSetting(ProcessingConfig.OUTPUT_FOLDER)

filename, filter = QFileDialog.getSaveFileName(self, self.tr("Save file"), path,
filename, filter = QFileDialog.getSaveFileName(self, self.tr("Save to GeoPackage"), path,
file_filter, options=QFileDialog.DontConfirmOverwrite)

if filename is not None:
if filename is None:
return

layer_name, ok = QInputDialog.getText(self, self.tr('Save to GeoPackage'), self.tr('Layer name'), text=self.parameter.name().lower())
if ok:
self.use_temporary = False
if not filename.lower().endswith('.sqlite'):
filename += '.sqlite'
if not filename.lower().endswith('.gpkg'):
filename += '.gpkg'
settings.setValue('/Processing/LastOutputPath',
os.path.dirname(filename))

uri = QgsDataSourceUri()
uri.setDatabase(filename)
uri.setDataSource('', self.parameter.name().lower(),
'the_geom' if isinstance(self.parameter, QgsProcessingParameterFeatureSink) and self.parameter.hasGeometry() else None)
self.leText.setText("spatialite:" + uri.uri())
uri.setDataSource('', layer_name,
'geom' if isinstance(self.parameter, QgsProcessingParameterFeatureSink) and self.parameter.hasGeometry() else None)
self.leText.setText("ogr:" + uri.uri())

def selectFile(self):
file_filter = getFileFilter(self.parameter)
@@ -517,7 +517,7 @@ QgsGeometry QgsInternalGeometryEngine::orthogonalize( double tolerance, int maxI
}

// if extraNodesPerSegment < 0, then use distance based mode
QgsLineString *doDensify( QgsLineString *ring, int extraNodesPerSegment = -1, double distance = 1 )
QgsLineString *doDensify( const QgsLineString *ring, int extraNodesPerSegment = -1, double distance = 1 )
{
QVector< double > outX;
QVector< double > outY;
@@ -627,11 +627,11 @@ QgsAbstractGeometry *densifyGeometry( const QgsAbstractGeometry *geom, int extra
const QgsPolygon *polygon = static_cast< const QgsPolygon * >( geom );
QgsPolygon *result = new QgsPolygon();

result->setExteriorRing( doDensify( static_cast< QgsLineString * >( polygon->exteriorRing()->clone() ),
result->setExteriorRing( doDensify( static_cast< const QgsLineString * >( polygon->exteriorRing() ),
extraNodesPerSegment, distance ) );
for ( int i = 0; i < polygon->numInteriorRings(); ++i )
{
result->addInteriorRing( doDensify( static_cast< QgsLineString * >( polygon->interiorRing( i )->clone() ),
result->addInteriorRing( doDensify( static_cast< const QgsLineString * >( polygon->interiorRing( i ) ),
extraNodesPerSegment, distance ) );
}

@@ -183,7 +183,7 @@ bool QgsProcessingAlgorithm::validateInputCrs( const QVariantMap &parameters, Qg
}
else if ( def->type() == QStringLiteral( "source" ) )
{
QgsFeatureSource *source = QgsProcessingParameters::parameterAsSource( def, parameters, context );
std::unique_ptr< QgsFeatureSource > source( QgsProcessingParameters::parameterAsSource( def, parameters, context ) );
if ( source )
{
if ( foundCrs && source->sourceCrs().isValid() && crs != source->sourceCrs() )
@@ -307,9 +307,9 @@ QString QgsProcessingUtils::stringToPythonLiteral( const QString &string )
return s;
}

void parseDestinationString( QString &destination, QString &providerKey, QString &uri, QString &format, QMap<QString, QVariant> &options )
void QgsProcessingUtils::parseDestinationString( QString &destination, QString &providerKey, QString &uri, QString &layerName, QString &format, QMap<QString, QVariant> &options, bool &useWriter )
{
QRegularExpression splitRx( QStringLiteral( "^(.{3,}):(.*)$" ) );
QRegularExpression splitRx( QStringLiteral( "^(.{3,}?):(.*)$" ) );
QRegularExpressionMatch match = splitRx.match( destination );
if ( match.hasMatch() )
{
@@ -319,9 +319,25 @@ void parseDestinationString( QString &destination, QString &providerKey, QString
providerKey = QStringLiteral( "postgres" );
}
uri = match.captured( 2 );
if ( providerKey == QLatin1String( "ogr" ) )
{
QgsDataSourceUri dsUri( uri );
if ( !dsUri.database().isEmpty() )
{
if ( !dsUri.table().isEmpty() )
{
layerName = dsUri.table();
options.insert( "layerName", layerName );
}
uri = dsUri.database();
}
options.insert( QStringLiteral( "update" ), true );
}
useWriter = false;
}
else
{
useWriter = true;
providerKey = QStringLiteral( "ogr" );
QRegularExpression splitRx( QStringLiteral( "^(.*)\\.(.*?)$" ) );
QRegularExpressionMatch match = splitRx.match( destination );
@@ -356,12 +372,9 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, Qgs
{
// memory provider cannot be used with QgsVectorLayerImport - so create layer manually
std::unique_ptr< QgsVectorLayer > layer( QgsMemoryProviderUtils::createMemoryLayer( destination, fields, geometryType, crs ) );
if ( !layer )
return nullptr;

if ( !layer->isValid() )
if ( !layer || !layer->isValid() )
{
return nullptr;
throw QgsProcessingException( QObject::tr( "Could not create memory layer" ) );
}

// update destination to layer ID
@@ -377,30 +390,42 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, Qgs
{
QString providerKey;
QString uri;
QString layerName;
QString format;
parseDestinationString( destination, providerKey, uri, format, options );
bool useWriter = false;
parseDestinationString( destination, providerKey, uri, layerName, format, options, useWriter );

if ( providerKey == QLatin1String( "ogr" ) )
if ( useWriter && providerKey == QLatin1String( "ogr" ) )
{
// 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, options.value( QStringLiteral( "fileEncoding" ) ).toString(), fields, geometryType, crs, format, QgsVectorFileWriter::defaultDatasetOptions( format ),
std::unique_ptr< QgsVectorFileWriter > writer = qgis::make_unique< QgsVectorFileWriter >( destination, options.value( QStringLiteral( "fileEncoding" ) ).toString(), fields, geometryType, crs, format, QgsVectorFileWriter::defaultDatasetOptions( format ),
QgsVectorFileWriter::defaultLayerOptions( format ), &finalFileName );

if ( writer->hasError() )
{
throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, writer->errorMessage() ) );
}
destination = finalFileName;
return writer;
return writer.release();
}
else
{
//create empty layer
std::unique_ptr< QgsVectorLayerExporter > exporter( new QgsVectorLayerExporter( uri, providerKey, fields, geometryType, crs, false, options ) );
std::unique_ptr< QgsVectorLayerExporter > exporter( new QgsVectorLayerExporter( uri, providerKey, fields, geometryType, crs, true, options ) );
if ( exporter->errorCode() )
return nullptr;
{
throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, exporter->errorMessage() ) );
}

// use destination string as layer name (eg "postgis:..." )
if ( !layerName.isEmpty() )
uri += QStringLiteral( "|layername=%1" ).arg( layerName );
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();
}
@@ -267,6 +267,8 @@ class CORE_EXPORT QgsProcessingUtils
*/
static QgsMapLayer *loadMapLayerFromString( const QString &string );

static void parseDestinationString( QString &destination, QString &providerKey, QString &uri, QString &layerName, QString &format, QMap<QString, QVariant> &options, bool &useWriter );

friend class TestQgsProcessing;

};

0 comments on commit 2b5aca5

Please sign in to comment.
You can’t perform that action at this time.