Repository navigation
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Part of #48. This tracks moving PolicyEngine's congressional-district data, maps and labels from the 119th to the 120th Congress, which is seated on January 3, 2027.
What changes
- Nine states use new congressional maps for the 120th Congress: Alabama, California, Florida, Louisiana, North Carolina, Ohio, Tennessee, Texas and Utah, 173 of the 435 seats.
- Missouri keeps its 2022 map. It used that map in 2026, and the November 2026 referendum on its new map failed.
- Virginia keeps its previous map, because its new one was struck down.
- Each district ID follows its state's new numbering: in the 120th data,
TX-35is the area Texas's new map numbers 35. Seat counts come from the 2020 apportionment, so the list of 436 IDs (AL-01…WY-01, plus DC) doesn't change. - In the nine states, an ID now names a different area, and different households. For example, 40% of the new TX-35's 2020 population was in the old TX-28, and only 10% was in the old TX-35.
Today every layer assumes 119th districts, and nothing records which Congress a result used:
- Households:
congressional_district_geoidis drawn from 119th PUMA overlaps (congressional_district_vintage.py, the block and PUMA ladders). - Targets: district targets use 119th codes.
- Map: app-v2 draws
cb_2024_us_cd119_20m, and that file is copied into ui-kit and about 19 dashboards.
Decisions
- Swap rather than run both. New releases use 120th districts in
congressional_district_geoid. Every district result carries acongressfield. A result without one draws on the 119th map, so saved reports keep theirs. - Ship when the checks below pass, aiming for mid-December, labeled "120th Congress districts (from January 3, 2027)".
- Label each district with its member's name and party, or "Not yet called" until the race is called. After January 3, take members from
unitedstates/congress-legislators. - Only the app and future projects move to the 120th. Existing dashboards keep the 119th districts they were built with, unless their owner chooses to update one.
Sources
- Census 120th Congress block equivalency files (
cd120.zip), regenerated on October 7, 2026 for Missouri: https://www.census.gov/geographies/mapping-files/2027/dec/rdo/120-congressional-district-bef.html - TIGER/Line 2026
cd120shapefiles, with state plans as of May 31, 2026: https://www2.census.gov/geo/tiger/TIGER2026/CD/ - The CD120 Summary File (2020 Census counts by 120th district), due December 2026. It's used to check district populations.
- Not yet published: a cartographic boundary file for the 120th, and ACS tables on 120th boundaries.
Work
Phase 1: shared 120th geography
- Check the registry's
120th_congressplan (Add the US block -> congressional-district plan registry (117th-120th) #1041) against Census's October 7 national file, block by block. Also check that the 41 unchanged states, DC and Missouri keep identical assignments. - Build county, tract and PUMA → 120th district population shares, and a 119th → 120th overlap table.
- Build the 120th map file: the nine redrawn states from TIGER/Line 2026, clipped to the shoreline and simplified, and every other state unchanged. Keep the current
DISTRICT_IDkeys and addCONGRESSandCD120FP. It will live inpolicyengine-ui-kit, versioned by Congress. - Check the hex layout positions inside the nine states.
- Build the labels table: district name, member-elect, party and status, with a source and date for each entry.
Phase 2: households on 120th districts (microcosm)
- Agree the route: switch the primary plan in the existing ladders, or attach plans through US household location v1: one census block per household, every geography derived #1047.
- Update the vintage constants, the GEOID prefix, the spec YAMLs (
vintage:cd_120) and theh5_io.pycheck. - Re-express the targets on 120th boundaries.
- Unchanged states reuse their ACS district values.
- The nine redrawn states are rebuilt from 5-year block-group or tract tables. Test that method on 119th districts first.
- The IRS SOI targets get a 117th → 120th crosswalk.
- Release an ACS-local revision to staging with the district effective-sample-size report (Gate the ACS local release on district ESS collapse (report-only by default) #1088), then to production.
Phase 3: model, API and app
- policyengine.py: pin the release, record the district plan in the bundle, and add
congressto the district impact output. - policyengine-sim-api: add
congressto the output contract. - policyengine-api: put the district plan in the metadata. The cache refreshes with the bundle version.
- policyengine-app-v2: choose the map by
congress, show member labels in tooltips, and add the 120th note.
Phase 4: dashboards (no migration)
- Existing dashboards stay on the 119th districts they were built with, unless their owner changes them.
- None of the 21 dashboards with district maps calls the API for district results; they use precomputed or pinned data, so none shifts on its own.
- A dashboard whose pins are moved to the 120th release should switch its map then, using ui-kit's
congress={120}.
- New projects use ui-kit's 120th maps and members (
congress={120}) instead of copying map files.
Checks before release
- District populations from block sums equal the CD120 Summary File.
- The map has 436 features matching the API's district list.
- District weights sum to the state totals.
- For a fixed reform, districts in unchanged states give the same results before and after.
- On a preview, a saved report draws the 119th map and a new report draws the 120th.
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request
Activity
DTrim99 commentedon Nov 5, 2026
Phase 1, step 1: the Census 120th file against the 119th
I compared Census's current national 120th block equivalency file with the 119th one, block by block.
cd120.zip(last modified 2026-10-07, SHA-256bc9a4fc5…8cc8a5).cd119.zip, SHA-2561433feb5…294bd6, which matches the registry's pin.ZZ(unassigned water)So 120th districts equal 119th districts everywhere except those nine states. Targets and shares for the other 41 states and DC carry over unchanged.
A stale pin in the registry
cd_plan_registry(#1041) pins the August 31cd120.zip(f3074ac5…). Census replaced the file at that URL on October 7, so a rebuild withtools/build_us_cd_plan_registry_artifact.pywould now fail its SHA check.known_deviations["29"]override should become a no-op check, or be removed with a note.Two questions for @MaxGhenis
Route for Phase 2.
120th_congress, reading the registry instead ofCD119_BEF_URL.I'd go with (a), unless US household location v1: one census block per household, every geography derived #1047 is close to landing.
Who rebuilds the registry. OK for me to open a PR that re-pins it and rebuilds it in CI? The build downloads about 4 GB of Census block files, which is more than I can run locally.
DTrim99 commentedon Nov 5, 2026
Phase 1: shared geography is up for review
PolicyEngine/policyengine-ui-kit#41 adds 120th Congress boundaries and hexes beside the 119th ones, chosen with a
congressoption that defaults to 119. Itsdata-sources/congressional-districts/folder (not packaged) has:congressional_district_geoid;Checks:
5002000US, confirmed from TIGER/Line'sGEOIDFQ.Checklist:
DTrim99 commentedon Nov 5, 2026
Phase 2 prep: district targets on 120th boundaries
The build calibrates to ACS 2024 1-year S0101 (18 age bands) and S2201 (households, receiving SNAP, not receiving) for each 119th district, from Ledger's
acs_s0101_district_2024andacs_s2201_district_2024. Census won't publish ACS on 120th boundaries until at least the 2026 1-year (around September 2027).Test 1: the block-group method against ground truth. I built every 119th district's 5-year value from block groups and compared it with Census's published 5-year value for that district.
Elsewhere, two states are off for coding reasons, and neither redrew:
In the nine redrawn states, every 2020 block's block group is in the ACS file.
Test 2: the crosswalk against the block-group estimate on 120th districts in the nine states. These are 5-year values, so both start from the same data.
The crosswalk misplaces age and SNAP targets by several percent at the median and by tens of percent in some districts. The block-group method's own error is about 0.2%.
Result. I've produced 1-year 2024 targets for all 436 districts on 120th boundaries:
State totals are preserved exactly. Codes use the
5002000USprefix (TIGER/Line 2026GEOIDFQ). @MaxGhenis, where should these live: a derived Ledger package beside the 119th ones, or a microcosm build input? I'll open the PR wherever you prefer. The script and output CSVs are ready.Proposal: how a result records its Congress
The app needs to know which boundaries a district result was computed on, so that saved 119th reports keep their map after the switch. A suggested chain:
reviewed_limitations[].layer_vintages.congressional_district("119th_congress"). The pool H5 also carries thepopulace_congressional_district_vintage_targetroot attribute.data_releases.us.congressional_district_plan.CongressionalDistrictImpactreportscongress(119 or 120) from the bundle entry for the dataset it ran on.congresstoCongressionalDistrictImpactOutput.congress ?? 119.All five steps are backward compatible, and they can land before the data switch. Results would say 119 until the 120th release is pinned. Does that match how you'd want the vintage carried, or should policyengine.py read it from the dataset itself?