Ship a tenth of each place's streets, at least ten, and a tenth of the US places owning a ZCTA, drawn by population

This commit is contained in:
2026-10-11 16:18:32 +02:00
committed by Lillem4n
parent b64b73cb9a
commit 88cea644b6
8 changed files with 181569 additions and 9083 deletions
+24 -11
View File
@@ -1,12 +1,14 @@
#!/usr/bin/env python3
"""Rebuild data/geo/US/*.tsv from the Census Bureau's Gazetteer, population estimates, ZCTA relationships and TIGER/Line files (public domain).
data-import/geo-us.py [--cache DIR] [--min-population N] [--streets-per-locality N] [--out DIR]
data-import/geo-us.py [--cache DIR] [--min-streets N] [--place-share F] [--street-share F] [--out DIR]
"""
import collections
import concurrent.futures
import csv
import io
import math
import random
import re
import struct
import sys
@@ -104,8 +106,8 @@ def place_name(geoid, name):
return stripped
def localities(cache, min_population, counties):
"""Each shipped place with its county, population and centroid."""
def localities(cache, counties):
"""Each incorporated place with its county, population and centroid."""
place_population, county_part = {}, collections.defaultdict(list)
for r in csv_rows(cache, PLACES, "sub-est2025.csv"):
if r["SUMLEV"] == "162":
@@ -115,7 +117,7 @@ def localities(cache, min_population, counties):
out = {}
for r in gazetteer(cache, "place"):
geoid, population = r["GEOID"], place_population.get(r["GEOID"], 0)
if r["FUNCSTAT"] not in ("A", "F", "N") or r["LSAD"] == CDP or population < min_population or not county_part.get(geoid):
if r["FUNCSTAT"] not in ("A", "F", "N") or r["LSAD"] == CDP or not county_part.get(geoid):
continue
county = max(county_part[geoid])[1]
if county not in counties:
@@ -125,8 +127,17 @@ def localities(cache, min_population, counties):
return out
def drawn(places, share):
"""The share of the places with people, each drawn with odds by its population, the same set on every run:
Efraimidis and Spirakis's weighted sampling without replacement."""
rng = random.Random(0)
peopled = sorted(code for code, p in places.items() if p["population"] > 0)
key = {code: math.log(1 - rng.random()) / places[code]["population"] for code in peopled}
return {code: places[code] for code in sorted(peopled, key=key.get, reverse=True)[:round(len(peopled) * share)]}
def zcta_localities(cache, localities):
"""Each ZCTA mapped to the incorporated place holding most of its land inside incorporated places, where that place is shipped."""
"""Each ZCTA mapped to the incorporated place holding most of its land inside incorporated places, where localities holds that place."""
parts = {}
for r in csv.DictReader(io.StringIO(text(source.fetch(ZCTA_PLACE, cache, "zcta-place.txt"))), delimiter="|"):
if r["GEOID_ZCTA5_20"] and r["GEOID_PLACE_20"] and not r["NAMELSAD_PLACE_20"].endswith(" CDP"):
@@ -135,7 +146,7 @@ def zcta_localities(cache, localities):
return {zcta: place for zcta, place in largest.items() if place in localities}
def streets(cache, counties, locality_of_zcta, per_locality):
def streets(cache, counties, locality_of_zcta, share, minimum):
"""The names with most TIGER address ranges per place, and the address ranges per ZCTA."""
with concurrent.futures.ThreadPoolExecutor(3) as pool:
list(pool.map(lambda c: (tiger_zip(cache, "addr", c), tiger_zip(cache, "featnames", c)), counties))
@@ -150,11 +161,11 @@ def streets(cache, counties, locality_of_zcta, per_locality):
if r["PAFLAG"] == "P" and r["FULLNAME"] and not HIGHWAY.search(r["FULLNAME"]):
for z in zips.get(r["TLID"], ()):
count[(locality_of_zcta[z], r["FULLNAME"])] += 1
return geo.top_streets(count, per_locality, "addresses"), addresses
return geo.top_streets(count, share, minimum, "addresses"), addresses
def main():
a = geo.parser(__doc__, "US", min_population={"type": int, "default": 25000}).parse_args()
a = geo.parser(__doc__, "US", place_share={"type": float, "default": 0.1}).parse_args()
cache, out = geo.directories(a)
state_population = {r["STATE"]: r[ESTIMATE] for r in csv_rows(cache, STATES, "nst-est2025.csv") if r["SUMLEV"] == "040"}
@@ -168,9 +179,11 @@ def main():
counties[r["GEOID"]] = {"code": r["GEOID"], "name": r["NAME"], "region": r["USPS"], "population": county_population[r["GEOID"]]}
print(f"counties without a population estimate, dropped: {dict(unestimated)}", file=sys.stderr)
places = localities(cache, a.min_population, counties)
locality_of_zcta = zcta_localities(cache, places)
named, addresses = streets(cache, sorted({l["municipality"] for l in places.values()}), locality_of_zcta, a.streets_per_locality)
places = localities(cache, counties)
owner_of_zcta = zcta_localities(cache, places)
places = drawn({p: places[p] for p in set(owner_of_zcta.values())}, a.place_share)
locality_of_zcta = {zcta: p for zcta, p in owner_of_zcta.items() if p in places}
named, addresses = streets(cache, sorted({l["municipality"] for l in places.values()}), locality_of_zcta, a.street_share, a.min_streets)
zctas = collections.defaultdict(list)
for zcta, locality in sorted(locality_of_zcta.items()):
if addresses[zcta]: