Setting up an OpenTripPlanner (OTP) app

Now that we've covered what OpenTripPlanner is and how it works, let's run it! Follow along this tutorial or fork the resulting repository to launch an OTP application on your machine, in just a few minutes. Here's what it looks like:

image

Prerequisites

Before diving into OpenTripPlanner's setup per se, make sure you have an IDE, Docker, and a repository to write, run and save your code.

For example, you can use Visual Studio Code, Docker Desktop and a GitHub repository created with the following commands:

  • git init
  • git branch -M main
  • git add .
  • git commit -m"v0.0.0"
  • git remote add origin https://github.com/[username]/opentripplanner-app.git
  • git push -u origin main

Also note that this tutorial implements the latest OTP 2, which runs a bit differently to the older OTP 1.

Now let's get cracking.

Datasets

OpenTripPlanner supports four types of datasets: OpenStreetMap, GTFS Schedule, GTFS Realtime and GBFS.

You can run the app with only the first and/or second dataset, but all four are necessary to create a complete picture of the geographic and transportation landscape of a region, and enable all of the engine's trip planning features.

So go ahead and create a new ./config/ folder at the root of your repository, then fill it with one or more files as described hereafter.

OpenStreetMap

OpenStreetMap (OSM) data represents the road network. It enables OpenTripPlanner to generate the geographic graph and compute walking, cycling and driving itineraries.

The OSM dataset covers most of the world in remarkable detail thanks to global collaboration. However, OpenTripPlanner only needs the relevant region(s) for each instance of the app, i.e. just a city or country in most cases.

Let's focus on Belgium as an example, using the ready-made belgium-latest.osm.pbf file. Alternatively, you can find other ready-made regions with a quick Google search or crop one yourself. Pick a small region to get started quickly, especially if your computer isn't very powerful!

GTFS Schedule

GTFS Schedule data represents a public transport network. This dataset enables OpenTripPlanner to compute itineraries with public buses, trains, trams, subways, etc.

Note that the file must apply to the same region as the previously selected OSM dataset for them to work together.

For example, you can obtain Belgium's railway GTFS from the NMBS-SNCB federal agency (simply register for a free access key).

Alternatively, you can easily find and download as many GTFS files as you need for your chosen OSM region. There's no hard limit on the number of GTFS files that OTP can handle at once. This is useful to cover multiple agencies in a single OpenTripPlanner app. However, be aware that the app's memory requirements and loading times will increase with the files' sizes.

After download, rename the file(s) if needed. All GTFS files must contain gtfs in their name and end with the .zip extension for OpenTripPlanner to detect them properly. It's recommended to use the [area].gtfs.zip file naming pattern for this reason.

GTFS Realtime

GTFS Realtime feeds enrich GTFS Schedule data with realtime updates about the public transport network, such as delays, cancelled trips, planned road works, etc.. They enable OpenTripPlanner to provide realtime information on itineraries that rely on public transportation.

There are three types of feeds which can be used together or independently:

  • Service Alerts are messages announcing, describing, or explaining changes to the network, such as road works, accidents, broken-down vehicles, manifestations, etc.

  • Trip Updates are realtime updates to GTFS scheduled trips, including delays, cancellations, skipped stops, etc.

  • Vehicle Positions are the realtime location of vehicles, with extra information such as vehicle type, occupancy rate, etc.

Unlike OSM and GTFS Schedule datasets, these feeds aren't static files that can be downloaded once per day, week or month. Realtime data is very shortlived, so GTFS RT feeds are usually available in the form of API endpoints which can be called every few seconds or minutes.

You can get started with the Belgian realtime data available from the same source as the GTFS Schedule above, or find another feed online. There are plenty available. Just be sure to select realtime feeds that match a previously selected GTFS Schedule file and OSM region.

GBFS

Finally, GBFS feeds describe the realtime network of shared vehicles. They enable OpenTripPlanner to compute itineraries using shared bicycles, motorbikes, scooters, cars, etc.

Just like GTFS data, GBFS contains information both static (e.g. stations) and in realtime (e.g. bike positions, battery levels). However, both are wrapped inside on single feed in this case, which works in much the same way as the GTFS RT feeds (i.e. based on an API that can be called every few minutes).

Again, make sure to select a feed that applies to your OSM region. If you're using our Belgian example, Lime Brussels (or Antwerp) and Pony Liège (or Charleroi) are both freely available to get started.

Graph builder

Okay we're done with collecting datasets. Now that you have a bunch of files stored under ./config/, the next step is to convert the static ones into a single, unified file called a graph, which basically serves as OpenTripPlanner's database. Here's how:

  1. Create a new ./docker-compose.yml file at the root of the repository.

  2. Add a service for OpenTripPlanner's graph builder:

services:
  otp-graph-builder-service:
    container_name: otp-graph-builder-container
    image: opentripplanner/opentripplanner:latest
    command: --build --save
    environment:
      JAVA_OPTIONS: "-Xmx4G"
    volumes:
      - ./config:/var/opentripplanner
  1. Run docker compose up otp-graph-builder-service in a terminal to download OTP's Docker image then launch the graph builder.

  2. Wait a couple of minutes for the process to complete. While you do, check out the logs, they're full of useful information!

