28QString QgsPackageAlgorithm::name()
const
30 return QStringLiteral(
"package" );
33QString QgsPackageAlgorithm::displayName()
const
35 return QObject::tr(
"Package layers" );
38QStringList QgsPackageAlgorithm::tags()
const
40 return QObject::tr(
"geopackage,collect,merge,combine,styles" ).split(
',' );
43QString QgsPackageAlgorithm::group()
const
45 return QObject::tr(
"Database" );
48QString QgsPackageAlgorithm::groupId()
const
50 return QStringLiteral(
"database" );
53void QgsPackageAlgorithm::initAlgorithm(
const QVariantMap & )
57 outputParameter->
setMetadata( QVariantMap( {{QStringLiteral(
"widget_wrapper" ), QVariantMap( {{QStringLiteral(
"dontconfirmoverwrite" ),
true }} ) }} ) );
58 addParameter( outputParameter );
60 addParameter(
new QgsProcessingParameterBoolean( QStringLiteral(
"SAVE_STYLES" ), QObject::tr(
"Save layer styles into GeoPackage" ),
true ) );
61 addParameter(
new QgsProcessingParameterBoolean( QStringLiteral(
"SAVE_METADATA" ), QObject::tr(
"Save layer metadata into GeoPackage" ),
true ) );
62 addParameter(
new QgsProcessingParameterBoolean( QStringLiteral(
"SELECTED_FEATURES_ONLY" ), QObject::tr(
"Save only selected features" ),
false ) );
63 addParameter(
new QgsProcessingParameterBoolean( QStringLiteral(
"EXPORT_RELATED_LAYERS" ), QObject::tr(
"Export related layers following relations defined in the project" ),
false ) );
67QString QgsPackageAlgorithm::shortHelpString()
const
69 return QObject::tr(
"This algorithm collects a number of existing layers and packages them together into a single GeoPackage database." );
72QgsPackageAlgorithm *QgsPackageAlgorithm::createInstance()
const
74 return new QgsPackageAlgorithm();
80 const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral(
"LAYERS" ), context );
82 for (
const QgsMapLayer *layer : std::as_const( layers ) )
85 mClonedLayerIds.insert( clonedLayer->id(), layer->id( ) );
86 mLayers.emplace_back( clonedLayer );
89 if ( mLayers.empty() )
91 feedback->
reportError( QObject::tr(
"No layers selected, geopackage will be empty" ),
false );
99 const bool overwrite = parameterAsBoolean( parameters, QStringLiteral(
"OVERWRITE" ), context );
100 const bool saveStyles = parameterAsBoolean( parameters, QStringLiteral(
"SAVE_STYLES" ), context );
101 const bool saveMetadata = parameterAsBoolean( parameters, QStringLiteral(
"SAVE_METADATA" ), context );
102 const bool selectedFeaturesOnly = parameterAsBoolean( parameters, QStringLiteral(
"SELECTED_FEATURES_ONLY" ), context );
103 const bool exportRelatedLayers = parameterAsBoolean( parameters, QStringLiteral(
"EXPORT_RELATED_LAYERS" ), context );
104 const QString packagePath = parameterAsString( parameters, QStringLiteral(
"OUTPUT" ), context );
105 const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral(
"LAYERS" ), context );
107 if ( packagePath.isEmpty() )
111 if ( overwrite && QFile::exists( packagePath ) )
113 feedback->
pushInfo( QObject::tr(
"Removing existing file '%1'" ).arg( packagePath ) );
114 if ( !QFile( packagePath ).remove() )
120 OGRSFDriverH hGpkgDriver = OGRGetDriverByName(
"GPKG" );
128 if ( exportRelatedLayers )
131 if ( project && ! project->relationManager()->relations().isEmpty() )
135 const int maxRecursion { 10 };
136 int recursionGuard { 0 };
139 const auto findReferenced = [ =, &project, &feedback, &recursionGuard, &layers ](
const QgsVectorLayer * vLayer,
bool onlySaveSelected,
auto &&findReferenced ) ->
void
141 const QgsVectorLayer *originalLayer { qobject_cast<QgsVectorLayer *>( project->mapLayer( mClonedLayerIds.value( vLayer->id(), vLayer->id() ) ) ) };
142 Q_ASSERT( originalLayer );
143 const QList<QgsRelation> relations { project->relationManager()->referencingRelations( originalLayer ) };
144 for (
const QgsRelation &relation : std::as_const( relations ) )
151 bool alreadyAdded {
false };
152 for (
const auto &layerToExport : std::as_const( mLayers ) )
154 const QString originalId { mClonedLayerIds.value( layerToExport->id() ) };
155 if ( originalId == referencedLayer->id() )
157 relatedLayer = qobject_cast<QgsVectorLayer *>( layerToExport.get() );
165 feedback->pushInfo( QObject::tr(
"Adding referenced layer '%1'" ).arg( referencedLayer->name() ) );
166 relatedLayer = referencedLayer->clone();
167 mLayers.emplace_back( relatedLayer );
168 mClonedLayerIds.insert( relatedLayer->id(), referencedLayer->id() );
173 if ( onlySaveSelected )
175 if ( ! layers.contains( qobject_cast<QgsMapLayer *>( referencedLayer ) ) || referencedLayer->selectedFeatureCount() > 0 )
177 Q_ASSERT( relatedLayer );
181 while ( it.nextFeature( selectedFeature ) )
183 QgsFeature referencedFeature { relation.getReferencedFeature( selectedFeature ) };
184 if ( referencedFeature.isValid() )
186 selected.insert( referencedFeature.id() );
194 if ( recursionGuard > maxRecursion )
196 feedback->pushWarning( QObject::tr(
"Max recursion (%1) adding referenced layer '%2', layer was not added" ).arg( QLocale().toString( recursionGuard ), referencedLayer->name() ) );
201 findReferenced( relatedLayer, onlySaveSelected, findReferenced );
208 const auto findReferencing = [ =, &project, &feedback, &recursionGuard, &layers ](
const QgsVectorLayer * vLayer,
bool onlySaveSelected,
auto &&findReferencing ) ->
void
210 const QgsVectorLayer *originalLayer { qobject_cast<QgsVectorLayer *>( project->mapLayer( mClonedLayerIds.value( vLayer->id(), vLayer->id() ) ) ) };
211 Q_ASSERT( originalLayer );
212 const QList<QgsRelation> relations { project->relationManager()->referencedRelations( originalLayer ) };
213 for (
const QgsRelation &relation : std::as_const( relations ) )
218 const bool layerWasExplicitlyAdded { layers.contains( qobject_cast<QgsMapLayer *>( referencingLayer ) ) };
221 bool alreadyAdded {
false };
222 for (
const auto &layerToExport : std::as_const( mLayers ) )
224 const QString originalId { mClonedLayerIds.value( layerToExport->id() ) };
225 if ( originalId == referencingLayer->id() )
227 relatedLayer = qobject_cast<QgsVectorLayer *>( layerToExport.get() );
237 if ( onlySaveSelected && ( ! layerWasExplicitlyAdded || referencingLayer->selectedFeatureCount() > 0 ) )
239 if ( ! layers.contains( qobject_cast<QgsMapLayer *>( referencingLayer ) ) || referencingLayer->selectedFeatureCount() > 0 )
243 while ( it.nextFeature( selectedFeature ) )
245 QgsFeatureIterator referencingFeaturesIterator { relation.getRelatedFeatures( selectedFeature ) };
247 while ( referencingFeaturesIterator.nextFeature( referencingFeature ) )
249 if ( referencingFeature.
isValid() )
251 selected.insert( referencingFeature.
id() );
258 if ( ! alreadyAdded && ( ! onlySaveSelected || ! selected.isEmpty() ) )
260 feedback->pushInfo( QObject::tr(
"Adding referencing layer '%1'" ).arg( referencingLayer->name() ) );
261 relatedLayer = referencingLayer->clone();
262 mLayers.emplace_back( relatedLayer );
263 mClonedLayerIds.insert( relatedLayer->id(), referencingLayer->id() );
266 if ( relatedLayer && ! selected.isEmpty() )
272 if ( recursionGuard > maxRecursion )
274 feedback->pushWarning( QObject::tr(
"Max recursion (%1) adding referencing layer '%2', layer was not added" ).arg( QLocale().toString( recursionGuard ), referencingLayer->name() ) );
276 else if ( relatedLayer )
279 findReferencing( relatedLayer, onlySaveSelected, findReferencing ) ;
285 for (
const QgsMapLayer *layer : std::as_const( layers ) )
287 const QgsVectorLayer *vLayer { qobject_cast<const QgsVectorLayer *>( layer ) };
292 findReferenced( vLayer, onlySaveSelected, findReferenced );
294 findReferencing( vLayer, onlySaveSelected, findReferencing );
303 if ( !QFile::exists( packagePath ) )
307 throw QgsProcessingException( QObject::tr(
"Creation of database %1 failed (OGR error: %2)" ).arg( packagePath, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
313 throw QgsProcessingException( QObject::tr(
"Opening database %1 failed (OGR error: %2)" ).arg( packagePath, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
317 bool errored =
false;
321 QStringList outputLayers;
324 for (
const auto &layer : mLayers )
326 if ( feedback->isCanceled() )
329 multiStepFeedback.setCurrentStep( i );
335 feedback->pushDebugInfo( QObject::tr(
"Error retrieving map layer." ) );
340 feedback->pushInfo( QObject::tr(
"Packaging layer %1/%2: %3" ).arg( i ).arg( mLayers.size() ).arg( layer ? layer->name() : QString() ) );
342 switch ( layer->type() )
344 case Qgis::LayerType::Vector:
346 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer.get() );
347 if ( !packageVectorLayer( vectorLayer, packagePath, context, &multiStepFeedback, saveStyles, saveMetadata, selectedFeaturesOnly ) )
350 outputLayers.append( QStringLiteral(
"%1|layername=%2" ).arg( packagePath, layer->name() ) );
354 case Qgis::LayerType::Raster:
357 feedback->pushDebugInfo( QObject::tr(
"Packaging raster layers is not supported." ) );
362 case Qgis::LayerType::Plugin:
364 feedback->pushDebugInfo( QObject::tr(
"Packaging plugin layers is not supported." ) );
368 case Qgis::LayerType::Mesh:
370 feedback->pushDebugInfo( QObject::tr(
"Packaging mesh layers is not supported." ) );
374 case Qgis::LayerType::PointCloud:
376 feedback->pushDebugInfo( QObject::tr(
"Packaging point cloud layers is not supported." ) );
380 case Qgis::LayerType::VectorTile:
382 feedback->pushDebugInfo( QObject::tr(
"Packaging vector tile layers is not supported." ) );
386 case Qgis::LayerType::Annotation:
388 feedback->pushDebugInfo( QObject::tr(
"Packaging annotation layers is not supported." ) );
392 case Qgis::LayerType::Group:
394 feedback->pushDebugInfo( QObject::tr(
"Packaging group layers is not supported." ) );
404 outputs.insert( QStringLiteral(
"OUTPUT" ), packagePath );
405 outputs.insert( QStringLiteral(
"OUTPUT_LAYERS" ), outputLayers );
413 options.
driverName = QStringLiteral(
"GPKG" );
429 const int fidIndex = fields.
lookupField( QStringLiteral(
"fid" ) );
434 const QVariant::Type fidType { layer->
fields().
field( fidIndex ).
type() };
435 if ( ! layer->
fieldConstraints( fidIndex ).testFlag( QgsFieldConstraints::Constraint::ConstraintUnique )
436 && ! layer->
fieldConstraints( fidIndex ).testFlag( QgsFieldConstraints::Constraint::ConstraintNotNull )
437 && fidType != QVariant::Int
438 && fidType != QVariant::UInt
439 && fidType != QVariant::LongLong
440 && fidType != QVariant::ULongLong )
457 feedback->
reportError( QObject::tr(
"Packaging layer failed: %1" ).arg( error ) );
464 std::unique_ptr< QgsVectorLayer > res = std::make_unique< QgsVectorLayer >( QStringLiteral(
"%1|layername=%2" ).arg( newFilename, newLayer ) );
468 QDomDocument doc( QStringLiteral(
"qgis" ) );
471 if ( !errorMsg.isEmpty() )
473 feedback->
reportError( QObject::tr(
"Could not retrieve existing layer style: %1 " ).arg( errorMsg ) );
477 if ( !res->importNamedStyle( doc, errorMsg ) )
479 feedback->
reportError( QObject::tr(
"Could not set existing layer style: %1 " ).arg( errorMsg ) );
485 const QVariant prevOverwriteStyle = settings.
value( QStringLiteral(
"qgis/overwriteStyle" ) );
486 settings.
setValue( QStringLiteral(
"qgis/overwriteStyle" ),
true );
487 res->saveStyleToDatabase( newLayer, QString(),
true, QString(), errorMsg );
488 settings.
setValue( QStringLiteral(
"qgis/overwriteStyle" ), prevOverwriteStyle );
489 if ( !errorMsg.isEmpty() )
491 feedback->
reportError( QObject::tr(
"Could not save layer style: %1 " ).arg( errorMsg ) );
498 feedback->
reportError( QObject::tr(
"Could not save layer style -- error loading: %1 %2" ).arg( newFilename, newLayer ) );
@ AddToSelection
Add selection to current selection.
Wrapper for iterator of features from vector data provider or vector layer.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
bool isValid() const
Returns the validity of this feature.
Container of fields for a vector layer.
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Base class for all map layer types.
virtual void exportNamedStyle(QDomDocument &doc, QString &errorMsg, const QgsReadWriteContext &context=QgsReadWriteContext(), QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories) const
Export the properties of this layer as named style in a QDomDocument.
QgsLayerMetadata metadata
virtual QgsMapLayer * clone() const =0
Returns a new instance equivalent to this one except for the id which is still unique.
Contains information about the context in which a processing algorithm is executed.
QString defaultEncoding() const
Returns the default encoding to use for newly created files.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
QgsProject * project() const
Returns the project in which the algorithm is being executed.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
Processing feedback object for multi-step operations.
A multi-layer output for processing algorithms which create map layers, when the number and nature of...
A boolean parameter for processing algorithms.
void setMetadata(const QVariantMap &metadata)
Sets the parameter's freeform metadata.
A generic file based destination parameter, for specifying the destination path for a file (non-map l...
A parameter for processing algorithms which accepts multiple map layers.
@ TypeVector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
The class is used as a container of context for various read/write operations on other objects.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Options to pass to writeAsVectorFormat()
QString fileEncoding
Encoding to use.
QString driverName
OGR driver to use.
QgsLayerMetadata layerMetadata
Layer metadata to save for the exported vector file.
QString layerName
Layer name. If let empty, it will be derived from the filename.
bool saveMetadata
Set to true to save layer metadata for the exported vector file.
QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile
Action on existing file.
QgsAttributeList attributes
Attributes to export (empty means all unless skipAttributeCreation is set)
bool onlySelectedFeatures
Write only selected features of layer.
bool skipAttributeCreation
Only write geometries.
QgsFeedback * feedback
Optional feedback object allowing cancellation of layer save.
static QgsVectorFileWriter::WriterError writeAsVectorFormatV3(QgsVectorLayer *layer, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *errorMessage=nullptr, QString *newFilename=nullptr, QString *newLayer=nullptr)
Writes a layer out to a vector file.
@ CreateOrOverwriteLayer
Create or overwrite layer.
Represents a vector layer which manages a vector based data sets.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsFieldConstraints::Constraints fieldConstraints(int fieldIndex) const
Returns any constraints which are present for a specified field index.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
QSet< QgsFeatureId > QgsFeatureIds