Veterans live a veteran-weighted average of ~16 miles from their nearest VA facility, but the gap ranges from under 2 miles in DC to nearly 100 miles in Alaska
Straight-line distance from each county's population centroid to the nearest of 1,046 VHA facilities (VAMC/CBOC/Vet Center/nursing home/RRTP), weighted by 2023 ACS county veteran counts
Summary
Weighting by where the nation's veterans actually live (2023 ACS county veteran counts), the average veteran's nearest VA facility is about 15.9 miles away, as a straight line from their county's population center. 81% of veterans live within 25 miles of a facility and 97% within 50 miles, but the picture varies enormously by geography: veterans in Washington, DC average under 2 miles to a facility, while veterans in Alaska average 96 miles — and several Alaska island communities (Aleutians West, Ketchikan, Sitka, Juneau) sit 500-1,250 miles from the nearest facility as the crow flies, since they have no road connection to the mainland network. Outside Alaska, the next-farthest states are large, low-density Mountain/Plains states (North Dakota, Wyoming, Montana, South Dakota, Nevada, Idaho) averaging roughly 25-35 miles — a rural-access gap of roughly 4x versus the dense Northeast corridor.
Method
Facility universe: lands.va_facilities, HIFLD's geocoded roster of 1,046 live Veterans Health Administration facilities — VA medical centers, community-based outpatient clinics, Vet Centers, VA nursing homes, and residential rehab programs. All 1,046 rows carry non-null latitude/longitude (verified: 0 of 1,046 have missing coordinates), so the latitude IS NOT NULL AND longitude IS NOT NULL filter used in every query below excluded no facilities in practice — it is a defensive filter, not an actual exclusion. "Nearest facility" here means the nearest of ANY of these types, which best matches how most veterans actually access VA care (a CBOC or Vet Center, not necessarily a full medical center).
Distance: great-circle (haversine) distance in miles from each county's Census Gazetteer population-weighted centroid (geo.gazetteer_counties, 2023 vintage) to the nearest facility's coordinates. This is a straight-line proxy, not drive time or drive distance — actual road/ferry distance in rural and non-contiguous areas (all of coastal Alaska, mountainous terrain) will be substantially longer than the straight-line figure reported here, so these numbers understate real-world travel burden, especially in Alaska.
Weighting: each county's minimum distance is weighted by its 2023 ACS 5-year veteran population (census.acs_veterans) to produce population-weighted national and state averages, rather than treating every county (regardless of how many veterans live there) equally. An unweighted county average is 33.1 miles — nearly double the veteran-weighted figure — because many low-veteran-population rural counties sit far from a facility while pulling the simple county average up; weighting by where veterans live is the right denominator for "how far is the average veteran," and is reported here for that reason.
Coverage caveat: 3,222 of 3,222 counties in the 2023 Gazetteer file successfully matched to both a facility distance and a 2023 ACS veteran count (no counties were dropped for missing centroid or veteran data at this join). ACS county veteran figures are 2023 5-year-estimate values (most recent available; the source publishes ~2 years behind, so no 2024-2026 vintage exists yet in this corpus).
Nearest-facility distance distribution (veteran-weighted)
| Within 10 miles | 45.7% |
| Within 25 miles | 80.7% |
| Within 50 miles | 96.7% |
| Over 100 miles | 0.2% |
| Over 100 miles | 0.2% |
| Within 50 miles | 96.7% |
| Within 25 miles | 80.7% |
| Within 10 miles | 45.7% |
| Within 50 miles | 96.7% |
| Within 25 miles | 80.7% |
| Within 10 miles | 45.7% |
| Over 100 miles | 0.2% |
The over-100-mile tail is small in population terms but concentrated: it is almost entirely Alaska's non-road-connected boroughs.
Farthest counties (straight-line)
| Aleutians West Census Area | AK | 1,252 mi |
| Prince of Wales-Hyder Census Area | AK | 659 mi |
| Petersburg Borough | AK | 653 mi |
| Wrangell City and Borough | AK | 652 mi |
| Ketchikan Gateway Borough | AK | 591 mi |
| Sitka City and Borough | AK | 579 mi |
| Juneau City and Borough | AK | 570 mi |
| Aleutians East Borough | AK | 537 mi |
| Hoonah-Angoon Census Area | AK | 516 mi |
| Haines Borough | AK | 503 mi |
| Aleutians East Borough | AK | 537 mi |
| Aleutians West Census Area | AK | 1,252 mi |
| Haines Borough | AK | 503 mi |
| Hoonah-Angoon Census Area | AK | 516 mi |
| Juneau City and Borough | AK | 570 mi |
| Ketchikan Gateway Borough | AK | 591 mi |
| Petersburg Borough | AK | 653 mi |
| Prince of Wales-Hyder Census Area | AK | 659 mi |
| Sitka City and Borough | AK | 579 mi |
| Wrangell City and Borough | AK | 652 mi |
| Aleutians West Census Area | AK | 1,252 mi |
| Prince of Wales-Hyder Census Area | AK | 659 mi |
| Petersburg Borough | AK | 653 mi |
| Wrangell City and Borough | AK | 652 mi |
| Ketchikan Gateway Borough | AK | 591 mi |
| Sitka City and Borough | AK | 579 mi |
| Juneau City and Borough | AK | 570 mi |
| Aleutians East Borough | AK | 537 mi |
| Hoonah-Angoon Census Area | AK | 516 mi |
| Haines Borough | AK | 503 mi |
| Aleutians West Census Area | AK | 1,252 mi |
| Prince of Wales-Hyder Census Area | AK | 659 mi |
| Petersburg Borough | AK | 653 mi |
| Wrangell City and Borough | AK | 652 mi |
| Ketchikan Gateway Borough | AK | 591 mi |
| Sitka City and Borough | AK | 579 mi |
| Juneau City and Borough | AK | 570 mi |
| Aleutians East Borough | AK | 537 mi |
| Hoonah-Angoon Census Area | AK | 516 mi |
| Haines Borough | AK | 503 mi |
All 10 farthest U.S. counties are in Alaska, driven by island/coastal communities with no road link to the facility network centered on Anchorage and Fairbanks — real travel distance for these communities is typically by air or ferry, so the true access burden is understated by a straight-line number, if anything.
Every query behind this report
7 warehouse calls ran in this session, in order. Each is reproducible against the same snapshot.
critique_query — 124 ms
WITH c AS (
SELECT county_fips, state_fips, latitude AS clat, longitude AS clon
FROM geo.gazetteer_counties WHERE "year" = '2023'
),
f AS (
SELECT station_number, latitude AS flat, longitude AS flon
FROM lands.va_facilities WHERE latitude IS NOT NULL AND longitude IS NOT NULL
),
d AS (
SELECT c.county_fips, c.state_fips,
MIN(3958.8 * acos(LEAST(1.0, GREATEST(-1.0,
sin(radians(c.clat))*sin(radians(f.flat)) +
cos(radians(c.clat))*cos(radians(f.flat))*cos(radians(f.flon - c.clon))
)))) AS min_dist_miles
FROM c CROSS JOIN f
GROUP BY c.county_fips, c.state_fips
)
SELECT count(*) AS n_counties, avg(min_dist_miles) AS avg_dist, min(min_dist_miles) AS min_d, max(min_dist_miles) AS max_d
FROM dquery — 1 rows — 4031 ms
WITH c AS (
SELECT county_fips, state_fips, latitude AS clat, longitude AS clon
FROM geo.gazetteer_counties WHERE "year" = '2023'
),
f AS (
SELECT station_number, latitude AS flat, longitude AS flon
FROM lands.va_facilities WHERE latitude IS NOT NULL AND longitude IS NOT NULL
),
d AS (
SELECT c.county_fips, c.state_fips,
MIN(3958.8 * acos(LEAST(1.0, GREATEST(-1.0,
sin(radians(c.clat))*sin(radians(f.flat)) +
cos(radians(c.clat))*cos(radians(f.flat))*cos(radians(f.flon - c.clon))
)))) AS min_dist_miles
FROM c CROSS JOIN f
GROUP BY c.county_fips, c.state_fips
)
SELECT count(*) AS n_counties, avg(min_dist_miles) AS avg_dist, min(min_dist_miles) AS min_d, max(min_dist_miles) AS max_d
FROM dquery — 1 rows — 5006 ms
WITH c AS (
SELECT county_fips, state_fips, latitude AS clat, longitude AS clon
FROM geo.gazetteer_counties WHERE "year" = '2023'
),
f AS (
SELECT station_number, latitude AS flat, longitude AS flon
FROM lands.va_facilities WHERE latitude IS NOT NULL AND longitude IS NOT NULL
),
d AS (
SELECT c.county_fips, c.state_fips,
MIN(3958.8 * acos(LEAST(1.0, GREATEST(-1.0,
sin(radians(c.clat))*sin(radians(f.flat)) +
cos(radians(c.clat))*cos(radians(f.flat))*cos(radians(f.flon - c.clon))
)))) AS min_dist_miles
FROM c CROSS JOIN f
GROUP BY c.county_fips, c.state_fips
),
v AS (
SELECT county_fips, veterans FROM census.acs_veterans WHERE "year" = '2023'
)
SELECT
sum(d.min_dist_miles * v.veterans) / sum(v.veterans) AS weighted_avg_dist,
sum(v.veterans) AS total_veterans_matched,
count(*) AS n_counties_matched
FROM d JOIN v ON d.county_fips = v.county_fipsquery — 1 rows — 4850 ms
WITH c AS (
SELECT county_fips, state_fips, latitude AS clat, longitude AS clon
FROM geo.gazetteer_counties WHERE "year" = '2023'
),
f AS (
SELECT station_number, latitude AS flat, longitude AS flon
FROM lands.va_facilities WHERE latitude IS NOT NULL AND longitude IS NOT NULL
),
d AS (
SELECT c.county_fips, c.state_fips,
MIN(3958.8 * acos(LEAST(1.0, GREATEST(-1.0,
sin(radians(c.clat))*sin(radians(f.flat)) +
cos(radians(c.clat))*cos(radians(f.flat))*cos(radians(f.flon - c.clon))
)))) AS min_dist_miles
FROM c CROSS JOIN f
GROUP BY c.county_fips, c.state_fips
),
v AS (
SELECT county_fips, state, veterans FROM census.acs_veterans WHERE "year" = '2023'
),
j AS (
SELECT d.*, v.veterans FROM d JOIN v ON d.county_fips = v.county_fips
)
SELECT
sum(CASE WHEN min_dist_miles <= 10 THEN veterans ELSE 0 END)*1.0/sum(veterans) AS pct_within_10,
sum(CASE WHEN min_dist_miles <= 25 THEN veterans ELSE 0 END)*1.0/sum(veterans) AS pct_within_25,
sum(CASE WHEN min_dist_miles <= 50 THEN veterans ELSE 0 END)*1.0/sum(veterans) AS pct_within_50,
sum(CASE WHEN min_dist_miles > 100 THEN veterans ELSE 0 END)*1.0/sum(veterans) AS pct_over_100
FROM jquery — 10 rows — 2995 ms
WITH c AS (
SELECT county_fips, state_fips, county_name, latitude AS clat, longitude AS clon
FROM geo.gazetteer_counties WHERE "year" = '2023'
),
f AS (
SELECT station_number, latitude AS flat, longitude AS flon
FROM lands.va_facilities WHERE latitude IS NOT NULL AND longitude IS NOT NULL
),
d AS (
SELECT c.county_fips, c.state_fips, c.county_name,
MIN(3958.8 * acos(LEAST(1.0, GREATEST(-1.0,
sin(radians(c.clat))*sin(radians(f.flat)) +
cos(radians(c.clat))*cos(radians(f.flat))*cos(radians(f.flon - c.clon))
)))) AS min_dist_miles
FROM c CROSS JOIN f
GROUP BY c.county_fips, c.state_fips, c.county_name
)
SELECT state_fips, county_name, min_dist_miles FROM d ORDER BY min_dist_miles DESC LIMIT 10query — 51 rows — 7182 ms
WITH c AS (
SELECT county_fips, state_fips, latitude AS clat, longitude AS clon
FROM geo.gazetteer_counties WHERE "year" = '2023'
),
f AS (
SELECT station_number, latitude AS flat, longitude AS flon
FROM lands.va_facilities WHERE latitude IS NOT NULL AND longitude IS NOT NULL
),
d AS (
SELECT c.county_fips, c.state_fips,
MIN(3958.8 * acos(LEAST(1.0, GREATEST(-1.0,
sin(radians(c.clat))*sin(radians(f.flat)) +
cos(radians(c.clat))*cos(radians(f.flat))*cos(radians(f.flon - c.clon))
)))) AS min_dist_miles
FROM c CROSS JOIN f
GROUP BY c.county_fips, c.state_fips
),
v AS (
SELECT county_fips, veterans FROM census.acs_veterans WHERE "year" = '2023'
),
j AS (
SELECT d.*, v.veterans FROM d JOIN v ON d.county_fips = v.county_fips
)
SELECT s.state_abbr,
sum(j.min_dist_miles*j.veterans)/sum(j.veterans) AS weighted_avg_dist,
sum(j.veterans) AS veterans
FROM j JOIN geo.state_ref s ON j.state_fips = s.state_fips
GROUP BY s.state_abbr
ORDER BY weighted_avg_dist DESCquery — 1 rows — 1199 ms
SELECT count(*) AS total, sum(CASE WHEN latitude IS NULL OR longitude IS NULL THEN 1 ELSE 0 END) AS missing_coords FROM lands.va_facilitiesSources
- lands.va_facilities — HIFLD VHA Medical Facilities roster
Show SQL
SELECT station_number, facility_name, latitude, longitude FROM lands.va_facilities WHERE latitude IS NOT NULL AND longitude IS NOT NULL - census.acs_veterans — 2023 ACS 5-year veteran population by county
Show SQL
SELECT county_fips, veterans FROM census.acs_veterans WHERE "year" = '2023' - geo.gazetteer_counties — 2023 county population-weighted centroids
Show SQL
SELECT county_fips, state_fips, latitude, longitude FROM geo.gazetteer_counties WHERE "year" = '2023'