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 QGS_MARK_ALGORITHM_SOURCE
120 const bool overwrite = parameterAsBoolean( parameters, u
"OVERWRITE"_s, context );
121 const bool saveStyles = parameterAsBoolean( parameters, u
"SAVE_STYLES"_s, context );
122 const bool saveMetadata = parameterAsBoolean( parameters, u
"SAVE_METADATA"_s, context );
123 const bool selectedFeaturesOnly = parameterAsBoolean( parameters, u
"SELECTED_FEATURES_ONLY"_s, context );
124 const bool exportRelatedLayers = parameterAsBoolean( parameters, u
"EXPORT_RELATED_LAYERS"_s, context );
125 const QString packagePath = parameterAsString( parameters, u
"OUTPUT"_s, context );
126 const QList<QgsMapLayer *> layers = parameterAsLayerList( parameters, u
"LAYERS"_s, context );
128 if ( packagePath.isEmpty() )
132 if ( overwrite && QFile::exists( packagePath ) )
134 feedback->
pushInfo( QObject::tr(
"Removing existing file '%1'" ).arg( packagePath ) );
135 if ( !QFile( packagePath ).remove() )
141 OGRSFDriverH hGpkgDriver = OGRGetDriverByName(
"GPKG" );
149 if ( exportRelatedLayers )
155 const int maxRecursion { 10 };
156 int recursionGuard { 0 };
159 const auto findReferenced = [
this, &project, &feedback, &recursionGuard, &layers](
const QgsVectorLayer *vLayer,
bool onlySaveSelected,
auto &&findReferenced ) ->
void {
160 const QgsVectorLayer *originalLayer { qobject_cast<QgsVectorLayer *>( project->
mapLayer( mClonedLayerIds.value( vLayer->id(), vLayer->id() ) ) ) };
161 Q_ASSERT( originalLayer );
163 for (
const QgsRelation &relation : std::as_const( relations ) )
169 bool alreadyAdded {
false };
170 for (
const auto &layerToExport : std::as_const( mLayers ) )
172 const QString originalId { mClonedLayerIds.value( layerToExport->id() ) };
173 if ( originalId == referencedLayer->
id() )
175 relatedLayer = qobject_cast<QgsVectorLayer *>( layerToExport.get() );
183 feedback->pushInfo( QObject::tr(
"Adding referenced layer '%1'" ).arg( referencedLayer->
name() ) );
184 relatedLayer = referencedLayer->
clone();
185 mLayers.emplace_back( relatedLayer );
186 mClonedLayerIds.insert( relatedLayer->
id(), referencedLayer->
id() );
191 if ( onlySaveSelected )
193 if ( !layers.contains( qobject_cast<QgsMapLayer *>( referencedLayer ) ) || referencedLayer->
selectedFeatureCount() > 0 )
195 Q_ASSERT( relatedLayer );
201 QgsFeature referencedFeature { relation.getReferencedFeature( selectedFeature ) };
202 if ( referencedFeature.
isValid() )
204 selected.insert( referencedFeature.
id() );
212 if ( recursionGuard > maxRecursion )
214 feedback->pushWarning( QObject::tr(
"Max recursion (%1) adding referenced layer '%2', layer was not added" ).arg( QLocale().toString( recursionGuard ), referencedLayer->
name() ) );
219 findReferenced( relatedLayer, onlySaveSelected, findReferenced );
225 const auto findReferencing = [
this, &project, &feedback, &recursionGuard, &layers](
const QgsVectorLayer *vLayer,
bool onlySaveSelected,
auto &&findReferencing ) ->
void {
226 const QgsVectorLayer *originalLayer { qobject_cast<QgsVectorLayer *>( project->
mapLayer( mClonedLayerIds.value( vLayer->id(), vLayer->id() ) ) ) };
227 Q_ASSERT( originalLayer );
229 for (
const QgsRelation &relation : std::as_const( relations ) )
233 const bool layerWasExplicitlyAdded { layers.contains( qobject_cast<QgsMapLayer *>( referencingLayer ) ) };
236 bool alreadyAdded {
false };
237 for (
const auto &layerToExport : std::as_const( mLayers ) )
239 const QString originalId { mClonedLayerIds.value( layerToExport->id() ) };
240 if ( originalId == referencingLayer->
id() )
242 relatedLayer = qobject_cast<QgsVectorLayer *>( layerToExport.get() );
252 if ( onlySaveSelected && ( !layerWasExplicitlyAdded || referencingLayer->
selectedFeatureCount() > 0 ) )
254 if ( !layers.contains( qobject_cast<QgsMapLayer *>( referencingLayer ) ) || referencingLayer->
selectedFeatureCount() > 0 )
260 QgsFeatureIterator referencingFeaturesIterator { relation.getRelatedFeatures( selectedFeature ) };
262 while ( referencingFeaturesIterator.
nextFeature( referencingFeature ) )
264 if ( referencingFeature.
isValid() )
266 selected.insert( referencingFeature.
id() );
273 if ( !alreadyAdded && ( !onlySaveSelected || !selected.isEmpty() ) )
275 feedback->pushInfo( QObject::tr(
"Adding referencing layer '%1'" ).arg( referencingLayer->
name() ) );
276 relatedLayer = referencingLayer->
clone();
277 mLayers.emplace_back( relatedLayer );
278 mClonedLayerIds.insert( relatedLayer->
id(), referencingLayer->
id() );
281 if ( relatedLayer && !selected.isEmpty() )
287 if ( recursionGuard > maxRecursion )
289 feedback->pushWarning( QObject::tr(
"Max recursion (%1) adding referencing layer '%2', layer was not added" ).arg( QLocale().toString( recursionGuard ), referencingLayer->
name() ) );
291 else if ( relatedLayer )
294 findReferencing( relatedLayer, onlySaveSelected, findReferencing );
299 for (
const QgsMapLayer *layer : std::as_const( layers ) )
301 const QgsVectorLayer *vLayer { qobject_cast<const QgsVectorLayer *>( layer ) };
306 findReferenced( vLayer, onlySaveSelected, findReferenced );
308 findReferencing( vLayer, onlySaveSelected, findReferencing );
316 if ( !QFile::exists( packagePath ) )
320 throw QgsProcessingException( QObject::tr(
"Creation of database %1 failed (OGR error: %2)" ).arg( packagePath, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
326 throw QgsProcessingException( QObject::tr(
"Opening database %1 failed (OGR error: %2)" ).arg( packagePath, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
329 const bool validExtent = parameters.value( u
"EXTENT"_s ).isValid();
331 bool errored =
false;
335 QStringList outputLayers;
338 for (
const auto &layer : mLayers )
340 if ( feedback->isCanceled() )
343 multiStepFeedback.setCurrentStep( i );
349 feedback->pushDebugInfo( QObject::tr(
"Error retrieving map layer." ) );
354 feedback->pushInfo( QObject::tr(
"Packaging layer %1/%2: %3" ).arg( i ).arg( mLayers.size() ).arg( layer ? layer->name() : QString() ) );
358 switch ( layer->type() )
362 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer.get() );
368 feedback->pushWarning( QObject::tr(
"No spatial index exists for layer %1, performance will be severely degraded" ).arg( vectorLayer->
name() ) );
371 extent = parameterAsExtent( parameters, u
"EXTENT"_s, context, mDestinationCrs.isValid() ? mDestinationCrs : layer->crs() );
374 if ( !packageVectorLayer( vectorLayer, packagePath, context, &multiStepFeedback, saveStyles, saveMetadata, selectedFeaturesOnly, extent ) )
377 outputLayers.append( u
"%1|layername=%2"_s.arg( packagePath, layer->name() ) );
384 feedback->pushDebugInfo( QObject::tr(
"Packaging raster layers is not supported." ) );
391 feedback->pushDebugInfo( QObject::tr(
"Packaging plugin layers is not supported." ) );
397 feedback->pushDebugInfo( QObject::tr(
"Packaging mesh layers is not supported." ) );
403 feedback->pushDebugInfo( QObject::tr(
"Packaging point cloud layers is not supported." ) );
409 feedback->pushDebugInfo( QObject::tr(
"Packaging vector tile layers is not supported." ) );
415 feedback->pushDebugInfo( QObject::tr(
"Packaging annotation layers is not supported." ) );
421 feedback->pushDebugInfo( QObject::tr(
"Packaging group layers is not supported." ) );
427 feedback->pushDebugInfo( QObject::tr(
"Packaging tiled scene layers is not supported." ) );
437 outputs.insert( u
"OUTPUT"_s, packagePath );
438 outputs.insert( u
"OUTPUT_LAYERS"_s, outputLayers );
442bool QgsPackageAlgorithm::packageVectorLayer(
459 if ( mDestinationCrs.isValid() )
468 const int fidIndex = fields.
lookupField( u
"fid"_s );
473 const QMetaType::Type fidType { layer->
fields().
field( fidIndex ).
type() };
476 && fidType != QMetaType::Type::Int
477 && fidType != QMetaType::Type::UInt
478 && fidType != QMetaType::Type::LongLong
479 && fidType != QMetaType::Type::ULongLong )
501 feedback->
reportError( QObject::tr(
"Packaging layer failed: %1" ).arg( error ) );
508 auto res = std::make_unique<QgsVectorLayer>( u
"%1|layername=%2"_s.arg( newFilename, newLayer ) );
509 if ( res->isValid() )
512 QDomDocument doc( u
"qgis"_s );
515 if ( !errorMsg.isEmpty() )
517 feedback->
reportError( QObject::tr(
"Could not retrieve existing layer style: %1 " ).arg( errorMsg ) );
521 if ( !res->importNamedStyle( doc, errorMsg ) )
523 feedback->
reportError( QObject::tr(
"Could not set existing layer style: %1 " ).arg( errorMsg ) );
529 const QVariant prevOverwriteStyle = settings.
value( u
"qgis/overwriteStyle"_s );
530 settings.
setValue( u
"qgis/overwriteStyle"_s,
true );
532 settings.
setValue( u
"qgis/overwriteStyle"_s, prevOverwriteStyle );
535 feedback->
reportError( QObject::tr(
"Could not save layer style: %1 " ).arg( errorMsg ) );
542 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, bool validateIds=false)
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