Comparing Fixed-Radius and Network Catchments

This guide, part of Catchment & Service Area Delineation, sets a circle and a road-network catchment side by side for the same facilities and measures what the circle gets wrong.

Problem Context & Constraints

Fixed-radius catchments persist for good reasons. Some federal and state programs define service areas or shortage designations by straight-line distance. Radius catchments are trivial to compute, need no network data, and are easy to explain at a public meeting. And for a first look at a dense city grid, a circle is not badly wrong.

They fail in predictable places. Barriers: a river without a bridge, a mountain ridge, a limited-access highway or a rail yard makes nearby areas far in travel time. Road sparsity: in rural areas, the road network winds, so the drive time to a point 10 miles away can be 30 minutes or more. Road density: along a highway corridor, areas 20 miles away are reachable in 20 minutes and lie outside the circle. The result is a catchment that includes people who cannot practically reach the facility and excludes people who can.

When a radius is required by regulation, the task is not to replace it but to compute it correctly and report alongside it a network catchment, so readers can see how far the regulatory definition departs from actual access. When a radius is not required, the comparison is an argument for using the network catchment instead. Either way, the comparison is cheap once isochrones exist, and it turns a debate about methods into a table of population counts that planners and program staff can read without any GIS background.

Where the Circle and the Network Disagree A clinic with a ten-mile circle and a twenty-minute drive-time catchment overlaid. The two overlap in the centre. The circle alone covers an area across a river with no nearby bridge, where residents cannot reach the clinic within twenty minutes; these are false inclusions. The drive-time catchment alone extends along a highway beyond the circle; these residents can reach the clinic quickly but the circle excludes them. Population is counted in each of the three zones. Three zones: both, circle only, network only river, no bridge highway corridor network only reachable, excluded circle only included, not reachable dashed: 10-mile radius; shaded: 20-minute drive time

Prerequisites

  • python 3.11
  • geopandas 0.14.4, shapely 2.0.4, pandas 2.2.2, numpy 1.26.4
  • Drive-time polygons per facility, from Drive-Time Isochrone Generation, at thresholds matching the service line
  • Block-level population, or a population raster, for apportioning population within the zones
  • Facility points in a projected metric coordinate reference system appropriate for distance, as discussed in Picking a CRS for Multi-State Surveillance Regions
  • The regulatory definition, if one applies: radius, measurement method and population source

Step-by-Step Solution

Build the circle in a distance-preserving projection, intersect circle and network catchment into three zones, count population in each from block points, and summarise agreement per facility.

# Compare fixed-radius and drive-time catchments per facility.
# Pinned: geopandas==0.14.4, shapely==2.0.4, pandas==2.2.2, numpy==1.26.4
import logging

import numpy as np
import pandas as pd
import geopandas as gpd

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("radius_vs_network")

MILE_M = 1609.344


def radius_catchments(facilities: gpd.GeoDataFrame, miles: float) -> gpd.GeoDataFrame:
    """Circles built in an azimuthal equidistant projection centred on each facility."""
    out = []
    for _, f in facilities.to_crs(4326).iterrows():
        aeqd = f"+proj=aeqd +lat_0={f.geometry.y} +lon_0={f.geometry.x} +units=m"
        pt = gpd.GeoSeries([f.geometry], crs=4326).to_crs(aeqd)
        circle = pt.buffer(miles * MILE_M, resolution=64).to_crs(facilities.crs)
        out.append({"facility_id": f["facility_id"], "geometry": circle.iloc[0]})
    return gpd.GeoDataFrame(out, crs=facilities.crs)


def zones(circle, network) -> dict:
    return {"both": circle.intersection(network),
            "circle_only": circle.difference(network),
            "network_only": network.difference(circle)}


def compare(facilities, network_polys, blocks: gpd.GeoDataFrame, miles: float) -> pd.DataFrame:
    circles = radius_catchments(facilities, miles).set_index("facility_id")
    net = network_polys.set_index("facility_id")
    pts = blocks.copy()
    pts["geometry"] = pts.geometry.representative_point()
    rows = []
    for fid in circles.index.intersection(net.index):
        z = zones(circles.at[fid, "geometry"], net.at[fid, "geometry"])
        pop = {k: float(pts.loc[pts.within(g), "population"].sum()) for k, g in z.items()}
        union = sum(pop.values())
        rows.append({"facility_id": fid, **{f"pop_{k}": v for k, v in pop.items()},
                     "jaccard": pop["both"] / union if union else np.nan,
                     "false_inclusion": pop["circle_only"] / (pop["both"] + pop["circle_only"] or np.nan),
                     "missed": pop["network_only"] / (pop["both"] + pop["network_only"] or np.nan)})
    df = pd.DataFrame(rows)
    log.info("facilities=%d median population Jaccard=%.2f worst=%.2f",
             len(df), df["jaccard"].median(), df["jaccard"].min())
    return df

