qdgc-py
qdgc-py is a lightweight Python package for Extended Quarter Degree Grid Cell
(QDGC) codes.
It modernizes the legacy scripts in this repository into a reusable API that can be imported by other projects.
QDGC is a hierarchical lon/lat square grid in EPSG:4326:
- level 0 cells are 1 deg x 1 deg
- each level splits each cell into 4 quadrants
- side length in degrees is
1.0 / (2 ** level)
Install
Once published to PyPI:
pip install qdgc-py
The distribution name is qdgc-py; the import name is qdgc_py:
import qdgc_py
Quickstart (from source)
From qdgc/qdgc_py:
pip install -e .
pytest -q
API
from qdgc_py import (
encode,
decode_bounds,
decode_centroid,
cell_to_boundary,
cell_to_polygon,
polygon_to_cells,
bbox_to_cells,
cell_to_parent,
cell_to_children,
average_cell_area,
estimate_cell_count,
)
code = encode(38.98754324, -9.87548764, level=5)
bounds = decode_bounds(code)
center = decode_centroid(code)
ring_latlon = cell_to_boundary(code) # (lat, lon)
ring_lonlat = cell_to_polygon(code) # (lon, lat)
exterior = [
(10.0, 20.0),
(12.0, 20.0),
(12.0, 22.0),
(10.0, 22.0),
(10.0, 20.0),
]
cells = polygon_to_cells(exterior, level=4, predicate="intersects")
bbox_cells = bbox_to_cells(10.0, 20.0, 12.0, 22.0, level=4)
parent = cell_to_parent(code)
children = cell_to_children(parent)
area_km2 = average_cell_area(4, lat=45.0)
estimate = estimate_cell_count(exterior, 4)
API summary
encode(lon, lat, level)->strencode_many(points, level)->list[str]decode_bounds(code)->QDGCCelldecode_centroid(code)->(lon, lat)cell_to_boundary(code)->list[(lat, lon)]cell_to_polygon(code)->list[(lon, lat)]bbox_to_cells(min_lon, min_lat, max_lon, max_lat, level)->list[str]polygon_to_cells(exterior, level, holes=None, predicate="intersects")->list[str]cell_to_parent(code, parent_level=None)->strcell_to_children(code, child_level=None)->list[str]level_degrees(level)->floataverage_cell_area(level, lat=None, unit="km^2")->floatestimate_cell_count(exterior, level, bbox=None)->intis_valid_cell(code)->bool
H3-style convenience aliases are also available:
latlng_to_cell(lat, lng, res)cell_to_latlng(cell)average_hexagon_area(res, unit="km^2")
Level table
Approximate side lengths and equatorial cell areas:
| level | side (deg) | side at equator (km) | area at equator (km^2) |
|---|---|---|---|
| 0 | 1.0 | 111.32 | 12364.35 |
| 1 | 0.5 | 55.66 | 3091.09 |
| 2 | 0.25 | 27.83 | 772.77 |
| 3 | 0.125 | 13.92 | 193.19 |
| 4 | 0.0625 | 6.96 | 48.30 |
| 5 | 0.03125 | 3.48 | 12.07 |
| 6 | 0.015625 | 1.74 | 3.02 |
Values are approximate because metric size varies with latitude.
Boundary behavior at origin is deterministic and legacy-compatible:
encode(0.0, 0.0, 1) # E000N00C
encode(0.0, 0.0, 2) # E000N00CC
Current scope
- Deterministic
encode()implementation compatible with legacyqdgc_lib.py - Decode helpers for bounds and centroid
- Cell geometry, hierarchy, validation, and area estimation helpers
- Polygon/bbox to cells fill helpers in pure stdlib Python (no shapely dependency)
- Compatibility tests against legacy implementation plus AOI fill edge cases
Legacy compatibility and boundary behavior
This package intentionally preserves legacy QDGC behavior from qdgc_lib.py.
- Level 0 uses hemisphere-prefixed degree cells (
E/W+ 3-digit longitude,N/S+ 2-digit latitude), consistent with the extended QDGC description. - Subdivision letters follow the original QDGC orientation:
A=upper-left,B=upper-right,C=lower-left,D=lower-right. - Boundary points are deterministic. At
(lon=0, lat=0), encoding resolves to east/north and then to the lower-left subcell at each level, e.g.:- level 1:
E000N00C - level 2:
E000N00CC
- level 1:
The boundary choice at exact split lines is a legacy convention and is kept for backward compatibility.
Reference
Larsen, R., Holmern, T., Prager, S. D., Maliti, H., and Røskaft, E. (2009). Using the extended quarter degree grid cell system to unify mapping and sharing of biodiversity data. African Journal of Ecology. https://doi.org/10.1111/j.1365-2028.2008.00997.x
Versioning and releases
- Current package version is defined in
pyproject.toml(project.version). - Bump the version when behavior or public API changes.
- Tag releases in git using the same version (for example
v0.1.1).
Publishing to PyPI
Releases publish automatically via GitHub Actions using PyPI Trusted Publishing
(OIDC) - no API tokens or stored secrets. See
.github/workflows/qdgc_py-release.yml.
One-time setup on pypi.org (project -> Publishing -> add a trusted publisher):
| Field | Value |
|---|---|
| Owner | ragnvald |
| Repository | qdgc |
| Workflow name | qdgc_py-release.yml |
| Environment | pypi |
To cut a release:
# 1. bump project.version in qdgc_py/pyproject.toml (e.g. 0.1.1)
# 2. tag and push
git tag v0.1.1
git push origin v0.1.1
The workflow runs the tests, builds the sdist and wheel, validates metadata with
twine check, and uploads to PyPI.
Manual fallback (from qdgc_py/, requires a PyPI API token):
pip install build twine
python -m build
twine check dist/*
twine upload dist/*
Next steps
- Performance tuning for very large AOIs and high levels
- Publish tagged releases to PyPI when API is stable
Metadata
Release files for qdgc-py 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| qdgc_py-0.1.0.tar.gz | 13.7 kB | With provenance | Details for qdgc_py-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| qdgc_py-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for qdgc_py-0.1.0-py3-none-any.whl |
Total release size: 23.7 kB
Release files / qdgc_py-0.1.0.tar.gz
| Download URL | qdgc_py-0.1.0.tar.gz |
|---|---|
| Size | 13.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a3eb4c2b4992d1364cc69c8570a006da90a67a8c2f9f3ee4777002a96646c65e
|
|
BLAKE2b-256 checksum How to use checksums |
902cdee03cac8605193c7797bd278684c704d8edd66e3c9661774f2e8b5bd1f2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 16, 2026.
Transparency logRelease files / qdgc_py-0.1.0-py3-none-any.whl
| Download URL | qdgc_py-0.1.0-py3-none-any.whl |
|---|---|
| Size | 10.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
db04a0c0951e28e92dc40ee1536057bd13cec9951d57de66eed1b4c7afdcdc01
|
|
BLAKE2b-256 checksum How to use checksums |
b45b4ab1979d10107d9e830255543174aa72b1a43031b2a89f6b2c1470ec0644
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 16, 2026.
Transparency logSecurity for qdgc-py 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for qdgc_py-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 16, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.