Channel strategy · A field note

The next best location is hiding in public data.

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.

197 exits scored
4 public datasets
1 overnight run
Top pick: 98.3 / 100
Question

If Waffle House could open one new location in Southwestern Virginia, where should it go?

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?

Answer

Exit 150B / 150A · I-81 · Lynchburg

98.3
FINAL composite
(out of 100)
5.8 mi
Gap from nearest existing WH
(low cannibalization risk)
56k
AADT mainline traffic
34% trucks
153k
Population within 10 mi
just below peer median

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.

197 exits, ranked, mapped.

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.

Regression on revealed preference, scored against public data.

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
Claude Code · Overpass API · virginiaroads.org · api.census.gov · overturemaps.org · Google Places API · Mapbox GL

Every exit, every direction, every AADT segment.

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.

Don't guess what demographics matter. Measure the brand's existing stores.

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.

The control profile is the regression.

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).

What businesses cluster with the brand — at lift ratios that violate chance.

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.

From "is there" to "is thriving" — the binary-footprint problem.

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.

This is the outside-in poor-man's Placer.ai.

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 Maps Popular Times

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.

developers.google.com/maps/places →

Google Reviews velocity

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 →

Yelp Fusion

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 →

BLS QCEW

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.

bls.gov/cew →

Census LEHD LODES

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 →

Placer.ai / SafeGraph / Foursquare

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.

Every dataset, every link, every version.

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.

Five steps to score the next location for any chain.

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.

1 · Existing locations

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 →

2 · State DOT traffic

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 →

3 · Census ACS demographics

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 →

4 · Overture Places (co-location)

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 →

5 · Tune the weights

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 →

+ · Claude Code

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 →

Download the full data bundle.

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.

↓ next-best-location-bundle.zip
Snapshot 2026-04-13 · 197 exits scanned · 486-location control set · 21-row co-location lift table · ~140 KB zipped