28using namespace Qt::StringLiterals;
32QString QgsPackageAlgorithm::name()
const
37QString QgsPackageAlgorithm::displayName()
const
39 return QObject::tr(
"Package layers" );
42QStringList QgsPackageAlgorithm::tags()
const
44 return QObject::tr(
"geopackage,collect,merge,combine,styles" ).split(
',' );
47QString QgsPackageAlgorithm::group()
const
49 return QObject::tr(
"Database" );
52QString QgsPackageAlgorithm::groupId()
const
57void QgsPackageAlgorithm::initAlgorithm(
const QVariantMap & )
61 outputParameter->
setMetadata( QVariantMap( { { u
"widget_wrapper"_s, QVariantMap( { { u
"dontconfirmoverwrite"_s,
true } } ) } } ) );
62 addParameter( outputParameter );
67 addParameter(
new QgsProcessingParameterBoolean( u
"EXPORT_RELATED_LAYERS"_s, QObject::tr(
"Export related layers following relations defined in the project" ),
false ) );
68 auto extentParam = std::make_unique<QgsProcessingParameterExtent>( u
"EXTENT"_s, QObject::tr(
"Extent" ), QVariant(),
true );
69 extentParam->setHelp( QObject::tr(
"Limit exported features to those with geometries intersecting the provided extent" ) );
70 addParameter( extentParam.release() );
72 auto crsParam = std::make_unique< QgsProcessingParameterCrs >( u
"CRS"_s, QObject::tr(
"Destination CRS" ), QVariant(),
true );
74 crsParam->setHelp( QObject::tr(
"If set, all layers will be transformed to the destination CRS during packaging." ) );
75 addParameter( std::move( crsParam ) );
80QString QgsPackageAlgorithm::shortHelpString()
const
82 return QObject::tr(
"This algorithm collects a number of existing layers and packages them together into a single GeoPackage database." );
85QString QgsPackageAlgorithm::shortDescription()
const
87 return QObject::tr(
"Packages a number of existing layers together into a single GeoPackage database." );
90QgsPackageAlgorithm *QgsPackageAlgorithm::createInstance()
const
92 return new QgsPackageAlgorithm();
97 const QList<QgsMapLayer *> layers = parameterAsLayerList( parameters, u
"LAYERS"_s, context );
99 for (
const QgsMapLayer *layer : std::as_const( layers ) )
102 mClonedLayerIds.insert( clonedLayer->
id(), layer->id() );
103 mLayers.emplace_back( clonedLayer );
106 if ( mLayers.empty() )
108 feedback->
reportError( QObject::tr(
"No layers selected, geopackage will be empty" ),
false );
111 mDestinationCrs = parameterAsCrs( parameters, u
"CRS"_s, context );
118 const bool overwrite = parameterAsBoolean( parameters, u
"OVERWRITE"_s, context );
119 const bool saveStyles = parameterAsBoolean( parameters, u
"SAVE_STYLES"_s, context );
120 const bool saveMetadata = parameterAsBoolean( parameters, u
"SAVE_METADATA"_s, context );
121 const bool selectedFeaturesOnly = parameterAsBoolean( parameters, u
"SELECTED_FEATURES_ONLY"_s, context );
122 const bool exportRelatedLayers = parameterAsBoolean( parameters, u
"EXPORT_RELATED_LAYERS"_s, context );
123 const QString packagePath = parameterAsString( parameters, u
"OUTPUT"_s, context );
124 const QList<QgsMapLayer *> layers = parameterAsLayerList( parameters, u
"LAYERS"_s, context );
126 if ( packagePath.isEmpty() )
130 if ( overwrite && QFile::exists( packagePath ) )
132 feedback->
pushInfo( QObject::tr(
"Removing existing file '%1'" ).arg( packagePath ) );
133 if ( !QFile( packagePath ).remove() )
139 OGRSFDriverH hGpkgDriver = OGRGetDriverByName(
"GPKG" );
147 if ( exportRelatedLayers )
153 const int maxRecursion { 10 };
154 int recursionGuard { 0 };
157 const auto findReferenced = [
this, &project, &feedback, &recursionGuard, &layers](
const QgsVectorLayer *vLayer,
bool onlySaveSelected,
auto &&findReferenced ) ->
void {
158 const QgsVectorLayer *originalLayer { qobject_cast<QgsVectorLayer *>( project->
mapLayer( mClonedLayerIds.value( vLayer->id(), vLayer->id() ) ) ) };
159 Q_ASSERT( originalLayer );
161 for (
const QgsRelation &relation : std::as_const( relations ) )
167 bool alreadyAdded {
false };
168 for (
const auto &layerToExport : std::as_const( mLayers ) )
170 const QString originalId { mClonedLayerIds.value( layerToExport->id() ) };
171 if ( originalId == referencedLayer->
id() )
173 relatedLayer = qobject_cast<QgsVectorLayer *>( layerToExport.get() );
181 feedback->pushInfo( QObject::tr(
"Adding referenced layer '%1'" ).arg( referencedLayer->
name() ) );
182 relatedLayer = referencedLayer->
clone();
183 mLayers.emplace_back( relatedLayer );
184 mClonedLayerIds.insert( relatedLayer->
id(), referencedLayer->
id() );
189 if ( onlySaveSelected )
191 if ( !layers.contains( qobject_cast<QgsMapLayer *>( referencedLayer ) ) || referencedLayer->
selectedFeatureCount() > 0 )
193 Q_ASSERT( relatedLayer );
199 QgsFeature referencedFeature { relation.getReferencedFeature( selectedFeature ) };
200 if ( referencedFeature.
isValid() )
202 selected.insert( referencedFeature.
id() );
210 if ( recursionGuard > maxRecursion )
212 feedback->pushWarning( QObject::tr(
"Max recursion (%1) adding referenced layer '%2', layer was not added" ).arg( QLocale().toString( recursionGuard ), referencedLayer->
name() ) );
217 findReferenced( relatedLayer, onlySaveSelected, findReferenced );
223 const auto findReferencing = [
this, &project, &feedback, &recursionGuard, &layers](
const QgsVectorLayer *vLayer,
bool onlySaveSelected,
auto &&findReferencing ) ->
void {
224 const QgsVectorLayer *originalLayer { qobject_cast<QgsVectorLayer *>( project->
mapLayer( mClonedLayerIds.value( vLayer->id(), vLayer->id() ) ) ) };
225 Q_ASSERT( originalLayer );
227 for (
const QgsRelation &relation : std::as_const( relations ) )
231 const bool layerWasExplicitlyAdded { layers.contains( qobject_cast<QgsMapLayer *>( referencingLayer ) ) };
234 bool alreadyAdded {
false };
235 for (
const auto &layerToExport : std::as_const( mLayers ) )
237 const QString originalId { mClonedLayerIds.value( layerToExport->id() ) };
238 if ( originalId == referencingLayer->
id() )
240 relatedLayer = qobject_cast<QgsVectorLayer *>( layerToExport.get() );
250 if ( onlySaveSelected && ( !layerWasExplicitlyAdded || referencingLayer->
selectedFeatureCount() > 0 ) )
252 if ( !layers.contains( qobject_cast<QgsMapLayer *>( referencingLayer ) ) || referencingLayer->
selectedFeatureCount() > 0 )
258 QgsFeatureIterator referencingFeaturesIterator { relation.getRelatedFeatures( selectedFeature ) };
260 while ( referencingFeaturesIterator.
nextFeature( referencingFeature ) )
262 if ( referencingFeature.
isValid() )
264 selected.insert( referencingFeature.
id() );
271 if ( !alreadyAdded && ( !onlySaveSelected || !selected.isEmpty() ) )
273 feedback->pushInfo( QObject::tr(
"Adding referencing layer '%1'" ).arg( referencingLayer->
name() ) );
274 relatedLayer = referencingLayer->
clone();
275 mLayers.emplace_back( relatedLayer );
276 mClonedLayerIds.insert( relatedLayer->
id(), referencingLayer->
id() );
279 if ( relatedLayer && !selected.isEmpty() )
285 if ( recursionGuard > maxRecursion )
287 feedback->pushWarning( QObject::tr(
"Max recursion (%1) adding referencing layer '%2', layer was not added" ).arg( QLocale().toString( recursionGuard ), referencingLayer->
name() ) );
289 else if ( relatedLayer )
292 findReferencing( relatedLayer, onlySaveSelected, findReferencing );
297 for (
const QgsMapLayer *layer : std::as_const( layers ) )
299 const QgsVectorLayer *vLayer { qobject_cast<const QgsVectorLayer *>( layer ) };
304 findReferenced( vLayer, onlySaveSelected, findReferenced );
306 findReferencing( vLayer, onlySaveSelected, findReferencing );
314 if ( !QFile::exists( packagePath ) )
318 throw QgsProcessingException( QObject::tr(
"Creation of database %1 failed (OGR error: %2)" ).arg( packagePath, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
324 throw QgsProcessingException( QObject::tr(
"Opening database %1 failed (OGR error: %2)" ).arg( packagePath, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
327 const bool validExtent = parameters.value( u
"EXTENT"_s ).isValid();
329 bool errored =
false;
333 QStringList outputLayers;
336 for (
const auto &layer : mLayers )
338 if ( feedback->isCanceled() )
341 multiStepFeedback.setCurrentStep( i );
347 feedback->pushDebugInfo( QObject::tr(
"Error retrieving map layer." ) );
352 feedback->pushInfo( QObject::tr(
"Packaging layer %1/%2: %3" ).arg( i ).arg( mLayers.size() ).arg( layer ? layer->name() : QString() ) );
356 switch ( layer->type() )
360 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer.get() );
366 feedback->pushWarning( QObject::tr(
"No spatial index exists for layer %1, performance will be severely degraded" ).arg( vectorLayer->
name() ) );
369 extent = parameterAsExtent( parameters, u
"EXTENT"_s, context, mDestinationCrs.isValid() ? mDestinationCrs : layer->crs() );
372 if ( !packageVectorLayer( vectorLayer, packagePath, context, &multiStepFeedback, saveStyles, saveMetadata, selectedFeaturesOnly, extent ) )
375 outputLayers.append( u
"%1|layername=%2"_s.arg( packagePath, layer->name() ) );
382 feedback->pushDebugInfo( QObject::tr(
"Packaging raster layers is not supported." ) );
389 feedback->pushDebugInfo( QObject::tr(
"Packaging plugin layers is not supported." ) );
395 feedback->pushDebugInfo( QObject::tr(
"Packaging mesh layers is not supported." ) );
401 feedback->pushDebugInfo( QObject::tr(
"Packaging point cloud layers is not supported." ) );
407 feedback->pushDebugInfo( QObject::tr(
"Packaging vector tile layers is not supported." ) );
413 feedback->pushDebugInfo( QObject::tr(
"Packaging annotation layers is not supported." ) );
419 feedback->pushDebugInfo( QObject::tr(
"Packaging group layers is not supported." ) );
425 feedback->pushDebugInfo( QObject::tr(
"Packaging tiled scene layers is not supported." ) );
435 outputs.insert( u
"OUTPUT"_s, packagePath );
436 outputs.insert( u
"OUTPUT_LAYERS"_s, outputLayers );
455 if ( mDestinationCrs.isValid() )
464 const int fidIndex = fields.
lookupField( u
"fid"_s );
469 const QMetaType::Type fidType { layer->
fields().
field( fidIndex ).
type() };
472 && fidType != QMetaType::Type::Int
473 && fidType != QMetaType::Type::UInt
474 && fidType != QMetaType::Type::LongLong
475 && fidType != QMetaType::Type::ULongLong )
497 feedback->
reportError( QObject::tr(
"Packaging layer failed: %1" ).arg( error ) );
504 auto res = std::make_unique<QgsVectorLayer>( u
"%1|layername=%2"_s.arg( newFilename, newLayer ) );
505 if ( res->isValid() )
508 QDomDocument doc( u
"qgis"_s );
511 if ( !errorMsg.isEmpty() )
513 feedback->
reportError( QObject::tr(
"Could not retrieve existing layer style: %1 " ).arg( errorMsg ) );
517 if ( !res->importNamedStyle( doc, errorMsg ) )
519 feedback->
reportError( QObject::tr(
"Could not set existing layer style: %1 " ).arg( errorMsg ) );
525 const QVariant prevOverwriteStyle = settings.
value( u
"qgis/overwriteStyle"_s );
526 settings.
setValue( u
"qgis/overwriteStyle"_s,
true );
528 settings.
setValue( u
"qgis/overwriteStyle"_s, prevOverwriteStyle );
532 feedback->
reportError( QObject::tr(
"Could not save layer style: %1 " ).arg( errorMsg ) );
539 feedback->
reportError( QObject::tr(
"Could not save layer style -- error loading: %1 %2" ).arg( newFilename, newLayer ) );
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
@ NotPresent
No spatial index exists for the source.
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
@ AddToSelection
Add selection to current selection.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
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.
@ ConstraintNotNull
Field may not be null.
@ ConstraintUnique
Field must have a unique value.
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).
Q_INVOKABLE 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.
@ DatabaseWriteFailed
An error occurred when attempting to write to the database.
@ QmlGenerationFailed
Generation of the QML failed, and was not written to the database.
QgsCoordinateReferenceSystem crs
QgsLayerMetadata metadata
QFlags< SaveStyleResult > SaveStyleResults
Results of saving styles to database.
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.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
QgsRelationManager * relationManager
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
A container for the context for various read/write operations on objects.
A rectangle specified with double values.
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
QList< QgsRelation > referencingRelations(const QgsVectorLayer *layer=nullptr, int fieldIdx=-2) const
Gets all relations where the specified layer (and field) is the referencing part (i....
QMap< QString, QgsRelation > relations() const
Gets access to the relations managed by this class.
Represents a relationship between two vector layers.
Stores settings for use within QGIS.
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 QgsVectorFileWriter::writeAsVectorFormat().
QString fileEncoding
Encoding to use.
QString driverName
OGR driver to use.
QgsCoordinateTransform ct
Transform to reproject exported geometries with, or invalid transform for no transformation.
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.
QgsRectangle filterExtent
If not empty, only features intersecting the extent will be saved.
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 dataset.
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.
QgsVectorLayer * clone() const override
Returns a new instance equivalent to this one.
Q_INVOKABLE void selectByIds(const QgsFeatureIds &ids, Qgis::SelectBehavior behavior=Qgis::SelectBehavior::SetSelection)
Selects matching features using a list of feature IDs.
Qgis::SpatialIndexPresence hasSpatialIndex() const override
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
QSet< QgsFeatureId > QgsFeatureIds