freestiler’s Python package shares the same Rust engine as the R package. This article covers Python-specific installation and usage. For the full walkthrough of tiling concepts (zoom levels, drop rates, multi-layer, clustering, DuckDB queries), see the Getting Started article - the API is nearly identical between R and Python.
Installation
Install from PyPI:
pip install freestilerPublished wheels include the full feature set: GeoPandas input, direct file tiling, DuckDB-backed file input, and SQL query support. Supported wheel targets are Python 3.9 through 3.14.
Building from source
You only need a Rust toolchain if a wheel isn’t available for your platform or you want an editable local build:
git clone https://github.com/walkerke/freestiler.git
cd freestiler/python
python3 -m venv .venv
source .venv/bin/activate
pip install maturin
python3 -m maturin develop --releaseBasic usage
The Python API mirrors R closely. Here’s the equivalent of the North Carolina example from the Getting Started article:
import geopandas as gpd
from freestiler import freestile
gdf = gpd.read_file("nc.shp")
freestile(gdf, "nc_counties.pmtiles", layer_name="counties")Multi-layer tilesets use a dictionary instead of R’s named list:
from freestiler import freestile, freestile_layer
centroids = gdf.copy()
centroids.geometry = gdf.geometry.centroid
freestile(
{
"counties": freestile_layer(gdf, min_zoom=0, max_zoom=10),
"centroids": freestile_layer(centroids, min_zoom=6, max_zoom=14),
},
"nc_layers.pmtiles"
)File input and DuckDB queries work the same way:
from freestiler import freestile_file, freestile_query
freestile_file("census_blocks.parquet", "blocks.pmtiles")
freestile_query(
query="SELECT * FROM read_parquet('blocks.parquet') WHERE state = 'NC'",
output="nc_blocks.pmtiles",
layer_name="blocks"
)Performance note
freestile(gdf, ...) is the most convenient Python entry point, but it does more preprocessing in GeoPandas before Rust starts tiling. For large datasets, freestile_file() and freestile_query() are usually the faster path.
In practice, the most expensive part of the GeoDataFrame path is often reprojection to WGS84 plus geometry serialization before the Rust tiler takes over. If your data is already on disk or in DuckDB, prefer those paths for serious workloads.
Viewing tiles
I’d recommend creating tiles with tile_format="mvt" for Python-facing work for now. Python viewer stacks are still catching up on MLT support, and MVT works everywhere.
One important caveat: Python’s built-in http.server does not support byte-range requests, so it won’t work as a PMTiles server. You’ll want to use a real static file server instead:
npx http-server /path/to/tiles -p 8082 --cors -c-1You can view any PMTiles file (MLT or MVT) in the browser with MapLibre GL JS 5.17+ and the PMTiles protocol. If you’re also an R user, the mapgl package is the most reliable local viewing path right now.
R vs Python API
| Feature | R | Python |
|---|---|---|
| Input type | sf data frame | GeoDataFrame |
| Multi-layer | Named list | Dict |
| Default format | "mlt" |
"mlt" |
| Zoom range | min_zoom = 0, max_zoom = 14 |
min_zoom=0, max_zoom=14 |
| Feature dropping | drop_rate = 2.5 |
drop_rate=2.5 |
| Point clustering | cluster_distance = 50 |
cluster_distance=50 |
| Feature coalescing | coalesce = TRUE |
coalesce=True |
| File input | freestile_file() |
freestile_file() |
| DuckDB queries | freestile_query() |
freestile_query() |
