RiderUtils.Gtfs (RiderUtils v0.1.0)

Copy Markdown View Source

A GenServer that downloads and handles updating GTFS files.

Add it to your application and it'll load the files to disk on initialization.

children = [
  RiderUtils.Gtfs
]

And you can get the values and do things with them:

all_inbound_destinations = case RiderUtils.Gtfs.get("directions.txt") do
  {:ok, directions} ->
    directions
    |> Enum.filter(& &1["direction_id"] == "1")
    |> Enum.map(& {&1["route_id"], &1["direction_destination"]})
  other ->
    other
end

You probably shouldn't do it, but technically can have more than one of these:

children = [
  {RiderUtils.Gtfs, name: Green, gtfs_url: "https://cdn.mbta.com/MBTA_GTFS-dev-green.zip"},
  {RiderUtils.Gtfs, name: Blue, gtfs_url: "https://cdn.mbta.com/MBTA_GTFS-dev-blue.zip"}
]

Which'll let you handle multiple sets of files, I guess?

# true???
RiderUtils.Gtfs.get("agency.txt", Green) != RiderUtils.Gtfs.get("agency.txt", Blue)

Options

All fields are optional.

  • :gtfs_url - The HTTP endpoint from which to download the GTFS archive. Defaults to "https://cdn.mbta.com/MBTA_GTFS.zip".
  • :on_update - An MFA tuple that will be invoked after successful GTFS file download.
  • :refresh_interval - How frequently to check for GTFS file changes. Defaults to :timer.minutes(5).

What refreshing means

After the initial download, the Last-Modified response header is saved for future calls. Subsequent calls leverage the If-Modified-Since request header. If the file on the :gtfs_url server hasn't changed since, we get a 304 response and don't need to update our own data. So, the :refresh_interval merely defines how frequently we are checking, not how often we're downloading the .zip file.

Summary

Functions

Get the parsed contents of a GTFS file.

Functions

get(gtfs_file, server \\ __MODULE__)

@spec get(String.t(), GenServer.server()) :: {:ok, [map()]} | {:error, term()}

Get the parsed contents of a GTFS file.

Expects a valid GTFS filename such as "calendar.txt", "trips_properties.txt", and such. Returns a map per row.

Because the files can be quite large, and the file contents do not change often, it is recommended to run this prudently.

Usage

{:ok, [%{"feed_version" => version} | _]} = RiderUtils.Gtfs.get("feed_info.txt")
version # "Summer 2026, 2026-08-10T18:56:26+00:00, version D"

If using a named GenServer,

{:ok, routes} = RiderUtils.Gtfs.get("routes.txt", CustomGtfsServer)
ferry_routes = Enum.find(routes, & &1["route_type"] == "4")