# Geo (/docs/strategies/geo)



Geo strategies route by **location**. They compare the routable's position to
each candidate's `Location&#x60; and either pick the closest or bias a weighted split
toward nearer recipients. Both ship in the optional &#x2A;*`geographic`** module
(`modules/strategies/geographic`).

| Strategy                      | Capabilities         | Best for                                  |
| ----------------------------- | -------------------- | ----------------------------------------- |
| [Geo nearest](#geo-nearest)   | geo\_aware           | Send to the physically closest recipient. |
| [Geo weighted](#geo-weighted) | geo\_aware, weighted | Favor closeness but keep spreading load.  |

<Callout title="Optional module" type="warn">
  Both strategies are registered by `Register` in
  `modules/strategies/geographic/strategy/module.go`, not by the built-in pack.
  They require candidates to carry a `Location`, and both take a
  `fallback_strategy` for routables or candidates with no known location.
</Callout>

## Geo nearest [#geo-nearest]

**Capabilities:** geo\_aware

Routes to the **nearest** recipient by geographic distance. When no location is
available for the routable or a candidate, it defers to a fallback strategy.

**Why use it.** When physical proximity is the objective — dispatching a field
technician, assigning a delivery, routing to the closest regional office or
data-center. Nearest-wins is the simplest and most intuitive geo rule.

**How to configure it.**

| Config key          | Type     | Default         | Effect                               |
| ------------------- | -------- | --------------- | ------------------------------------ |
| `max_distance_km`   | number   | `0` (unlimited) | Ignore candidates farther than this. |
| `fallback_strategy` | strategy | —               | Used when location data is missing.  |

```json
{
  "strategy": "geo_nearest",
  "options": { "max_distance_km": 50, "fallback_strategy": "smooth_weighted_round_robin" }
}
```

**Where it fits.** The `Select` stage. It reads `Candidate.Location` and the
routable's location; with `max_distance_km` it also acts as a coverage filter,
excluding anyone out of range.

<Callout title="Behavior change: true great-circle ordering" type="info">
  Nearest selection now ranks candidates by true great-circle (Haversine) distance
  rather than the planar order the underlying spatial R-tree returned. Two effects
  are visible in routing outcomes. A recipient just across the ±180° antimeridian
  that sits inside the radius is no longer missed — the search now unions the two
  sides of the date line. And the chosen recipient no longer depends on how many
  candidates were in the set. Where the old planar ordering disagreed with true
  distance — mostly at high latitudes and near the date line — nearest may now pick
  a different, genuinely closer recipient than before.
</Callout>

## Geo weighted [#geo-weighted]

**Capabilities:** geo\_aware, weighted

Blends **distance with weight**. Closer recipients are favored, but recipient
weight still spreads load — so a slightly farther but higher-capacity recipient
can still win a share. It's the middle ground between pure proximity and pure
weighted distribution.

**Why use it.** When you want proximity to *matter* without letting the single
closest recipient absorb everything. Useful when nearby recipients have limited
capacity and you'd rather trade a little distance for better balance.

**How to configure it.**

| Config key          | Type         | Default | Effect                                                                             |
| ------------------- | ------------ | ------- | ---------------------------------------------------------------------------------- |
| `max_distance_km`   | number       | `100`   | Ignore candidates farther than this.                                               |
| `distance_weight`   | number (0–1) | `0.5`   | How strongly distance pulls the selection; `0` ignores distance, `1` maximizes it. |
| `fallback_strategy` | strategy     | —       | Used when location data is missing.                                                |

```json
{
  "strategy": "geo_weighted",
  "options": { "distance_weight": 0.7, "max_distance_km": 150 }
}
```

**Where it fits.** The `Select` stage. It is both `geo_aware` and `weighted`,
combining `Candidate.Location` with `Candidate.Weight`.

## Related [#related]

<Cards>
  <Card title="Territory eligibility" href="/docs/strategies/eligibility">
    Coverage areas — regions, postal sets, polygons, radii — for eligibility-based geo routing.
  </Card>

  <Card title="Multi-objective score" href="/docs/strategies/scoring#multi-objective-score">
    Fold distance into a broader score alongside capacity, deadline, and margin.
  </Card>

  <Card title="Eligibility" href="/docs/strategies/eligibility">
    Territory-aware matching that combines geography with skills and licenses.
  </Card>
</Cards>
