The "breadcrumbs" element is always present, on top of the page.
It shows you where you are on the lab.
It enables you to reach any page on the lab.
Every option below is also a live page of the demo app: the same native widgets, one settings screen shared by all of them, real searches on the Belgian network.
The settings screen, not the planner form. Two sub-screens, both compatible with every option below: the planner form still carries one of the inline components (presets, listbox, toggles, switch). 1a user preferences are per-request parameters that ride along every query of a search, whatever the mode option, so they never add a query. 1b routing settings restrict the modes: they trim what the inline components offer, and the transit checkboxes drive the transportModes column of every query list on this page. Done well, with the right defaults, most travellers never open this screen: the defaults below are the OTP 2 ones, which send nothing at all.
The things a traveller sets once: pace, how far they accept to walk or cycle to a stop, transfers, accessibility. Each control maps to one OTP 2 parameter of both recipes, written straight into the snippet; defaults send nothing. The "at most" caps are what OTP calls performance parameters: they can only lower the server caps (45 min to or from a stop, 4 h door to door; above that OTP answers with an error) and they are hard cut-offs (verified live: capping the walk to the stop at 5 min on a suburban origin left no itinerary at all, noTransitConnection). For a soft preference the lever is walkReluctance, not exposed here on purpose. Public transit and rental list the operators and the GBFS networks OTP 2 knows, all on by default; unticking one bans it (bannedAgencies, bannedRentalNetworks). OTP 2's trip query knows no "preferred" operator, so a subscription badge stays a client side concern.
Try the settings screen in the demo app
| control | parameters | OTP 2 default (nothing sent) |
|---|---|---|
| Wheelchair accessible trips only | wheelchairAccessible | wheelchairAccessible = false (ui: off) |
| Walking pace | walkSpeed | walkSpeed = 1.33 m/s (ui: normal) |
| Walk to and from the stop, at most | maxAccessEgressDurationFoot | maxAccessEgressDurationFoot = 45 min (server cap, can only be lowered) (ui: no limit) |
| Walk-only trips, at most | maxDirectDurationFoot | maxDirectDurationFoot = 4 h (server cap, can only be lowered) (ui: no limit) |
| Cycling pace | bikeSpeed | bikeSpeed = 5 m/s (ui: normal) |
| Bike routes | bicycleOptimisationMethod | bicycleOptimisationMethod = safe (ui: safest) |
| Cycle to and from the stop, at most | maxAccessEgressDurationBicycle | maxAccessEgressDurationBicycle = 45 min (server cap, can only be lowered) (ui: no limit) |
| Bike-only trips, at most | maxDirectDurationBicycle | maxDirectDurationBicycle = 4 h (server cap, can only be lowered) (ui: no limit) |
| Only rental bikes available right now | useBikeRentalAvailabilityInformation | useBikeRentalAvailabilityInformation = false (ui: off) |
| Transfers | transferPenalty, maximumTransfers | transferPenalty = 0 s; maximumTransfers = 12 (ui: fastest trip) |
| Time to change vehicles, at least | transferSlack | transferSlack = 120 s (ui: 2 min) |
| Be at the platform 5 min before the train | boardSlackRail | boardSlackRail = 0 s (boardSlackDefault) (ui: off) |
| Use real-time information | ignoreRealtimeUpdates | ignoreRealtimeUpdates = false (ui: on) |
| Itinerary texts | locale | locale = en (ui: english) |
| Public transit operators | bannedAgencies | bannedAgencies = none sent, every operator (ui: all ticked) |
| Rental networks | bannedRentalNetworks | bannedRentalNetworks = none sent, every network (ui: all ticked) |
Every operator is used by default. Untick one to ban it from the results (bannedAgencies), for a traveller without a pass for it.
Every rental network OTP 2 reads (GBFS manifest) is used by default. You can ban a rental agency by disabling it here (bannedRentalNetworks): a traveller keeps the networks they have a subscription for.
One global setting to allow options. Default is walk & transit only. Every extra street mode allowed is another vehicle kind the app has to query for, since this control alone carries no intent (the allowed list is ui state, not a parameter). Unchecked transit modes go to transportModes: try unticking one and look at the other views. Used as the only control it is a fan out, one query per allowed kind, with the paging caveat that comes with it.ℹ see: one cursor for many queries, how the preset recipe pages
| allowed street mode | queries | accessMode / directMode / egressMode ⇒ result shapes |
|---|---|---|
| walk & transit (always) | 1 | foot / foot / foot ⇒ [walk → transit → walk] + [walk] |
| bike | 1 | bicycle / bicycle / bicycle ⇒ [bike → transit → bike] + [bike] |
| bike, park | 1 | bike_park / bicycle / foot ⇒ [bike, park → transit → walk] + [bike] |
| rental bike | 1 | bike_rental / bike_rental / bike_rental ⇒ [rental bike → transit → rental bike] + [rental bike] |
| rental scooter | 1 | scooter_rental / scooter_rental / scooter_rental ⇒ [rental scooter → transit → rental scooter] + [rental scooter] |
| car | 1 | foot / car / foot · no transit ⇒ [car] |
| car, park | 1 | car_park / foot / foot ⇒ [car, park → transit → walk] + [walk] |
| taxi | 1 | car_pickup / foot / foot ⇒ [taxi → transit → walk] + [walk] |
| rental car | 1 | car_rental / car_rental / car_rental ⇒ [rental car → transit → rental car] + [rental car] |
| on-demand | 1 | flexible / flexible / flexible ⇒ [on-demand → transit → on-demand] + [on-demand] |
One triple per preset, custom is one triple too. Car needs a second query because OTP drops the transit itineraries next to a direct car trip. Level 1: the main presets. Level 2: more nuanced presets behind "more options". Level 3: custom access / direct / egress (DIY), which is where asymmetric trips belong (walk one way, rental the other: the rental modes already fall back to walking when no vehicle is around). A preset is one modePreset id of the preset recipe; custom is the three slots of planner-v2-1. Car is the one two-query preset here; it pages with one cursor like the others, the car trip being a first page thing.ℹ see: one cursor for many queries, how the preset recipe pages
| preset | modePreset | queries | accessMode / directMode / egressMode ⇒ result shapes |
|---|---|---|---|
| Public transit | public-transit | 1 | foot / foot / foot ⇒ [walk → transit → walk] + [walk] |
| Bike | bike | 1 | bicycle / bicycle / bicycle ⇒ [bike → transit → bike] + [bike] |
| Car | car | 2 | foot / foot / foot ⇒ [walk → transit → walk] + [walk] foot / car / foot · no transit ⇒ [car] |
| Bike & park at the stop | bike-park-ride | 1 | bike_park / bicycle / foot ⇒ [bike, park → transit → walk] + [bike] |
| Car, park & ride | car-park-ride | 1 | car_park / foot / foot ⇒ [car, park → transit → walk] + [walk] |
| Rental bike | bike-rental | 1 | bike_rental / bike_rental / bike_rental ⇒ [rental bike → transit → rental bike] + [rental bike] |
| Rental car | car-rental | 1 | car_rental / car_rental / car_rental ⇒ [rental car → transit → rental car] + [rental car] |
| Rental scooter | scooter-rental | 1 | scooter_rental / scooter_rental / scooter_rental ⇒ [rental scooter → transit → rental scooter] + [rental scooter] |
| Transit only | transit-only | 1 | foot / (unset) / foot ⇒ [walk → transit → walk] |
| Just drive | car-only | 1 | foot / car / foot · no transit ⇒ [car] |
| Just cycle | bike-only | 1 | foot / bicycle / foot · no transit ⇒ [bike] |
| On-demand transport | on-demand | 1 | flexible / flexible / flexible ⇒ [on-demand → transit → on-demand] + [on-demand] |
Same widget as view 2, other defaults. Here the bare vehicle name means "every way to combine that vehicle with transit": Bike runs own bike, own bike parked at the stop and rental bike; Car runs drive, park & ride and rental car. The narrow, single-query variants keep a qualified name (own, rental, parked, just) and sit behind "more options": one list, the name says how wide the preset is, the table says what it costs. Same ids as view 2 where the preset is the same, so the two views stay in sync. Bike and Car are multi query presets: one cursor pages all their queries at once, so the cost of this option is the query count (latency), not the paging.ℹ see: one cursor for many queries, how the preset recipe pages
| preset | modePreset | queries | accessMode / directMode / egressMode ⇒ result shapes |
|---|---|---|---|
| Public transit | public-transit | 1 | foot / foot / foot ⇒ [walk → transit → walk] + [walk] |
| Bike | bike-multimodal | 3 | bicycle / bicycle / bicycle ⇒ [bike → transit → bike] + [bike] bike_park / bicycle / foot ⇒ [bike, park → transit → walk] + [bike] bike_rental / bike_rental / bike_rental ⇒ [rental bike → transit → rental bike] + [rental bike] |
| Car | car-multimodal | 3 | foot / car / foot · no transit ⇒ [car] car_park / foot / foot ⇒ [car, park → transit → walk] + [walk] car_rental / car_rental / car_rental ⇒ [rental car → transit → rental car] + [rental car] |
| Scooter | scooter-rental | 1 | scooter_rental / scooter_rental / scooter_rental ⇒ [rental scooter → transit → rental scooter] + [rental scooter] |
| On-demand transport | on-demand | 1 | flexible / flexible / flexible ⇒ [on-demand → transit → on-demand] + [on-demand] |
| Own bike | bike | 1 | bicycle / bicycle / bicycle ⇒ [bike → transit → bike] + [bike] |
| Own bike, parked at the stop | bike-park-ride | 1 | bike_park / bicycle / foot ⇒ [bike, park → transit → walk] + [bike] |
| Rental bike | bike-rental | 1 | bike_rental / bike_rental / bike_rental ⇒ [rental bike → transit → rental bike] + [rental bike] |
| Just cycle | bike-only | 1 | foot / bicycle / foot · no transit ⇒ [bike] |
| Just drive | car-only | 1 | foot / car / foot · no transit ⇒ [car] |
| Own car, park & ride | car-park-ride | 1 | car_park / foot / foot ⇒ [car, park → transit → walk] + [walk] |
| Rental car | car-rental | 1 | car_rental / car_rental / car_rental ⇒ [rental car → transit → rental car] + [rental car] |
| Transit only | transit-only | 1 | foot / (unset) / foot ⇒ [walk → transit → walk] |
Same presets as view 2, same counts, flat "equal list" widget; the Car entry pages with one cursor as in view 2.ℹ see: one cursor for many queries, how the preset recipe pages
| preset | modePreset | queries | accessMode / directMode / egressMode ⇒ result shapes |
|---|---|---|---|
| Public transit | public-transit | 1 | foot / foot / foot ⇒ [walk → transit → walk] + [walk] |
| Bike | bike | 1 | bicycle / bicycle / bicycle ⇒ [bike → transit → bike] + [bike] |
| Car | car | 2 | foot / foot / foot ⇒ [walk → transit → walk] + [walk] foot / car / foot · no transit ⇒ [car] |
| Bike & park at the stop | bike-park-ride | 1 | bike_park / bicycle / foot ⇒ [bike, park → transit → walk] + [bike] |
| Car, park & ride | car-park-ride | 1 | car_park / foot / foot ⇒ [car, park → transit → walk] + [walk] |
| Rental bike | bike-rental | 1 | bike_rental / bike_rental / bike_rental ⇒ [rental bike → transit → rental bike] + [rental bike] |
| Rental car | car-rental | 1 | car_rental / car_rental / car_rental ⇒ [rental car → transit → rental car] + [rental car] |
| Rental scooter | scooter-rental | 1 | scooter_rental / scooter_rental / scooter_rental ⇒ [rental scooter → transit → rental scooter] + [rental scooter] |
| Transit only | transit-only | 1 | foot / (unset) / foot ⇒ [walk → transit → walk] |
| Just drive | car-only | 1 | foot / car / foot · no transit ⇒ [car] |
| Just cycle | bike-only | 1 | foot / bicycle / foot · no transit ⇒ [bike] |
| On-demand transport | on-demand | 1 | flexible / flexible / flexible ⇒ [on-demand → transit → on-demand] + [on-demand] |
Google style: pick the mode, then refine it in place. The toggles are additive, "enable on top of": own vehicle stays, rental and parked are extra shapes, each one another OTP 2 query. Scooter is rental only, so no toggle. Both toggles on = the multimodal preset (one call, three queries); one toggle = two presets, two calls. Every toggle adds a query, so it adds a page cursor to move along with the others.ℹ see: one cursor for many queries, how the preset recipe pages Compare with 4b where the switch replaces the shape instead.
| mode + toggles | modePreset | queries | accessMode / directMode / egressMode ⇒ result shapes |
|---|---|---|---|
| Walk & transit | public-transit | 1 | foot / foot / foot ⇒ [walk → transit → walk] + [walk] |
| Bike | bike | 1 | bicycle / bicycle / bicycle ⇒ [bike → transit → bike] + [bike] |
| Bike + rental | bike + bike-rental | 2 | bicycle / bicycle / bicycle ⇒ [bike → transit → bike] + [bike] bike_rental / bike_rental / bike_rental ⇒ [rental bike → transit → rental bike] + [rental bike] |
| Bike + park at the stop | bike + bike-park-ride | 2 | bicycle / bicycle / bicycle ⇒ [bike → transit → bike] + [bike] bike_park / bicycle / foot ⇒ [bike, park → transit → walk] + [bike] |
| Bike + both | bike-multimodal | 3 | bicycle / bicycle / bicycle ⇒ [bike → transit → bike] + [bike] bike_park / bicycle / foot ⇒ [bike, park → transit → walk] + [bike] bike_rental / bike_rental / bike_rental ⇒ [rental bike → transit → rental bike] + [rental bike] |
| Car | car | 2 | foot / foot / foot ⇒ [walk → transit → walk] + [walk] foot / car / foot · no transit ⇒ [car] |
| Car + rental | car + car-rental | 3 | foot / foot / foot ⇒ [walk → transit → walk] + [walk] foot / car / foot · no transit ⇒ [car] car_rental / car_rental / car_rental ⇒ [rental car → transit → rental car] + [rental car] |
| Car + park & ride | car + car-park-ride | 3 | foot / foot / foot ⇒ [walk → transit → walk] + [walk] foot / car / foot · no transit ⇒ [car] car_park / foot / foot ⇒ [car, park → transit → walk] + [walk] |
| Car + both | car-multimodal | 3 | foot / car / foot · no transit ⇒ [car] car_park / foot / foot ⇒ [car, park → transit → walk] + [walk] car_rental / car_rental / car_rental ⇒ [rental car → transit → rental car] + [rental car] |
| Scooter | scooter-rental | 1 | scooter_rental / scooter_rental / scooter_rental ⇒ [rental scooter → transit → rental scooter] + [rental scooter] |
Variation: the mode list stays clean, one switch below applies to the chosen mode (greyed out for walk, rental only for scooter) and replaces the shape rather than adding one. "Own + rental at once" is what 4a does, at the cost of extra queries. Every position is exactly one modePreset; only own car runs two queries and inherits their paging caveat.ℹ see: one cursor for many queries, how the preset recipe pages
| mode · vehicle | modePreset | queries | accessMode / directMode / egressMode ⇒ result shapes |
|---|---|---|---|
| Walk & transit | public-transit | 1 | foot / foot / foot ⇒ [walk → transit → walk] + [walk] |
| Walk & transit | public-transit | 1 | foot / foot / foot ⇒ [walk → transit → walk] + [walk] |
| Walk & transit | public-transit | 1 | foot / foot / foot ⇒ [walk → transit → walk] + [walk] |
| Bike · own | bike | 1 | bicycle / bicycle / bicycle ⇒ [bike → transit → bike] + [bike] |
| Bike · rental | bike-rental | 1 | bike_rental / bike_rental / bike_rental ⇒ [rental bike → transit → rental bike] + [rental bike] |
| Bike · parked at the stop | bike-park-ride | 1 | bike_park / bicycle / foot ⇒ [bike, park → transit → walk] + [bike] |
| Car · own | car | 2 | foot / foot / foot ⇒ [walk → transit → walk] + [walk] foot / car / foot · no transit ⇒ [car] |
| Car · rental | car-rental | 1 | car_rental / car_rental / car_rental ⇒ [rental car → transit → rental car] + [rental car] |
| Car · parked at the stop | car-park-ride | 1 | car_park / foot / foot ⇒ [car, park → transit → walk] + [walk] |
| Scooter · rental | scooter-rental | 1 | scooter_rental / scooter_rental / scooter_rental ⇒ [rental scooter → transit → rental scooter] + [rental scooter] |
One planner-v2-1 query = one [access → transit → egress] + [direct] pair, both shapes come back together, unless the direct mode is fast enough to trigger the transit-vs-street filter (car, taxi): then the transit shapes need their own query. Several queries means merging, deduping and paging them together: the preset recipe does it server side behind one modePreset and one pageCursor (planner-v2 merges up to 9 queries too, but leaves one cursor per query to the client).
| Option | Queries per search | Why |
|---|---|---|
| 1a User preferences | +0 | Per-request parameters (pace, caps, transfers, accessibility, subscriptions, language) shared by every query of the search: they change the itineraries, never the number of queries. |
| 1b Restrict modes, as the only control | 1 + allowed kinds | Walk & transit by default = 1. Every extra allowed street mode is another vehicle kind to query for, up to 10 with everything allowed: planner-v2 again. Used as a filter on top of the other options it costs nothing. |
| 2 Presets (+ more, + custom) | 1, Car: 2 | A preset is one triple. Car pairs a direct car trip with transit alternatives, which OTP will not return together: two queries. Every other preset is one triple (park & ride keeps foot as direct mode for that reason). Rental presets are one query too: the direct rental trip and the rental + transit trips come back together. |
| 2b Multimodal presets | 1 to 3 | Bare vehicle name = every way to use it with transit: Bike and Car run 3 queries, walk, scooter, on-demand run 1. The single-query variants keep qualified names behind "more options". |
| 3 Open listbox | 1, Car: 2 | Same presets as view 2, same count, different widget. |
| 4a Rent / park toggles, additive | 1 to 4 | Bike: own + rental + parked = up to 3. Car: 2 by itself, up to 4 with both toggles. Scooter and walk: 1. |
| 4b Vehicle switch (radio) | 1, Car (own): 2 | Own / rental / parked is one triple by construction; only own car needs the extra transit query. |
transportModes, no query multiplication there.Rule of thumb: options 2 and 4b keep planner-v2-1 at one query (two for car), which is the whole point of the recipe versus planner-v2. Option 1 belongs on top of them: 1a as parameters on every query, 1b as a filter. 4a and custom-with-lists are the two places where the fan out sneaks back in; if a customer wants that, the honest answer is the preset recipe, or a client that fires 2 to 4 planner-v2-1 calls in parallel and shows them as separate tabs rather than merging them. One api follow-up would remove the remaining exception: a product-grade way to keep transit next to a car trip in one query (a keepTransitAlternatives flag mapping to listAll with the notices stripped server side).