Skip to content

Commit 2b5aca5

Browse files
authored
Merge pull request #5689 from nyalldawson/gpkg_alg
[processing] Fixes to non flat-file exports
2 parents 6278245 + 5b66ea7 commit 2b5aca5

12 files changed

+292
-109
lines changed

python/plugins/processing/algs/qgis/GeometryByExpression.py

100644100755
+1-5
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,7 @@ def prepareAlgorithm(self, parameters, context, feedback):
9494
return False
9595

9696
self.expression_context = self.createExpressionContext(parameters, context)
97-
98-
if not self.expression.prepare(self.expression_context):
99-
feedback.reportErro(
100-
self.tr('Evaluation error: {0}').format(self.expression.evalErrorString()))
101-
return False
97+
self.expression.prepare(self.expression_context)
10298

10399
return True
104100

python/plugins/processing/gui/DestinationSelectionPanel.py

100644100755
+18-14
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

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

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

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

183183
settings = QgsSettings()
184184
if settings.contains('/Processing/LastOutputPath'):
185185
path = settings.value('/Processing/LastOutputPath')
186186
else:
187187
path = ProcessingConfig.getSetting(ProcessingConfig.OUTPUT_FOLDER)
188188

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

192-
if filename is not None:
192+
if filename is None:
193+
return
194+
195+
layer_name, ok = QInputDialog.getText(self, self.tr('Save to GeoPackage'), self.tr('Layer name'), text=self.parameter.name().lower())
196+
if ok:
193197
self.use_temporary = False
194-
if not filename.lower().endswith('.sqlite'):
195-
filename += '.sqlite'
198+
if not filename.lower().endswith('.gpkg'):
199+
filename += '.gpkg'
196200
settings.setValue('/Processing/LastOutputPath',
197201
os.path.dirname(filename))
198202

199203
uri = QgsDataSourceUri()
200204
uri.setDatabase(filename)
201-
uri.setDataSource('', self.parameter.name().lower(),
202-
'the_geom' if isinstance(self.parameter, QgsProcessingParameterFeatureSink) and self.parameter.hasGeometry() else None)
203-
self.leText.setText("spatialite:" + uri.uri())
205+
uri.setDataSource('', layer_name,
206+
'geom' if isinstance(self.parameter, QgsProcessingParameterFeatureSink) and self.parameter.hasGeometry() else None)
207+
self.leText.setText("ogr:" + uri.uri())
204208

205209
def selectFile(self):
206210
file_filter = getFileFilter(self.parameter)

src/core/geometry/qgsinternalgeometryengine.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ QgsGeometry QgsInternalGeometryEngine::orthogonalize( double tolerance, int maxI
517517
}
518518

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

