Skip to content

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.

← All case studies

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.

Region
Shows

Western Europe : the built-in reference demo, 50m borders, the Alps in full relief texture.
The Strait of Hormuz : the Gulf, the Zagros rising straight out of it, and thirty-nine kilometres of water.
The Caribbean : two oceans joined by seventy-seven kilometres of isthmus, and the sea the Antilles close off.
Himalaya : an all-land plate with no sea to contrast against, so relief carries the whole plate.
The Andes : one mountain wall, and the three climates it makes: Amazon, Altiplano, Atacama.
The Sahel : where the Sahara ends — and the river that runs the wrong way into it.
The South China Sea : the basin, the shelf and the reefs — and deliberately no claim line.
Iberia : Spain and Portugal, the clearest single example of the texture-shading relief technique.
Switzerland, zoomed : a small enough bbox switches to the 10m border tier automatically.
Ukraine : government, occupied and contested along an approximate 2024 contact line. The Kakhovka Reservoir is drawn as former: it drained after the dam breach of 6 June 2023.
Sudan : the war’s east–west partition in five classes, with the fighting in Kordofan. One assessed and one reported axis of advance, drawn differently.
Eastern DRC : who holds the Kivus. Goma and Bukavu face each other across Lake Kivu, which is why the lake has to be on the map at all.
Syria : a four-way split, an illustrative snapshot from around 2018.
Libya : loyalist, rebel and the contested oil crescent, around spring 2011.

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.

Sequential ramp : pale-to-navy blue, magnitude reads by lightness alone.
Diverging ramp : auto-detected from the data's sign, terracotta ↔ navy through a real neutral zero.
At scale : the relief layer (Andes, Rockies, Alps, Himalaya) reads clearly once rendered larger.

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.

Bivariate choropleth : Two variables blended on a 3×3 color key.
Proportional-symbol map : Area-true √-scaled bubbles on a world basemap.
Connection / flow map : Great-circle arcs weighted by volume.
Pie-glyph map : A small pie chart placed at each region.
Bar-glyph map : Mini bar charts placed at each region.
Tile-grid cartogram : One equal tile per country, geographic layout.
Diverging + annotated : Signed change, no-data gray, key countries labeled.
Small multiples : One panel per year, class breaks frozen.

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_prec 2 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.

Real data: the last five French presidential runoffs
Year Winner % Runner-up %
2002Jacques Chirac82.21Jean-Marie Le Pen17.79
2007Nicolas Sarkozy53.06Ségolène Royal46.94
2012François Hollande51.64Nicolas Sarkozy48.36
2017Emmanuel Macron66.10Marine Le Pen33.90
2022Emmanuel Macron58.55Marine Le Pen41.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.

Dot-density map : One dot per N people inside real regions.
Hexbin map : Hexagonal binning of point density over a map.
Spike map : Vertical spikes at locations, height by value.
Hexbin map : Earthquakes binned into hexagons by count, tracing the Ring of Fire.
Binned grid map : Square-bin aggregation over geography, cells coloured by count.

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.