One hex per state

A hex tile map is a cartogram, not geography: every region becomes one equal hexagon at a hand-authored position, so Rhode Island is as legible as Texas and land area stops deciding how loud a value looks. Built in a chart core it is a grid of tiles, which leaves the reader supplying 51 hex coordinates alongside every value. Here it is a layout: geometry keyed exactly the way the boundary pack is keyed, so one dataset and one joinBy serve both, and the scale, legend, tooltip, labels, selection and keyboard navigation are the machinery that already exists.

geo: { map: 'us', layout: 'hex' }
geo: { map: 'us' } · the same series, same scale
layout: 'hex'

Press it and watch the states, not the map. Toggling the layout morphs each region between the two representations rather than swapping them, which is the answer to the one real problem with a cartogram: a reader who cannot navigate a honeycomb can navigate the map it grew out of, and watching Texas walk to its cell is what tells them which cell is Texas. Each outline is resampled at equal arc length, rotated to its closest match and walked across; Alaska travels furthest, from the corner it is inset into to the corner the grid parks it in. Labels step out for the flight, because they are placed against the geometry that has already arrived.

Illustrative data. A smooth function of longitude and latitude, not a real indicator. The layout is hand-authored and verified against the boundary pack it claims to represent: npm run check:layout scores order against state centroids and adjacency against shared TopoJSON arcs.

Three things you did not have to configure

A layout is a diagram, and it knows that about itself. Every one of these was set by hand while prototyping, which is the evidence that none of them belongs to the caller:

Each is a default, not a rule: pass geo.projection, dataLabels.field or interaction.zoom.enabled and yours wins.

Same 51 regions, two representations
What the pack says about itself

Seven region sets have one

A layout has no canonical answer, so each of these is a judgement about which real adjacencies matter most, scored against the boundary pack it claims to represent. Each one has a shape that fights the grid, and the interesting part is which compromise it forces:

map: 'au', layout: 'hex' · 8 states
map: 'ca', layout: 'hex' · 13 provinces
map: 'de', layout: 'hex' · 16 Laender
map: 'br', layout: 'hex' · 27 states
map: 'jp', layout: 'hex' · 47 prefectures
map: 'eu', layout: 'hex' · 37 NUTS0

Europe's set is not the EU. NUTS is a statistical classification, so level 0 also covers the United Kingdom, the four EFTA countries and the five candidate countries. Boundaries © EuroGeographics; the cell positions are ours.

Author your own

registerLayout takes a table of key → [col, row], row 0 north and col 0 west, keyed by whatever field your boundary pack joins on. There is no canonical layout for any country, so curating the table is the work: it is a judgement about which real adjacencies matter most, and the numbers below are how you tell whether the judgement was good.

A four-cell layout, registered in this page
ApexMaps.registerLayout('demo/quad@hex', {
  keyField: 'code',
  cells: { NW: [0, 0], NE: [1, 0], SW: [0, 1], SE: [1, 1] },
  names: { NW: 'North west', NE: 'North east',
           SW: 'South west', SE: 'South east' },
})

// geo: { map: 'demo/quad@hex' }

The generator rejects a malformed pack rather than drawing it: two regions on one cell, a cell that is not [col, row], or an offset convention that contradicts the orientation. All three render something plausible and wrong, which is worse than a throw.