630-
result->setExteriorRing( doDensify( static_cast< QgsLineString * >( polygon->exteriorRing()->clone() ),
630+
result->setExteriorRing( doDensify( static_cast< const QgsLineString * >( polygon->exteriorRing() ),
631631
extraNodesPerSegment, distance ) );
632632
for ( int i = 0; i < polygon->numInteriorRings(); ++i )
633633
{
634-
result->addInteriorRing( doDensify( static_cast< QgsLineString * >( polygon->interiorRing( i )->clone() ),
634+
result->addInteriorRing( doDensify( static_cast< const QgsLineString * >( polygon->interiorRing( i ) ),
635635
extraNodesPerSegment, distance ) );
636636
}
637637

src/core/processing/qgsprocessingalgorithm.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ bool QgsProcessingAlgorithm::validateInputCrs( const QVariantMap &parameters, Qg
183183
}
184184
else if ( def->type() == QStringLiteral( "source" ) )
185185
{
186-
QgsFeatureSource *source = QgsProcessingParameters::parameterAsSource( def, parameters, context );
186+
std::unique_ptr< QgsFeatureSource > source( QgsProcessingParameters::parameterAsSource( def, parameters, context ) );
187187
if ( source )
188188
{
189189
if ( foundCrs && source->sourceCrs().isValid() && crs != source->sourceCrs() )

src/core/processing/qgsprocessingutils.cpp

+38-13
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,9 @@ QString QgsProcessingUtils::stringToPythonLiteral( const QString &string )
307307
return s;
308308
}
309309

310-
void parseDestinationString( QString &destination, QString &providerKey, QString &uri, QString &format, QMap<QString, QVariant> &options )
310+
void QgsProcessingUtils::parseDestinationString( QString &destination, QString &providerKey, QString &uri, QString &layerName, QString &format, QMap<QString, QVariant> &options, bool &useWriter )
311311
{
312-
QRegularExpression splitRx( QStringLiteral( "^(.{3,}):(.*)$" ) );
312+
QRegularExpression splitRx( QStringLiteral( "^(.{3,}?):(.*)$" ) );
313313
QRegularExpressionMatch match = splitRx.match( destination );
314314
if ( match.hasMatch() )
315315
{
@@ -319,9 +319,25 @@ void parseDestinationString( QString &destination, QString &providerKey, QString
319319
providerKey = QStringLiteral( "postgres" );
320320
}
321321
uri = match.captured( 2 );
322+
if ( providerKey == QLatin1String( "ogr" ) )
323+
{
324+
QgsDataSourceUri dsUri( uri );
325+
if ( !dsUri.database().isEmpty() )
326+
{
327+
if ( !dsUri.table().isEmpty() )
328+
{
329+
layerName = dsUri.table();
330+
options.insert( "layerName", layerName );
331+
}
332+
uri = dsUri.database();
333+
}
334+
options.insert( QStringLiteral( "update" ), true );
335+
}
336+
useWriter = false;
322337
}
323338
else
324339
{
340+
useWriter = true;
325341
providerKey = QStringLiteral( "ogr" );
326342
QRegularExpression splitRx( QStringLiteral( "^(.*)\\.(.*?)$" ) );
327343
QRegularExpressionMatch match = splitRx.match( destination );
@@ -356,12 +372,9 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, Qgs
356372
{
357373
// memory provider cannot be used with QgsVectorLayerImport - so create layer manually
358374
std::unique_ptr< QgsVectorLayer > layer( QgsMemoryProviderUtils::createMemoryLayer( destination, fields, geometryType, crs ) );
359-
if ( !layer )
360-
return nullptr;
361-
362-
if ( !layer->isValid() )
375+
if ( !layer || !layer->isValid() )
363376
{
364-
return nullptr;
377+
throw QgsProcessingException( QObject::tr( "Could not create memory layer" ) );
365378
}
366379

367380
// update destination to layer ID
@@ -377,30 +390,42 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, Qgs
377390
{
378391
QString providerKey;
379392
QString uri;
393+
QString layerName;
380394
QString format;
381-
parseDestinationString( destination, providerKey, uri, format, options );
395+
bool useWriter = false;
396+
parseDestinationString( destination, providerKey, uri, layerName, format, options, useWriter );
382397

383-
if ( providerKey == QLatin1String( "ogr" ) )
398+
if ( useWriter && providerKey == QLatin1String( "ogr" ) )
384399
{
385400
// use QgsVectorFileWriter for OGR destinations instead of QgsVectorLayerImport, as that allows
386401
// us to use any OGR format which supports feature addition
387402
QString finalFileName;
388-
QgsVectorFileWriter *writer = new QgsVectorFileWriter( destination, options.value( QStringLiteral( "fileEncoding" ) ).toString(), fields, geometryType, crs, format, QgsVectorFileWriter::defaultDatasetOptions( format ),
403+
std::unique_ptr< QgsVectorFileWriter > writer = qgis::make_unique< QgsVectorFileWriter >( destination, options.value( QStringLiteral( "fileEncoding" ) ).toString(), fields, geometryType, crs, format, QgsVectorFileWriter::defaultDatasetOptions( format ),
389404
QgsVectorFileWriter::defaultLayerOptions( format ), &finalFileName );
405+
406+
if ( writer->hasError() )
407+
{
408+
throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, writer->errorMessage() ) );
409+
}
390410
destination = finalFileName;
391-
return writer;
411+
return writer.release();
392412
}
393413
else
394414
{
395415
//create empty layer
396-
std::unique_ptr< QgsVectorLayerExporter > exporter( new QgsVectorLayerExporter( uri, providerKey, fields, geometryType, crs, false, options ) );
416+
std::unique_ptr< QgsVectorLayerExporter > exporter( new QgsVectorLayerExporter( uri, providerKey, fields, geometryType, crs, true, options ) );
397417
if ( exporter->errorCode() )
398-
return nullptr;
418+
{
419+
throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, exporter->errorMessage() ) );
420+
}
399421

400422
// use destination string as layer name (eg "postgis:..." )
423+
if ( !layerName.isEmpty() )
424+
uri += QStringLiteral( "|layername=%1" ).arg( layerName );
401425
std::unique_ptr< QgsVectorLayer > layer( new QgsVectorLayer( uri, destination, providerKey ) );
402426
// update destination to layer ID
403427
destination = layer->id();
428+
404429
context.temporaryLayerStore()->addMapLayer( layer.release() );
405430
return exporter.release();
406431
}

src/core/processing/qgsprocessingutils.h

+2
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ class CORE_EXPORT QgsProcessingUtils
267267
*/
268268
static QgsMapLayer *loadMapLayerFromString( const QString &string );
269269

270+
static void parseDestinationString( QString &destination, QString &providerKey, QString &uri, QString &layerName, QString &format, QMap<QString, QVariant> &options, bool &useWriter );
271+
270272
friend class TestQgsProcessing;
271273

272274
};

0 commit comments

Comments
 (0)