8

I have a function that uses a layer already styled using another function and saves it as a .gpkg and I want to save the style in that .gpkg:

def saving_gpkg(styled_layer):
    options = QgsVectorFileWriter.SaveVectorOptions()
    options.driverName = "GPKG"
    QgsVectorFileWriter.writeAsVectorFormatV2(styled_layer, "nice_filename", QgsCoordinateTransformContext(), options)
    styled_layer.saveStyleToDatabase("polygon", "style automatically created", True, "")

The .gpkg is created, but if I open the created file, it doesn't have the defined style, just a random single symbol. I'm missing something?

4 Answers 4

11

A working example based on your code. Be aware that my code scope is limited as I use a layer name without space for everything whereas I should sanitized it in particular for layername parameter reused in the GPKG. Most of the code logic is based on qgsalgorithmpackage.cpp C++ code used in Processing algorithm "Package layers" in the answer from @leo-cardona

# Import required if not present
from qgis.PyQt.QtXml import QDomDocument
from qgis.core import (QgsReadWriteContext, QgsProject,
                       QgsVectorFileWriter, QgsVectorLayer)

def saving_gpkg(styled_layer, out_path):
    context = QgsProject.instance().transformContext()
    name = styled_layer.name()
    options = QgsVectorFileWriter.SaveVectorOptions()
    options.layerName = name
    options.fileEncoding = styled_layer.dataProvider().encoding()
    options.driverName = "GPKG"
    QgsVectorFileWriter.writeAsVectorFormatV2(styled_layer, out_path, context, options)
    doc = QDomDocument();
    readWriteContext = context = QgsReadWriteContext()
    styled_layer.exportNamedStyle(doc);
    gpkg_layer = QgsVectorLayer(f"{out_path}|layername={name}", name, "ogr")
    gpkg_layer.importNamedStyle(doc)
    gpkg_layer.saveStyleToDatabase(name, "", True, "")
3
  • 1
    I can confirm this works Using QGIS 3.16.6 :-), the only thing I needed to do was to import those extra functions: from qgis.PyQt.QtXml import QDomDocument, from qgis.core import QgsReadWriteContext and just a few renames like the input layer object and the output path. FYI my layers/datasets are in Postgres and my styles are also saved in Postgres.
    – GIS_py
    Commented Aug 2, 2021 at 13:38
  • Edited to add all required import
    – ThomasG77
    Commented Aug 2, 2021 at 14:18
  • 1
    The function above only seems to save the active style. Any idea how you could adapt this for multiple styles ? I sometimes have multiple styles applied to the same layer (saved in a postgres DB).
    – lhowarth
    Commented Apr 15, 2022 at 13:43
10

If you want to save one or several layers in a geopackage, the easiest way is to use the package algorithm which has the SAVE_STYLES parameter which must be True if you want to save the styles that your layers have in the geopackage that will be generated.

filename = '/home/ai/Desktop/prueba.gpkg'
layers = [styled_layer]
processing.run("native:package", {'LAYERS': layers, 'OUTPUT': filename, 'OVERWRITE': False, 'SAVE_STYLES': True})
2

I developed a plugin that saves all styles with each layer as a .gpkg. Published on the official QGIS plugin repo: https://plugins.qgis.org/plugins/SaveAllScript/

3
  • 1
    This is not answering the question. The Q is about programming. Commented Jun 30, 2023 at 8:08
  • 1
    I would like to clarify that this comment is relevant to the question because I created a QGIS plug-in that addresses some of the questions that the original poster had. I am sharing it here because maybe people in the future who also share the same questions can easily fix their problem with the plug-in. People are welcome to reference the code for the plug-in in order to help them with their own programming goals. Commented Jun 30, 2023 at 23:17
  • It's well documented code nonetheless - nice work @CaptainDang, Thanks
    – LeasMaps
    Commented Jan 29 at 23:33
0

This is adapted from @CaptainDang's plugin and allows for you to save each layer to the single geopackage (qgis v3.22):

Usage example:

  #overwrite any existing geopackages as this is the first layer I'm saving
  self.saveAs_gpkg(tmpMemory_Layer, theGeopack, True) 
        
  # Sets the layer's data source to the newly created gpkg, replaces temp layer with the permanent one
  tmpMemory_Layer.setDataSource(theGeopack + f'|layername={tmpMemory_Layer.name()}', tmpMemory_Layer.name(), 'ogr')
 

requirements:

from qgis import processing

code:

def saveAs_gpkg(self, styled_layer, theFileandPath, OptionsOverwriteFile):
    #output_file = os.path.join(new_folder_path, layer_name + ".gpkg")
    #if not os.path.exists(theFileandPath):
    parameters = {
        'LAYERS': [styled_layer],
        'OUTPUT': theFileandPath + ".gpkg",
        # Specify the output file with .gpkg extension
        'OVERWRITE': OptionsOverwriteFile,
        'SAVE_STYLES': True,
        'SAVE_METADATA': True,
        'SELECTED_FEATURES_ONLY': False,
        'EXPORT_RELATED_LAYERS': False}

    feedback = QgsProcessingFeedback()

    # Execute the package algorithm
    try:
        result = processing.run("native:package", parameters, feedback=feedback)
        if result['OUTPUT']:
            pass
        else:
            iface.messageBar().pushMessage("Failed: ",
                                           "layer '{}' was not saved.".format(
                                               styled_layer.name()), level=2)
            failedSaves.append(layer_name)
    except QgsProcessingException as e:
        iface.messageBar().pushMessage("Error: ",
                                       "An error occurred while packaging layer '{}': '{}'".format(
                                           styled_layer.name(), str(e)), level=2)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.