Location Analysis Explained: Catchments, Accessibility and Site Selection
Problem statement
Location analysis is the part of GIS that answers practical questions about places people go to: which residents a pharmacy serves, who lives too far from one, where a new one could go, and where several should go so the whole county is served. Each question sounds like the others, and each has its own method with its own way of being wrong.
The differences are not academic. Measured on one county โ Chittenden County, Vermont, with 168,323 residents in 2,241 populated census blocks, 27 pharmacies in and around it, and the OpenStreetMap drive network:
- Choosing where to add one pharmacy gave two sites 0.8 km apart when the goal was "reach the most new people within 10 minutes" or "shorten the average trip", and a third site 16.0 km away when the goal was "shorten the longest trip".
- Ranking tracts by how badly served they are with three standard accessibility measures put 8 tracts in the worst fifth each time โ but only 6 tracts were in the worst fifth under all three.
- Straight-line distance said 33.0% of residents were within 1 km of a pharmacy; along the road network it was 14.6%.
The method you pick is a statement about what the analysis values. This guide maps the questions to the methods, so the choice is deliberate.
Quick answer
question method output
who does this facility serve? catchment: buffer, isochrone, population per facility
Voronoi or network assignment
how well served is each place? accessibility: nearest, a score per home or area
cumulative, gravity, 2SFCA
how much custom will a site draw? gravity or Huff model a share per store
where could a facility go at all? site suitability a score per cell
where should k facilities go together? location-allocation: p-median, a set of k sites
coverage, p-centre
All of them start from the same inputs: where people are, where facilities are or could be, and a cost of travelling between them. Get those right first โ a network travel time is usually worth the effort over a straight line โ and every method downstream inherits the improvement.
Step-by-step solution
1. Represent demand as small populated units
People are not spread evenly across a county, and the unit you use for them decides every distance you compute. Census blocks โ here 2,241 populated blocks, each as a point inside the block โ are small enough that a home's distance to a facility is close to its block's. Tracts and block groups work, but each point then stands in for thousands of people spread over a large area.
2. Get facilities from a source you can check
OpenStreetMap is the usual open source for pharmacies, supermarkets, schools and fire stations. On 11 September 2026 the public Overpass API refused connections and then returned HTTP 504, so the facilities here came from a Geofabrik extract of Vermont instead. Mapped facilities also include duplicates โ a shop recorded as both a point and a building outline โ which must be removed before any method that assumes one point per facility.
3. Build a travel cost, not a straight line
Straight-line distance is fast and consistently optimistic. For the county's residents, the population-weighted median distance to the nearest pharmacy was 1,381 m in a straight line and 2,342 m along the drive network, and for 13.4% of residents the nearest pharmacy by road was a different pharmacy. On the network, the median drive to the nearest pharmacy was 2.8 minutes and the longest 22.9 minutes.
4. Answer "who does it serve?" with a catchment
A catchment either counts everyone within reach of a facility โ a buffer or an isochrone โ or assigns every resident to one facility, as a Voronoi cell does. The median pharmacy served 2,528 people inside a 1 km circle, 803 within a 1 km walk and 5,850 in its Voronoi cell. Say which one a number comes from.
5. Answer "how well served?" with an accessibility measure
Accessibility scores each place, not each facility. The simplest is the time to the nearest facility; a cumulative measure counts facilities within a time; a gravity measure adds them up with a weight that decays with time. They agree broadly and disagree at the margins that matter for targeting: 16,018 residents, 9.5%, had no pharmacy within 10 minutes, yet under a gravity measure their scores still ranged across a factor of 17.
6. Answer "where could it go?" with suitability
Site suitability combines constraints and graded factors on a grid โ slope, road access, nearby population โ into a score. In the county, constraints removed 14.9% of the land, and summing factors in their raw units instead of rescaled 0โ1 values changed 7 of the 10 best sites.
7. Answer "where should several go?" with location-allocation
Location-allocation chooses a set of sites together, because the best second site depends on where the first one is. Its objectives are distinct โ minimise total travel, maximise the population within a standard, minimise the longest trip โ and step 8 shows they do not agree even for a single site.
8. Write the objective down before choosing a site
Adding one pharmacy to the county, with every populated block as a candidate site:
objective new people within 10 min mean time longest trip
maximise coverage +4,437 3.98 min 22.9 min
minimise mean time +4,330 3.97 min 22.9 min
minimise the longest trip +2,175 4.13 min 21.5 min
Coverage and mean time pointed to neighbouring blocks 0.8 km apart. Minimising the longest trip moved the site 16 km to the county's edge, where it helped fewer people and shortened the worst journey by 1.4 minutes. Each answer is optimal; they optimise different things.
Code examples
Example 1 โ three accessibility measures from one travel-time matrix
import numpy as np
import pandas as pd
def wquantile(values, weights, q):
order = np.argsort(values)
cum = np.cumsum(weights[order]) / weights.sum()
return values[order][np.searchsorted(cum, q)]
def access_measures(T, w, cutoffs=(5, 10, 15), half_lives=(2.5, 5, 10)):
"""Nearest, cumulative and gravity accessibility from an origins x facilities matrix of minutes."""
nearest = T.min(axis=1)
out = {"nearest": nearest}
for c in cutoffs:
out[f"within_{c}"] = (T <= c).sum(axis=1)
for h in half_lives:
out[f"gravity_{h}"] = np.exp(-np.log(2) / h * T).sum(axis=1)
none = out["within_10"] == 0
print(f"nearest: median {wquantile(nearest, w, .5):.1f} min, p90 {wquantile(nearest, w, .9):.1f}, "
f"max {nearest.max():.1f}")
print(f"nothing within 10 min: {w[none].sum():,.0f} people ({w[none].sum() / w.sum():.1%})")
return pd.DataFrame(out)
nearest: median 2.8 min, p90 9.8, max 22.9
nothing within 10 min: 16,018 people (9.5%)
T has one row per home and one column per pharmacy, in minutes of drive time; w is the population of each home. A gravity score with a 5-minute half-life counts a pharmacy 5 minutes away as half a pharmacy.
Example 2 โ the best extra site under three objectives
def best_new_site(nearest, C, w, ids, threshold=10):
"""Best candidate for one extra facility under coverage, mean-time and worst-time objectives."""
new = np.minimum(nearest[:, None], C) # homes x candidates
gain = (w[:, None] * ((new <= threshold) & (nearest[:, None] > threshold))).sum(axis=0)
mean_time = (w[:, None] * new).sum(axis=0) / w.sum()
worst = new.max(axis=0)
picks = {"coverage": int(np.argmax(gain)), "mean time": int(np.argmin(mean_time)),
"worst time": int(np.argmin(worst))}
for name, k in picks.items():
print(f"{name:10} {ids[k]}: +{gain[k]:,.0f} within {threshold} min, "
f"mean {mean_time[k]:.2f} min, worst {worst[k]:.1f} min")
return picks
coverage 500070028004011: +4,437 within 10 min, mean 3.98 min, worst 22.9 min
mean time 500070028004019: +4,330 within 10 min, mean 3.97 min, worst 22.9 min
worst time 500070030001049: +2,175 within 10 min, mean 4.13 min, worst 21.5 min
C is the travel time from every home to every candidate site โ here the 2,240 reachable populated blocks themselves, a 2,240 ร 2,240 matrix that took 1.5 s to compute. Evaluating all candidates is exhaustive for one site; for several sites at once the combinations explode, which is what location-allocation solvers are for.
Example 3 โ do the measures agree on who is worst served?
def worst_fifth_agreement(areas):
"""Areas in the worst-served fifth under each measure, and how many agree."""
q = pd.DataFrame({
"nearest": pd.qcut(-areas["nearest"], 5, labels=False),
"within_10": pd.qcut(areas["within_10"].rank(method="first"), 5, labels=False),
"gravity_5": pd.qcut(areas["gravity_5"], 5, labels=False),
})
worst = {k: set(q.index[q[k] == 0]) for k in q}
print(f"worst fifth per measure: {[len(v) for v in worst.values()]}; "
f"under all three: {len(set.intersection(*worst.values()))}; "
f"under at least one: {len(set.union(*worst.values()))} of {len(areas)}")
return worst
worst fifth per measure: [8, 8, 8]; under all three: 6; under at least one: 11 of 40
areas holds population-weighted means of each measure per census tract. A programme targeted at the worst-served fifth reaches a different set of tracts depending on which measure was chosen, so publish the measure with the list.
Explanation
Why everything depends on the cost matrix
Every method in this family is arithmetic on distances between demand and facilities. An error in those distances โ straight lines where roads bend, a snap to the wrong side of a river, missing facilities across a boundary โ flows unchanged into catchments, scores and chosen sites. The network matrix for 2,241 homes and 27 pharmacies took 51 ms; the 2,240-candidate matrix took 1.5 s. The expensive part is building and checking the network, not using it.
Why the objectives disagree
Minimising mean travel time rewards serving many people a little better, so it favours dense areas. Maximising coverage rewards bringing people inside a threshold, which also favours populated edges of existing catchments. Minimising the longest trip cares only about the single worst-served home, however few people live there. The first two are efficiency objectives and usually agree; the third is an equity objective and usually does not.
Why accessibility measures disagree at the margins
Nearest-facility time ignores choice: a home with one pharmacy 3 minutes away scores the same as one with ten. Cumulative counts ignore distance within the cutoff and jump at its edge. Gravity scores are smooth in both but depend on a half-life that someone has to choose. Across blocks the cumulative and gravity measures correlated at 0.965, while nearest time and cumulative correlated at 0.834, and the worst-served lists diverged accordingly.
Why straight lines are optimistic in a specific way
Roads rarely run directly from a home to a facility, so network distance is longer than straight-line distance almost everywhere. The error is not uniform: it is largest where water, ridges or sparse rural roads force detours, which is where people are already worst served. A straight-line analysis therefore understates exactly the gaps it is usually commissioned to find.
Edge cases or notes
- Demand outside the study area still counts. Residents beyond a boundary use facilities inside it, and vice versa.
- Facilities outside the boundary matter too. For 3.4% of the county's residents, the nearest pharmacy by road was outside the county.
- Free-flow speeds understate travel times at busy hours; state the speed model.
- One unreachable home can break a solver. Remove or fix homes whose snapped node is disconnected before building a matrix.
- Population is not demand. Pharmacy use depends on age and health; weight by the relevant population where you can.
- OSM completeness varies by facility type. Check facility lists against an official register before publishing.
- Results are tied to one year of data. Population, roads and facilities all change; record the vintages.
Internal links
- Catchment areas explained: buffers, isochrones and Voronoi compared โ who a facility serves
- Accessibility measures explained: nearest, cumulative and gravity โ how well each place is served
- Gravity and Huff models explained: predicting where people go โ market share between competing sites
- Site suitability explained: constraints, factors and weights โ where a facility could go
- Location-allocation explained: p-median, coverage and what each optimises โ where several facilities should go
- How to measure distance to the nearest facility for every home โ building the cost that everything uses
- How to find the areas nobody can reach within a travel time โ coverage gaps on a network
- Street networks as graphs explained โ the network underneath the costs
FAQ
What is location analysis in GIS?
The analysis of where facilities are, who they serve and where new ones should go. It covers catchments, accessibility, market share, site suitability and location-allocation, all built on travel costs between people and facilities.
Which method answers which question?
Catchments say who a facility serves; accessibility says how well each place is served; gravity models estimate market share; suitability says where a site could go; location-allocation chooses several sites together.
Do I need a road network, or is straight-line distance enough?
Use the network if you can. Straight-line distance put 33.0% of residents within 1 km of a pharmacy; the road network put 14.6% there.
Why do different optimisation objectives choose different sites?
They value different things. For one extra pharmacy, maximising coverage and minimising mean time chose sites 0.8 km apart, while minimising the longest trip chose a site 16 km away.
How do I identify the worst-served areas?
Compute more than one accessibility measure and compare. Three standard measures each flagged 8 of 40 tracts as the worst fifth, but agreed on only 6.
What data do I need to start?
Population in small units such as census blocks, a facility list you have checked, and a road network with travel times. Everything else is computed from those three.