sprezzature · case studies
Maps
Publication-grade plates for any region on Earth: real elevation, real borders, and a configuration file you can argue with.
Work in progress: the generators are versioned and every map below is rebuilt from them in one command, but the cartographic judgement is still being revised plate by plate — label placement, relief strength and the confidence scale have all changed this month. Read these as illustrations of the method, not as operational assessments.
The plates
A layered reference plate for any region: coastline, international frontiers, country labels, a bathymetry contour halo, dual-unit scale bar and north arrow, on an auto-centred Lambert Conformal Conic projection (conformal, so shapes and angles stay true at city/province scale). Borders switch automatically between Natural Earth's 50m and 10m tiers depending on how zoomed in the region is. Put thematic layers on top of that plate and it becomes a geopolitics-desk product: classed pastel fills with white boundary casing, letter-spaced place labels, flashpoint markers, tapered axes of advance, and a floating legend.
Every areas-of-control plate below is schematic, says so on its own face, and keeps its claim lines in readable Python rather than an opaque data file: a reader who disagrees can see exactly which coordinates to argue with. None of it is intelligence. The reference and chokepoint plates assert nothing beyond Natural Earth's own geography.
No plate matches both filters yet. Clearing one of them brings the rest back.
Every plate here is generated by sprezzature-maps, from the builders in scripts/build_situation_examples.py. The configuration behind each one is the kit on its card.
Choropleth
One value per country on a hand-written, closed-form Equal Earth projection: it restores the equal-area property a plain equirectangular map quietly loses (Greenland does not read fourteen times its real size here). Color comes from an OKLCH-interpolated ramp, not a naive per-channel RGB lerp, verified colour-vision-deficiency-safe by actually running the simulation rather than asserting it. A diverging ramp switches on automatically when the data has a meaningful zero. By default the ramp runs unbroken from the lowest value to the highest; --classes cuts it into 2 to 12 classes, with breaks placed by --method (quantile, equal, Jenks or head/tail), and the legend names the method it used. A warm/cool duotone relief layer, real GMTED2010 elevation rather than a flat hillshade, sits under the fills.
From sprezzature-maps, the cartography package split out of sprezzature-figures.
Thematic maps
Further glyph and cartogram techniques from sprezzature-figures, layered over the same offline Natural Earth basemap.
For conflict research: ACLED and UCDP, at the precision they record
The two datasets peace-and-conflict research runs on — ACLED and the UCDP Georeferenced Event Dataset — each record, beside every coordinate, how well that coordinate is known. ACLED codes geo_precision 1–3; UCDP codes where_prec 1–7. Those are not file metadata: ACLED’s 3 is a provincial capital standing in for a whole province, and UCDP’s 6 means only the country is known and the coordinates are a representative point for an entire state.
Maps of this data draw all of them as the same dot. That asserts a street corner the source never gave, once per row, several hundred times a figure. Here the code reaches the mark:
- A named location
- A solid disc with a white casing. The mark means what it has always meant: we know the place.
- A place standing in for an area
- A hollow ring. The old cartographic signal for “about here”, and unlike a disc of invented radius it claims nothing it cannot support. Where the dataset states a radius — UCDP’s
where_prec2 says about 25 km — it is drawn, to the plate’s own scale. - A region pinned to its capital
- A hollow broken ring: one step further from “a place”. The date is spoken the same way, from
time_precision— “week of”, “month of”, “in 2023”.
Rows that cannot be placed honestly are not placed. A UCDP where_prec 6 row is refused a dot by default, and the count goes onto the plate’s own method line rather than disappearing — a silent drop being the other half of the same dishonesty.
The step between the file and the mark is one call. acled_events("sudan-2024.csv", derive_region=True) returns a config fragment whose every key the generator already read, so cfg.update(...) is the whole integration and a hand-written config keeps everything it says. ucdp_events does the same for GED.
Column names, code ranges and the wording of each code are taken from the ACLED codebook and the UCDP GED codebook, read rather than recalled. What this package deliberately leaves to CShapes, PRIO-GRID, geoBoundaries and GeoPandas is listed in LANDSCAPE.md.
How the choropleth and situation maps are built
Every map above is a hand-authored SVG document: no chart-grammar library, no headless browser, no external tiles fetched at render time. Everything geographic is a vendored, offline Natural Earth asset.
- Equal Earth, not equirectangular
- A closed-form polynomial projection (Šavrič, Patterson & Jenny, 2018), equal-area, so a choropleth's colored surface isn't quietly lying about country size.
- OKLCH color, not RGB lerp
- Ramps interpolate in a perceptually-uniform space (Björn Ottosson's OKLab), verified colour-vision-deficiency-safe by actually simulating protanopia/deuteranopia/tritanopia on the rendered output.
- Natural Earth, 50m and 10m
- Public-domain vector borders, vendored offline. Situation maps pick the tier automatically from how zoomed-in the requested region is.
- Real elevation, not a flat hillshade
- Situation maps compute their relief from real GMTED2010 elevation: a directional hillshade blended with texture shading (Brown, 2010: a fractional-order Laplacian applied in the frequency domain), which draws out ridgelines and drainage networks at every scale at once, then retinted warm-highlight/cool-shadow instead of flat grey. Reprojected pixel-by-pixel into each map's own projection (an exact analytic inverse for Lambert Conformal Conic; Newton-Raphson for the world choropleth's Equal Earth, which has no closed-form inverse).
- Millimetres, not pixels
- Nature wants 90 mm for a single column, Science 57. Ask for one and the file lands at that size in a layout instead of at whatever a consumer infers — and the plate measures its own smallest label against that width and tells you what it will really be. Dropped into a 90 mm column, a label can set at 1.6 pt and render without complaint, because a vector file always does.
- PDF and EPS come out drawn, not photographed
- A raster inside a PDF wrapper satisfies the extension and nothing a journal wants, and nobody can tell until they zoom in. Vector export goes through
rsvg-convert— an optional system package. Without it the page is still written at the right physical size, with a warning saying it is a raster and what to install.
Real data: the last five French presidential runoffs
The same carefully authored SVG map engine, on real data from the French Ministère de l'Intérieur: the winner's second-round vote share by département across the last five presidential elections, on one frozen blue scale so the panels compare. 2002 (the republican sprezzature against the far right) is near-uniform; 2012 (the closest) is the palest; 2017 and 2022 show the lighter départements where Marine Le Pen ran strongest.
| Year | Winner | % | Runner-up | % |
|---|---|---|---|---|
| 2002 | Jacques Chirac | 82.21 | Jean-Marie Le Pen | 17.79 |
| 2007 | Nicolas Sarkozy | 53.06 | Ségolène Royal | 46.94 |
| 2012 | François Hollande | 51.64 | Nicolas Sarkozy | 48.36 |
| 2017 | Emmanuel Macron | 66.10 | Marine Le Pen | 33.90 |
| 2022 | Emmanuel Macron | 58.55 | Marine Le Pen | 41.45 |
Source: Ministère de l'Intérieur (2nd-round results by département).
Map techniques (carefully authored SVG)
When a projection or a glyph falls outside a chart grammar, the same loop drives a carefully drawn Scalable Vector Graphics (SVG) map instead.
Frequently asked questions
How do I install sprezzature-maps?
Run pip install sprezzature-maps (Python 3.10 to 3.13): one command installs the three generators, the make-map command line, the HTTP API, the MCP server and the maps Studio, and pulls in sprezzature-figures, whose font and rendering code it shares. It is free and open source under the BSD 3-Clause licence.
What kinds of maps can sprezzature-maps draw?
sprezzature-maps draws three kinds of plate as SVG: a world choropleth on an Equal Earth projection, a situation_map that centres itself on any region with a Lambert conformal conic projection and layers zones, events and a two-unit scale bar, and a density map of where point events fell. make-map situation-new --country Moldova writes a starter configuration for a new regional plate.
Where does sprezzature-maps get its map data?
sprezzature-maps ships its geography inside the package: Natural Earth borders, coastlines, lakes and rivers, GMTED2010 elevation for the relief, and administrative units for the United States, France, Germany, Italy and Switzerland. Your own numbers come as CSV, TSV or JSON rows (a world choropleth keys countries on ISO 3166-1 numeric codes), and acled_events and ucdp_events turn an ACLED or UCDP GED export into a situation-map layer.
How does the sprezzature-maps choropleth classify values?
By default the sprezzature-maps choropleth stretches one colour ramp from the lowest value to the highest; --classes (2 to 12) cuts it into classes, and --method places the breaks by quantile, equal, jenks or headtail. The legend prints the breaks and names the method used, and a diverging ramp switches on by itself when the values change sign.
Does sprezzature-maps work offline?
Yes: sprezzature-maps needs no tile server, no API key and no account, because the base maps and the elevation are bundled, and the finished SVG carries its own fonts. The only steps that use a model are make-map improve and the Studio's repair loop, which call a vision model served by Ollama on your machine by default.
What does sprezzature-maps need besides pip?
sprezzature-maps needs nothing more to render SVG, PNG or JPEG. A vector PDF or EPS uses the system tool rsvg-convert (librsvg); without it, the package writes a raster page and says so. make-map improve needs a local vision model, reached through best-engine-ai-helper and Ollama.
Can I use sprezzature-maps without Claude Code, for example in CI?
Yes: sprezzature-maps is also a Python library, a command line (make-map or sprezzature-maps), an HTTP API with a Docker image, and the MCP server sprezzature-maps-mcp; the Claude Code skill is one of five ways in. In CI, make-map audit reads finished plates against eleven rules (a named classification, printed breaks, a stated date, a title) and exits 1 on any finding, with --format json for a program.
How is sprezzature-maps different from Folium, kepler.gl or QGIS?
Folium and kepler.gl make maps a reader pans and zooms over tiles or a GPU layer, and QGIS is a desktop GIS for analysing geography; sprezzature-maps draws one static, publication-ready plate as a self-contained SVG, and an audit that reads it back. If the geography still needs joining or buffering, or the map has to move, those tools are the better choice, as LANDSCAPE.md in the repository says.