Skip to content

Remove the app's handling of x and y on UK area results - #1436

Draft
anth-volk wants to merge 19 commits into
mainfrom
chore/remove-uk-area-xy
Draft

anth-volk wants to merge 19 commits into
mainfrom
chore/remove-uk-area-xy

Conversation

@anth-volk

@anth-volk anth-volk commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #1435

Targets main. Builds on #1448, now merged into main (8f617005). #1448 places v2's UK hex maps from the app's layout files as v3's are, and draws the areas disclosure control withheld as "Not reported". #1448 replaces #1433, which this PR was first built on, and holds #1433's commits unchanged.

Merge order: met. This branch contained #1448's commits up to 813a7414, so #1448 had to merge first, and it has. main has since been merged in (62c1fe72), so this PR's diff against main now shows only its own changes: 40 files.

Hold until after the Budget on 28 October.

What changed

Nothing in the app reads x or y from UK area results now. The hex maps place each area only by its ONS code, from app/src/data/ukHexLayouts/, and HexagonalMap draws only the tiled grid.

An area missing from the layout

This covers an area with no code, and an area whose code the layout lacks, such as a district abolished in April 2023, for example Allerdale (E07000026).

Results from the list shape and from local-area breakdowns carry codes, and the layouts hold all 650 constituencies and 361 local authorities, so for these results nothing changes.

Older saved results in the keyed by_constituency shape have no codes, so their hex map now draws no hexagons. Their geographic map still matches areas to boundaries by name. The Public API path that built that shape, compare_economic_outputs, has no callers left in the Public API.

Areas disclosure control withheld

#1448 draws the areas a breakdown withheld as hatched "Not reported" hexagons, but only on the tiled grid, since there a result's areas can still fall back to x and y. Here every map tiles, so that check is gone: the v2 charts and v3 draw a disclosure-controlled level's withheld areas whenever withheldUKHexAreas lists them.

withheldUKHexAreas still lists none when any of the result's areas has no code, or a code outside the region's layout. A layout area such a result lacks might be one of its unmatched areas under another code, such as the districts a 2023 unitary authority replaced, so calling it withheld could be wrong. A map that leaves out areas the layout can't place therefore draws no withheld areas either.

Stored results that still hold x and y

They load as before:

  • the normalizer no longer copies x and y into normalized areas;
  • the v2 adapters no longer copy them into map points;
  • a keyed result is returned unchanged, so its x and y stay in the object, but nothing reads them.

Fixtures for the Public API's rows and for older saved results keep x and y, and tests show they are ignored.

HexagonalMap's untiled layout is removed

After the first commit, the only HexagonalMap callers are v3's UKLocalAreaMaps and v2's four constituency and local-authority charts. All of them passed tiled: true. Only the Storybook story drew the untiled grid, and no non-UK code uses the component. These options went with that layout:

  • tiled: the tiled grid is now the only layout.
  • hexSize: it sized untiled hexagons. On the tiled grid it only set the margin, which is now a fixed 16 units, its old default. v2's local-authority charts passed 11, so their margin narrows from 22 to 16 units.
  • coordinateScale: no caller set it. On the tiled grid it had no effect, because positions and hexagon size scale together.

The story now draws every constituency in the layout instead of a made-up offset grid.

Commits

