Four open datasets (traffic counts, exit geometry, census demographics, business co-location) triangulate a starting point to where any consumer-facing chain should go next. No proprietary feeds. No site-selection committee. We ran it on Waffle House.
WH already operates 486 restaurants across the 6-state region, 294 of them within 5 miles of an interstate exit. That existing footprint is the answer key. Where in SW Virginia — along I-81, I-77, I-581 — does the next one belong?
Why it wins: median HHI $62.6k sits in the heart of WH's revealed-preference band ($61k–$83k from the control profile). Unemployment 5.0%, labor pool large enough, and the exit pair captures both directions of I-81. deterministic
Runners-up: Exit 14 I-77 Hillsville (92.8) · Exit 80 I-81 Christiansburg (88.5) · Exit 7 I-81 Bristol (86.7). All 185 scored exits below.
Each dot is one interstate exit, colored by composite score. Click any dot to see the full breakdown. Click a row in the table below the map to fly there. Top three candidates ship straight to a real-estate broker.
deterministic 185 scored exits + 9 rest areas + 2 weigh stations · final scores recomputed from JSON inputs against editable thresholds (workbook formulas in the downloadable bundle) · Mapbox base map: light-v11.
Mechanically the diagnostic is straightforward: every existing location is a positive label; the chain has already decided this exit is a yes. Pull the public-data features around each existing location, fit the implicit preference, and score candidate exits on how closely they match. Every layer below feeds either the training set (existing locations) or the scoring set (candidate exits).
| Layer | Signal | Data source (public) | Tool |
|---|---|---|---|
| 01 | Traffic volume + corridor fit |
VDOT 2024 AADT (VA Open Data Portal) — bidirectional traffic counts + truck % per segment OpenStreetMap motorway_junction nodes via Overpass API |
Python · GeoPandas |
| 02 | Revealed-preference demographics |
Census ACS 5-Year (2018–2022) — Median HHI · Unemployment · Labor pool · Education · Age · Home value Census 2022 Gazetteer — tract centroids |
Census API |
| 03 | Co-location lift (who clusters with the brand) | Overture Maps Places — Meta/Microsoft/AWS open POI graph; ~258k businesses across 6 states | DuckDB · S3 parquet |
| 04 | Foot-traffic proxies closes the binary-footprint gap |
Google Places (New) — Popular Times · Google Reviews velocity · Yelp Fusion · BLS QCEW · Census LEHD LODES | Places API · BestTime · BLS API |
| 05 | Composite + ranked output | Live-formula XLSX (editable thresholds) · Mapbox GL map · 21-feature candidate table | openpyxl · Mapbox |
Overpass gives you the where: every interstate junction as a coordinate. VDOT's open traffic-volume dataset gives you the how-much: 123,766 segments with Annual Average Daily Traffic + truck percentage, bidirectional, refreshed yearly. Spatial-join the two and every exit gets an audited traffic score.
# 1. Overpass: every interstate exit in a state [out:json][timeout:60]; area["ISO3166-2"="US-VA"]->.a; node["highway"="motorway_junction"](area.a); out body; # 2. VDOT GeoJSON (636 MB) — bidirectional AADT per segment # Each row: route_name, AADT, truck_pct, geometry (LineString) gdf = gpd.read_file("VDOT_Bidirectional_Traffic_Volume_2024.geojson") # 3. For each exit, find the nearest mainline segment on its route # and lift AADT + truck_pct. Distributor-collector ramps are filtered. def match_exit_to_segment(exit_pt, gdf): same_route = gdf[gdf.route_name == exit_pt.route] nearest = same_route.distance(exit_pt.geometry).idxmin() return same_route.loc[nearest, ["AADT", "truck_pct"]]
deterministic Filters applied: weigh stations + rest areas flagged; AADT=0 segments dropped (no DOT match); off-corridor exits excluded for chains with a stated corridor strategy.
Every chain already chose hundreds of trade areas. Their existing footprint is the answer key. Pull Census ACS 5-Year demographics for the catchment around every current location, build a control profile (p25 / median / p75 on each metric), then score candidates on how closely they match.
Mechanically: every existing Waffle House is a positive label, every public-data feature around it is an input, and the p25–p75 band on each metric is the fitted preference. A naïve "look for high HHI" filter screens out WH's actual sweet spot (median HHI $69,735, well below the national "good trade area" threshold). The brand has spent 70 years revealing what works — let the model learn from that, not from a consultant's intuition.
Candidate score: +10 points if four of the six metrics fall within the band, +5 points if three. Hand-tuned weights; thresholds editable in the Score_Breakdown tab of the bundle. Honest caveat: every existing store contributes 1 regardless of whether it's a top performer or marginal. Layer 04 closes that gap.
deterministic
Census ACS 5-Year Estimates (2018–2022) · 294 control catchments · 10-mi catchment radius · api.census.gov (free, no key for <500 req/day).
Overture Maps Places is the open POI graph published by Meta, Microsoft, AWS, and TomTom — every business with a name, category, and coordinates. One DuckDB query against the S3-hosted parquet gives you 258k business points across six states for ~$2 of egress. Compare the brand's 1-mi POI density to a random-exit baseline and the lift ratio surfaces every co-location the brand's site-selection committee already knows about.
# Overture Places via DuckDB (no download — read parquet directly from S3) INSTALL httpfs; LOAD httpfs; SELECT id, names.primary AS name, brands[1].names.primary AS brand, categories.primary AS cat, ST_Y(geometry) AS lat, ST_X(geometry) AS lon FROM read_parquet( 's3://overturemaps-us-west-2/release/2026-03-18.0/theme=places/type=place/*') WHERE bbox.xmin BETWEEN -84.5 AND -75.5 AND bbox.ymin BETWEEN 34.5 AND 40.0; # ~258k rows across 6 states · ~30s query · ~$2 of S3 egress
deterministic Overture Maps 2026-03-18 release · 538 existing WHs anchored · baseline = uniform-random exits with no WH within 5 mi · 1-mi co-location radius · lift = (WH 1-mi mean POI count) ÷ (baseline 1-mi mean POI count). Hand-tuned bonus: STRONG lift (≥3x) = +5 pts/category.
The regression above treats every existing location as a single positive vote. But a thriving Lynchburg Waffle House and a marginal rural one both contribute 1 to the training set. Real site selection weights by store quality, and quality comes from sales data the chain owns and won't share. So we triangulate from public-data proxies that correlate with store quality.
Placer.ai, SafeGraph Patterns, and Foursquare Movement sell the gold-standard answer — anonymized cell-phone foot traffic per location, $25K+/yr. The public-data stack below gets you 60–70% of the signal for free or near-free. Pair it with the binary-regression model above and you've got a defensible "starting-point" ranking that any chain's real-estate team can interrogate.
Google's public busy-ness signal — 168 hourly values per location plus a live currentPopularity. Exposed via the Places API (New) popularityScore field (preview), or the open-source populartimes library, or BestTime.app as a wrapper. Single best free proxy for raw foot traffic.
Same Places API call returns review count + average rating. New reviews / month at each existing location is a fresh-foot-traffic proxy that's harder to game than absolute count — a 2026 location with 800 reviews is more active than a 2014 location with 1,200. Free.
Place Details · reviews →Independent panel from Google — review count, rating, transaction tags ("delivery", "reservations"), price tier. Free tier ~500 calls/day. Combine Yelp + Google review velocity to triangulate around platform-specific biases.
docs.developer.yelp.com →Quarterly Census of Employment & Wages — county-level establishment counts, payroll, and employment by 6-digit NAICS (e.g. 722511 limited-service restaurants). Tells you which counties have category demand surplus. Free, quarterly, BLS-grade quality.
Origin-destination employment data at block-group level — workplace vs residence vs commuter flows. Surfaces daytime population concentrations and commuter corridors that explain why an I-81 stop posts mid-day numbers. Free.
lehd.ces.census.gov →The gold standard — cell-phone-derived visits, dwell time, trade-area capture rates, cross-shopping. $25K+/yr for Placer; SafeGraph Patterns has limited academic free tier. Worth knowing about even if you'll never buy: it sets the ceiling on how much signal foot-traffic proxies can recover.
placer.ai → · safegraph.com →
The Waffle House × SW Virginia worked example uses Layers 01–03 only. Layer 04 is the documented path to upgrade the regression's positive labels from 1 (presence) to a continuous quality weight. The method is the same, just with each existing store's vote scaled by its outside-in foot-traffic estimate.
No proprietary feeds. No "trust me, the data exists." Every row in the candidate table traces back to one of these public sources. Click through to verify any number.
The method is sector-agnostic: restaurants, dealers, big-box, specialty retail, anything with a consumer-facing storefront and an existing footprint. Replace the inputs, keep the structure. Full reproduction guide in the downloadable bundle.
Scrape the brand's store locator. You need lat / lon / name / city for every current location. Snap-to-exit (5-mi buffer) gives you the "interstate set" — the control group.
e.g. locations.wafflehouse.com →Each state has an open traffic-counts dataset. VDOT for VA, NCDOT for NC, TDOT for TN, etc. AADT + truck % + segment geometry. Free GeoJSON downloads.
virginiaroads.org · VDOT AADT →Median HHI, unemployment, labor pool, education, age — all available at tract level via the Census API. Build the control profile from existing locations, score candidates against it.
api.census.gov · ACS 5-Year →One DuckDB query against the S3-hosted Overture parquet returns every business POI in your bounding box. ~30 seconds, ~$2 of S3 egress, ~258k businesses across six states.
overturemaps.org →Composite = w1·traffic + w2·demo + w3·colocation. Hand-tune on a held-out sample of known good/bad sites the brand has already accepted/rejected. Or expose the weights as editable XLSX formulas (we do this).
openpyxl docs →Wrote the scrapers, the spatial joins, the threshold scoring, and the audit reconciliation. The pipeline took two evenings end-to-end on a 6-state region.
claude.com/product/claude-code →Live-formula XLSX (editable thresholds), the merged JSON the site reads, the method write-up, and a self-contained Mapbox HTML copy of the candidate map. CC BY 4.0 — use it, audit it, run it on your own brand.