Plans an EV journey with its charge stops (see module docs).
The algorithm
- Base route. One auto route over origin + waypoints + destination, with the request's costing_options verbatim.
- Leg sampling. Each manoeuvre of that route becomes one [sn_ev::LegSample]: its length and its own average speed, which is finer than a leg summary and so makes the steady-state assumption in [sn_ev::consumption_over_legs] better. Manoeuvre-free routes fall back to one sample per leg, and a route with more than [MAX_LEG_SAMPLES] manoeuvres has consecutive ones coalesced.
- Elevation, or an honest absence. When elevation is enabled the gateway's own height service is asked for the elevation at every leg boundary, and each leg's gain and loss come from its two ends. When it is not enabled, or the DEM has no coverage, the legs are built with [sn_ev::LegSample::without_gradient], the consumption model runs flat (the only thing it can do) and the response says gradient_data: "absent". A gradient is never silently assumed to be zero.
- Direct plan. [sn_ev::consumption_over_legs] over the whole route from start_soc. If the arrival state of charge is at or above min_arrival_soc and the reserve was never breached, that is the plan: no charger work and no matrix call happen at all.
- Candidates. Otherwise, charge points from the store are projected onto the route ([corridor::nearest_on_route]), filtered to those the vehicle can plug into at a rated power inside the corridor budget, and capped at [MAX_PLAN_CANDIDATES] spread along the route rather than clustered; the /v1/charging/along corridor machinery, with a spread instead of a power ranking, because a planner needs reach and a search needs kilowatts.
- One matrix. Origin + candidates → candidates + destination, one (n+1)² matrix through the gateway's own engine with the request's costing options, giving every hop a real driving time and distance: never a crow-flies guess.
- Greedy with lookahead. [crate::ev::plan_stops] walks the route choosing stops so the state of charge never breaches reserve_soc, preferring a stop that finishes the journey, then the furthest one (fewer stops), then the cheapest by driving plus charging time. A charger from which nothing further can be reached even on a full battery is refused rather than taken. At most eight passes.
- Final geometry. The chosen stops are merged with the waypoints in route order and the route is recomputed through them, so the geometry returned is the journey actually described.
- Or an honest no. When no plan exists (a charger desert, a connector mismatch, a gap wider than the vehicle's range), the answer is a 200 with feasible: false, the named cause, and the furthest point on the route the vehicle can actually reach. Never a fabricated plan.
Stated limitations
A hop's energy is sliced out of the base route's legs and the detour surplus charged as one gradient-unknown leg (see [crate::ev::hop_legs]); matrix cells are upper bounds on route cost, not route costs; the consumption model's own ±15 % motorway bound and its urban bias are stated in [sn_ev]; charging curves are best-case, measured on a warm pack. The plan is a plan, not a promise.
Request
- Base URL: https://api.mapmap.ai
- URL: https://api.mapmap.ai/v1/ev/plan
- Auth: one of:
- HTTP bearer
- API key in query parameter api_key
Request body
Response
The plan, feasible or honestly not: {feasible, plan: {stops, total_drive_s, total_charge_s, total_duration_s, total_distance_m, start_soc, arrival_soc, min_arrival_soc, reserve_soc, energy_kwh}, stops[]: {charger_id, name, operator, source, lat, lon, connector: {standard, power_kw, dc, power_kw_source; derived when the feed did not publish the rating}, charger_kw, arrive_soc, depart_soc, charge_s, detour_s, along_route_position, off_route_m, status, status_live}, legs[]: {from, to, duration_s, distance_m, start_soc, end_soc, min_soc, consumed_wh, regen_wh}, soc_trace[], gradient_data: "complete" | "partial" | "absent", gradient: {legs_total, legs_with_gradient, fraction}, route: {duration_s, distance_m, length_m}, geometry_polyline6, coverage_note (ALWAYS present), sources[], availability: {live, note}, provenance: {profile, availability, consumption_model, charge_model}, charging_attribution}. When feasible is false the answer carries reason: {code, message} and furthest_reachable, never a fabricated stop list
Changes
No recorded changes to this operation across all 1 revision of this API.