Methodology
The formulas behind MapDove's models: Huff gravity, calibration, cannibalization, trade areas, MCDA suitability, catchment aggregation and cross-shopping.
This page documents the models exactly as they are implemented. Every SDSS result in MapDove can be reproduced from these formulas and the data described in Demo data. Distances are great-circle distances between points in kilometers; all models run on the city’s hexagonal grid (cells of about 0.8 km²).
Notation
| Symbol | Meaning |
|---|---|
i |
a hex cell (origin of demand) |
j, k |
stores of the category being modeled |
d_ij |
distance from the centroid of hex i to store j, in km |
A_j |
attractiveness of store j |
λ |
distance-decay exponent of the category |
R |
trade radius of the category, in km |
D_i |
demand of hex i |
P_ij |
probability that demand in hex i goes to store j |
E_j |
expected (modeled) demand captured by store j |
Demand
D_i = population_i + 0.4 · daytime_pop_i
Residents count fully; the daytime population (workers, students, visitors present during the day) counts at 40% to reflect that it drives a share of trips without being the household’s main shopping base.
Attractiveness
A_j = (sqft_j / base_sqft)^0.7 · brand_strength_j
base_sqft— typical size of the category (see the table below).0.7— returns-to-scale exponent: larger stores are more attractive, with diminishing returns.brand_strength— brand multiplier (fictional chains 0.9–1.2; independents 0.8–1.15; candidate stores 0.6–1.5 as entered).
Huff gravity model
For each hex i, only stores within the trade radius (d_ij ≤ R) are considered:
U_ij = A_j / max(d_ij, 0.4)^λ
u0 = 1 / (0.6 · R)^λ
P_ij = U_ij / ( Σ_k U_ik + u0 )
U_ij— utility of storejfor residents of hexi. The 0.4 km distance floor prevents a store inside a hex from receiving infinite utility.u0— utility of the outside option (shopping in another category, online, outside the city, or not at all). It equals the utility of an average-attractiveness store at 60% of the trade radius. Hexes far from every store therefore send most of their demand to the outside option, and probabilities in a hex sum to less than 1.Σ_k U_ik— sum over all stores withinRof hexi.
Expected demand captured by store j:
E_j = Σ_i P_ij · D_i
Calibration and forecast
E_j is in population units. A single scaling factor per city and category converts it to visits:
k = Σ_j V_j / Σ_j E_j
V_j— observed average daily visits of existing storejover the last 90 days of data.- Sums run over all existing stores of the category in the city.
For a candidate store c (added to the store set with its own A_c):
predicted_daily_visits = k · E_c
predicted_annual_visits = 365 · k · E_c
market_share = E_c / Σ_j E_j_after (all stores, candidate included)
Cannibalization
The model is evaluated without and with the candidate:
Δ_j = k · (E_j_after − E_j_before)
Δ%_j = 100 · (E_j_after − E_j_before) / E_j_before
total_cannibalized_daily = −Σ_j Δ_j
The ten stores with the largest losses (losses below 0.5 visits/day omitted) are reported. See Cannibalization.
Trade areas
Flow from hex i to store j is F_ij = P_ij · D_i, and the hex’s share is:
s_j(i) = F_ij / Σ_i' F_i'j
Hexes are sorted by s_j(i) descending and accumulated. A hex is included while the cumulative share before it is below the cut-off:
| Trade area | Cut-off |
|---|---|
Existing place (/places/:id/trade-area) |
85% |
| Candidate in a Huff forecast | 80% |
Summary demographics are sums (population, households) or population-weighted means (income, age, education, family share) over the included hexes. Average distance is visitor-share-weighted; p50_distance_km is the smallest distance at which cumulative share (sorted by distance) reaches 50%.
Site suitability (MCDA)
Criteria
For each hex h in the city, with the selected category:
| Criterion | Raw value x_c(h) |
|---|---|
| population | population / area_km2 |
| income | median income |
| daytime | daytime_pop / area_km2 |
| traffic | traffic index |
| growth | growth rate |
| rent | rent index |
| competition | Σ_(j ∈ category, d ≤ 4 km) exp(−d_hj / 1.5) |
| complementary | Σ_(p ∈ complement categories, d ≤ 2.5 km) exp(−d_hp / 0.8) |
| access (used by gap) | Σ_j A_j / max(d_hj, 0.4)^λ over all stores of the category |
| demand (used by gap) | D_h |
Normalization
Min–max normalization over all hexes of the city:
x'_c(h) = (x_c(h) − min_c) / (max_c − min_c)
Kernel sums (competition, complementary, access) are log-scaled first, x ← ln(1 + x), so a single dense cluster does not compress every other hex toward zero. If max_c = min_c, the criterion is 0 for all hexes.
Direction
Cost criteria are inverted:
competition' = 1 − x'_competition
rent' = 1 − x'_rent
Demand gap
gap(h) = x'_demand(h) · (1 − x'_access(h))
High where demand is high and existing stores’ reach is low.
Score
score(h) = 100 · Σ_c w_c · x'_c(h) / Σ_c w_c
w_c— user weight for criterionc, 0–10 (missing = 0; at least one must be positive).- Only weight ratios matter.
Top sites with spacing
Hexes are visited in descending score order; a hex is accepted if its centroid is at least min_spacing_km from all previously accepted sites, until top_n sites are selected (greedy maximal spacing).
Catchment aggregation
A ring of radius r is a geodesic buffer around the point (48 segments per quarter circle). Drive-time rings use r = minutes · v / 60, where v is the city’s average speed (Austin 42, Houston 45, Phoenix 50, Miami 35, Dubai 55 km/h). This is a radius approximation, not a network isochrone.
For each hex h intersecting ring R:
f_h = min(1, |h ∩ R| / |h|)
count_R = Σ_h count_h · f_h population, households, daytime_pop
mean_R = Σ_h value_h · population_h · f_h / Σ_h population_h · f_h
median_income, median_age, pct_college,
pct_family, growth_rate
index_R = (1/n) · Σ_h index_h traffic_index, rent_index (unweighted)
Medians and rates are therefore population-weighted means of hex values, not recomputed medians. Segment populations are Σ_h population_h · f_h per segment, and area_km2 = π · r².
Cross-shopping
For every place p in the city (all categories, each modeled against its own category):
s_p(h) = F_hp / Σ_h' F_h'p
overlap(A, B) = Σ_h min(s_A(h), s_B(h))
affinity(A, B) = overlap(A, B) · m, m = 1.35 complement, 0.6 same category, 1.0 otherwise
Results are ranked by affinity. See Cross-shopping.
Place metrics
visitors_range = Σ_days visitors_day / (1 + 0.15 · ln(days))
frequency = visits_range / visitors_range
avg_dwell = Σ dwell_day · visits_day / Σ visits_day
change_pct = 100 · (visits − visits_prev) / visits_prev
percentile = 100 · #{same-category places in city with fewer visits} / (n − 1)
The previous period has the same length and ends the day before from.
Category parameters
| Category | Slug | λ | Trade radius (km) | Base size (sq ft) | Typical dwell (min) | Complements |
|---|---|---|---|---|---|---|
| Coffee & Cafés | coffee |
2.2 | 6 | 1,800 | 18 | fitness, apparel, mall, pharmacy |
| Grocery | grocery |
1.8 | 9 | 40,000 | 28 | pharmacy, fuel, coffee, qsr |
| Quick Service | qsr |
2.0 | 7 | 3,000 | 14 | fuel, cinema, mall, electronics |
| Full-Service Dining | dining |
1.9 | 10 | 5,000 | 62 | cinema, hotel, mall, apparel |
| Fitness | fitness |
2.4 | 6 | 18,000 | 65 | coffee, grocery, qsr |
| Apparel | apparel |
1.7 | 14 | 9,000 | 26 | mall, department, coffee, dining |
| Pharmacy & Health | pharmacy |
2.1 | 7 | 12,000 | 12 | grocery, coffee, fuel |
| Home Improvement | home |
1.5 | 18 | 100,000 | 34 | fuel, electronics, qsr |
| Electronics | electronics |
1.6 | 16 | 25,000 | 30 | mall, home, qsr |
| Department Stores | department |
1.4 | 20 | 120,000 | 45 | apparel, mall, dining |
| Entertainment | cinema |
1.5 | 16 | 40,000 | 140 | dining, qsr, mall |
| Hotels | hotel |
1.2 | 40 | 80,000 | 600 | dining, coffee, cinema |
| Fuel & Convenience | fuel |
2.6 | 5 | 3,500 | 7 | qsr, grocery, coffee |
| Shopping Centers | mall |
1.3 | 25 | 900,000 | 95 | apparel, department, dining, cinema |
Higher λ means steeper distance decay — customers are less willing to travel. Convenience categories combine high λ with small radii; destination categories the opposite.
Known limitations
- Straight-line distance; no road network, travel time or barriers.
- Competition is within-category only.
- A single calibration factor per city and category.
- Static demographics; no forecast of population change beyond the growth criterion.
- All inputs in the hosted demo are synthetic (Demo data).