Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

saveStyleToDatabase() bug when saving more than one layer to a GPKG #42988

Closed
djes opened this issue Apr 27, 2021 · 6 comments
Closed

saveStyleToDatabase() bug when saving more than one layer to a GPKG #42988

djes opened this issue Apr 27, 2021 · 6 comments
Labels
Bug Either a bug report, or a bug fix. Let's hope for the latter! Feedback Waiting on the submitter for answers PyQGIS Related to the PyQGIS API

Comments

@djes
Copy link

djes commented Apr 27, 2021

Describe the bug
For a plugin, I need a way to save my layers in a geopackage with their own style, exactly like the "Package" Tool.
The problem arises when two or more layers are saved. In the destination gpkg, the layer_styles table contains one row by layer, the styleName field is OK but the f_table_name field contains another layer name. I'm almost sure that the bug is in the saveStyleToDatabase() function. I've tried everything, almost reproducing the Package tool function

How to Reproduce
Create two layers with their own style named test and test2 (or download this geopackage http://djes.free.fr/qgis/test.gpkg). Adjust the script to the layers name and destination geopackage. Launch the script for the first layer, destination file is OK. Adjust for the second layer : the style is not correctly saved.

QGIS and OS versions
3.16.5 and 3.18.1 / Windows 10

Additional context
Simplified code (or download it on http://djes.free.fr/qgis/saveLayerWithStyle.py)

layer_name = 'test' #change me
gpkg_name = 'c:\\temp\\mytest.gpkg' #output, change me if needed

#- First, saves the layer
src_layer = QgsProject.instance().mapLayersByName(layer_name)[0]
options = QgsVectorFileWriter.SaveVectorOptions()
options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer
options.layerName = layer_name
#options.SymbologyExport = QgsVectorFileWriter.FeatureSymbology
#options.driverName = "GPKG"
error = QgsVectorFileWriter.writeAsVectorFormatV2(src_layer, gpkg_name, QgsProject.instance().transformContext(), options)

if error[0] == QgsVectorFileWriter.ErrCreateDataSource:
    print("Create mode")
    options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteFile #Create mode
    error = QgsVectorFileWriter.writeAsVectorFormatV2(src_layer, gpkg_name, QgsProject.instance().transformContext(), options)
del options
if error[0] == QgsVectorFileWriter.NoError:
    print('Layer "' + layer_name + '" saved in "' + gpkg_name + '"')        
else:
    print(error)
    raise Exception('Failed to save layer')

#- Second, saves the style
#- Load just saved layer
dst_layer = QgsVectorLayer(gpkg_name, layer_name)
if not dst_layer.isValid():
    raise Exception('Failed to load layer')
#print(dst_layer)

myDocument = QDomDocument('qgis')
src_layer.exportNamedStyle(myDocument)
#print(myDocument.toString())
success, message = dst_layer.importNamedStyle(myDocument)
dst_layer.saveStyleToDatabase(layer_name, '', True, '')
QgsProject.instance().removeMapLayer(dst_layer)
del src_layer
del dst_layer
del myDocument
@djes djes added the Bug Either a bug report, or a bug fix. Let's hope for the latter! label Apr 27, 2021
@gioman gioman added the PyQGIS Related to the PyQGIS API label Apr 27, 2021
@elpaso
Copy link
Contributor

elpaso commented Jun 2, 2021

I wrote a test and I cannot reproduce:

    def testSaveStyle(self):

        tmp_dir = QTemporaryDir()
        tmpfile = os.path.join(tmp_dir.path(), 'testSaveStyle.gpkg')
        ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile)
        lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint)
        f = ogr.Feature(lyr.GetLayerDefn())
        f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)'))
        lyr.CreateFeature(f)
        f = ogr.Feature(lyr.GetLayerDefn())
        f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(1 1)'))
        lyr.CreateFeature(f)
        del (lyr)

        lyr = ds.CreateLayer('test2', geom_type=ogr.wkbPoint)
        f = ogr.Feature(lyr.GetLayerDefn())
        f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)'))
        lyr.CreateFeature(f)
        f = ogr.Feature(lyr.GetLayerDefn())
        f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(1 1)'))
        lyr.CreateFeature(f)
        del(lyr)

        vl1 = QgsVectorLayer(f'{tmpfile}|layername=test'.format(tmpfile), 'test', 'ogr')
        self.assertTrue(vl1.isValid())
        myDocument = QDomDocument('qgis')
        vl1.exportNamedStyle(myDocument)
        success, message = vl1.importNamedStyle(myDocument)
        self.assertTrue(success, message)
        vl1.saveStyleToDatabase(vl1.name(), '', True, '')

        vl2 = QgsVectorLayer(f'{tmpfile}|layername=test2'.format(tmpfile), 'test2', 'ogr')
        self.assertTrue(vl2.isValid())
        myDocument = QDomDocument('qgis')
        vl2.exportNamedStyle(myDocument)
        success, message = vl2.importNamedStyle(myDocument)
        self.assertTrue(success, message)
        vl2.saveStyleToDatabase(vl2.name(), '', True, '')

        md = QgsProviderRegistry.instance().providerMetadata('ogr')
        conn = md.createConnection(tmpfile, {})
        self.assertEqual(conn.executeSql('SELECT f_table_name FROM layer_styles'), [['test'], ['test2']])

@elpaso elpaso added the Feedback Waiting on the submitter for answers label Jun 2, 2021
@djes
Copy link
Author

djes commented Jun 2, 2021

@elpaso Thank you for digging around ! It works with your example. The syntax I was using for the QgsVectorLayer() function was giving this bad result (even if it seems to work for almost everything).

By modifying my code
dst_layer = QgsVectorLayer(gpkg_name, layer_name)
with
dst_layer = QgsVectorLayer(f'{gpkg_name}|layername={layer_name}', layer_name, 'ogr')

It works like it should. It seems a bit odd anyway to have to repeat the layer name twice but it's alright for me.

Thank you again !

@gioman
Copy link
Contributor

gioman commented Jun 2, 2021

It works like it should. It seems a bit odd anyway to have to repeat the layer name twice but it's alright for me.

@djes closing?

@djes
Copy link
Author

djes commented Jun 2, 2021

It works like it should. It seems a bit odd anyway to have to repeat the layer name twice but it's alright for me.

@djes closing?

In a perfect world, I think one of the QGIS function should be corrected to use the correct layer name. But we're not in a perfect world ;)
For me, you can close.

@gioman
Copy link
Contributor

gioman commented Jun 2, 2021

In a perfect world, I think one of the QGIS function should be corrected to use the correct layer name. But we're not in a perfect world ;)

@djes feature request?

@djes
Copy link
Author

djes commented Jun 2, 2021

Yes, or documentation clarification with a good example. Geopackage and styles are so useful that we need something solid.

@djes djes closed this as completed Jun 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Either a bug report, or a bug fix. Let's hope for the latter! Feedback Waiting on the submitter for answers PyQGIS Related to the PyQGIS API
Projects
None yet
Development

No branches or pull requests

3 participants