Choropleth
ShippedGeoMap · form="choropleth"
One fill per polygon, coloured by a classified value. The default form, and the one every other form on this site is an argument about.
Choropleth
ShippedGeoMap · form="choropleth"
One fill per polygon, coloured by a classified value. The default form, and the one every other form on this site is an argument about.
- Shows
- Where a rate is high and low, at whatever resolution the boundaries were drawn at.
- Hides
- How many people each colour stands for. A polygon's area is its visual weight, and area is not population, which is what `/elections` and `/cartogram` are both about.
Minimal usage
<GeoMap
tiles={{ url, sourceLayer: "tract", joinKey: "geoid" }}
bounds={bounds}
dark={dark}
form="choropleth"
colors={colors}
noDataColor={noDataFor(dark)}
label="Median household income by census tract, New York, choropleth"
/>Input contract
| Prop | Type | Note |
|---|---|---|
| tiles · required | GeoMapTiles | PMTiles url, source layer, join key. Construction-scope: a change refits and rebuilds layers. |
| bounds · required | [w, s, e, n] | The subject extent. Fitted once, and again only when this or the tile identity changes, never on resize. |
| colors | Record<string, string> | geoid → fill. A geoid absent here takes `noDataColor`, which is how absence stays distinct from a low value. |
| noDataColor · required | string | Required, not defaulted: a map with no declared absence colour would paint absence as whatever the ramp's zero happens to be. |
| highlight | readonly string[] | Author's marks, usually the extremes. State in the caption how they were chosen. |
| selected | readonly string[] | The reader's selection, controlled. Distinct from `highlight` and from `hovered`. |
| camera | GeoMapCamera | null | Supplying this suppresses the refit and takes down `constrainToSubject`'s walls; a map that stops following gets them back without a refit. |
| label | string | The map's accessible name. Names the subject, not the canvas. |
Compatible with
Every dataset: the three cities, Europe, US counties, the world, and presidential returns with their history. Any measure the dataset declares; intensive measures are the honest case, and `areaBiasCaveat` says so for extensive ones.
Data it requires
A polygon tileset with a stable join key, and a flat geoid → value map. Geometry and values stay separate; the paint is a `match` expression on the join property, not a feature-state join.
How it behaves when things are missing
| Loading | `useScopedData` holds the previous scope's values as `stale` so the map dims rather than blanking. See `/europe` mid-level-change. |
|---|---|
| Failed request | An explicit failed-request panel with retry, never a no-data map. `/europe` and `/cartogram` both show it. |
| No features | A tileset with no features renders the basemap and the legend reports zero classified features. |
| Missing values | Four cases kept apart: zero population, sparse, suppressed, unmatched. `/suppression` is the page about it. |
| Light and dark | `basemapStyle(dark)` is rebuilt and `setStyle` swapped; the ramp is resolved per theme by `lib/scale.ts`. |
| Narrow screen | Fills its container; the explorer stacks its controls. Checked at 320px. |
Accessibility
`role="region"` with a real `aria-label` naming measure, geography, place and form. Tooltips are click-to-pin with Escape and focus return (`FeatureInspector`), so the facts are reachable without hover. The canvas is not a tab stop and does not trap navigation.
Known limits
| Exact Fisher–Jenks | 5000 | Above this many valid values the classifier runs on a deterministic sample; the work is quadratic in the sample. JENKS_EXACT_LIMIT |
|---|---|---|
| Paint expression | one `match` per repaint | 3,144 counties is a 6,288-element expression. Measured fine; nationwide tracts are not a claim this repo has tested. |