Building each circle in an azimuthal equidistant projection centred on its facility guarantees the radius is exact in every direction. A buffer in a state plane or UTM projection is accurate near the projection’s centre and drifts elsewhere; in Web Mercator it is badly wrong at most latitudes. Regulations that specify miles expect true miles.

Population-weighted comparison matters more than area. Two catchments that differ by a large area of empty forest agree for every practical purpose; two that differ by a few city blocks can disagree by thousands of people. Counting block representative points in each zone gives the population answer directly.

The three summary numbers answer different questions. The population Jaccard index says how interchangeable the two catchments are overall. The false inclusion rate says how much of the circle’s population is counted as served without practical access — the number that matters when the circle is used to argue an area is adequately covered. The missed share says how much of the reachable population the circle ignores — the number that matters when the circle defines who a program will contact. A facility can score well on one and badly on another, so report all three.

Agreement Falls With Road Sparsity Bars show the population-weighted overlap between ten-mile radius and twenty-minute drive-time catchments for clinics in four settings. Urban core clinics agree at about zero point eight. Suburban clinics agree at about zero point seven. Small-town clinics agree at about zero point five. Rural clinics agree at about zero point three, because winding roads make much of the circle unreachable in twenty minutes. The radius is a tolerable proxy in cities and a poor one in the countryside. Population overlap, 10-mile radius vs 20-minute drive 0.80 urban core 0.70 suburban 0.50 small town 0.30 rural population-weighted Jaccard index: shared population over combined population

Validation & Edge Cases

Mismatched thresholds. Comparing a 10-mile circle with a 10-minute drive is comparing two different sizes, not two methods. Calibrate the pair: find the drive time whose catchment population matches the circle’s median population across facilities, then compare shapes at matched size. Otherwise the comparison mostly reflects the threshold choice.

Isochrone quality. A drive-time polygon generated as a concave hull of reachable nodes can swallow unreachable pockets or miss reachable spurs. Check a sample against observed travel times, as in Validating Isochrones Against Observed Travel Times, before treating the network catchment as the reference.

Blocks straddling a boundary. A representative point inside a large rural block assigns the whole block to one zone. For large blocks crossing a zone edge, apportion by the share of block area or, better, by a population raster, and report which method was used.

Facilities that move or add sites. A clinic that relocates across town keeps its name and identifier but changes both catchments. Tie each comparison to a dated facility location, and rerun it when the inventory changes rather than carrying forward an old result under the same identifier.

Water and uninhabited land. Circles over lakes and national forests inflate area comparisons but not population ones. This is another reason to compare by population.

Regulatory measurement rules. Some programs specify how distance is measured — from the facility’s street address, from a ZIP code centroid, or to the population-weighted centroid of each area rather than to every block. Implement the rule as written for the regulatory figure, even where a better method exists, and report the better method beside it.

Traffic. Free-flow drive times make the network catchment look larger than it is at the hours patients travel. For clinics with daytime hours, use typical weekday travel times, or see Modeling Peak-Hour Travel Time Variation.

Overlapping facilities. With several clinics close together, each circle and each drive-time polygon overlaps its neighbours, and summing catchment populations double-counts residents. For facility-level comparison that is fine. For a system-wide coverage figure, dissolve all circles and all network polygons first, then compare the two unions.

Populations without cars. A drive-time catchment assumes access to a car. In neighbourhoods where many households have no vehicle, both the circle and the drive-time polygon overstate practical access. Where vehicle availability is low, add a transit or walking catchment to the comparison, since that is the one that describes the residents most likely to be under-served.

INFO radius_vs_network facilities=64 median population Jaccard=0.58 worst=0.21
INFO radius_vs_network worst: clinic 3107 (river crossing), false_inclusion=0.62 missed=0.08
Reporting a Required Radius Honestly Two boxes side by side for one facility. The regulatory figure uses the ten-mile radius as required, with its population. The access figure uses the twenty-minute drive-time catchment with its population. Beneath them, a single line states how many residents the radius includes who cannot reach the facility in twenty minutes, and how many it excludes who can. Compute the rule as written, show the reality beside it Regulatory: 10-mile radius population 84,200 as defined by the program Access: 20-minute drive population 61,900 typical weekday traffic radius includes 29,400 who cannot reach it in 20 minutes; excludes 7,100 who can one sentence that lets a reader judge the regulatory definition

Compliance Notes

Record the radius and measurement rule, the projection used to build circles, the network source, date and travel-time assumptions for the drive-time catchment, the population source and apportionment method, and the agreement statistics per facility.

When a fixed-radius service area is submitted for a designation or grant, the submission must follow the program’s rules exactly; the network comparison is supporting information, not a substitute. Keep the two clearly labelled so a reviewer cannot mistake one for the other, and keep the network catchment available for planning decisions the regulation does not govern.