5

I want to create empty GeoPackage layers with a defined schema. This seems to just work with Fiona regardless of whether I have spatial or non-spatial layers:

def create_blank_gpkg_layer( gpkg: Path, layer_name: str, crs: dict, schema: dict ) -> dict: """ Thin wrapper around fiona.open to create an empty layer in a geopackage If the geopackage doesn't exist it is created. Any existing geopackage or layer that shares the same names are overwritten """ gpkg.parent.mkdir(exist_ok=True, parents=True) with fiona.open( gpkg, "w", driver="GPKG", crs=crs, layer=layer_name, schema=schema, overwrite=True, ) as new_layer: meta = new_layer.meta print(f"{layer_name} created in {gpkg}") return meta wgs_crs = from_epsg(4326) poly_schema = { "properties": OrderedDict([("name":"str"),("code":"int")]), "geometry": "Polygon" } not_spatial_schema = {"properties": OrderedDict([("name":"str"),("code":"int")])} create_blank_gpkg_layer(gpkg, layer_name=poly, crs=crs, schema=poly_schema) create_blank_gpkg_layer(gpkg, layer_name=not_spatial, crs=crs, schema=not_spatial_schema) 

This is fine, but I'd rather not have fiona as a dependency if the capability is already within QGIS - which is where the GeoPackages will end up, and I will have seperate code to configure symbology etc.

So I'd like to do the same using the PyQGIS QgsVectorFileWriter or whatever is most appropriate.

I've managed to make a workaround that creates a memory layer and then writes it, but I feel like there is probably a more 'idiomatic' way of solving this, as I then have to get the layer I've created in the GeoPackage (the one I actually want) and add it to the map.

# Layers that have a geometry foo_layer = QgsVectorLayer("Point?crs=epsg:4326&field=name:string", "foo layer", "memory") params={'INPUT': foo_layer, 'OPTIONS':'-update -nln foo','OUTPUT': '/tmp/temp_gpkg.gpkg'} processing.run("gdal:convertformat", params) # Layers with no geometry bar_layer = QgsVectorLayer('None', 'bar', 'memory') bar_layer.startEditing() bar_layer.addAttribute(QgsField('name',QVariant.String)) bar_layer.commitChanges() params={'INPUT': bar_layer,'OPTIONS':'-update -nln bar','OUTPUT': '/tmp/temp_gpkg.gpkg'} processing.run("gdal:convertformat", params) 
2
  • It takes less writing to do it with ogrinfo ogrinfo -sql "create table foo (attr1 text, attr2 int)" test.gpkg. But if your aim is to create a table with a geometry column then it gets more complicated. Commented Dec 2, 2021 at 19:03
  • Indeed, I could do it all in sql registering the geometry columns etc, but thought there might be another way. Commented Dec 2, 2021 at 19:36

1 Answer 1

7

You can create a method like this:

def create_blank_gpkg_layer(gpkg_path: str, layer_name: str, geometry: int, crs: str, schema: QgsFields, append: bool = False ) -> bool: # To add a layer to an existing GPKG file, pass 'append' as True options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = "GPKG" options.layerName = layer_name if append: options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer writer = QgsVectorFileWriter.create( gpkg_path, schema, geometry, QgsCoordinateReferenceSystem(crs), QgsCoordinateTransformContext(), options) del writer return True 

And then use it in this way for spatial layers:

# Create a layer gpkg_path = "/tmp/test.gpkg" layer_name = "my_layer" geom = QgsWkbTypes.PolygonZ crs = 'epsg:4326' schema = QgsFields() schema.append(QgsField("double_field", QVariant.Double)) schema.append(QgsField("text_field", QVariant.String)) create_blank_gpkg_layer(gpkg_path, layer_name, geom, crs, schema) 

Or in this way, for non-spatial layers:

# Create a table layer_name = "my_table" geom = QgsWkbTypes.NoGeometry crs = '' schema = QgsFields() schema.append(QgsField("int_field", QVariant.Int)) schema.append(QgsField("bool_field", QVariant.Bool)) create_blank_gpkg_layer(gpkg_path, layer_name, geom, crs, schema, True) 

Adapted from: https://github.com/qgis/QGIS/issues/37386

1
  • Excellent, that's much better. QgsWkbTypes.NoGeometry was the missing piece! Commented Dec 3, 2021 at 9:18

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.