Published

Poland (PL) build pipeline

Postal Codes Dataset for Poland, PL including name of the city, town, or place, various administrative divisions and alternative city names.

Poland (PL) build pipeline

Single-source GeoNames PL rebuild. Self-contained: every script resolves its paths relative to itself and fetches directly from public sources.

Run order

python3 fetch_source.py                 # GeoNames PL.txt + sources/manifest.json
python3 build_base.py                   # -> data/pl.csv (TERYT preserved, ISO codes derived)
python3 recover_alt_names.py --fetch     # restores alternative_city_name from the R2 copy
python3 integrity_checker.py            # writes data/consolidated_stats.json; must be PASS
python3 package.py                      # datapackage.json + README.md + ATTRIBUTION.txt + pl.zip
python3 publish_r2.py                   # dry run by default; --apply to publish

recover_alt_names.py --fetch and publish_r2.py need the postal-codes repo .env (R2_ACCESS_KEY, R2_SECRET_KEY, R2_ENDPOINT_URL) — the EU-jurisdiction endpoint. See docs/R2_ACCESS.md in the fedex repo; the two repos use different variable names.

Sources

  • GeoNames PL postal export — CC BY 4.0. Attribution is a licence condition and is carried in data/ATTRIBUTION.txt, data/README.md and datapackage.json.

Poczta Polska is not usable and must not be reintroduced. The Oficjalny Spis PNA is a commercial product whose terms permit use "for the customer's own purposes … without the right to resale to third parties" — including the freely-downloadable PDF edition. See ../.licensing-notes.md.

GUS TERYT was assessed and deliberately not used: it is commercially reusable under the Polish public-sector-information re-use act, but its download page is ASP.NET post-back only (no direct file URLs, fetch_tier: dynamic) and GeoNames already carries the TERYT powiat and gmina codes this build needs.

What this rebuild fixes

The R2 copy this replaces was an unpackaged raw parse that round-tripped the data through pandas and destroyed the official codes:

fieldGeoNamesold R2 filethis build
admin_code2 (TERYT powiat)0201201.00201
admin_code3 (TERYT gmina)02010220102.0020102
admin_code172 (source-internal)72DS (ISO 3166-2:PL)
admin_name1Kujawsko-PomorskieKujawsko PomorskieKujawsko-Pomorskie
empty admin cellsemptynan (string)empty

Row counts and column names agreed throughout, which is why the corruption shipped. integrity_checker.py therefore checks values, not shape — the load-bearing assertion is that the TERYT gmina code always extends the powiat code (020102.startswith(0201)). A stripped leading zero breaks that relation, so it cannot pass silently again.

Verification (2026-08-27)

Cold fetch → build → check → package, all green:

  • 72,899 rows, 20,299 distinct postal codes, zero duplicate primary keys.
  • 16/16 voivodeships. The ISO mapping is derived from the TERYT prefix and cross-checked three ways: GeoNames region number ↔ TERYT voivodeship prefix ↔ region name must be 1:1:1 on every row, or the build aborts.
  • 380 powiats — matches Poland's real count (314 land powiats + 66 city powiats).
  • 2,476 gminas of 2,477 — the one absent gmina is the single source row that carries no TERYT code at all (88-420 Jeziora), left blank rather than guessed.
  • 10,268 alternative names recovered; 14,752 legacy values that merely echoed place_name were dropped as carrying no information.
  • Frictionless validation passed against the packaged resource.

One upstream conflict, reported not hidden: Pilów / 78-650 appears under two gminas (Wałcz 321705 and Mirosławiec 321703), 90 m apart. Both are genuine source rows, so admin_code3 is part of the primary key and both are kept. integrity_checker.py prints any such split rather than collapsing it.

Not run

publish_r2.py --apply has not been run. The R2 objects and _meta/catalog.json still hold the old single-file version.