Skip to content

Trips, legs & maps

PlanResult.trips[] contains TripPattern objects. Each trip has legs[]; each leg contains one of four detail objects.

LegStart / end labelsUseful details
transitLegfromEstimatedCall.expectedQuay / toEstimatedCall.expectedQuay (upcoming; currently quay)Line, destination, platforms, intermediate stops, realtime, alerts
nonTransitLegfromPlace / toPlaceWALK, BICYCLE, CAR or rental mode; rental station/vehicle data
waitingLegSurrounding travel legsWaiting start and end time
flexibleLegfromPlace / toPlaceBooking arrangement, uncertain times, line, operator

The scheduled and expected quay fields are upcoming. The example below reads expectedQuay when available and accepts the current JSON quay field during the transition.

Use leg.duration in minutes and leg.distance in metres. Trip-level duration, walking time and waiting time are minutes too.

Leg summary — enums normalized to strings
function describeLeg(leg) {
if (leg.transitLeg) {
const t = leg.transitLeg;
const label = t.line?.publicCode || t.line?.name || t.mode;
const fromQuay = t.fromEstimatedCall?.expectedQuay ?? t.fromEstimatedCall?.quay;
const from = fromQuay?.name || 'Board';
const toQuay = t.toEstimatedCall?.expectedQuay ?? t.toEstimatedCall?.quay;
const to = toQuay?.name || 'Alight';
return `${label}: ${from} → ${to}`;
}
if (leg.flexibleLeg) {
const f = leg.flexibleLeg;
return `Flexible: ${f.fromPlace?.name ?? ''} → ${f.toPlace?.name ?? ''}`;
}
if (leg.nonTransitLeg) {
return `${leg.nonTransitLeg.mode}: ${(leg.distance / 1000).toFixed(1)} km`;
}
if (leg.waitingLeg) return `Wait ${leg.duration} min`;
return 'Journey segment';
}

Use line.publicCode for the line badge, fromEstimatedCall.destinationDisplay.frontText for its boarding destination and line.branding for a passenger-facing brand. operator identifies the service operator.

Use returned line presentation colors where available, with a readable fallback. Render notices and names as text.

Show boarding/alighting calls, expand intermediateEstimatedCalls, and display cancelled calls as unavailable. Show staySeated at the end of that transit leg as “Stay on board” and use returned waiting legs in the timeline.

If pricing supplies transferInstruction, use its full-array leg indices. Walking and waiting legs may exist between the two referenced transit legs.

Use quay.id for the returned quay identity, quay.privateCode for source or journey-pattern code context, and optional quay.stopPlace to group quays under a parent stop or station. Keep these identities separate; source codes and parent data can be empty. See the schema reference.

leg.pointsOnLink.points is an encoded polyline. Decode it to coordinate pairs, then adapt coordinate order for your map library. A typical polyline decoder returns [latitude, longitude], while GeoJSON uses [longitude, latitude].

Install the decoder with npm install @mapbox/polyline.

Using @mapbox/polyline
import polyline from '@mapbox/polyline';
function legFeature(leg) {
const encoded = leg.pointsOnLink?.points;
if (!encoded) return null;
const coordinates = polyline.decode(encoded).map(([lat, lon]) => [lon, lat]);
if (coordinates.length < 2) return null;
return {
type: 'Feature', properties: { legId: leg.id },
geometry: { type: 'LineString', coordinates }
};
}

Draw the geometry where it is available. Rental stations and vehicles use their own place coordinates and may include availability or range information when supplied by the source.

Tram 20 through Utrecht, with intermediate stops and the selected route on the map.
A complete journey, leg by leg

Tram 20 through Utrecht, with intermediate stops and the selected route on the map.