QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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  // remove any existing FID field, let this be completely recreated
175  // since many layer sources have fid fields which are not compatible with gpkg requirements
176  QgsFields fields = layer->fields();
177  const int fidIndex = fields.lookupField( QStringLiteral( "fid" ) );
178 
179  options.attributes = fields.allAttributesList();
180  if ( fidIndex >= 0 )
181  options.attributes.removeAll( fidIndex );
182  if ( options.attributes.isEmpty() )
183  {
184  // fid was the only field
185  options.skipAttributeCreation = true;
186  }
187 
188  QString error;
189  if ( QgsVectorFileWriter::writeAsVectorFormat( layer, path, options, &error ) != QgsVectorFileWriter::NoError )
190  {
191  feedback->pushDebugInfo( QObject::tr( "Packaging layer failed: %1" ).arg( error ) );
192  return false;
193  }
194  else
195  {
196  return true;
197  }
198 }
199 
A boolean parameter for processing algorithms.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
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:63
Base class for providing feedback from a processing algorithm.
Processing feedback object for multi-step operations.
QgsAttributeList attributes
Attributes to export (empty means all unless skipAttributeCreation is set)
void setCurrentStep(int step)
Sets the step which is being executed.
Container of fields for a vector layer.
Definition: qgsfields.h:42
Added in 3.2.
Definition: qgsmaplayer.h:110
Options to pass to writeAsVectorFormat()
QString defaultEncoding() const
Returns the default encoding to use for newly created files.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
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.
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:320
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 skipAttributeCreation
Only write geometries.
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:67
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 cancellation 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.
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
Definition: qgsfields.cpp:347
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.