SuperAPI AI Directory

qgis-core-data-providers

Data & analytics

View repository

Updated 10-15 22:18

About this skill

qgis-core-data-providers

Quick Reference

Provider System Overview

QGIS uses a plugin-based provider architecture. Providers are registered in QgsProviderRegistry and loaded during QgsApplication.initQgis(). Each provider handles one or more data formats.

Provider Key Type Formats
"ogr" Vector Shapefile, GeoPackage, GeoJSON, FlatGeoBuf, KML, DXF, GPX
"postgres" Vector PostGIS tables and views
"spatialite" Vector SpatiaLite databases
"memory" Vector In-memory temporary layers
"delimitedtext" Vector CSV, TSV, custom-delimited text files
"WFS" Vector OGC Web Feature Service
"virtual" Vector SQL queries across loaded layers
"gdal" Raster GeoTIFF, JPEG2000, COG, VRT, GeoPackage raster
"wms" Raster WMS, WMTS, XYZ tiles
"wcs" Raster OGC Web Coverage Service
"postgresraster" Raster PostGIS raster tables
"pdal" Point Cloud LAS, LAZ (QGIS 3.18+)
"copc" Point Cloud Cloud Optimized Point Cloud (QGIS 3.26+)
"ept" Point Cloud Entwine Point Tile (QGIS 3.18+)
"mdal" Mesh NetCDF, GRIB, XMDF, DAT
"vectortile" Vector Tile Mapbox Vector Tiles (MVT)
"cesiumtiles" Tiled Scene Cesium 3D Tiles (QGIS 3.34+)

Layer Construction Pattern

# Vector layer
vlayer = QgsVectorLayer(data_source_uri, display_name, provider_key)

# Raster layer
rlayer = QgsRasterLayer(data_source_uri, display_name, provider_key)

# Point cloud layer (QGIS 3.18+)
pclayer = QgsPointCloudLayer(data_source_uri, display_name, provider_key)

# Mesh layer
mlayer = QgsMeshLayer(data_source_uri, display_name, provider_key)

# Vector tile layer
vtlayer = QgsVectorTileLayer(data_source_uri, display_name)

ALWAYS check validity immediately after creation:

layer = QgsVectorLayer(uri, name, provider)
if not layer.isValid():
    raise RuntimeError(f"Failed to load layer '{name}' from: {uri}")

Critical Warnings

NEVER skip isValid() after layer creation. A layer object is ALWAYS returned even when loading fails -- the constructor NEVER raises exceptions.

NEVER use backslashes in URIs, even on Windows. QGIS/Qt normalizes to forward slashes internally. Backslashes in URIs cause silent provider failures.

NEVER pass True to uri.uri(expandAuthConfig) when logging or displaying URIs. This exposes authentication credentials in plain text. ALWAYS use uri.uri(False).

NEVER assume a GeoPackage contains a single layer. ALWAYS use explicit |layername= in the URI or enumerate sublayers first.

ALWAYS call layer.updateExtents() after adding features to a memory layer. Without this, zoom-to-layer returns a wrong extent.

ALWAYS call layer.updateFields() after calling dataProvider().addAttributes(). Without this, the layer schema is stale.

ALWAYS use file:/// prefix (three slashes) for delimited text URIs with absolute paths.

NEVER access features or data provider methods on an invalid layer -- this causes crashes or undefined behavior.


Decision Tree: Which Format to Use

Need to store spatial data?
├── Temporary / in-memory only?
│   └── USE: memory provider
├── Exchange with non-GIS tools?
│   ├── JSON-based → USE: GeoJSON
│   └── Tabular → USE: CSV with delimitedtext provider
├── Single-layer vector file?
│   ├── Small dataset → USE: GeoPackage (single layer)
│   └── Streaming / append-heavy → USE: FlatGeoBuf
├── Multi-layer project database?
│   └── USE: GeoPackage (recommended default)
├── Enterprise / multi-user database?
│   └── USE: PostGIS with postgres provider
├── Web service?
│   ├── Vector features → USE: WFS
│   ├── Map images → USE: WMS
│   ├── Tile basemaps → USE: XYZ tiles via wms provider
│   └── Raw raster coverage → USE: WCS
├── Raster data?
│   ├── Local file → USE: GeoTIFF via gdal provider
│   ├── Cloud storage → USE: COG via /vsicurl/
│   └── Database → USE: postgresraster
├── Point cloud / LiDAR?
│   ├── Local file → USE: pdal (LAS/LAZ)
│   └── Cloud optimized → USE: copc
└── Legacy requirement?
    └── Shapefile ONLY if mandated by external system

GeoPackage is the recommended default format. It supports vector, raster, and attribute tables in a single SQLite-based file with no file count limitations (unlike Shapefile's multi-file structure).


Essential Patterns

Pattern 1: Load a GeoPackage Layer

from qgis.core import QgsVectorLayer, QgsProject

# Single known layer
vlayer = QgsVectorLayer("data/project.gpkg|layername=buildings", "Buildings", "ogr")
if not vlayer.isValid():
    raise RuntimeError("Layer failed to load")
QgsProject.instance().addMapLayer(vlayer)

Pattern 2: Enumerate All Sublayers

from qgis.core import QgsDataProvider, QgsVectorLayer, QgsProject

gpkg_path = "data/project.gpkg"
layer = QgsVectorLayer(gpkg_path, "probe", "ogr")
for sub in layer.dataProvider().subLayers():
    name = sub.split(QgsDataProvider.SUBLAYER_SEPARATOR)[1]
    uri = f"{gpkg_path}|layername={name}"
    sub_layer = QgsVectorLayer(uri, name, "ogr")
    if sub_layer.isValid():
        QgsProject.instance().addMapLayer(sub_layer)

Pattern 3: PostGIS Connection with QgsDataSourceUri

from qgis.core import QgsDataSourceUri, QgsVectorLayer

uri = QgsDataSourceUri()
uri.setConnection("localhost", "5432", "mydb", "user", "password")
uri.setDataSource("public", "roads", "geom", "status = 'active'", "gid")

vlayer = QgsVectorLayer(uri.uri(False), "Active Roads", "postgres")
if not vlayer.isValid():
    raise RuntimeError("PostGIS connection failed")

Pattern 4: Create a Memory Layer with Fields

from qgis.core import QgsVectorLayer, QgsField, QgsFeature, QgsGeometry, QgsPointXY
from qgis.PyQt.QtCore import QVariant

# URI with inline field definitions
layer = QgsVectorLayer(
    "Point?crs=EPSG:4326&field=name:string(100)&field=value:double",
    "Results",
    "memory"
)

# OR add fields programmatically
layer = QgsVectorLayer("Point?crs=EPSG:4326", "Results", "memory")
pr = layer.dataProvider()
pr.addAttributes([
    QgsField("name", QVariant.String),
    QgsField("value", QVariant.Double),
])
layer.updateFields()

# Add features
feat = QgsFeature()
feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(5.0, 52.0)))
feat.setAttributes(["Sample", 42.0])
pr.addFeatures([feat])
layer.updateExtents()

Pattern 5: Load WMS / XYZ Tiles

from qgis.core import QgsRasterLayer, QgsProject

# WMS
wms_uri = (
    "crs=EPSG:4326"
    "&format=image/png"
    "&layers=my_layer"
    "&styles"
    "&url=https://example.com/wms"
)
wms_layer = QgsRasterLayer(wms_uri, "WMS Layer", "wms")

# XYZ tiles -- {z}/{x}/{y} MUST be URL-encoded
xyz_uri = (
    "type=xyz"
    "&url=https://tile.openstreetmap.org/%7Bz%7D/%7Bx%7D/%7By%7D.png"
    "&zmin=0&zmax=19"
    "&crs=EPSG3857"
)
xyz_layer = QgsRasterLayer(xyz_uri, "OpenStreetMap", "wms")

for lyr in [wms_layer, xyz_layer]:
    if not lyr.isValid():
        raise RuntimeError(f"Layer '{lyr.name()}' failed to load")
    QgsProject.instance().addMapLayer(lyr)

Pattern 6: Load CSV with Coordinates

import os
from qgis.core import QgsVectorLayer

csv_path = os.path.abspath("data/stations.csv").replace("\\", "/")
uri = (
    f"file:///{csv_path}"
    "?delimiter=,"
    "&xField=longitude"
    "&yField=latitude"
    "&crs=EPSG:4326"
)
vlayer = QgsVectorLayer(uri, "Stations", "delimitedtext")

Common Operations

List Available Providers

from qgis.core import QgsProviderRegistry

registry = QgsProviderRegistry.instance()
for key in registry.providerList():
    print(key)

Load Raster from GeoPackage

from qgis.core import QgsRasterLayer

rlayer = QgsRasterLayer("GPKG:/data/rasters.gpkg:elevation", "Elevation", "gdal")

Load Cloud Optimized GeoTIFF (COG)

from qgis.core import QgsRasterLayer

rlayer = QgsRasterLayer("/vsicurl/https://example.com/data.tif", "Remote COG", "gdal")

Load WFS Layer

from qgis.core import QgsVectorLayer

uri = "https://example.com/wfs?service=WFS&version=2.0.0&request=GetFeature&typename=ns:layer"
vlayer = QgsVectorLayer(uri, "WFS Layer", "WFS")

Load SpatiaLite Layer

from qgis.core import QgsDataSourceUri, QgsVectorLayer

uri = QgsDataSourceUri()
uri.setDatabase("/data/regions.sqlite")
uri.setDataSource("", "regions", "geometry")
vlayer = QgsVectorLayer(uri.uri(), "Regions", "spatialite")

Virtual Layer (SQL Across Loaded Layers)

from qgis.core import QgsVectorLayer

uri = "?query=SELECT * FROM airports WHERE elevation > 500"
vlayer = QgsVectorLayer(uri, "High Airports", "virtual")

Materialize Selection as Memory Layer

from qgis.core import QgsFeatureRequest, QgsProject

memory_layer = source_layer.materialize(
    QgsFeatureRequest().setFilterFids(source_layer.selectedFeatureIds())
)
QgsProject.instance().addMapLayer(memory_layer)

Reference Links

Official Sources