QGIS API Documentation  3.2.0-Bonn (bc43194)
qgsalgorithmpackage.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsalgorithmpackage.cpp
3  ---------------------
4  begin : November 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsalgorithmpackage.h"
19 #include "qgsgeometryengine.h"
20 #include "qgsogrutils.h"
21 #include "qgsvectorfilewriter.h"
22 
24 
25 QString QgsPackageAlgorithm::name() const
26 {
27  return QStringLiteral( "package" );
28 }
29 
30 QString QgsPackageAlgorithm::displayName() const
31 {
32  return QObject::tr( "Package layers" );
33 }
34 
35 QStringList QgsPackageAlgorithm::tags() const
36 {
37  return QObject::tr( "geopackage,collect,merge,combine" ).split( ',' );
38 }
39 
40 QString QgsPackageAlgorithm::group() const
41 {
42  return QObject::tr( "Database" );
43 }
44 
45 QString QgsPackageAlgorithm::groupId() const
46 {
47  return QStringLiteral( "database" );
48 }
49 
50 void QgsPackageAlgorithm::initAlgorithm( const QVariantMap & )
51 {
52  addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "LAYERS" ), QObject::tr( "Input layers" ), QgsProcessing::TypeVector ) );
53  addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Destination GeoPackage" ), QObject::tr( "GeoPackage files (*.gpkg)" ) ) );
54  addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "OVERWRITE" ), QObject::tr( "Overwrite existing GeoPackage" ), false ) );
55  addOutput( new QgsProcessingOutputMultipleLayers( QStringLiteral( "OUTPUT_LAYERS" ), QObject::tr( "Layers within new package" ) ) );
56 }
57 
58 QString QgsPackageAlgorithm::shortHelpString() const
59 {
60  return QObject::tr( "This algorithm collects a number of existing layers and packages them together into a single GeoPackage database." );
61 }
62 
63 QgsPackageAlgorithm *QgsPackageAlgorithm::createInstance() const
64 {
65  return new QgsPackageAlgorithm();
66 }
67 
68 QVariantMap QgsPackageAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
69 {
70  bool overwrite = parameterAsBool( parameters, QStringLiteral( "OVERWRITE" ), context );
71  QString packagePath = parameterAsString( parameters, QStringLiteral( "OUTPUT" ), context );
72  if ( packagePath.isEmpty() )
73  throw QgsProcessingException( QObject::tr( "No output file specified." ) );
74 
75  // delete existing geopackage if it exists
76  if ( overwrite && QFile::exists( packagePath ) )
77  {
78  feedback->pushInfo( QObject::tr( "Removing existing file '%1'" ).arg( packagePath ) );
79  if ( !QFile( packagePath ).remove() )
80  {
81  throw QgsProcessingException( QObject::tr( "Could not remove existing file '%1'" ) );
82  }
83  }
84 
85  OGRSFDriverH hGpkgDriver = OGRGetDriverByName( "GPKG" );
86  if ( !hGpkgDriver )
87  {
88  throw QgsProcessingException( QObject::tr( "GeoPackage driver not found." ) );
89  }
90 
91  gdal::ogr_datasource_unique_ptr hDS( OGR_Dr_CreateDataSource( hGpkgDriver, packagePath.toUtf8().constData(), nullptr ) );
92  if ( !hDS )
93  throw QgsProcessingException( QObject::tr( "Creation of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
94 
95  bool errored = false;
96  const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "LAYERS" ), context );
97 
98  QgsProcessingMultiStepFeedback multiStepFeedback( layers.count(), feedback );
99 
100  QStringList outputLayers;
101  int i = 0;
102  for ( QgsMapLayer *layer : layers )
103  {
104  if ( feedback->isCanceled() )
105  break;
106 
107  multiStepFeedback.setCurrentStep( i );
108  i++;
109 
110  feedback->pushInfo( QObject::tr( "Packaging layer %1/%2: %3" ).arg( i ).arg( layers.count() ).arg( layer ? layer->name() : QString() ) );
111 
112  if ( !layer )
113  {
114  // don't throw immediately - instead do what we can and error out later
115  feedback->pushDebugInfo( QObject::tr( "Error retrieving map layer." ) );
116  errored = true;
117  continue;
118  }
119 
120  switch ( layer->type() )
121  {
123  {
124  if ( !packageVectorLayer( qobject_cast< QgsVectorLayer * >( layer ), packagePath,
125  context, &multiStepFeedback ) )
126  errored = true;
127  else
128  outputLayers.append( QStringLiteral( "%1|layername=%2" ).arg( packagePath, layer->name() ) );
129  break;
130  }
131 
133  {
134  //not supported
135  feedback->pushDebugInfo( QObject::tr( "Raster layers are not currently supported." ) );
136  errored = true;
137  break;
138  }
139 
141  //not supported
142  feedback->pushDebugInfo( QObject::tr( "Packaging plugin layers is not supported." ) );
143  errored = true;
144  break;
145 
147  //not supported
148  feedback->pushDebugInfo( QObject::tr( "Packaging mesh layers is not supported." ) );
149  errored = true;
150  break;
151  }
152  }
153 
154  if ( errored )
155  throw QgsProcessingException( QObject::tr( "Error obtained while packaging one or more layers." ) );
156 
157  QVariantMap outputs;
158  outputs.insert( QStringLiteral( "OUTPUT" ), packagePath );
159  outputs.insert( QStringLiteral( "OUTPUT_LAYERS" ), outputLayers );
160  return outputs;
161 }
162 
163 bool QgsPackageAlgorithm::packageVectorLayer( QgsVectorLayer *layer, const QString &path, QgsProcessingContext &context,
164  QgsProcessingFeedback *feedback )
165 {
167  options.driverName = QStringLiteral( "GPKG" );
168  options.layerName = layer->name();
170  options.fileEncoding = context.defaultEncoding();
171  options.feedback = feedback;
172 
173  QString error;
174  if ( QgsVectorFileWriter::writeAsVectorFormat( layer, path, options, &error ) != QgsVectorFileWriter::NoError )
175  {
176  feedback->pushDebugInfo( QObject::tr( "Packaging layer failed: %1" ).arg( error ) );
177  return false;
178  }
179  else
180  {
181  return true;
182  }
183 }
184 
A boolean parameter for processing algorithms.
A parameter for processing algorithms which accepts multiple map layers.
QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile
Action on existing file.
Base class for all map layer types.
Definition: qgsmaplayer.h:61
Base class for providing feedback from a processing algorithm.
Processing feedback object for multi-step operations.
void setCurrentStep(int step)
Sets the step which is being executed.
Added in 3.2.
Definition: qgsmaplayer.h:107
Options to pass to writeAsVectorFormat()
Custom exception class for processing related exceptions.
Definition: qgsexception.h:82
A generic file based destination parameter, for specifying the destination path for a file (non-map l...
virtual void pushDebugInfo(const QString &info)
Pushes an informational message containing debugging helpers from the algorithm.
A multi-layer output for processing algorithms which create map layers, when the number and nature of...
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
QString defaultEncoding() const
Returns the default encoding to use for newly created files.
static QgsVectorFileWriter::WriterError writeAsVectorFormat(QgsVectorLayer *layer, const QString &fileName, const QString &fileEncoding, const QgsCoordinateReferenceSystem &destCRS=QgsCoordinateReferenceSystem(), const QString &driverName="GPKG", bool onlySelected=false, QString *errorMessage=nullptr, const QStringList &datasourceOptions=QStringList(), const QStringList &layerOptions=QStringList(), bool skipAttributeCreation=false, QString *newFilename=nullptr, QgsVectorFileWriter::SymbologyExport symbologyExport=QgsVectorFileWriter::NoSymbology, double symbologyScale=1.0, const QgsRectangle *filterExtent=nullptr, QgsWkbTypes::Type overrideGeometryType=QgsWkbTypes::Unknown, bool forceMulti=false, bool includeZ=false, const QgsAttributeList &attributes=QgsAttributeList(), QgsVectorFileWriter::FieldValueConverter *fieldValueConverter=nullptr)
Write contents of vector layer to an (OGR supported) vector formt.
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition: qgsprocessing.h:53
QString name
Definition: qgsmaplayer.h:65
QString layerName
Layer name. If let empty, it will be derived from the filename.
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
Definition: qgsogrutils.h:114
QgsFeedback * feedback
Optional feedback object allowing cancelation of layer save.
Represents a vector layer which manages a vector based data sets.
Contains information about the context in which a processing algorithm is executed.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.