An open pipeline for construction activity in Los Angeles: every building, electrical, mechanical and right-of-way permit the city publishes, downloaded with provenance, geocoded with a documented method, joined to census tracts and council districts, and mapped — so development intensity can be seen and compared with the neighbourhoods it happens in.
Every point on the map can be traced to a row in a public file. Downloads carry checksums, geocodes carry their method and score, and data problems are published rather than patched.
- Acquires the Los Angeles Department of Building and Safety permit tables (building / electrical / mechanical, issued and submitted, 1990s–present; ~5.5M rows) and the Bureau of Engineering right-of-way permits (~640k rows) from data.lacity.org, plus the city's own BOE permit geometries, GeoHub boundary and zoning layers, TIGER census tracts, ACS demographics and the LA County assessor roll.
- Normalizes them into one permits schema (Parquet + DuckDB) while keeping every raw column.
- Geocodes with a tiered, cached chain: the source's own coordinates → the AHJ's own geometry service → the LA County CAMS batch locator → the city centerline locator, with a parser for free-text locations ("S/W CORNER OF X AVE and Y ST", "619,621,623 TOWNE AVE") and strict acceptance rules. Every row records how it was located.
- Analyzes development intensity (permits, new buildings, ADUs, net dwelling units, valuation, solar, EV) by tract, council district, community plan area, neighborhood council and H3 hex, by year, and relates it to income, tenure, density, building age, zoning and rail proximity.
- Publishes figures (
analysis/figures/), a standalone Leaflet map (pc export --inline→data/export/map.html) and the LA Permit Atlas (scripts/build_atlas.py, deployed withscripts/deploy.sh). Clicking an area on the atlas lists its permits: the page reads a single spatially sorted Parquet file with HTTP range requests, so the site stays static. Net dwelling units count only permits that reached a certificate of occupancy plus finaled demolitions, by completion year.
The AHJ (authority having jurisdiction) layer is pluggable: config/sources.yaml registers
sources, geometry services, reference layers and geocoders per jurisdiction, and a small
package under src/plancheck/ahj/<slug>/ supplies the column mappers. Adding a city is a
YAML block and a mapper.
- Provenance over convenience.
manifests/records URL, sha256, size, ETag and the portal's own row count for every file;pc checkverifies ingested rows against it. - Geocoding is a documented method, not magic. Method, score and match type travel with every permit; false-positive rules are explicit and tested; coverage by method is a published figure.
- Count once. Issued permits only, one row per permit id; submitted tables are kept for lead-time analysis but never summed with issued ones.
- Associations, not causes. Tract-level relationships are reported as rank correlations with the sample size shown.
- Data quality in the open.
known_issues/and the generatedDATA_QUALITY.mdare part of the deliverable.
uv sync --extra dev # Python 3.12+, managed by uv
cp .env.example .env # add CENSUS_API_KEY (free)
uv run pc acquire # ~4 GB from data.lacity.org, GeoHub, TIGER, ACS
uv run pc ingest # Parquet + DuckDB views (data/plancheck.duckdb)
uv run pc geocode # tier chain; locator results cached
uv run pc check # -> DATA_QUALITY.md
uv run pc analyze # aggregates + analysis/figures/*.png
uv run pc export --inline # data/export/*.geojson + map.htmluv run pc geocode-one "1016-20 1/2 W. 23RD ST. and TOBERMAN ST." shows the parse and
each locator's answer for one string.
| Path | Contents |
|---|---|
src/plancheck/ |
pipeline: ahj/, acquire/, ingest/, geocode/, quality/, analysis/, cli.py |
config/sources.yaml |
AHJ registry: datasets, geometry services, reference layers, geocoders, tier chain |
config/analysis.yaml |
tunables: years, score floor, H3 resolutions, ACS tables |
manifests/ |
provenance for every downloaded file (committed) |
known_issues/ |
documented problems in the source data (committed) |
analysis/figures/ |
rendered figures (committed) |
site/index.html |
standalone Leaflet map reading data/export/ |
DATA_QUALITY.md |
generated by pc check |
Code is MIT-licensed. Permit data is published by the City of Los Angeles (Department of
Building and Safety; Bureau of Engineering) on data.lacity.org and maps.lacity.org; boundary
and zoning layers by the city's GeoHub; the batch geocoder and assessor roll by Los Angeles
County; tracts and ACS estimates by the U.S. Census Bureau. Derived aggregates are
redistributed with attribution. Geocodes produced by the county locator are cached under
data/ and not committed.