The work was done red-green: each commit's tests were written first and seen failing before the code.

  1. Leave areas the layout cannot place off the UK hex maps. Failing first:

    • placeOnUKHexLayout's tests, because it still returned { points, tiled } and kept unplaced points;
    • each v2 chart's and v3's test that a result without codes draws no hexagons;
    • v3's test that one uncoded area out of three leaves two hexagons on the tiled grid.
  2. Merge of fix/v2-uk-hex-layouts: the frame fix and README corrections from Draw v2's constituency and local-authority hex maps from the app's layout files #1433's review. One test conflict is resolved.

  3. Find the stored constituency in UKLocalAreaSection's tests by its ranking. Three tests found a saved result's constituency through its hexagon. That constituency has no code, so it lost its hexagon in commit 1. The tests now find its ranking row instead.

  4. Stop carrying x and y on UK area results. This removes them from:

    • UKAreaImpact, and the normalizer's row type and copy;
    • the v2 adapters, which now return UKAreaChoroplethDataPoint;
    • v3's points, and placeOnUKHexLayout's input;
    • the app-typed fixtures.

    Failing first: the normalizer, breakdown and adapter tests, whose areas and points still had x and y.

  5. Draw HexagonalMap's tiled grid only. Failing first, without tiled:

    • rows sat a full column apart, where √3/2 was expected;
    • neighbouring hexagons were 0.2 of a column apart, not touching;
    • a three-area map's top hexagon reached y = −45.3, outside its frame.
  6. Type the UK area estimate fields against the maps' area point. These are fixes from review round 1, with no change in behaviour:

    • areaEstimateFields is now typed against UKAreaChoroplethDataPoint, whose comment says both maps read it;
    • a test label moves to its fixtures;
    • a fixture's comment is updated.
  7. Merge of fix/v2-uk-hex-layouts: Draw v2's constituency and local-authority hex maps from the app's layout files #1433's all-or-nothing placement, described below.

  8. Merge of fix/uk-hex-maps-not-reported (813a7414): rebuilds this PR on Show withheld UK areas as not reported on the hex maps, placing v2's hex maps from the layout files #1448, described below.

  9. Say in the layouts README when no withheld areas are drawn. Documentation only: Show withheld UK areas as not reported on the hex maps, placing v2's hex maps from the layout files #1448's README sentence about the untiled fallback, corrected for this PR.

  10. Correct UKLocalAreaSection's comment on where the hex layout comes from. Documentation only, from the review after the rebuild: the comment said the hex layout comes with the results.

  11. Merge of main (62c1fe72): brings in Show withheld UK areas as not reported on the hex maps, placing v2's hex maps from the layout files #1448's eight commits after 08f9cb07, described below.

Commits a5278ddc and 3c14a10c cancel each other out, so they are not listed.

Review

  • Round 1 (an independent reviewer): no bugs. It found typing and documentation drift around areaEstimateFields and UKAreaChoroplethDataPoint, fixed in commit 6. It also pointed out that the placement wiring is repeated in the five maps; that is left as it is.
  • Round 2: no bugs. It checked these paths:
    • empty and single-area hex maps;
    • an unplaced area chosen from the rankings;
    • the image download;
    • other users of the removed options: none in app, calculator-app or website.

Rebuilt on #1448

#1433 and #1447 were closed as superseded by #1448, which holds #1433's six commits unchanged, then #1447's work adapted to main. The merge commit 813a7414 brings #1448 in. Its conflicts keep this PR's behaviour and #1448's withheld areas:

Measured against #1448's branch at 08f9cb07, this PR's own diff touches the same 39 files as its diff against #1433 did. #1448 has had commits since then, which this branch will get when main is merged in after #1448 merges.

Merging main after #1448

#1448 had eight more commits before it merged:

  • one shared placeUKHexMap for the four v2 charts and v3;
  • the withheld-area count, by code against the layout (withheldUKAreaCodes);
  • stripe spacing in screen pixels, on a phone and at any zoom;
  • a check of the name files against .py's area lists;
  • a shared gridPosition;
  • documentation that coordinates can be negative.

The merge keeps all of them, and this PR's behaviour:

  • placeUKHexMap returns the placed points and the withheld areas. It has no tiled flag and no "only when tiled" check. On Show withheld UK areas as not reported on the hex maps, placing v2's hex maps from the layout files #1448, a map was untiled exactly when some area's code was missing from the layout, and withheldUKAreaCodes then lists none anyway, so nothing drawn changes.
  • The four v2 charts and v3 call it as on main.
  • HexagonalMap keeps main's screen-pixel and zoom stripe spacing, with the tiled hexagon size.
  • Tests: placeUKHexMap's tests no longer check a tiled flag, and expect an area the layout can't place to be left out. Main's two new stripe tests drop the removed config. A breakdown fixture loses its x and y.

Merging #1433's all-or-nothing placement

#1433 later changed its fallback to all or none, so that a partly placed map no longer mixes two grids. The merge commit bac47b4c brought that change in, and its conflicts keep this PR's behaviour.

Visual regression

Chromatic runs on PRs based on main, which this PR now is. The HexagonalMap stories draw the real constituency layout on the tiled grid, so their snapshots will differ from main's, and someone will need to accept them.

To check by hand

  1. On a saved UK report with constituency results from the list shape, which carries codes, choose Hex grid on v2's constituency and local-authority tabs and on v3's Local areas. Each area should sit in the same hex as on Show withheld UK areas as not reported on the hex maps, placing v2's hex maps from the layout files #1448.
  2. On an older saved report in the keyed shape, choose Hex grid. The hex map should be empty. Geographic should still colour the areas it can match by name.

🤖 Generated with Claude Code

anth-volk and others added 11 commits October 27, 2026 15:53
v2's absolute and relative constituency hex maps drew each constituency
at the x and y its result gives, on the untiled square grid. Those
positions are the Open Innovations layout's [q, r], but the untiled grid
shifts the even rows where the layout's odd-r rows need the odd ones
shifted, so v2's arrangement differed from v3's in every other row.

Both now place their areas with placeOnUKHexLayout, which holds the
rule v3's local-area map had inline: each area whose ONS code the layout
has takes the layout's position, on the tiled grid. An area the layout
cannot place (no code, as in an older saved result, or a code the layout
lacks) keeps its result's position, and the map then draws the untiled
grid those positions were made for. v3's map now calls the same
function, with no change in what it draws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v2's absolute and relative local-authority hex maps drew each authority
at the x and y its result gives, on the untiled square grid. The Public
API's positions differ from the layout for 56 of the 361 authorities,
and put Cumberland, Westmorland and Furness, North Yorkshire, Somerset
and Bournemouth, Christchurch and Poole, created in 2019 and 2023, all
at (0, 0), so v2 drew them on top of one another.

Both maps now place their areas with placeOnUKHexLayout, as v3's
local-area map and v2's constituency maps do: each authority whose ONS
code the layout has takes the layout's position, on the tiled grid. An
area the layout cannot place keeps its result's position, on the
untiled grid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README said only v3's "Local areas" tab draws from the layout
files. v2's constituency and local-authority tabs now do as well,
through placeOnUKHexLayout. The test fixture that compares v2's and
v3's hex maps also takes UKAreaLevel from its adapter instead of
repeating the type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
placeOnUKHexLayout kept a result's own x and y for an area the layout
had no position for, and turned the whole map back to the untiled
grid. It now leaves that area out and returns only the areas it
places, so v2's and v3's UK hex maps always draw the tiled grid and
no longer read a result's x and y.

An older saved result in the keyed shape has no ONS codes, so its hex
map now draws no hexagons; its geographic map still joins areas to
boundaries by name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The hex maps draw an area only where the app's layout has a position
for its ONS code, so an older saved result without codes gave an empty
map with no explanation. A note under v2's and v3's hex maps now says
how many of the level's areas are not on the map and why, or that the
map has no position for any of them. It shows only with the hex grid
chosen, and not when every area is on the map.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the tiled grid a hexagon's radius is 0.55 of the scale, but the map
fitted only the hexagons' centres inside a margin of twice hexSize. A
map of few areas has a large scale, so its top and bottom hexagons ran
past the frame and were clipped, in the map and in its download. v2's
hex map of three Welsh constituencies, now tiled, reached y = -15.6 in
its 380-unit frame. v3's map of a nation had the same problem.

The tiled map's bounds now take in each hexagon's edges, its radius up
and down and √3/2 of it sideways, so the scale leaves room for them.
HexMapConfig's hexSize comment now says what it sets on each grid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README said the hex maps no longer depend on the x and y the Public
API sends. An area without a code in the layout still falls back to
them, so it now says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	app/src/tests/unit/pages/report-output/constituency/AbsoluteChangeByConstituency.test.tsx
…king

Three of the section's tests confirmed that a saved result's
constituency map was shown by finding Westminster North's hexagon. That
constituency has no ONS code, so since the hex maps leave out areas the
layout cannot place, it has no hexagon and the tests failed. They now
find its row in the rankings, which the map shows whichever map type is
chosen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The app no longer reads the x and y hex positions that the Public API
sends with each constituency and local authority: the hex maps place
each area by its ONS code. They are now gone from:

- UKAreaImpact, the normalized area type, and the normalizer's row
  type, whose rows no longer copy them;
