Skip to main content

geodataframe-to-pmtiles

Write PMTiles vector archives from one or more GeoPandas GeoDataFrames using GDAL directly — no subprocesses, no temporary files.

Install

Install the latest release from PyPI:

pip install geodataframe-to-pmtiles

To install from a source checkout instead:

git clone https://github.com/palewire/geodataframe-to-pmtiles.git
cd geodataframe-to-pmtiles
uv sync

Note: The library itself is pure Python, but gpm.write() needs a native GDAL runtime with the PMTiles driver available. The package imports without GDAL; calling gpm.write() without it raises a clear RuntimeError. In CI we install GDAL from conda-forge. Locally, install GDAL separately via conda-forge, Homebrew, or your operating system package manager before writing PMTiles archives.

Diagnose GDAL

After installing GDAL, check that its Python bindings, native library, PMTiles driver, and a real in-memory write all work:

python -m geodataframe_to_pmtiles check
python -m geodataframe_to_pmtiles check --json

--json prints a small, stable report that is safe to include in a bug report. The check runs only when requested; it is not an install hook. Conda-forge is the most reliable setup on every platform. On macOS, install GDAL with Homebrew and use Python bindings built for that installation. On Linux, install matching GDAL runtime and Python packages from the same system package source.

Usage

import geopandas as gpd
from pathlib import Path
import geodataframe_to_pmtiles as gpm

# Any explicit CRS is accepted — reprojection to EPSG:4326 is automatic.
points = gpd.read_file("points.geojson")
polygons = gpd.read_file("polys.geojson")

gpm.write(
    {"points": points, "polygons": polygons},
    Path("output.pmtiles"),
    min_zoom=0,
    max_zoom=8,
    name="my map",
    description="Points and polygons",
    on_overflow="error",  # default: reject reported tile-level data loss
    attribution="© OpenStreetMap contributors",  # optional; stored in TileJSON metadata
)

GeoDataFrames passed to gpm.write() must already carry a CRS. If your source format does not store CRS metadata, set one before writing:

points = points.set_crs("EPSG:4326")
polygons = polygons.set_crs("EPSG:4326")

Write to a BytesIO stream instead of a file:

import io

buf = io.BytesIO()
gpm.write({"points": points}, buf)

Write a single GeoDataFrame with an explicit layer name:

import geodataframe_to_pmtiles as gpm

gpm.write(points, Path("output.pmtiles"), layer="points")

Test coverage

The test suite includes semantic conformance checks that write tracked climate and Tippecanoe fixtures through GDAL, then decode the resulting PMTiles archives with the official pmtiles reader and mapbox-vector-tile. The tests assert header metadata, source-layer names, property schemas, hole preservation, and feature order while ignoring raw bytes and protobuf ordering.

Documentation

The concise, single-page documentation is built from docs/index.md and published at https://palewi.re/docs/geodataframe-to-pmtiles/.

gpm.write — two call forms

Mapping form — multiple named layers:

gpm.write({"name": gdf, ...}, output, *, min_zoom, max_zoom, ...)

Single-frame form — one layer with an explicit name:

gpm.write(gdf, output, *, layer="name", min_zoom, max_zoom, ...)
Parameter Type Default Description
layers Mapping[str, GeoDataFrame] or GeoDataFrame required Layer name → GeoDataFrame mapping (mapping form), or a single GeoDataFrame (single-frame form, requires layer). Any explicit CRS accepted; non-EPSG:4326 layers are auto-reprojected. Inputs must still carry a CRS and are not mutated.
output str | Path | BinaryIO required Destination file path (string or Path) or binary stream.
layer str (omit for mapping) Non-empty layer name. Required for the single-frame form; must be omitted entirely when layers is a mapping.
min_zoom int 0 Archive-wide minimum zoom level (0-22).
max_zoom int 8 Archive-wide maximum zoom level (0-22).
name str "" Tileset name stored in archive metadata.
description str "" Human-readable description in archive metadata.
attribution str "" Attribution string stored in TileJSON metadata under "attribution". Unicode and HTML preserved. Omit or pass "" to skip.
json_fields Collection[str] | None None Columns to JSON-encode (list/dict values). None auto-encodes all; explicit set restricts to named columns only.
on_overflow "error" | "unsafe" "error" Reject detected GDAL tile-limit actions, or explicitly accept them.
simplification float | None None Geometry simplification tolerance (tile units). None = disabled.

Property normalisation

Python / pandas type MVT field Notes
str String
bool / np.bool_ Boolean Native MVT boolean
int / np.integer Integer64
float / np.float_ Real NaN → null
datetime String ISO 8601
list / dict String JSON-encoded; column must be in json_fields or json_fields=None (auto)
None / pd.NA null
other — UnsupportedPropertyTypeError

Boolean columns may contain nulls, including pandas BooleanDtype values even if every value is null. They must not mix booleans with numeric 0 or 1: those are integers and remain numeric. Mixed scalar boolean/non-boolean columns raise UnsupportedPropertyTypeError instead of silently changing values.

Exceptions

Exception When raised
EmptyLayerError The layers mapping is empty, or a nonempty GDF has no usable geometries.
MissingCRSError A GDF has no CRS set (explicit source CRS required).
UnsupportedCRSError A GDF's CRS definition cannot be resolved by the installed stack (chained from root cause).
CRSTransformError Coordinate transformation to EPSG:4326 failed at runtime (chained from root cause).
UnsupportedPropertyTypeError A column has an unrecognised type, or a list/dict column not in json_fields.
TileOverflowError GDAL reported a feature-cap rebuild or size-driven geometry recode. The destination is unchanged.

Overflow policy

GDAL's MVT encoder can drop features after MAX_FEATURES is reached and reduce geometry precision after MAX_SIZE is exceeded. The writer uses practical limits (300,000 features and 10 MB per tile) and captures the encoder's diagnostics during finalization. The default on_overflow="error" raises TileOverflowError and leaves the Path or stream untouched when either action occurs. Its violations describe the limit, configured value, observed value, and tile coordinate when GDAL reports one.

The 200,001-feature z0 spike remains supported and is independently decoded in the test suite. This is not a capacity promise: a clustered layer or dense geometry can still exceed a tile limit, but it cannot be published through the default API after GDAL reports that action.

on_overflow="unsafe" is an explicit opt-out. It emits a warning and may publish an archive with missing features or lower-precision geometry.

Climate-monitor guidance

Use the default policy for climate cell layers and treat TileOverflowError as a signal to split the layer or lower its density at the affected zoom. Do not use on_overflow="unsafe" for maps where holes or coordinate changes would alter reported conditions.

Known limitations

  • Feature count inflation when reading back: MVT stores features in every intersecting tile; read-back counts exceed input counts. This is not data loss.
  • Simplification disabled by default: pass simplification=<float> to enable.

Development

make install
make check   # lint, format, type checks
make verify  # full suite: checks, tests, build, docs

See AGENTS.md and CONTRIBUTING.md.

Metadata

Release files for geodataframe-to-pmtiles 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for geodataframe-to-pmtiles 0.5.0
File Size Uploaded Provenance
geodataframe_to_pmtiles-0.5.0.tar.gz 39.9 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for geodataframe-to-pmtiles 0.5.0
File Interpreter ABI Platform
geodataframe_to_pmtiles-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 74.4 kB

Release files / geodataframe_to_pmtiles-0.5.0.tar.gz

Download URL geodataframe_to_pmtiles-0.5.0.tar.gz
Size 39.9 kB
Tags Source
SHA-256 checksum
How to use checksums
a020200b5952ccb10df0edad1e9ebbccaa0232a1e2884ce52a3abd6f85043d57
BLAKE2b-256 checksum
How to use checksums
76d2760916ccb04e14df32b9099c912206a07e325a2a0d1798abe6f926843d85
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 23, 2026.

Transparency log

Release files / geodataframe_to_pmtiles-0.5.0-py3-none-any.whl

Download URL geodataframe_to_pmtiles-0.5.0-py3-none-any.whl
Size 34.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d8f8ea5a0bd459266dbc2c214f64b881ad7e34299a95e281ddedabbbc09e2c41
BLAKE2b-256 checksum
How to use checksums
11498e6240e5765d4f320d91104abc093aa83ebb58cc3d42a19e36950f721bb0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 23, 2026.

Transparency log

Security for geodataframe-to-pmtiles 0.5.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

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for geodataframe_to_pmtiles-0.5.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 Oct 23, 2026.

Transparency log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page