Technical annex#
A step-by-step description of the data pipeline that produces the canonical CHEER footprint inventory, spanning from raw data ingestion to the final curation stage.
Stage 1: ingest#
This stage downloads and extracts raw footprint, parcel, and reference point datasets:
Precursor ingestion#
Administrative boundaries
Downloads US Census administrative boundaries (
US_admin-census-2021_admin2,US_admin-census-2021_admin3, andUS_admin-census-2021_admin4).Dependency: Foundational step. Microsoft footprint allocation requires Admin 2 (state) and Admin 3 (county) units, and image scraping recipes operate at Admin 4 (townships).
Function:
openplaces.io.ingester.ingest()OBM tile index and linking
Downloads OBM tile geometries (
tile-obm-2025) and generates a precomputed tile-to-admin boundary link overlay.Dependency: Requires administrative boundaries to resolve the spatial overlay.
Function:
openplaces.geo.link.create_entity_link()
Core dataset ingestion#
Base footprints
Downloads raw building geometries from OpenBuildingMap (OBM) 2025 (
footprint-obm-2025), Microsoft v2 (US_footprint-microsoft-v2), and auto-discovered state/local GIS footprint layers.Dependency: Tile-partitioned footprints (OBM) depend on the tile index link to resolve tiles for the requested admin units.
Function:
openplaces.io.ingester.ingest()FEMA occupancy footprints
Downloads FEMA USA Structures footprints (
US_footprint-fema-2023). These are ingested for parcel-occupancy linkage, not as a footprint-spine geometry source (due to intersection/IoU errors).Dependency: Spatial inputs are limited to requested admin unit IDs.
Function:
openplaces.io.ingester.ingest()Parcels and assessor rolls
Gathers assessor geometry and property tax rolls from state/local GIS agencies.
Function:
openplaces.io.ingester.ingest()Reference point databases
Downloads the National Structure Inventory (NSI) 2022 point database (
US_building-nsi-2022) and Overture 2025 dwelling address points (dwelling-overture-2025).Function:
openplaces.io.ingester.ingest()
Stage 2: harmonize#
This stage merges geometries and links datasets to build the core entities.
Footprint spine harmonization#
Recipe: US_footprint-spine-2026
Merge source footprints
Combines raw footprint geometries from OBM, Microsoft, and local layers. It filters out shapes below the
min_area_m2threshold (10.0 m²), resolves duplicates using theoverlap_iou_maxthreshold (0.02), and filters parallel-shifted shapes using aspect ratio and angle tolerances viaelongated_aspect_min(2.5),elongated_angle_tol(15.0), andelongated_long_overlap_min(0.5) with lateral separation ratioelongated_lateral_sep_ratio(2.0). To protect larger structures, it enforces keep-out buffers for features exceedingbuffer_min_area_m2(250.0 m²) with a keep-out distance calculated as:keep-out distance=buffer_base_m(2.0 m) +buffer_area_scale(0.5) * sqrt(area_m2).Intersect footprints with parcels
Performs a spatial identity overlay between footprints and parcels based on the
joinmethod (spatial_overlay) andsource_geometry_type(mixed_type_footprint) for theentity_typeparcel. It filters out minor intersections underarea_intersection_m2_min(10 m²) or slivers less thanmin_fraction_of_largest(0.1667) of the largest parcel intersection, sorting bysort_by(area_intersection_m2). To resolve systematic spatial displacements between footprint and parcel layers that inflate parcel counts, it snaps chain-displaced footprints to their dominant parcel when all minor overlaps are below thechain_fraction_max(0.75) threshold and land on neighbor parcels that have their own building.Function:
openplaces.io.harmonizer.links.link_to_reference()Infer synthetic fallback footprints
Generates synthetic footprint geometries for the
entity_typeparcelusing parcel boundaries where tax assessor records indicate a structure exists but no footprint is detected by spatial sources. It uses the thresholdsn_per_group_min(0.2) andvalue_per_ha_quantile(0.05).Function:
openplaces.io.harmonizer.links.infer_spine_additions()Trim overlapping boundaries
Adjusts and trims geometry boundaries to resolve all remaining overlaps in the spine, preventing intersecting footprint representations.
Link building structure points
Connects structure-level point evidence from the NSI database using a tiered containment,
proximity_m(10 m) inner proximity, orfar_proximity_m(100 m) outer proximity join (Lochhead et al. 2026). The join is configured asjoin(spatial_point) for thesource_geometry_type(single_building_point) using therecipe_id(US_building-nsi-2022) with a remapping crosswalk specified byremap_id(US_building-nsi-2022_occupancy-type-remap). It resolves and flags colocated duplicate points from low-rank sources (grouping by building_id_ubid and labeling low-rank twins fromESRIandHAZUS/NSI-2015as'colocated low-rank source'viaflag_duplicate_points()) to be excluded from downstream aggregates.Function:
openplaces.io.harmonizer.links.link_to_reference()Link dwelling address points
Integrates Overture geocoded residential address points from the
recipe_id(dwelling-overture-2025) using a tiered proximity join configured viajoin(spatial_point) for thesource_geometry_type(single_dwelling_point). It uses a proximity range fromproximity_m(10 m) tofar_proximity_m(50 m), withaggregate_multipoint(true) and address deduplication enabled viadedup_addresses(true).Function:
openplaces.io.harmonizer.links.link_to_reference()Classify structural role
Determines whether a footprint represents a primary or secondary structure on multi-building parcels based on NSI and Overture matches, using the
entity_typeparcel. Synthetic, parcel-derived fallback geometries are always classified as'primary'.Function:
openplaces.io.harmonizer.attributes.classify_footprint_priority()Package raw variables
Aggregates all joined source evidence from NSI, Overture, and parcels into intermediate columns on the footprint spine using the configured list of
sources. Unhandled numeric columns (such as NSI’sn_storiesandarea_sqft) are aggregated using the attribute registry’s default function so they are carried onto the spine. Point reference records flagged by the duplicate resolution (whereduplicate_resolutionis non-null) are filtered out and excluded from all aggregates (match counts, sums, means, and value-weighted picks).Function:
openplaces.io.harmonizer.attributes.reconcile_attributes()
Parcel spine harmonization#
Recipe: US_parcel-spine-2026
Establish parcel boundary baseline
Merges discovered statewide and local parcel geometry layers into a unified spatial spine, automatically discovering sources with the
auto_discover(true) flag for theentity_typeparceland keeping the configured list ofkeep_columns.Merge assessor tax records
Discovers and joins county/local assessment tables by matching local ID keys, applying custom attribute remapping crosswalks. It automatically discovers and links tables for the
entity_typeparcelusingauto_discover(true).Standardize property use codes
Constructs a combined property use description and maps it to normalized use classifications.
Function:
openplaces.io.harmonizer.attributes.derive_use_classes()Associate building points to parcels
Joins NSI point data from the
recipe_id(US_building-nsi-2022) using a spatial overlay and proximity boundaries configured viajoin(spatial_point),source_geometry_type(single_building_point),proximity_m(10 m), andfar_proximity_m(100 m). It resolves and flags colocated duplicate points from low-rank sources (grouping by building_id_ubid and flaggingESRIandHAZUS/NSI-2015twins viaflag_duplicate_points()) to exclude them from downstream aggregates.Function:
openplaces.io.harmonizer.links.link_to_reference()Identify dominant building group
Resolves and summarizes NSI building attributes to find the modal building group per parcel using the specified
sources. Point records flagged by the duplicate resolution step are excluded from these summaries.Function:
openplaces.io.harmonizer.attributes.reconcile_attributes()Integrate FEMA footprint occupancy
Links FEMA footprints to parcels via spatial overlay from the
recipe_id(US_footprint-fema-2023) using thejoin(spatial_overlay) method for thesource_geometry_type(mixed_type_footprint) and filters out minor intersections underarea_intersection_m2_min(10 m²).Function:
openplaces.io.harmonizer.links.link_to_reference()Extract dominant FEMA occupancy
Reconciles FEMA occupancy types from the
sourcesusing the remapping crosswalk specified byremap_id(US_footprint-fema-2023_occupancy-type-remap).Function:
openplaces.io.harmonizer.attributes.reconcile_attributes()Summarize footprint morphology
Counts total, primary, and small elongated footprint features on each parcel to feed downstream land-use classification, and tracks the maximum parcels spanned and maximum dwellings contained by any single footprint on the parcel. It links footprints from
footprint_recipe_id(US_footprint-spine-2026) matching onon(parcel_id), filtering bysmall_area_max_m2(185 m²),elongated_aspect_min(2.0), andmin_overlap_m2(10 m²), computingmax_dwellings_per_footprintandmax_parcels_per_footprintfor townhome and multi-family detection.Dependency: Has a hard dependency on Footprint Spine Harmonization, as it queries the completed footprint spine.
Function:
openplaces.io.harmonizer.attributes.summarize_footprint_morphology()
Stage 3: ingest images#
This stage fetches imagery required for deep-learning visual classification:
Satellite imagery (
image-googlesatellite-z20.yaml): Scrapes zoom-level 20 Google Satellite tiles using footprint geometries via BRAILS++.Street View imagery (
image-googlestreetview-2026.yaml): Downloads street-level Google Street View photos and depth maps.
Dependency: Requires the completed footprint geometries from Stage 2 to calculate image scrape bounds.
Stage 4: enrich#
This stage runs deep learning models (BRAILS++) to predict visual building attributes:
Infer roof shape
Runs BRAILS++ deep learning classifiers on satellite imagery to predict roof shape, using visual models on the footprint spine (Cetiner et al. 2025).
Dependency: Requires satellite imagery from Stage 3.
Function:
openplaces.io.enricher.attributes.classify_roof_shape()Detect story counts
Uses computer vision detectors on street-level photos to estimate floors of living area, predicting story height (Cetiner et al. 2025).
Dependency: Requires Street View photos from Stage 3.
Function:
openplaces.io.enricher.attributes.detect_n_stories()
Stage 5: curate#
This stage curates the spines into clean, canonical datasets.
Parcel curation#
Recipe: US_parcel-openplaces-2026
This stage curates the parcel spine to produce clean assessor attributes:
Impute parcel occupancy group
Assigns groups based on NSI modal counts per use code using
group_column(use_group_combined),value_column(group_building_nsi), the Mode statistic viastatistic(mode), and saving tooutput(group_parcel).Function:
openplaces.io.curator.imputers.impute_from_group_statistic()Score relative footprint area
Calculates log-space z-scores to assist Vacant and Townhome rule classification by measuring how anomalously small the largest footprint is relative to other parcels sharing the same assessor use code. It uses
group_column(use_group_combined),value_column(max_footprint_area_m2),output(footprint_area_log_zscore),transform(log1p), andstatistic(zscore).Function:
openplaces.io.curator.inferers.score_relative_to_group()Classify parcel land use
Assigns parcel land-use classes via weighted voting. It runs multi-indicator voting rules to identify Manufactured Home Park, RV Park, Standalone Manufactured Home, Townhome, Vacant, etc., saving to
output(land_use_class) with flags mapped toflag_column(manufactured_home_park) andflag_class(Manufactured Home Park), scoring columns viascore_columns(Vacant:land_use_vacancy_score), review tracking onreview_column(land_use_review) with a margin ofreview_margin(1.0) and the configuredrules. In particular, Townhome classification uses the new morphology indicatorsmax_parcels_per_footprintandmax_dwellings_per_footprintcombined with assessor keyword matches to detect shared-footprint row houses.Function:
openplaces.io.curator.inferers.classify_parcel_land_use()Reconcile default land use
Determines default land-use class for parcels not claimed by specific rules via consensus voting across three group-vocabulary columns (NSI, FEMA, and parcel use groups).
Function:
openplaces.io.curator.reconcilers.reconcile_land_use()Derive story count from height
Approximates the story count (
n_stories_footprint_fema) asheight / 3.05, rounded and floored at one story, using the LiDAR-derived FEMA footprint height.Function:
openplaces.io.curator.inferers.derive_stories_from_height()Standardize data categories
Casts string columns to pandas Categorical types to optimize storage footprint and query performance.
Function:
openplaces.io.curator.formatters.cast_categoricals()Format curated parcel schema
Enforces a standard column order on the final curated parcel schema.
Footprint curation#
Recipe: US_footprint-cheer-2026
This stage curates the footprint spine, integrating parcel, imagery, and point evidence into the final exposure dataset. The curation recipe steps are chronologically executed, structured into functional sub-stages to highlight data-flow dependencies:
Input integration and value apportionment
Integrate clean assessor data
Matches each footprint in the spine to its corresponding parcel in the curated parcel lane using
recipe_id(US_parcel-openplaces-2026) and joins the configuredcolumns(use_group_combined, group_parcel, manufactured_home_park, group_footprint_fema, and land_use_class). Note that the joined curated-parcel columns overwrite any raw harmonized_parcelevidence columns.Dependency: Requires the completed parcel curation lane.
Function:
openplaces.io.curator.evidence.link_curated_entity()Apportion parcel values
Joins property valuation and construction year columns from the curated parcel lane, then: (1) splits
improvement_value_parcelacross a multi-footprint parcel’s dwelling-linked (or, absent those, all) primary footprints by floor-area share; (2) keepsland_value_parcelwhole on the principal footprint only; and (3) assigns the averageyear_built_parcelto all linked footprints.Dependency: Requires the completed parcel curation lane.
Function:
openplaces.io.curator.evidence.apportion_curated_values()Collect overlapping parcel IDs
Retains the full n:m footprint-parcel membership as a canonical column (parcel_id_all), pipe-joined with the dominant parcel first, from the overlay link sidecar.
Dependency: Requires the spatial overlay from Harmonization.
Dwelling and address evidence prep
Correct address evidence
Suppresses Overture dwelling unit counts on vacant parcels. It sets
column(n_dwellings_overture) to null if thecondition_column(land_use_class_parcel) matches the valuecondition_value(Vacant).Dependency: Requires curated parcel land-use classification integrated in Step 1.a.
Function:
openplaces.io.curator.reconcilers.suppress_where()Determine implied Overture occupancy
Assigns a temporary occupancy class to
target(occupancy_type_dwelling_overture) based on the corrected Overture count using the configureddecisionsrules.Dependency: Requires corrected address evidence from Step 2.a.
Function:
openplaces.io.curator.reconcilers.resolve_by_vote()
Core value and metric reconciliation
Select canonical values
Resolves conflicts between competing source attributes by selecting the canonical value from the prioritized lists in the
prioritymapping (e.g. for dwelling counts, year built, and financial valuation).Dependency: Requires apportioned parcel values (Step 1.b) and implied Overture occupancy (Step 2.b).
Function:
openplaces.io.curator.reconcilers.reconcile_values()Reconcile street addresses
Reconciles and harmonizes street addresses from competing source inputs (parcel assessor addresses and Overture dwelling address components), completing missing components (like state names derived from administrative units) and checking for consistency.
Dependency: Requires integrated assessor data (Step 1.a) and Overture address evidence.
Function:
openplaces.io.curator.reconcilers.reconcile_addresses()Zero-fill address counts
Fills missing or suppressed Overture dwelling unit counts listed in
columns([n_dwellings_overture]) with0and casts the column to integer.Dependency: Requires reconciled dwelling counts from Step 3.a.
Function:
openplaces.io.curator.imputers.fill_missing_numeric()Compute footprint metrics
Calculates structural indicators such as footprint area in square meters from geometry and computes structural value-per-area metrics for all value columns. Note that
value_per_areais kept in the final output.Dependency: Requires canonical values from Step 3.a and footprint geometries.
Impute missing residential units
Imputes residential unit counts when no matched source evidence exists based on the occupancy base class.
Dependency: Requires reconciled dwelling counts from Step 3.a.
Function:
openplaces.io.curator.imputers.impute_n_dwellings()
Occupancy consensus and correction
Establish baseline occupancy class
Resolves a weighted consensus vote across present evidence columns (NSI, FEMA, parcel, Overture) where the heaviest class wins and Overture has a fractional (0.5) vote weight; (2) applies a geometry-based Manufactured Home fallback for long/narrow structures; (3) fills residential gaps using a single-family dwelling count check; and (4) assigns accessory structures to the Secondary class (excluding habitable structures in manufactured home parks).
Dependency: Requires joined parcel occupancy groups (Step 1.a) and implied Overture occupancy (Step 2.b).
Function:
openplaces.io.curator.inferers.impute_occupancy_type()Apply property-use keyword corrections
Refines baseline occupancy by correcting classes using property-use keywords from the ruleset (only rules marked reviewed can override the base class, and they never override Secondary). It also writes the
occupancy_type_parcelcolumn, sets theoccupancy_type_reviewflag for low improvement-value shares, generates theoccupancy_type_conflictsummary, and outputs a county-level conflicts CSV report.Dependency: Requires baseline occupancy class from Step 4.a.
Function:
openplaces.io.curator.reconcilers.resolve_occupancy()
Imagery enrichment integration
Merge visual model predictions
Merges predicted building attributes from the configured
recipes(e.g.US_footprint_built-roof-shape-brails-2026andUS_footprint_built-n-stories-brails-2026) and their respective columns.Dependency: Requires completed Stage 4 (Visual enrichment).
Function:
openplaces.io.curator.evidence.merge_enrichments()Reconcile story counts
Resolves conflicts between competing story count sources (street-level imagery predictions
n_stories_brailsand NSI block-median countsn_stories_building_nsi) by selecting the canonical value.Dependency: Requires merged visual predictions from Step 5.a.
Function:
openplaces.io.curator.reconcilers.reconcile_values()
Final occupancy voting and refinement
Score manufactured home probability
Computes a probability score for manufactured home classification based on the
ruleset(parcel-occupancy-keywords.csv) while setting theupdate_occupancyparameter tofalse.Dependency: Requires footprint metrics from Step 3.c.
Function:
openplaces.io.curator.inferers.classify_manufactured_homes()Resolve occupancy by weighted vote
Resolves final occupancy class (specifically Manufactured Home vs. Multi-Family conflicts) to the
targetcolumn (occupancy_type) using the configureddecisionsrules. Footprints under 20 m² are prevented from becoming Manufactured Home by a hard precondition, and the winning decision records its source label intooccupancy_type_source.Dependency: Requires reconciled dwelling counts (Step 3.a) and manufactured home probability (Step 6.a).
Function:
openplaces.io.curator.reconcilers.resolve_by_vote()Split height bands
Splits the standard
multi_family_class(Multi-Family) into HAZUS height bands based on the reconciled number of stories using the configuredbands.Dependency: Requires final occupancy (Step 6.b) and reconciled story counts (Step 5.b).
Function:
openplaces.io.curator.inferers.refine_occupancy_height()Flag manufactured home communities
Re-evaluates mobile home park boundaries and flags parcels containing more than
min_homes(3) final Manufactured Home footprints (i.e., 4 or more). It writes the count ton_manufactured_homes_per_parceland the boolean flag tomanufactured_home_community.Dependency: Requires final occupancy classification from Step 6.b.
Function:
openplaces.io.curator.inferers.flag_manufactured_home_communities()
Schema standardization and formatting
Standardize data categories
Converts string columns to pandas Categorical types.
Function:
openplaces.io.curator.formatters.cast_categoricals()Cast year built to integer
Rounds and casts the year of construction columns listed in
columns([year_built]) to nullable integer data types.Clean up and order columns
Enforces standard column order and drops transient helper columns (including
group_parcel,occupancy_type_dwelling_overture,occupancy_type_dwelling_overture_source,occupancy_type_base,p_manufactured_home,manufactured_home_park, andn_stories_brails_source).