- the v2 constituency and local-authority adapters, which now return
  UKAreaChoroplethDataPoint, the point both maps read;
- v3's local-area map points, and placeOnUKHexLayout's input;
- the test fixtures that stand for normalized results and adapter
  output.

Results still carry them, so the fixtures for API rows and older saved
results keep them, and tests show they load with x and y ignored. The
layouts README now says the app does not read x and y.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since the UK hex maps stopped falling back to results' x and y, every
caller (v3's local-area map and v2's four constituency and
local-authority charts) passed tiled: true, and only HexagonalMap's
story drew the untiled square grid of flat-topped hexagons. No other
code uses the component.

HexagonalMap now always draws the odd-r grid of touching pointy-top
hexagons, and HexMapConfig loses the options that only shaped the
untiled grid:

- tiled, as the tiled grid is the only one;
- hexSize, which sized untiled hexagons and otherwise set only the
  margin, now a fixed 16 units (its default); v2's local-authority
  charts passed 11, so their margin narrows from 22 to 16;
- coordinateScale, which no caller set and which has no effect on
  the tiled grid, as positions and hexagon size scale together.

The story now draws every constituency in the app's layout, placed by
placeOnUKHexLayout, in place of a made-up offset grid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
policyengine-calculator-next Ready Ready Preview Oct 27, 2026 9:18pm UTC
policyengine-website Ready Ready Preview Oct 27, 2026 9:18pm UTC

Request Review

areaEstimateFields still returned a slice of HexMapDataPoint and said
its fields were "as a hex map point carries them", but its callers, the
v2 adapters and v3's local-area map, now build UKAreaChoroplethDataPoint,
and only placeOnUKHexLayout makes hex map points. It is now typed
against UKAreaChoroplethDataPoint, whose comment says it is the point
both maps read.

In the tests, UKLocalAreaSection's stored-constituency ranking label
moves to its fixtures, and MOCK_UK_SHAPED_HEX_DATA's comment says what
the grid stands for now that results' positions no longer reach the
hex map.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk and others added 2 commits October 27, 2026 18:50
When the layout had no position for some of a result's areas,
placeOnUKHexLayout moved the others to their layout positions and left
those at the x and y their results gave, all on the untiled grid. The
two sets of positions are different grids (a row apart for
constituencies, entirely different for local authorities), so the
map's hexagons overlapped or scattered. A saved result from before
April 2023 reaches this: the layout follows the April 2023 boundaries
and has no code for a district abolished then, such as Allerdale
(E07000026), Craven (E07000163) or Mendip (E07000187).

Placement is now all or none. If the layout lacks any point's code,
every point keeps its result's position, on the untiled grid; only when
it has every code do the points take its positions, tiled. v2's and v3's
hex maps both use it. The README and v3's comment say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1433 now places a hex map's areas from the layout all or none: if the
layout lacks any area's code, every area keeps its result's position on
the untiled grid. This branch replaces that fallback, so the conflicts
keep its behaviour: placeOnUKHexLayout leaves out an area the layout
cannot place, the map always tiles, and the note counts the areas left
out.

