How the Huff model forecasts visits for a new store

A practitioner's walk-through of Huff gravity forecasts in MapDove: utilities, the outside option, calibration and a worked example.

Also available in: العربية, Español

Every site selection conversation eventually reaches the same question: if we open here, how many people will walk through the door? In MapDove that forecast comes from a calibrated Huff gravity model. This post covers what the model does, where its assumptions sit, and how to read the numbers it returns.

The idea: shoppers split their trips by attraction and distance

The Huff model treats every origin, which for us is a hex cell of roughly 0.8 km², as a pool of demand. That demand gets divided among every store in the category that residents could plausibly reach. Each store gets a utility for each hex. Bigger or stronger stores have higher utility, and utility falls as distance grows.

U_ij = A_j / d_ij^λ

P_ij = U_ij / ( Σ_k U_ik + u0 )
  • A_j is the store’s attractiveness.
  • d_ij is the distance from hex i to store j in km. It is floored at 0.4 km so a store sitting on top of a hex centroid doesn’t get infinite utility.
  • λ is the category’s distance-decay exponent. Higher values mean shoppers are less willing to travel. In the demo dataset fuel & convenience uses 2.6, coffee 2.2, grocery 1.8 and shopping centers 1.3.
  • P_ij is the probability that a trip starting in hex i goes to store j.

A store only competes for hexes inside the category’s trade radius R: 5 km for fuel, 6 km for coffee, 9 km for grocery, 25 km for shopping centers.

The outside option: why probabilities don’t sum to one

A textbook Huff model assumes every trip lands at one of the modeled stores. That breaks at the edge of a city. A household 5 km from the nearest café is not guaranteed to visit a café you know about. It might brew at home, buy coffee near the office or go somewhere outside the dataset.

So MapDove adds an outside option to the denominator:

u0 = 1 / (0.6 · R)^λ

Read u0 as the utility of a standard store at 60% of the trade radius. Where a hex is well served, the store utilities swamp u0 and almost all demand is captured. In thin areas, u0 holds a meaningful share. This one term does three jobs. It stops sparse suburbs from producing inflated forecasts. It gives a new store somewhere to win net new demand from. And it keeps cannibalization estimates honest (more on that in the cannibalization guide).

Demand and attractiveness

Each hex’s demand is a blend of residents and daytime population:

demand_i = population_i + 0.4 × daytime_population_i

The 0.4 weight reflects that workers and visitors shop near where they spend the day, but less than residents shop near home.

For a candidate site, attractiveness comes from its size relative to a typical store in the category, scaled by brand strength:

A = (sqft / category_base_sqft)^0.7 × brand_strength      (brand_strength 0.6 – 1.5)

The 0.7 exponent captures diminishing returns: double the floor plate and attractiveness rises by about 62%, not 100%. Brand strength is a judgment call: 1.2–1.4 for a well-loved chain, below 1.0 for an unknown independent.

Calibration: turning model units into visits

Raw Huff output is “expected demand captured”, which is in population units, not visits. To convert it, MapDove calibrates against the stores that already exist:

k = Σ observed avg daily visits (last 90 days, existing stores)
    ÷ Σ modeled expected demand of those same stores

predicted daily visits = k × modeled demand captured by the candidate

k is a single scalar for the city–category pair. It absorbs everything the gravity model doesn’t represent: trip frequency, conversion and how often a resident visits that category at all. Because it is fitted to recent visits in the demo dataset, the forecast reflects how stores in that market perform.

An illustrative worked example

Take one hex in a coffee market (λ = 2.2, R = 6 km) with 4,000 residents and 6,000 daytime workers. Its demand is 4,000 + 0.4 × 6,000 = 6,400. The outside option is u0 = 1 / 3.6^2.2 ≈ 0.060.

Two cafés already serve it. Bean Republic is 1.0 km away with A = 1.2. Copperline Coffee is 2.0 km away with A = 1.0. We test a candidate with 2,200 sq ft (category base is 1,800) and brand strength 1.1, located 0.8 km from the hex. Its attractiveness is (2,200/1,800)^0.7 × 1.1 ≈ 1.27.

Option Utility Share before Share after
Bean Republic (1.0 km) 1.200 81.2% 33.9%
Copperline (2.0 km) 0.218 14.7% 6.1%
Candidate (0.8 km) 2.068 – 58.3%
Outside option 0.060 4.0% 1.7%

The candidate captures 58.3% × 6,400 ≈ 3,733 demand units from this hex. With an illustrative k of 0.0138, that comes to about 51 visits per day from this one cell. MapDove repeats this calculation for every hex within 6 km and sums the results.

Look at where those 51 visits come from: about 42 from Bean Republic, 7.6 from Copperline and only about 2 from the outside option. In a dense, well-served hex, nearly everything the new store wins is transferred, not new. That is the most useful thing the model will tell you.

Reading the output

A Huff run in Site Selection returns:

  • Predicted daily and annual visits, already calibrated with k.
  • Market share: the candidate’s share of modeled category demand after it opens.
  • Category average daily visits, so you can tell whether the forecast is plausible for the format.
  • Total cannibalized visits and the most affected existing stores, with distances.
  • A forecast trade area: the hexes that make up the first 80% of the candidate’s modeled visitors.

A forecast well above the category average deserves suspicion. Check whether a generous brand strength or an oversized floor plate is doing the work.

Practical takeaways

  • Treat brand strength as a scenario input. Run 0.8, 1.0 and 1.2 and present a range, not a single number.
  • Compare the forecast with the category average before you trust it.
  • Always read the cannibalization list next to the headline. A high forecast that is mostly transferred demand may add nothing at network level.
  • Remember the model’s limits. It knows distance, size and competition. It does not know about parking, visibility or a bad left turn. Walk the site.

For the full parameter reference, see the Huff forecast docs and methodology.

All figures shown on this site come from MapDove’s synthetic demo dataset, modeled from neighborhood-level parameters. No real device data is used.

Back to the blog

Drive-time vs. radius catchments: when each is right

Rings in miles and rings in drive minutes answer different questions. Here is how to choose, and what MapDove's drive-time approximation does and doesn't do.

Marcus Bell 5 min read

Your next location is already on the map.

Open the demo cities, run 25 analyses a month on the Free plan, and bring your team when you are ready.