Skip to main content

Create QGIS projects programmatically using Python

Project description

qgis-project

qgis-project

PyPI version Python versions Tests Docs License

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
RasterStylePaletted Paletted / unique-value colors for categorical rasters
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)

RasterStylePaletted colors a categorical raster (land cover, classification mask) with one fixed color per discrete band value — no interpolation. Pass an explicit colors mapping (with optional labels), or omit it to auto-detect the distinct values and color them from colormap:

from qgis_project import RasterStylePaletted

layer = RasterLayer(
    file="landcover.tif",
    style=RasterStylePaletted(
        colors={1: "#1b9e77", 2: "#d95f02", 3: "#7570b3"},
        labels={1: "Forest", 2: "Urban", 3: "Water"},
    ),
)
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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qgis_project-1.1.5.tar.gz (1.1 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qgis_project-1.1.5-py3-none-any.whl (34.2 kB view details)

Uploaded Python 3

File details

Details for the file qgis_project-1.1.5.tar.gz.

File metadata

  • Download URL: qgis_project-1.1.5.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

Hashes for qgis_project-1.1.5.tar.gz
Algorithm Hash digest
SHA256 827d639a5302f273dad3f437e258d682bf11db4e978f654dca8f62142d0a4341
MD5 6ed398c08ca8450ec651fd9aad3b57a0
BLAKE2b-256 fbb317f50c505613b73634998093923494690c35e534924b037a8d3a90813d2c

See more details on using hashes here.

Provenance

The following attestation bundles were made for qgis_project-1.1.5.tar.gz:

Publisher: publish.yml on ColinMoldenhauer/qgis-project

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qgis_project-1.1.5-py3-none-any.whl.

File metadata

  • Download URL: qgis_project-1.1.5-py3-none-any.whl
  • Upload date:
  • Size: 34.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for qgis_project-1.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 04844b9ed520e5cf84eab2ba8547c33148035847adfd4a13d302c04cb3d60e27
MD5 15b71733dcf0028caacbd04f1d28025b
BLAKE2b-256 90246747d212479c2b230e7da8406bca332c315f08ea25bbf37022a9b56badb1

See more details on using hashes here.

Provenance

The following attestation bundles were made for qgis_project-1.1.5-py3-none-any.whl:

Publisher: publish.yml on ColinMoldenhauer/qgis-project

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page