Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Working with Census Geographies

morpc-census provides tools to fetch Census geometries and data for specific places (scopes) at specific resolutions (scales). This notebook shows the typical workflow:

  1. Choose a scope — where are we looking?

  2. Choose a scale — at what resolution?

  3. Fetch the geometries

  4. Work with GEOIDFQs — the fully-qualified geographic IDs the Census API returns

from morpc_census import (
    SCOPES,
    SumLevel,
    GeoIDFQ,
    geoinfo_from_scope_sumlevel,
    fetch_geos_from_scope_sumlevel,
)

1. Scopes — choosing where

A scope names a geographic extent — a county, a multi-county region, the whole US. morpc-census ships with built-in scopes for the US, all 50 states, all Ohio counties, and several MORPC study areas.

# All available scope names
list(SCOPES.keys())
# A single-county scope — Franklin County
SCOPES["franklin"]
# A multi-county scope — the 15-county MORPC region
# for_param holds the FIPS codes of all counties in the region
SCOPES["region15"]

2. Scales — choosing resolution

A scale (summary level) sets the resolution of the data — county, census tract, place, block group, etc. Pass a name or a three-digit Census code; both refer to the same thing.

# Look up a scale by name
SumLevel("county")
# Same result from the three-digit code
SumLevel("050")
# Unrecognised names raise a ValueError that lists all valid options
try:
    SumLevel("neighborhood")
except ValueError as e:
    print(e)

3. Fetching geometries

Network required — the cells below call the Census API and TIGERweb REST API.

fetch_geos_from_scope_sumlevel(scope, sumlevel) is the primary entry point. When sumlevel is omitted it defaults to the scope’s own level (counties for region15, a single county for franklin).

# County boundaries for the 15-county MORPC region
region_counties = fetch_geos_from_scope_sumlevel("region15")
region_counties[["NAME", "GEOID", "geometry"]].head()
region_counties.plot()
# Census tract boundaries within Franklin County
franklin_tracts = fetch_geos_from_scope_sumlevel("franklin", "tract")
franklin_tracts[["NAME", "GEOID", "geometry"]].head()
franklin_tracts.plot()

Geographic IDs without geometry

geoinfo_from_scope_sumlevel queries the Census API for GEOIDFQs and names without downloading geometry — useful when you only need to enumerate geographies or collect IDs to pass to other queries.

# GEOIDFQs for all counties in the MORPC region (no geometry download)
geoinfo_from_scope_sumlevel("region15")
# As a DataFrame with GEO_ID and NAME columns
geoinfo_from_scope_sumlevel("region15", output="table")
# Drill down to a finer resolution
geoinfo_from_scope_sumlevel("franklin", "tract", output="table")

4. GEOIDFQs

Every geography returned by the Census API has a GEOIDFQ — a fully-qualified geographic identifier that encodes the geography type and component FIPS codes in a single string.

For example, 1400000US39049010100 is the GEOIDFQ for census tract 101 in Franklin County, Ohio:

140  00  00  US  39    049     010100
─┬─  ─┬  ─┬  ─┬  ─┬─  ─┬─    ──┬──
 │    │   │   │   │    │       tract
 │    │   │   │   │    county
 │    │   │   │   state
 │    │   │   literal
 │    │   geocomp
 │    variant
 sumlevel (140 = census tract)

GeoIDFQ lets you parse, inspect, and construct these strings.

# Parse a GEOIDFQ string into its components
tract = GeoIDFQ.parse("1400000US39049010100")
tract
# Summary level tells you what kind of geography this is
print(tract.sumlevel.name)

# Component FIPS codes
print(tract.parts)
# .geoid is the short-form ID used in Census REST API queries
tract.geoid
# Build a GEOIDFQ from a summary level code and component FIPS codes
county = GeoIDFQ.build("050", state="39", county="049")
str(county)    # Franklin County, Ohio
# GEOIDFQs from a fetch can be parsed directly from the GEOIDFQ column
geoidfqs = [GeoIDFQ.parse(fq) for fq in region_counties["GEOIDFQ"]]
[(g.parts["county"], g.geoid) for g in geoidfqs]