Create QGIS projects programmatically using Python
Project description
qgis-project
Create QGIS projects programmatically using Python.
This package is a thin wrapper around QGIS's most essential functions: loading layers (vector/raster, local/web), basic visualization, and basic processing.
This package is not a tool for advanced or complex visual analysis or cartographic mapping — for that, use QGIS itself or a dedicated mapping library.
from qgis_project import Project, RasterStyleBW
proj = Project()
proj.add_layer("dem.tif") # raster, auto-detected
proj.add_layer("boundaries.geojson") # vector, auto-detected
proj.add_layer("dem.tif", style=RasterStyleBW(vmin=0, vmax=3000)) # style a path directly
proj.save("output.qgz")
proj.open() # launch QGIS for visual inspection
Installation
qgis-project is pure Python with no runtime dependencies. The real work
is done by QGIS's Python API, which is not on PyPI and must be available
separately. There are two good ways to set this up — pick based on whether you
already use the QGIS desktop application.
Option A — drive a local QGIS install (lightweight env)
If you have the QGIS desktop app installed, the package auto-discovers it and
runs your script through QGIS's own bundled Python via its entrypoint
launcher. You can then install qgis-project into any Python environment
(venv, conda, system) with minimal overhead — the qgis package from conda is
not required.
pip install qgis-project
# …and have a standalone QGIS desktop install on the machine (auto-discovered)
- ✅ Tiny environment, a single QGIS install, and no QGIS/Python version conflicts.
- ⚠️ Slightly hacky: execution runs in a separate process, so there are no live
QGIS objects (e.g. no
snapshot()).
Ready-made env: environments/strategy2-wrapper.yml.
Option B — install QGIS from conda-forge (in-process)
Create a conda environment that includes the qgis package; that environment's
Python interpreter then runs your script directly, in-process.
conda env create -f environments/strategy3-conda.yml
conda activate qgis-project-conda
Or add QGIS to an existing environment:
conda install -c conda-forge qgis
pip install qgis-project
- ✅ Clean, conventional, with full access to live QGIS objects.
- ⚠️ Larger environment (the QGIS stack is >1 GB), and if you also have the desktop app you end up with multiple QGIS installs.
See How qgis-project connects to QGIS
in the docs for a full explanation of every approach and how to force a specific
one with QGIS_PROJECT_LAUNCH_MODE.
Usage
Basic project
from qgis_project import Project
proj = Project()
proj.add_layer("dem.tif")
proj.add_layer("roads.geojson")
proj.save("my_project.qgz")
proj.exit()
Layer groups
proj.add_layer(RasterLayer("dem.tif", group="terrain"))
proj.add_layer(RasterLayer("slope.tif", group=["terrain", "derived"]))
Raster styling
| Class | Effect |
|---|---|
RasterStyleBW |
Grayscale with contrast stretch |
RasterStyleSinglePseudocolor |
Single-band color ramp |
RasterStyleMultiBandColor |
Multi-band RGB/false-color composite |
from qgis_project import RasterLayer, RasterStyleBW
layer = RasterLayer(
file="dem.tif",
name="Elevation",
group="terrain",
style=RasterStyleBW(vmin=0, vmax=3000),
)
proj.add_layer(layer)
You can also pass a path and styling straight to add_layer without
constructing a RasterLayer yourself — a RasterLayer is built automatically
when a raster-specific keyword (a RasterStyle, band_idx, or
statistics_kwargs) is given:
proj.add_layer("dem.tif", name="Elevation", group="terrain",
style=RasterStyleBW(vmin=0, vmax=3000))
If vmin/vmax are omitted they are computed from the layer data.
RasterStyleMultiBandColor requires band_idx to be a list of three band
numbers [R, G, B]:
from qgis_project import RasterStyleMultiBandColor
layer = RasterLayer(
file="rgb.tif",
band_idx=[1, 2, 3],
style=RasterStyleMultiBandColor(),
)
proj.add_layer(layer)
Vector styling
| Class | Effect |
|---|---|
VectorStyleSingleSymbol |
Uniform fill/line/marker color and outline |
VectorStyleCategorized |
One color per unique attribute value |
VectorStyleGraduated |
Equal-interval color classes (choropleth) for a numeric attribute |
from qgis_project import Layer, VectorStyleSingleSymbol
layer = Layer(
file="regions.geojson",
style=VectorStyleSingleSymbol(color="red", outline_color="black", outline_width=1.0),
)
proj.add_layer(layer)
VectorStyleCategorized and VectorStyleGraduated work on point, line, and
polygon layers alike:
from qgis_project import VectorStyleCategorized, VectorStyleGraduated
layer = Layer("regions.geojson", style=VectorStyleCategorized(field="class", colormap="Spectral"))
layer = Layer("regions.geojson", style=VectorStyleGraduated(field="value", num_classes=5, colormap="Viridis"))
If vmin/vmax are omitted from VectorStyleGraduated, they are computed
from the field's data. outline_color/outline_width on
VectorStyleSingleSymbol have no effect on line layers (a line has no
separate outline). For point layers, VectorStyleSingleSymbol also accepts
size (marker size in mm) and marker_shape (e.g. "circle", "square",
"triangle", "star").
Filtering, labels, and scale-based visibility
Layer.filter restricts a vector layer to features matching a QGIS
expression (QgsVectorLayer.setSubsetString):
layer = Layer("regions.geojson", filter="population > 1000")
Layer.labels adds attribute-based labels, independent of style:
from qgis_project import VectorLabels
layer = Layer("regions.geojson", labels=VectorLabels(field="name", size=12, color="black"))
Layer.min_scale/Layer.max_scale set scale-dependent visibility (scale
denominators). min_scale is the most zoomed-out scale at which the layer
is still visible; max_scale is the most zoomed-in scale at which it's
still visible:
layer = Layer("regions.geojson", max_scale=50000) # hidden once zoomed in past 1:50,000
Open in QGIS
proj.open("output.qgz") # saves and launches QGIS
proj.print_layer_tree() # inspect the layer tree in the terminal
Development
Install with dev dependencies:
pip install -e ".[dev]"
Run unit tests (no QGIS required):
pytest -m "not qgis"
Run integration tests (QGIS environment required):
pytest -m qgis
Run the manual visual test:
python scripts/manual_test.py
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file qgis_project-1.0.3.tar.gz.
File metadata
- Download URL: qgis_project-1.0.3.tar.gz
- Upload date:
- Size: 1.1 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af42b9c9646253afaa1a5a247465b69a7fce985a6d8507b303a4a990f9a4d4a2
|
|
| MD5 |
652a54ad226c8d2b45ed94e35a13304a
|
|
| BLAKE2b-256 |
910ac825f9b7cc41b1dc4c0099c381010419eb0d2140838f116b9674abb07af3
|
Provenance
The following attestation bundles were made for qgis_project-1.0.3.tar.gz:
Publisher:
publish.yml on ColinMoldenhauer/qgis-project
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qgis_project-1.0.3.tar.gz -
Subject digest:
af42b9c9646253afaa1a5a247465b69a7fce985a6d8507b303a4a990f9a4d4a2 - Sigstore transparency entry: 2019515345
- Sigstore integration time:
-
Permalink:
ColinMoldenhauer/qgis-project@817ea7db2c938e0a169e296f1b89a55a7bcf729c -
Branch / Tag:
refs/tags/v1.0.3 - Owner: https://github.com/ColinMoldenhauer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@817ea7db2c938e0a169e296f1b89a55a7bcf729c -
Trigger Event:
push
-
Statement type:
File details
Details for the file qgis_project-1.0.3-py3-none-any.whl.
File metadata
- Download URL: qgis_project-1.0.3-py3-none-any.whl
- Upload date:
- Size: 30.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6dde19e5a9e9ec5066d8fc5416563a0e1415993891f30b3c1586e4950fc487f5
|
|
| MD5 |
52049df3c6aaee3ae71967eb61cc1d21
|
|
| BLAKE2b-256 |
82f26b7784d34217d91bae27488fe985092badf493c51b4dac68a898db3c2a76
|
Provenance
The following attestation bundles were made for qgis_project-1.0.3-py3-none-any.whl:
Publisher:
publish.yml on ColinMoldenhauer/qgis-project
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qgis_project-1.0.3-py3-none-any.whl -
Subject digest:
6dde19e5a9e9ec5066d8fc5416563a0e1415993891f30b3c1586e4950fc487f5 - Sigstore transparency entry: 2019515413
- Sigstore integration time:
-
Permalink:
ColinMoldenhauer/qgis-project@817ea7db2c938e0a169e296f1b89a55a7bcf729c -
Branch / Tag:
refs/tags/v1.0.3 - Owner: https://github.com/ColinMoldenhauer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@817ea7db2c938e0a169e296f1b89a55a7bcf729c -
Trigger Event:
push
-
Statement type: