Choropleth Map

Pro

Value-per-region map. Equal-area projection by default, quantile / equal-interval / threshold class breaks, an explicit no-data fill and legend entry, a hatch for figures the host flags as estimates, and one tab stop with arrow-key navigation. Bring your own GeoJSON or TopoJSON — adapters and a simplifier are included. Pure SVG.

</>sv-choropleth-map chartmapchoroplethgeoprojection
import { SvChoroplethMapComponent } from '@styloviz-pro/charts';

Demo & usage

Basic usage

Pro

Usage example — open the gallery for the full interactive demo.

Pro

An interactive demo for this component lives in the gallery.

Open live demo

More examples

Equal-interval breaks against a fixed scale

Pro

Regulatory thresholds

Pro

Pre-projected geometry (Albers USA and friends)

Pro

Region interactions

Pro

TopoJSON in, simplified for a dashboard-sized map

Pro

GeoJSON with non-standard property keys

Pro

A figure that is an estimate

Pro

When to use

  • A single number per administrative region, where WHERE is the question.
  • Rates, densities and shares — anything already normalised by population or area.
  • For raw counts, prefer a bar chart or proportional symbols: a count map mostly reproduces the population map.
  • For a handful of regions, a ranked bar chart is read faster and more precisely.

Async states

  • loading
  • empty
  • error
  • retry

Inputs

NameTypeDefaultDescription
loadingbooleanfalseTrue while the geometry or the values are being fetched.
errorSvDataError | string | nullnullLoad failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`.
emptyMessagestring'No regions to map'Copy for the no-data state.
filteredbooleanfalseTrue when the emptiness is caused by a filter rather than an empty set.
featuresreadonly SvMapFeature[][]The shapes. Bring your own — see the class comment on why the library does not ship a world.
valuesreadonly SvChoroplethDatum[][]One row per region with a figure. Regions with no row are drawn as no-data.
projectionSvMapProjection'equal-earth'How lon/lat becomes a plane.
scaleSvChoroplethScale'quantile'How values are sorted into colours.
classesnumber5Number of colour bands, clamped to `2…9`. Ignored when `scale` is `continuous`.
thresholdsreadonly number[][]Explicit cut points, used only when `scale` is `threshold`.
rampSvChoroplethRamp'blue'Sequential colour ramp.
widthnumber640Chart width in pixels.
heightnumber360Chart height in pixels.
noDataFillstring'var(--color-gray-200)'Fill for regions with no figure.
noDataLabelstring'No data'Legend wording for regions with no figure.
uncertainLabelstring'Estimate'Legend wording, and the accessible suffix, for a figure the host flagged as uncertain. It is a word rather than only a hatch because a hatch is not a label. A reader who cannot see the texture — low contrast, a small map, a printed page — otherwise reads an estimate as a measurement, which is the whole thing the flag exists to prevent.
showLegendbooleantrueDraw the legend.
legendTitlestring''What the colours mean — the units, ideally. Worth filling in every time. "Cases" and "cases per 100 000" produce very different maps and the reader cannot tell them apart from the picture.
strokeColorstring'#ffffff'Border between regions.
titlestring''Chart title, rendered above the map.
ariaLabelstring''Accessible name override. Falls back to a generated summary.
customClassstring''Extra CSS classes applied to the root wrapper.

Outputs

EventPayload typeDescription
retryvoidThe host should re-run the failed load.
regionHoverSvChoroplethRegionEventEmitted when a region is hovered or keyboard-focused.
regionSelectSvChoroplethRegionEventEmitted when a region is clicked or activated with Enter/Space.

Wire it to your API

This component is headless about transport: it renders what you give it and reports what the user asks for. Nothing below requires @styloviz/data — drive it from whatever you already use.

Template

Pro

Bound to the names this component actually exposes.

Host class

Pro

Drive the three async inputs from your own fetch.

  • This component does not emit a query, so it renders what you give it. Fetch on your own trigger and bind `features`, `loading` and `error`.
  • Set `filtered` when a search or filter is active. It is the difference between "you have no orders" and "no orders match this filter" — telling a customer with 4,000 orders the first one reads as a bug.
  • Normalize failures with `toSvDataError()` from `@styloviz/core`. It derives `retryable` from the status, so a 404 does not get a "Try again" button that can only fail again.

Full walkthrough: Connecting to a backend.

Customization & theming

  • Four projections, including identity for pre-projected composites such as Albers USA.
  • Quantile, equal-interval, explicit-threshold and continuous scales.
  • Seven sequential ramps, plus a configurable no-data fill and label.
  • A hatch overlay for figures flagged `uncertain`, with its own legend entry and `uncertainLabel` wording.
  • `svGeoJsonFeatures`, `svTopoJsonFeatures` and `svSimplifyFeatures` convert real boundary files into the shape the component takes. None of them fetches anything — the host loads its own file and hands over the parsed object.

Best practices

  • Do: Map a rate, not a count, and say which in legendTitle.
  • Do: Leave the projection equal-area unless you must match a tile layer — Mercator inflates high latitudes by 1/cos²(latitude).
  • Do: Let regions with no figure stay in the no-data fill; never substitute zero.
  • Do: Flag a modelled, suppressed or wide-interval figure with `uncertain: true`. The region keeps its band colour and gains a hatch, so the number is still readable and is no longer presented with the same confidence as a counted one. It is a third state: "measured", "measured loosely", "not measured".
  • Do: Load boundaries with svTopoJsonFeatures or svGeoJsonFeatures rather than writing the conversion by hand. TopoJSON stores a shared border once and delta-encodes its coordinates, so a hand-rolled reader that misses the negative arc index or the delta pass still renders — as a bow tie, or as a spray of triangles.
  • Do: Run svSimplifyFeatures over anything detailed. Natural Earth at 1:10m is megabytes and a dashboard map is a few hundred pixels wide, so most of those vertices are smaller than a pixel. Start at a tolerance of 0.05 degrees and halve it until the outline stops changing at the size you render.
  • Don't: Do not use equal-interval breaks on skewed data — nearly every region lands in the palest band.
  • Tip: Split rings at ±180° before passing them in (RFC 7946 §3.1.9); the projection is point-wise and will not cut geometry for you.

Accessibility

  • The SVG is a `role="group"` with a generated summary naming the region count, the value range and the arrow-key model (override via `ariaLabel`).
  • Colour is the value here, and a sequential ramp is the least precise encoding in the kit — two adjacent bands are one shade apart. `sv-data-summary-table` publishes one row per region with its figure (the column takes its name from `legendTitle`) and its band where the scale is classed, and writes "No data" in words rather than leaving a blank cell that reads as zero.
  • One roving tab stop for the whole map rather than one per region: Tab enters and leaves, arrow keys and Home/End move between regions in reading order, and Enter/Space emits `regionSelect`. Every region carries an `aria-label` with its name and value, and says "No data" in words where there is none.
  • An estimate says so in words, not only in the hatch. A texture is invisible to a screen reader and easy to miss on a small or printed map, so `uncertainLabel` travels with the figure into the region's accessible name, the tooltip and the table row.

Feedback on Choropleth Map

Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.

Loads giscus.app and GitHub — nothing is fetched until you click.