Python package
Python package
qgis-rs is available as a Python package that ships a Rust binary (qgis-cli) and a PyO3 extension (qgis_rs._core) — both run at native speed.
- pip:
pip install qgis-rs→import qgis_rs+qgis-clion PATH - conda:
conda install -c conda-forge qgis-rs→ same, with optional QGIS backend
Installation
From PyPI (pip)
pip install qgis-rsWhat you get:
qgis_rsPython module (pure-Rust geometry + optional QGIS rendering)qgis-cliexecutable (Rust binary, built by maturin)qgis-cliandqgis-rsconsole scripts (python -m qgis_rs.cli)
Pre-built wheels for Linux x86_64, macOS arm64/x86_64, Windows x86_64. If no wheel matches, pip builds from source via maturin (requires Rust ≥1.96).
From conda-forge
conda install -c conda-forge qgis-rs# orpixi add qgis-rsmamba install -c conda-forge qgis-rsThe conda-forge package can be built in two modes:
- Lightweight (default): No QGIS dependency. Supports
info,tiles --dry-run,version, and the Python API for tile planning. This is whatpip install qgis-rsgives you. - Full: Depends on
qgis >=3.44.9. Supportsrender,tiles(actual rendering),export,serve. Get it viaconda install -c conda-forge qgis qgis-rs.
From source (development)
git clone https://github.com/Archont561/qgis-rscd qgis-rs
# Python dev with maturinpip install maturin(cd py-packages/qgis-rs && maturin develop)
# Or via pixi (conda env with QGIS)pixi install -e defaultpixi run -e default py-test
# Testpython -m pytest py-packages/qgis-rs/tests -qqgis-cli --helpPython API
from qgis_rs import Project, Extent, TilePlan, ZoomRange, Crs, plan_tiles
# Open a project (cheap — only checks path, no QGIS needed)project = Project.open("map.qgs")print(project.path, project.format)
info = project.info()print(info.to_dict())
# Pure-Rust geometry — native speed, no QGISextent = Extent.parse("14,50,15,51")assert extent.width() == 1.0
crs = Crs.from_epsg(3857)print(crs.auth_id, crs.is_projected()) # EPSG:3857 True
# Tile planning — counts tiles without renderingzooms = ZoomRange.parse("10-14")plan = TilePlan(extent, zooms)print(f"Would render {plan.tile_count()} tiles") # 4568for level in plan.levels(): print(f"z={level.zoom} {level.tile_count()} tiles")
# Fast functiontotal, levels = plan_tiles("14,50,15,51", "10-14")print(total) # 4568
# Rendering through the native QGIS backendtry: rendered = project.render("output.png", width=1920, height=1080) print(f"Wrote {rendered.path} ({rendered.bytes} bytes)")except ValueError as e: print(f"QGIS rendering failed: {e}")CLI
The same Rust code powers both the standalone binary and the Python wrapper.
# Binary on PATH (installed via pip or conda)qgis-cli --helpqgis-cli info map.qgs --jsonqgis-cli tiles map.qgs -z 10-14 -b 14,50,15,51 -o ./tiles/ --dry-runqgis-cli render map.qgs -o map.png --width 1920
# Via Python module (same speed — uses Rust extension directly)python -m qgis_rs.cli info map.qgs --jsonqgis-rs info map.qgs # aliasCommands
| Command | Pure Rust? | Needs QGIS? |
|---|---|---|
info | ✅ | No (basic), Yes (layers) |
tiles --dry-run | ✅ | No |
tiles | ✅ | Yes |
render | ✅ | Yes |
version | ✅ | No |
Architecture
qgis-rs wheel├── qgis_rs/│ ├── __init__.py → the public API│ ├── _api.py → Extent, Crs, Tile, TilePlan, ZoomRange, Project│ ├── _transport.py → invoke(): one JSON call into Rust, errors → exceptions│ ├── _core.so → Rust cdylib (PyO3) — exposes exactly one function, invoke()│ └── cli.py → Python CLI wrapper (uses _api, no subprocess)└── bin/qgis-cli → Rust binary (same logic, standalone)There is no pure-Python fallback: every number this package returns is computed
by Rust, and a second implementation would be a second set of answers. Without
a built _core, importing qgis_rs raises.
crates/qgis-protocol— the request/response envelope and the closed list of operationscrates/qgis-engine—invoke(request_json) -> response_json, one arm per operationcrates/qgis-render— pure Rust, no QGIS neededcrates/qgis-cli— CLI library + binary (reused by Python package)py-packages/qgis-rs— the Python distribution (client, tests, maturin config)crates/qgis-py— the PyO3 adapter +qgis-clibinary it compiles
Conda-forge
See py-packages/qgis-rs/conda-recipe/ for the recipe. It builds the Rust binary and Python extension and installs both to $PREFIX/bin and site-packages.
To publish to conda-forge:
- Fork https://github.com/conda-forge/staged-recipes
- Copy
py-packages/qgis-rs/conda-recipe/torecipes/qgis-rs/ - Open PR
Performance
Pure-Rust ops (no QGIS):
| Operation | Rust | Python (fallback) | Speedup |
|---|---|---|---|
| Extent parse | 0.5µs | 5µs | 10× |
| TilePlan 10-14 | 0.2ms | 3ms | 15× |