From #1433 it keeps the README's and the function comment's example of
a district abolished in April 2023, the check that the layout has no
pre-2023 local authority code, and the pre-2023 fixtures, without x and
y as this branch's typed fixtures are. The pre-2023 tests now expect
this branch's behaviour: Allerdale is left out while Darlington takes
its layout position, and on v2's and v3's maps the three pre-2023
districts are left off and counted in the note ("3 of 5 local
authorities are not on the hex map ..."). #1433's comparison with the
same result without codes, and its uncoded fixture, are dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The hex maps leave out an area the layout has no position for, and a
note under v2's and v3's hex maps said how many they left out. The note
is removed: the maps still leave such areas out, but no note says so.

UKHexMapUnplacedNote, its tests and its fixtures are gone, as are its
uses in the four v2 constituency and local-authority charts and v3's
local-area map, whose disclosure notes sit under the map as before. The
charts' and v3's tests for results without codes, with one uncoded
area, and with pre-2023 local authority codes now expect no such note.
The layouts README no longer mentions it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk added a commit that referenced this pull request Oct 27, 2026
#1436 drops the note under the UK hex maps about areas the layout
cannot place. The conflicts were the v2 chart tests' imports, resolved
to keep this branch's withheld-area fixtures without the dropped
note's. v3's test of a breakdown area without a code no longer checks
that note; it still checks that the hex map draws no withheld areas.
The "not reported" note for withheld areas stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk and others added 2 commits October 27, 2026 20:58
#1448 (1A · 2) holds #1433's six commits, cherry-picked unchanged, and
#1447's work adapted to main: the hex maps draw the areas a
disclosure-controlled breakdown withheld, hatched, as "Not reported",
but only on the tiled grid, because areas still carry x and y there.
This branch now builds on it. The conflicts keep this branch's
behaviour and #1448's withheld areas:

- The app reads no x or y: placeOnUKHexLayout takes the adapters'
  UKAreaChoroplethDataPoint, leaves out an area the layout cannot
  place, and returns only the placed points. #1448's Welsh fixture
  points lose their positions, and its fixture with every API position
  at the origin is dropped.
- HexagonalMap draws only the tiled grid. #1448's missingAreas are
  positioned as the valued areas are, by a positionOnGrid with no
  coordinate scale or row-spacing argument, and count in the bounds.
  The duplicate "tiled frame" tests and the missing-area tests'
  tiled config are dropped.
- No note says which areas are left off.
- The withheld areas are kept. Under this branch every map tiles, so
  the callers' "only when tiled" checks are gone: v3's UKLocalAreaMaps
  and the four v2 charts ask withheldUKHexAreas for a breakdown level's
  withheld areas whenever the level is disclosure-controlled.

withheldUKHexAreas still lists none when any of the result's areas has
no code or a code outside the region's layout. That check is about
matching the result to the layout, not about the grid: a layout area
the result lacks may be covered by one of its unmatched areas, such as
the districts a 2023 unitary authority replaced, so calling it withheld
could be wrong. A map that leaves out unplaceable areas therefore draws
no withheld areas, and the function's comment says why. #1448's v3 test
for a breakdown area without a code now expects that: Gower left off,
the other constituencies tiled, and no withheld areas.

The README keeps both sides' text, and the tests take #1448's "not
reported" wording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README, from #1448, said withheld areas are drawn only on the tiled
grid, as a result that falls back to its x and y lists none. On this
branch nothing falls back: it now says that a result with any area the
layout files cannot place lists none, and why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It said the hex layout comes with the results. The hex maps place each
area from the app's own layout files and read no position from the
results, so the comment now says the maps draw nothing before the
breakdown completes because they have no results to colour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1448 (1A · 2) merged into main with eight commits after the one this
branch took: one shared placeUKHexMap for the four v2 charts and v3,
withheld areas counted by code against the layout (withheldUKAreaCodes),
stripe spacing in screen pixels on a phone and at any zoom, a check of
the name files against .py's area lists, a shared gridPosition, and
coordinates documented as possibly negative. The merge keeps all of
them, and this branch's behaviour:

- placeUKHexMap returns placeOnUKHexLayout's placed points and the
  withheld areas; UKHexMap no longer extends the placement result, as
  there is no tiled flag. Its "withheld areas only when tiled" check is
  gone: a map is untiled on main exactly when an area's code is missing
  from the layout, and withheldUKAreaCodes then already lists none, so
  nothing drawn changes. It takes the adapters' UKAreaChoroplethDataPoint.
- The four v2 charts and v3 take main's placeUKHexMap call; v3's comment
  says the map leaves off areas it cannot place instead of keeping the
  API's positions.
- HexagonalMap keeps main's screen-pixel and zoom stripe spacing with
  this branch's tiled hexagon size, scale times HEX_RADIUS.
- withheldUKHexAreas' comment keeps this branch's reason a result the
  layout cannot fully match lists no withheld areas.
- Tests: placeUKHexMap's tests no longer check a tiled flag, and expect
  an area the layout cannot place to be left out rather than every area
  kept at its own position; main's two new stripe tests drop the removed
  tiled config; the Ards and North Down breakdown fixture loses its x
  and y.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

2 active deployments
Preview – policyengine-website — 62c1fe72 Deployed Oct 27, 2026 by vercel[bot]
Preview – policyengine-calculator-next — 62c1fe72 Deployed Oct 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove the app's handling of x and y on UK area results

1 participant