Costing Model
The costing model determines how the router evaluates paths. Each edge and each transition between edges has a cost (what the A search minimizes) and a time* (real-world elapsed seconds).
Cost vs Time
Cost is an abstract value the router minimizes — it includes time but also penalties for undesirable road attributes (tolls, poor surfaces, turns). Time is the estimated real-world elapsed seconds. A route with lower cost may have higher time if it avoids penalties.
DynamicCost trait
All cost models implement the DynamicCost trait:
pub trait DynamicCost {
fn edge_cost(&self, edge: &DirectedEdge, density: u32) -> Cost;
fn transition_cost(&self, edge: &DirectedEdge, node: &NodeInfo, pred: &PredInfo) -> Cost;
fn allowed(&self, edge: &DirectedEdge, pred: &PredInfo) -> bool;
}
edge_cost— cost to traverse a single edgetransition_cost— cost at the junction between two edgesallowed— access and turn restriction filtering
The Cost struct carries two values:
pub struct Cost {
pub cost: f32, // abstract cost (what A* minimizes)
pub secs: f32, // real elapsed seconds
}
Auto (driving) cost
Edge cost
The driving edge cost is:
elapsed_time = length / speed
cost = elapsed_time * factor
Where factor blends several components:
- Speed factor — pre-computed lookup table with 256 entries indexed by speed (kph). Lower speeds get a higher factor, biasing routes toward faster roads. The table is built once at startup from the
use_highwaysanduse_distanceparameters. - Highway factor — scales motorway/trunk edge costs based on
use_highwayspreference. At 0.0, highways are 3x more expensive; at 1.0, they're cheaper. - Toll factor — scales toll road costs based on
use_tollspreference. At 0.0, toll roads get a 6x multiplier; at 1.0, no penalty. - Surface factor — penalizes poor road surfaces based on
surface_factor. Surface types range from PavedSmooth (no penalty) to Impassable (maximum penalty). - Distance factor — when
use_distance > 0, blendslengthinto cost, producing shorter (not just fastest) routes.
Transition cost
The transition cost at a junction depends on:
| Factor | Source | Description |
|---|---|---|
| Turn type | Edge pair data | Right, left, sharp, U-turn, etc. (8 types) |
| Stop impact | Edge pair data | 0 (free-flow) to 7 (full stop) |
| Name consistency | Edge pair data | Whether the road name continues |
| Traffic signal | Node attribute | Adds signal_penalty seconds |
| Density | Node attribute | 0-15 scale, multiplies turn delay |
| Destination-only | Edge attribute | Adds destination_only_penalty |
| Toll booth | Edge attribute | Adds toll_booth_cost |
| Service road | Edge attribute | Adds service_penalty |
Turn cost formula
base_cost = TURN_COSTS[turn_type][stop_impact]
density_factor = TRANSITION_DENSITY_FACTOR[density]
cost = base_cost * density_factor
Where:
TURN_COSTSis a pre-computed[8][8]table (8 turn types × 8 stop impact levels). U-turns are most expensive; straight-throughs at stop impact 0 are free.TRANSITION_DENSITY_FACTORis a 16-entry table indexed by node density (0-15). Dense urban areas (density 15) multiply turn costs by ~4x compared to rural areas (density 0).
Name consistency adjustment
When the road name continues across a junction (name-consistent), the transition cost is heavily reduced — the router treats it as staying on the same road. When the name changes, maneuver_penalty seconds are added (cost only, not time) to penalize unnecessary turns.
Density
Density is a 4-bit value (0-15) stored on each node in the tile. It approximates the surrounding road network complexity:
| Density | Environment | Turn cost multiplier |
|---|---|---|
| 0-3 | Rural / highway | 1.0x |
| 4-7 | Suburban | 1.2-1.5x |
| 8-11 | Urban | 2.0-3.0x |
| 12-15 | Dense urban core | 3.5-4.0x |
Hierarchy transitions
When crossing hierarchy levels (e.g., level 2 local road to level 1 arterial), the per-pair data (turn type, stop impact, name consistency) comes from a different-level node and is unreliable. At these transitions, Calculon:
- Applies base penalties only (toll, destination-only, service road)
- Uses an estimated stop impact of 4.0 (moderate intersection)
- Detects near-U-turns by comparing headings across the transition — if the turn is within 30° of a U-turn, adds a full reverse-turn penalty
Turn restrictions
The allowed() function filters edges based on:
- Access mask — the edge must have forward access for the travel mode
- Turn restrictions — one-way violations and conditional restrictions encoded in the tile
- Not-thru pruning — avoids not-thru edges unless the path started on one
Bicycle cost
Edge cost
elapsed_time = length / cycling_speed
cost = elapsed_time * factor
Where factor adjusts for:
- Road class —
use_roadsscales cost on high-traffic roads. At 0.0, Primary roads get a 5x penalty; at 1.0, no penalty. - Surface quality —
avoid_bad_surfacespenalizes gravel, dirt, etc. Compacted surfaces add 20-50% cost; impassable surfaces block the edge entirely. - Living streets —
use_living_streetsreduces cost on residential streets
Transition cost
Similar to auto but with:
- No toll booth or service road penalties
gate_penaltyfor barriers (default 300 seconds — gates are very expensive for cyclists)- Lower turn costs overall (cyclists turn more easily)
Access filtering
Cyclists can use: roads with bicycle access, cycleways, living streets, footways (with dismount penalty). They cannot use: motorways, trunk roads, or edges without bicycle access.
Pedestrian cost
Edge cost
elapsed_time = length / walking_speed
cost = elapsed_time * factor
Where factor adjusts for path type:
| Path type | Default factor | Effect |
|---|---|---|
| Walkway / footway | 1.0 | Preferred |
| Sidewalk present | 1.0 | Preferred |
| Living street | 0.6-1.0 | Depends on use_living_streets |
| Alley | 2.0 | Avoided |
| Driveway | 5.0 | Strongly avoided |
| Steps | +30s penalty | Per staircase |
Additional factors
- Lighting —
use_litadds cost to unlit edges (0.0 = don't care, 1.0 = strongly prefer lit roads) - Hiking difficulty — edges above
max_hiking_difficulty(SAC scale 0-6) are blocked - Hill avoidance —
use_hillsadjusts cost based on grade (steeper = more expensive)
Access filtering
Pedestrians can use: footways, walkways, sidewalks, crossings, living streets, paths, steps. They cannot use: motorways, trunk roads, or edges without pedestrian access.
Shortest mode
When shortest: true, the cost model switches to pure distance-based routing:
- Edge cost becomes proportional to length only, ignoring speed and road class
- Turn costs are still applied to avoid unreasonable maneuvers
- The
use_distanceparameter is ignored (effectively set to 1.0) - Useful for debugging and comparing with other routers
Lookup tables
All cost models use pre-computed lookup tables built at construction time:
| Table | Size | Indexed by | Purpose |
|---|---|---|---|
| Speed factor | 256 entries | Speed (kph) | Edge cost multiplier |
| Turn costs | 8×8 entries | Turn type × stop impact | Base transition cost |
| Density factor | 16 entries | Node density (0-15) | Transition cost multiplier |
| Highway factor | Per road class | RoadClass enum | Highway preference scaling |
| Surface factor | Per surface type | Surface enum | Surface quality penalty |
Tables are computed once per costing instance (at request time if per-request options differ from defaults) and stored inline. Zero allocations during routing.