Satvik Akkarajuall systems operational
← All projects

NexLeg

Route interpreter with OTP and protobuf-based services

What it is

NexLeg is a transit trip planner. You click an origin and a destination on a map, it asks a routing engine for the available journeys, and it hands back step-by-step directions a person can actually follow. It is a proof of concept, built in February 2026.

How it works

Three services run together:

  • OpenTripPlanner builds a graph from transit schedules (GTFS) and street maps (OSM) and answers routing queries over GraphQL.
  • The route interpreter, a small Python service, receives one itinerary and turns it into readable directions using an LLM through LangChain.
  • The Next.js app shows the map (Ola Maps SDK), collects the two clicks, and runs the trip through the other two.

On each request a Next.js server action queries OTP for up to five itineraries, takes the first, and sends it to the interpreter over gRPC. The response comes back as text, rendered as Markdown next to the route drawn on the map. The route geometry arrives as encoded polylines, which the app decodes and swaps into the coordinate order the map expects.

Key decisions

A protobuf contract between the app and the interpreter. The whole interface is one RPC: GetHumanReadableDirections, which takes the itinerary as a string and returns the directions as a string. Because the boundary is that small, the model and prompt can change without touching the app, and the app never talks to an LLM directly.

Real local data. I built the OTP graph from the Hyderabad Metro and Telangana bus GTFS feeds plus a Telangana OpenStreetMap extract, so it plans real journeys and not a demo dataset.

Fares from a PDF. The metro publishes its fare chart as a PDF. I wrote scripts that extract the table with pdfplumber, repair the garbled station names by fuzzy-matching them against the known station list, and reconcile the result with the GTFS fare rules, which use station codes. That side of it is still rough, and fares aren’t shown in the app yet.

Tradeoffs and limits

  • Metro only, for now. The trip query is restricted to the subway mode, with walking and park-and-ride to get to a station. The bus feed is in the graph but isn’t queried yet.
  • A fixed departure time. The proof of concept plans every trip for one hard-coded departure, not the current time.
  • Only the first itinerary is interpreted, and there’s no check that the LLM’s directions match the data it was given.
  • A plaintext gRPC channel. Fine on localhost, not something to put on a network as is.
  • The setup README is out of date. It still describes a FastAPI backend from before the interpreter moved to gRPC.

Where it stands

It works end to end as a local proof of concept: click twice on the map, get a metro route and readable directions. The app repository has 24 commits from 19 to 24 February 2026, and I touched the interpreter again in March.