# Population input for SoccerMapd's pro soccer reach map

This file describes the population side of the calculation. Club seasons, league coverage and venue quality are separate inputs from SoccerMapd's history archive.

## What the numbers mean

The tool holds the **2020 Census population and residential distribution fixed** while changing the club network by season. It asks how many people in that fixed population would be within the selected straight-line distance of a recorded qualifying club location.

This is a comparison of the reach of different club networks. It is **not a count of how many people actually lived near soccer in a past year**, a population-growth series, attendance, fandom, ticket affordability, driving time or public-transit access. It cannot establish that a new club caused a measured social or economic change.

When multiple clubs cover the same tract, count that tract's population only once in the overall reach total. Gender- or league-specific reach figures can overlap and must not be added together. The denominator includes the 50 states and District of Columbia, including Alaska and Hawaii, in every season. Puerto Rico and other territories are outside this denominator.

## Official sources

Retrieved September 13, 2026 (UTC); the source files report a November 16, 2021 last-modified date.

- [Census Bureau: Centers of Population](https://www.census.gov/geographies/reference-files/time-series/geo/centers-population.html) describes the published tract fields and mean population centers.
- [2020 Census tract populations and mean population centers, national text file](https://www2.census.gov/geo/docs/reference/cenpop2020/tract/CenPop2020_Mean_TR.txt) supplies every saved population and coordinate.
- [2020 Census state populations and mean population centers](https://www2.census.gov/geo/docs/reference/cenpop2020/CenPop2020_Mean_ST.txt) is an independent geographic-level reconciliation source: every retained state's tract population sums to its published state total.
- [2020 Census national population and mean population center](https://www2.census.gov/geo/docs/reference/cenpop2020/CenPop2020_Mean_US.txt) supplies the independent national check, **331,449,281** residents.
- [Census Bureau: Centers of Population Computation, 1950–2020](https://www2.census.gov/geo/pdfs/reference/cenpop2020/COP2020_documentation.pdf) documents the weighting method. The 2020 calculation uses census blocks with computer-determined internal points; those underlying points do not capture population distribution within each block.

No estimated, interpolated or synthetic population counts are added by this build.

## Fixed source integrity

The builder verifies SHA-256 on the original response bytes, **including the UTF-8 byte-order mark**. Hashing the result of `response.text()` instead would omit that mark in Node and produce a different hash.

| Source | Original bytes | SHA-256 |
| --- | ---: | --- |
| National tract file | 3,583,604 | `137d5b7bc8139a385c2022f54a9aca00ea56bc5ad81c936cf8134e977d50d2c5` |
| State file | 2,324 | `af37e0d68c617f417ef805a94c07cbb0f788863e3cc4db21a3b35f28766f2f06` |
| National file | 66 | `d491fc64de4077c9360b3dc7614a14fefb37a004e5399109734ff7799ec36aba` |

## Saved file and checks

`soccer-reach-population.json` contains 83,848 populated census tracts and 331,449,281 residents. The saved UTF-8 file with LF line endings is 3,606,337 bytes (1,173,469 bytes with Node's default gzip); its SHA-256 is `0f578ef55bb2e60e340b4b2d74141ed4acd20d80e9bba3c4b4989cd83a7483f4`. Consumers can precompute national reach from this file; the full tract array does not need to be shipped to an interactive page.

The source has 85,395 records. The build:

1. Validates the exact field layout, unique 11-character tract IDs, nonnegative integer populations and finite coordinates.
2. Keeps only the explicit 51 state/DC FIPS codes. It excludes 981 Puerto Rico tracts containing 3,285,874 residents.
3. Omits 566 US/DC tracts with zero population. This changes the row count, not the denominator.
4. Checks all 51 retained state totals against the separate Census state file, then checks their combined total against the separate national file.
5. Sorts populated tracts by their official GEOID and preserves every coordinate at the numeric precision published by Census, up to six decimal places. It does not substitute county centers or city coordinates.

The compact schema is:

```text
version: 1
censusYear: 2020
geography: "US50+DC"
totalPopulation: 331449281
tractCount: 83848
columns: ["geoid", "population", "latitude", "longitude"]
tracts: [["01001020100", 1775, 32.476823, -86.486599], ...]
```

`geoid` is a string joining two-character STATEFP, three-character COUNTYFP and six-character TRACTCE. The first five characters identify the 2020 county. Do not assume those county identifiers match later county boundaries or a differently dated atlas. The `source`, `verificationSources` and `exclusions` fields retain provenance and filtering counts.

## Display crosswalk to the existing 2017 county atlas

The populated tracts have 3,143 distinct 2020 county identifiers; the existing `mls-counties.json` has 3,142 2017 county identifiers. Comparing those exact local files finds only two unmatched population-side codes, both from one Alaska split:

| 2020 Census county | Population | Display in 2017 atlas |
| --- | ---: | --- |
| `02063` Chugach Census Area | 7,102 | `02261` Valdez-Cordova Census Area |
| `02066` Copper River Census Area | 2,617 | `02261` Valdez-Cordova Census Area |

The [Census Bureau's 2019 geographic notes](https://www.census.gov/programs-surveys/acs/technical-documentation/table-and-geography-changes/2019/geography-changes.html) document that Valdez-Cordova split into these two new areas. Combining their tract results gives the older display area a population of 9,719. This is a display aggregation only: each tract keeps its original population-center coordinate in every distance calculation. There is no population redistribution or county-center substitution.

After this two-code crosswalk, every populated tract maps to one of the 3,142 atlas counties and the displayed county populations sum to 331,449,281, with no dropped residents. The reach-data builder should fail on any new unmatched identifier instead of silently omitting it.

There is no Connecticut identifier mismatch in this snapshot. Both files use the eight legacy counties. Connecticut's replacement by nine planning regions was a later change, documented in the [Census Bureau's 2022 final notice](https://www2.census.gov/geo/pdfs/reference/ct_county_equiv_change.pdf). The simplified 2017 outlines remain a display backdrop; identical county codes do not imply a legal-boundary comparison between every vintage.

## Geographic approximation

Each tract's residents are represented at its Census mean population center. A tract contributes its complete population when that point is within the chosen radius of at least one included club. Residents in a tract can live on both sides of a radius boundary, so this method can overcount or undercount local access. Larger rural tracts make this limitation more significant; additional decimal places in a center coordinate do not remove it.

Tracts provide finer geographic resolution than assigning an entire county's residents to one point. They still do not locate individual households or model roads, water crossings, borders or travel. The resulting reach should be presented as an estimate, with displayed counts rounded to avoid implying individual-person accuracy.

Club-side gaps and city-level location estimates add separate uncertainty. A zero from an uncovered season or omitted league does not demonstrate that no professional soccer existed. Comparisons must name the leagues represented and maintain the same league eligibility, season rules and location-quality policy across both years.

## Reproduction

From the repository root, with Node supporting built-in `fetch`:

```sh
node scripts/build-soccer-reach-population.mjs
```

The build downloads the three official files, verifies their pinned hashes and all reconciliations, and writes the deterministic JSON. An upstream source change fails before writing a replacement. To verify the checked-in result without changing it:

```sh
node scripts/build-soccer-reach-population.mjs --check
```

The comparison tolerates checkout CRLF conversion, but reported output hashes refer to LF bytes. Updating population vintages requires a deliberate new source/version and recomputing all seasons together; mixing yearly population estimates with this fixed baseline would change the meaning of the metric.
