How the map is built
Four decisions do most of the work here, and each of them is the difference between a map that is merely pretty and one you can quote from. None of them are subtle; they are just easy to skip.
1. The geometry is a topology, not a pile of polygons
Three thousand counties share every interior border twice. Stored as independent polygons, that border is stored twice, drawn twice — so it renders heavier and blurrier than a coastline — and, fatally, simplified twice, to two different lines. The map fills with white slivers and overlaps. Almost every amateur county map has them.
So the build cuts every ring into arcs at the points where three or more counties meet, stores each arc once, and lets counties reference arcs. Simplification then happens on the arc, and both neighbours move together: the map stays watertight at any level of detail. Interior borders can also be drawn exactly once, as hairlines, which is most of what makes a county map look like cartography rather than a screenshot.
It also produces something for free that is hard to get any other way: because the topology knows which counties share an arc, it knows which counties touch which — exactly, not by a distance heuristic. That contiguity graph is what makes the superstates possible. The mean U.S. county has 5.55 neighbours, which is about what a planar graph should give.
One consequence worth knowing: coarse detail levels are made by simplifying the full-resolution topology and then moving its arcs onto a coarser grid, in that order. Quantising first merges distinct vertices, which invents junctions, which shatters long shared borders into hundreds of tiny arcs — and the coarse layer then costs more than the fine one.
2. The projection is equal-area, everywhere
A choropleth asks the eye to add up coloured area. A projection that inflates area with latitude — Mercator, and every default web map — therefore makes northern counties shout and southern ones whisper before the data has said anything. Every projection in this atlas is an Albers equal-area conic with parallels chosen for the region on screen. Mercator is available and labelled as not equal-area, for tile alignment and nothing else.
The United States view is the composite Albers: the lower 48 at full size, with Alaska at 0.35× and Hawai‘i, Puerto Rico and the U.S. Virgin Islands moved into empty sea. The insets are a distortion, and the honest version of an inset is a labelled one with its scale stated. Alaska at true relative size would take a third of the frame; Puerto Rico dropped entirely would silently remove 3.2 million people from a national map.
3. Only stocks are stored. Every rate is recomputed.
This is the one that matters most, and the one most often got wrong.
A stock — personal income, population, jobs, births — adds up. A rate does not. The per-capita income of a state is not the mean of its counties' per-capita incomes; it is the state's income divided by the state's population. Averaging counties gives every county one vote, so Loving County, Texas (population 48) counts as much as Los Angeles (9.8 million).
So the data files hold only stocks, and every rate on this map is derived at the level currently
on screen, from that level's summed numerator and summed denominator. County, state, superstate
and nation all compute the same way from the same definition, which is why switching levels is
instant and why the numbers agree with the agencies'. The whole rule is about forty lines in
packages/geohier/hier.js.
The honest exception: a measure a publisher gives you as a rate, with no numerator and denominator to rebuild it from — Statistics Canada's medians, for instance. Those are marked and the map refuses to roll them up rather than showing a number somebody might quote. A median of medians is not a median.
4. The superstates are grown, not drawn
Ordinary clustering will put a county in Maine and a county in Oregon in the same group because their transfer shares match. That is a classification, not a map. A region has to be one connected piece of ground, and the constraint changes the answer everywhere.
The method is SKATER (Assunção, Neves, Câmara & Freitas, 2006), and it is short:
- Take the contiguity graph — counties as nodes, shared borders as edges.
- Weight each edge by how unlike its two counties are on the chosen axes, after robust standardisation so an axis measured in dollars does not outvote one measured as a share.
- Take the minimum spanning tree. It is the cheapest set of connections that keeps the country in one piece, so it already knows where the country is glued weakly.
- Cut the tree k−1 times, each time removing the edge whose removal buys the largest drop in population-weighted within-region variance.
Cutting a tree is what makes the last step tractable: every candidate cut splits it into exactly two connected pieces, so contiguity is guaranteed and the score is a subtree sum. The whole thing runs in about 300 ms in the browser, which is why the axes are a set of checkboxes rather than a batch job.
What else it honours, and what it could not
Variance is not the whole of what makes a superstate plausible, so three more things are priced into the same score — and a fourth could not be, which is worth saying as plainly as the three that worked.
Existing state lines are an energy barrier. Redrawing a state is expensive in a way no econometric axis can see. It is priced twice: a surcharge on tree edges that cross a state line, so the spanning tree runs inside states and enters a neighbour only where it must, and a flat barrier subtracted from any cut that falls inside a state. Either alone does nothing — the surcharge without the barrier still lets the cut fall anywhere, and the barrier without the surcharge has no whole states in the tree to cut around. Together they take the number of split states from 32 of 53 to 13, and the share of people who stay with their state from 80% to 92%.
Populations are pushed toward equal. Not by halving each cut — a greedy cut cannot know how many regions each side will hold — but by asking that the two sides look like some whole split of the shares the component is worth. Largest to smallest falls from 1.9:1 to 1.7:1.
Water is a floor rather than a preference, and that is the result of trying it the other way: as a soft penalty, asking for even per-capita water made it worse, 24.8:1 to 26.9:1. A greedy cut penalises the two parts in front of it, and a part that is proportional today splits into disproportionate regions three cuts later. It is off by default because it trades steeply against population balance, and because USGS publishes withdrawal, not supply — a county fed by a canal reads wet.
Sea access could not be delivered at all. A floor of a fair share of coastline: infeasible. An absolute 50 km a region: also infeasible. A penalty for stranding a coastless part: the count of landlocked regions did not move off two at any weight. Discounting tree edges that run toward the water, so coast-reaching chains exist to be cut: two became one, and cost the population ratio 3.0 to 4.0. The reason is structural — a region can only be what some subtree is, the tree is built from econometric similarity, and interior counties resemble each other, so no subtree runs from Nebraska to the Gulf. Eleven of the thirteen regions reach the ocean without being asked; the other two are the interior plains and the mountain west, which is where the country's landlocked people live. The panel therefore reports coastline and marks the inland regions, and offers no dial for it.
One more ordering decision, because getting it wrong was visible: when these collide, the resource floors give way first, then the state preference, and the population floor last. Asking hard for whole states makes the population floor unreachable, and when the floor relaxed first the map came back with regions three times the size of each other — a worse map than one with a split state.
Two places it needs help, and gets it openly
The size floor. Greedy variance reduction badly wants to shave off a single extreme unit: cutting Harris County, Texas loose removes more within-region variance than any balanced cut, so an unconstrained run returns one region of 2,600 counties and twelve regions of one county each. That is the true optimum of the stated objective and useless as a map. So a cut only counts if both sides keep a minimum share of the population — the slider says which share — and if k regions cannot be reached at that floor, the floor relaxes and the run repeats, and the map says what floor it ended up using.
The islands. Contiguity has no opinion about Hawai‘i. The county graph is not connected: Alaska, Hawai‘i, Puerto Rico, Nantucket, the San Juans and about twenty other places are their own components, and a spanning tree needs a connected graph. So each stranded piece is joined to the nearest county on the mainland by great-circle distance. Those bridges are counted and listed in the superstate panel, because they are the cartographer putting a bridge where there is water.
The names are a separate layer, and they are not computed
No clustering of transfer shares and migration rates produces the word "Appalachia". The names come from a fixed list, each pinned to a point on the ground, assigned to whichever region contains that point — so change the axes and the borders move while the names follow the geography. Every one of them is editable in the panel. The vocabulary borrows from the long tradition of dividing North America by culture rather than statute — Joel Garreau's The Nine Nations of North America (1981) and Colin Woodard's American Nations (2011) are the obvious ancestors — plus the regional names people already use. None of the boundaries here are theirs; only some of the words are.
Colour
Sequential scales are one hue, light to dark. Diverging scales are two hues around a neutral grey midpoint, never a rainbow, and the two arms carry the same lightness ladder so neither side shouts. Interpolation happens in OKLab rather than sRGB, so equal steps in the ramp look equal — in sRGB a blue ramp goes muddy in the middle and the map grows a band that is not in the data.
The classification method is a control rather than a default, and the legend always names the one in use. Quantile makes every class equally populous and therefore always looks like a story; equal-interval is honest about outliers and therefore often looks empty; natural breaks minimise variance inside each class and are usually the right answer for a county map. A choropleth that hides its breaks is a rhetorical device.
Why it is fast
Three thousand polygons is enough that the naive loop drops to five frames a second, and this map did, until it was measured. The fixes were not clever:
- Stop rebuilding what has not changed. The border geometry is 200,000 points in fixed screen space; panning is a canvas transform and moves none of them. Re-issuing those path commands every frame cost 183 ms. Cached, the same borders stroke in 1.6 ms.
- Use the right algorithm. Natural breaks ran the textbook O(k·n²) dynamic program on every recolour, at 121 ms. The same recurrence under the divide-and-conquer optimisation is O(k·n log n) and gives the identical answer in about 6 ms — checked against a brute-force solver, because that is a claim about the mathematics rather than about the code.
- Draw less. Two resolutions of every layer: the coarse one while the view is moving or zoomed out, the fine one when it settles. At continent scale the coarse tier is twenty-one points per county, which is more than a county gets on screen. Plus viewport culling, which does nothing when the whole country is visible and rejects most of it when you are inside a state.
Two plausible-sounding fixes were tried and thrown away because the measurement said no: merging counties into one path per colour class was slower, and the cost is not proportional to pixels either — a sixteenth of the canvas area only saved a third of the time.
A second pass added a triangulator and a WebGL renderer: every county is cut into triangles once, in a worker, and the whole map becomes a single GPU draw call with the colours in a texture. Borders and labels stay on a 2D canvas over the top, where real line joins live. Profiling that pass mostly produced a correction, though — after the first round, JavaScript was already down to about 1.3 ms a frame, and a CPU profile of three level switches put 60 ms in all of the JavaScript and 750 ms in the browser's own rasterisation. The biggest win available was duller than the GPU: the map was re-projecting and re-building three thousand paths on every click of County / State / Superstate, none of which depends on which of those you picked. Cached against a signature of the projection, that work happens once.
The honest part: the GPU path's correctness is verified — rendered against the Canvas2D
path pixel for pixel, the two agree everywhere except antialiasing along the county borders — but
its speed is not, because the machine this was built on has no GPU. It refuses to run on a
software renderer, where it would be slower, and the Canvas2D path remains complete underneath.
Add ?gl=0 or ?gl=1 to the URL to force one or the other and compare.
Rebuilding it
From a clean checkout, with no keys and no accounts:
node atlas/etl/build-geo.mjs # boundaries -> topologies, adjacency, places
node atlas/etl/build-data.mjs # agencies -> the county series
The raw archives are cached outside the repository; only the derived artefacts are committed.
The readers for ZIP, shapefiles, dBASE tables, CSV and the two map projections that Statistics
Canada and INEGI publish in are all in atlas/etl/lib/, dependency-free, because the
claim that the geometry comes from the primary source is worth about two hundred lines.