QGIS API Documentation  3.6.0-Noosa (5873452)
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 #include "qgsvectorlayer.h"
23 
25 
26 QString QgsPackageAlgorithm::name() const
27 {
28  return QStringLiteral( "package" );
29 }
30 
31 QString QgsPackageAlgorithm::displayName() const
32 {
33  return QObject::tr( "Package layers" );
34 }
35 
36 QStringList QgsPackageAlgorithm::tags() const
37 {
38  return QObject::tr( "geopackage,collect,merge,combine" ).split( ',' );
39 }
40 
41 QString QgsPackageAlgorithm::group() const
42 {
43  return QObject::tr( "Database" );
44 }
45 
46 QString QgsPackageAlgorithm::groupId() const
47 {
48  return QStringLiteral( "database" );
49 }
50 
51 void QgsPackageAlgorithm::initAlgorithm( const QVariantMap & )
52 {
53  addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "LAYERS" ), QObject::tr( "Input layers" ), QgsProcessing::TypeVector ) );
54  addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Destination GeoPackage" ), QObject::tr( "GeoPackage files (*.gpkg)" ) ) );
55  addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "OVERWRITE" ), QObject::tr( "Overwrite existing GeoPackage" ), false ) );
56  addOutput( new QgsProcessingOutputMultipleLayers( QStringLiteral( "OUTPUT_LAYERS" ), QObject::tr( "Layers within new package" ) ) );
57 }
58 
59 QString QgsPackageAlgorithm::shortHelpString() const
60 {
61  return QObject::tr( "This algorithm collects a number of existing layers and packages them together into a single GeoPackage database." );
62 }
63 
64 QgsPackageAlgorithm *QgsPackageAlgorithm::createInstance() const
65 {
66  return new QgsPackageAlgorithm();
67 }
68 
69 QVariantMap QgsPackageAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
70 {
71  bool overwrite = parameterAsBool( parameters, QStringLiteral( "OVERWRITE" ), context );
72  QString packagePath = parameterAsString( parameters, QStringLiteral( "OUTPUT" ), context );
73  if ( packagePath.isEmpty() )
74  throw QgsProcessingException( QObject::tr( "No output file specified." ) );
75 
76  // delete existing geopackage if it exists
77  if ( overwrite && QFile::exists( packagePath ) )
78  {
79  feedback->pushInfo( QObject::tr( "Removing existing file '%1'" ).arg( packagePath ) );
80  if ( !QFile( packagePath ).remove() )
81  {
82  throw QgsProcessingException( QObject::tr( "Could not remove existing file '%1'" ) );
83  }
84  }
85 
86  OGRSFDriverH hGpkgDriver = OGRGetDriverByName( "GPKG" );
87  if ( !hGpkgDriver )
88  {
89  throw QgsProcessingException( QObject::tr( "GeoPackage driver not found." ) );
90  }
91 
92  gdal::ogr_datasource_unique_ptr hDS( OGR_Dr_CreateDataSource( hGpkgDriver, packagePath.toUtf8().constData(), nullptr ) );
93  if ( !hDS )
94  throw QgsProcessingException( QObject::tr( "Creation of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
95 
96  bool errored = false;
97  const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "LAYERS" ), context );
98 
99  QgsProcessingMultiStepFeedback multiStepFeedback( layers.count(), feedback );
100 
101  QStringList outputLayers;
102  int i = 0;
103  for ( QgsMapLayer *layer : layers )
104  {
105  if ( feedback->isCanceled() )
106  break;
107 
108  multiStepFeedback.setCurrentStep( i );
109  i++;
110 
111  feedback->pushInfo( QObject::tr( "Packaging layer %1/%2: %3" ).arg( i ).arg( layers.count() ).arg( layer ? layer->name() : QString() ) );
112 
113  if ( !layer )
114  {
115  // don't throw immediately - instead do what we can and error out later
116  feedback->pushDebugInfo( QObject::tr( "Error retrieving map layer." ) );
117  errored = true;
118  continue;
119  }
120 
121  switch ( layer->type() )
122  {
124  {
125  if ( !packageVectorLayer( qobject_cast< QgsVectorLayer * >( layer ), packagePath,
126  context, &multiStepFeedback ) )
127  errored = true;
128  else
129  outputLayers.append( QStringLiteral( "%1|layername=%2" ).arg( packagePath, layer->name() ) );
130  break;
131  }
132 
134  {
135  //not supported
136  feedback->pushDebugInfo( QObject::tr( "Raster layers are not currently supported." ) );
137  errored = true;
138  break;
139  }
140 
142  //not supported
143  feedback->pushDebugInfo( QObject::tr( "Packaging plugin layers is not supported." ) );
144  errored = true;
145  break;
146 
148  //not supported
149  feedback->pushDebugInfo( QObject::tr( "Packaging mesh layers is not supported." ) );
150  errored = true;
151  break;
152  }
153  }
154 
155  if ( errored )
156  throw QgsProcessingException( QObject::tr( "Error obtained while packaging one or more layers." ) );
157 
158  QVariantMap outputs;
159  outputs.insert( QStringLiteral( "OUTPUT" ), packagePath );
160  outputs.insert( QStringLiteral( "OUTPUT_LAYERS" ), outputLayers );
161  return outputs;
162 }
163 
164 bool QgsPackageAlgorithm::packageVectorLayer( QgsVectorLayer *layer, const QString &path, QgsProcessingContext &context,
165  QgsProcessingFeedback *feedback )
166 {
168  options.driverName = QStringLiteral( "GPKG" );
169  options.layerName = layer->name();
171  options.fileEncoding = context.defaultEncoding();
172  options.feedback = feedback;
173 
174  QString error;
175  if ( QgsVectorFileWriter::writeAsVectorFormat( layer, path, options, &error ) != QgsVectorFileWriter::NoError )
176  {
177  feedback->pushDebugInfo( QObject::tr( "Packaging layer failed: %1" ).arg( error ) );
178  return false;
179  }
180  else
181  {
182  return true;
183  }
184 }
185 
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:64
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:111
Options to pass to writeAsVectorFormat()
Custom exception class for processing related exceptions.
Definition: qgsexception.h:82
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, QString *newLayer=nullptr)
Write contents of vector layer to an (OGR supported) vector format.
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.
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:68
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.