Skip to content

Commit

Permalink
Merge pull request #5689 from nyalldawson/gpkg_alg
Browse files Browse the repository at this point in the history
[processing] Fixes to non flat-file exports
  • Loading branch information
nyalldawson committed Nov 24, 2017
2 parents 6278245 + 5b66ea7 commit 2b5aca5
Show file tree
Hide file tree
Showing 12 changed files with 292 additions and 109 deletions.
6 changes: 1 addition & 5 deletions python/plugins/processing/algs/qgis/GeometryByExpression.py 100644 → 100755
Expand Up @@ -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

Expand Down
32 changes: 18 additions & 14 deletions python/plugins/processing/gui/DestinationSelectionPanel.py 100644 → 100755
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions src/core/geometry/qgsinternalgeometryengine.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -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 ) );
}

Expand Down
2 changes: 1 addition & 1 deletion src/core/processing/qgsprocessingalgorithm.cpp
Expand Up @@ -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() )
Expand Down
51 changes: 38 additions & 13 deletions src/core/processing/qgsprocessingutils.cpp
Expand Up @@ -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() )
{
Expand All @@ -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 );
Expand Down Expand Up @@ -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
Expand All @@ -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();
}
Expand Down
2 changes: 2 additions & 0 deletions src/core/processing/qgsprocessingutils.h
Expand Up @@ -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;

};
Expand Down

0 comments on commit 2b5aca5

Please sign in to comment.