First there's an overvriew of the graph builder's configuration, including OTP's version, provided config files (none so far), and enabled/disabled features:

image

Right below that there are the paths of files read, i.e. the OSM and GTFS Schedule files (but not the GTFS RT and GBFS feeds that come later). By the way this is where we found the /var/opentripplanner/ path mapped in the ./docker-compose.yml:

image

Then the graph builder logs the progress or parsing the OSM nodes, building the streets graph, linking transit stops, and everything else, plus any issues that arise.

Ultimately, the logs show that the build was successful, the data was saved to a file named graph.obj, and the programme is done, which leads the container to exit with a valid "zero" status code:

image

⚠️ Note tha the graph builder may end prematuraly with otp-graph-builder-container exited with code 1 due to OutOfMemoryError: Java heap space:

image

To resolve this, simply increase the 4 GB maximum heap memory size defined in docker-compose.yml's JAVA_OPTIONS: "-Xmx4G", then re-run docker compose up otp-graph-builder-service.

That's it for graph building! Your new graph is now available under ./config/graph.obj.

User interface

We have a graph. Time to use it. For this we need a new instance of OpenTripPlanner that can read a graph (as opposed to build it).

  1. Add a new container definition in ./docker-compose.yml:
services:
  otp-graph-builder-service:
    # ...

  otp-app-service:
    container_name: otp-app-container
    image: opentripplanner/opentripplanner:latest
    command: --load
    environment:
      JAVA_OPTIONS: "-Xmx4G"
    ports:
      - "8080:8080"
    volumes:
      - ./config:/var/opentripplanner
  1. Launch the app with docker compose up otp-app-service.

Once more you can find a bunch of useful information in the logs, including the app's configuration. Give OpenTripPlanner some time to read the graph, then you'll see Grizzly server running. as the logs last line which signals that the app is ready to handle user requests.

  1. Open http://localhost:8080/ to access the user interface. It looks like this:

image

  1. Right click somewhere in the region you selected (Belgium in this case) to select a start point, then again for the end point.

And here you have it, your first itinerary:

image

Updaters

Unlike OSM and GTFS Static data, GTFS Realtime and GBFS feeds are continuously updated so they can't be added to a static graph and be done with it. This is where OpenTripPlanner's configuration files come in.

  1. Under ./config/, add a new file named router-config.json containing an empty array of updaters:
{
  "updaters": []
}
  1. For GTFS Realtime datasets, add an entry for each Service Alerts, Trip Updates and Vehicle Positions feed:
{
  "type": "real-time-alerts",
  "feedId": "[agencyId]",
  "url": "[URL]"
},
{
  "type": "stop-time-updater",
  "feedId": "[agencyId]",
  "url": "[URL]"
},
{
  "type" : "vehicle-positions",
  "feedId" : "[agencyId]",
  "url" : "[URL]"
}

The [agencyId] should match the value defined in the corresponding GTFS Schedule agency.txt file. This is how OpenTripPlanner matches a GTFS Realtime feed with its underlying scheduled data.

The [URL] should point to a downloadable file in PBF format.

  1. For GBFS datasets, add an entry of type vehicle-rental per feed:
{
  "type": "vehicle-rental",
  "sourceType" : "gbfs",
  "url": "[URL]"
}

Note that the type is indeed "vehicle-rental", not "bike-rental" as you may find online. The former is applicable to OTP 2 (used in this article) while the latter is applicable to the previous OTP 1 version.

The [URL] expects the /gbfs.json endpoint of a GBFS API.

--

The resulting ./config/router-config.json file should look like this if you're using our Belgian datasets:

{
  "updaters": [
    {
      "type": "stop-time-updater",
      "feedId": "nmbssncb",
      "url": "https://sncb-opendata.hafas.de/gtfs/realtime/[YOUR KEY]"
    },
    {
      "type": "vehicle-rental",
      "sourceType" : "gbfs",
      "url": "https://data.lime.bike/api/partners/v2/gbfs/brussels/gbfs.json"
    },
    {
      "type": "vehicle-rental",
      "sourceType" : "gbfs",
      "url": "https://gbfs.getapony.com/v1/liege/en/gbfs.json"
    }
  ]
}

Refer to OpenTripPlanner's "Updater configuration" documentation for more options.

"What if my feeds are locally hosted?" you ask?

Then just make sure they're running on a Docker container on the same network as your OpenTripPlanner app, then use [service name]:[port number] as the host, e.g. http://my-gtfs-rt-api-service:8000/nmbssncb/trip-updates.pbf.

  1. Relaunch the app with docker compose up otp-app-service to enable the changes.

Now you can see realtime updates on the railway network:

image

And compute itineraries using shared scooters:

image

What's next?

OpenTripPlanner has a lot to offer. By playing around with the different settings and refering to the official documentation, you'll soon learn how to fully customise it to meet your expectations!