We’ve been using GeoPackages for a while now. For example, in Loading a vector layer we covered how to load a specific layer from a GeoPackage. However, it is easy to make a mistake when specifying the layer name. For example:

iface.addVectorLayer('E:/Geodata/NaturalEarth/natural_earth_vector.gpkg|layername=foo', '', 'ogr')

Causes an error because there is no layer called foo in the GeoPackage: “Layer is not valid: The layer E:/Geodata/NaturalEarth/natural_earth_vector.gpkg|layername=foo is not a valid layer and can not be added to the map.”

However, this error is not very clear because it does not tell us the reason why the layer is invalid. It would be much more convenient to have a way to add a GeoPackage layer that first checks if that layer actually exists. So how can we find out which layers are available in a GeoPackage? – OGR to the rescue! Using OGR, we can open the GeoPackage and get a a list of its layers:

from osgeo import ogr
my_gpkg = 'E:/Geodata/NaturalEarth/natural_earth_vector.gpkg'
gpkg_layers = [l.GetName() for l in ogr.Open(my_gpkg )]

So this is how we can get a list of all layers in a GeoPackage.

Next, we can use this list to check if a certain layer is available, for example:

print('foo' in gpkg_layers)

prints False on the console.

In contrast:

print('ne_110m_land' in gpkg_layers)

prints True on the console.

Therefore, we can use this statement to decide whether to attempt to load the layer or to display a more precise error message:

my_layer = 'foo'
if my_layer in layers:
    iface.addVectorLayer(gpkg + "|layername=" + my_layer, my_layer, 'ogr')
    print('Error: there is no layer named "{}" in {}!'.format(my_layer, gpkg))

Of course, it would be nice to be able to reuse this code for different layer names. We can achieve this goal by writing a new function. Let’s call our new function add_gpkg_layer:

def add_gpkg_layer(gpkg, layer):
    layers = [l.GetName() for l in ogr.Open(gpkg)]
    if layer in layers:
        iface.addVectorLayer(gpkg + "|layername=" + layer, layer, 'ogr')
        print('Error: there is no layer named "{}" in {}!'.format(layer, gpkg))

The first line of our function definition starts with def followed by the function name and the function parameters in parenthesis and a semicolon at the end. The function code that follows after the semicolon has to be indented.

Now we can add new layers easily:

add_gpkg_layer(my_gpkg, 'foo') 
add_gpkg_layer(my_gpkg, 'ne_110m_land')

Of course, we can also create a function that loads multiple layers:

def add_layers_from_gpkg(gpkg, layers):
    for layer in layers:
        add_gpkg_layer(gpkg, layer)

Note how this function reuses our previously defined add_gpkg_layer function.

With this function in place, we can now conveniently load multiple layers in one go:

add_layers_from_gpkg(my_gpkg, ['ne_110m_land', 'ne_110m_lakes'])

Finally, if we want to load all layers, we can write:

def add_all_layers_from_gpkg(gpkg):
    layers = [l.GetName() for l in ogr.Open(gpkg)]
    add_layers_from_gpkg(gpkg, layers)

Be careful when using this function with big GeoPackages because it can take a while to load all layers!

These are the basics of writing new functions in Python. Functions make it possible to reuse your code without having to copy-paste it again and again. You also saw how to use ogr.Open to create a list of all layers that are available in a GeoPackage.


PyQGIS 101 is a work in progress. I’d appreciate any feedback, particularly from beginners!

1 comment
  1. badger2100 said:

    When I try running the above code (if my_layer in layers:) I receive an error message stating the “layers” is not defined. Thoughts?
    Thanks much for feedback

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: