A Python helper that creates clean choropleth maps with defaults for projection, layout, legend and other key configurations.
- Easy to use: the common case works with one function call or CLI command
- Defaults first, flexibility when needed: well-designed defaults that you can override with small configs; explicit beats auto
- Speed: avoid unnecessary copies and Python loops; keep plotting fast for large GeoDataFrames
- Clean, production ready outputs: consistent spacing, legible labels, subtle legend; high DPI and exact canvas size
- Predictable and reproducible: deterministic classifications and colors when breaks are specified; versioned defaults
- Accessible and readable: offer color-vision-safe palettes and readable tick labels
- Small surface area: dataclasses capture configuration; CLI mirrors the Python API
- Composable design: separate modules for projection, legend and layout so parts can be swapped later
pip install chorokitFor development:
git clone https://github.com/mstiles/chorokit.git
cd chorokit
pip install -e ".[dev]"import geopandas as gpd
from chorokit import plot_choropleth
gdf = gpd.read_file("data/states.geojson")
fig, ax = plot_choropleth(
gdf=gdf,
value="value_column",
title="headline",
subtitle="subhead",
source="Source: dataset",
)
fig.savefig("out.png", dpi=300)Figure height is derived from the map's aspect ratio and the text/legend bands, so spacing stays consistent across geographies. Pass layout=LayoutConfig(width=10) to set the width in inches.
chorokit data/states.geojson value_column --title "headline" --subtitle "subhead" --source "Source: dataset" -o out.pngfrom chorokit import plot_choropleth, LegendConfig, LayoutConfig, Projection
legend = LegendConfig(
kind="binned",
title="value per 100k residents",
location="top",
scheme="quantiles",
k=5,
)
layout = LayoutConfig(
title="headline",
subtitle="subhead",
source="Source: dataset",
projection=Projection.us_albers(),
width=12,
)
fig, ax = plot_choropleth(gdf, value="value_column", cmap="Reds", legend=legend, layout=layout)# pass an EPSG code directly
fig, ax = plot_choropleth(gdf, value="value_column", projection=3857)
# or set in layout config
layout = LayoutConfig(projection="EPSG:3857")
fig, ax = plot_choropleth(gdf, value="value_column", layout=layout)chorokit data.geojson value_column \
--scheme quantiles -k 5 \
--legend-location top --legend-title "value per 100k"# 7-class Blues palette with natural breaks
chorokit us_states.geojson POPULATION --palette Blues:7 --scheme natural \
--title "US State Population" --source "Source: U.S. Census Bureau"
# 5-class Reds palette with quantile breaks
chorokit data.geojson value --palette Reds:5 --scheme quantilesfrom chorokit import plot_choropleth, LegendConfig
legend = LegendConfig(
kind="binned",
palette=("Reds", 5),
scheme="quantiles",
title="Population density",
)
fig, ax = plot_choropleth(gdf, value="density", legend=legend)import geopandas as gpd
from chorokit import plot_choropleth, LegendConfig, LayoutConfig
gdf = gpd.read_file("demographics.geojson")
legend = LegendConfig(
kind="binned",
title="Percent of population, by block",
breaks=[0, 5, 15, 30, 50, 90],
labels=["0", "5", "15", "30", "50", "90"],
)
layout = LayoutConfig(
title="Percent non-Hispanic Asian",
subtitle="Los Angeles County blocks, 2020",
source="Source: County of Los Angeles, Census 2020",
width=10,
)
fig, ax = plot_choropleth(gdf, value="pc_nh_asn", cmap="Reds", legend=legend, layout=layout)python examples/ag_census_maps.pyJoins tidy county CSVs to US boundaries (cached under examples/data/raw/ on first
run) and draws three CONUS maps that use state-boundary overlays, log + nice-round
breaks, compact/% legend labels, a left-aligned legend and a No-data swatch.
python examples/election_2024_margin.pyUses the county GeoJSON from the sibling presidential-elections repo (or a
cached copy under examples/data/raw/). Diverging RdBu_r scale with fixed
symmetric breaks around zero.
python examples/county_demographics.py
python examples/county_demographics.py --only gen_z_shareCaches Esri updated-demographics counties from
https://stilesdata.com/gis/usa_counties_demos_generations.geojson and draws
Gen Z share, median household income and population density.
- Layout: figure height comes from the map aspect plus fixed-size title, legend and source bands, so spacing is identical for wide, tall or square geographies
- Projection: auto-projects geographic data. Local/regional extents use a suitable UTM zone; large CONUS extents use EPSG:5070. You can pass an explicit CRS via int, EPSG string or
pyproj.CRS. - Legend: top or bottom placement (always horizontal); left or center align; binned or continuous; auto breaks via
schemeandk; optional log classification and nice-round edges; interval or boundary labels with compactk/M/%formatting; automatic No-data swatch - Overlays: pass
Overlaylayers (state lines, etc.) drawn on top of the fill - ColorBrewer palettes: access to ColorBrewer 2.0 sequential, diverging and qualitative color schemes with discrete class counts
- Theme: Barlow ships with the package for consistent typography; override via
LayoutConfig.theme - CLI: flags for projection, legend options and auto classification
pip install -e ".[dev]"
pytest # unit + image comparison tests
python tools/gallery.py # contact sheet across the case matrixRegenerate image baselines after intentional layout changes:
pytest tests/test_visual.py --mpl-generate-path=tests/baselineColorBrewer color specifications and designs were developed by Cynthia Brewer (https://colorbrewer2.org/). Please see the ColorBrewer Apache-Style license.
Copyright 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State University
MIT



