ontrack 0.0.4

High-performance Rust library for routing, searching, and querying GTFS transit data with minimal runtime allocations.
Documentation

On Track

On Track is a high-performance Rust library for loading, routing, searching, and querying GTFS transit data, designed for minimal runtime allocations and fast lookups.

[!NOTE] This project is early in development, if you like the idea and want to help improve it, please open an issue.

Installation

cargo add ontrack

Design

The ontrack engine is designed to be immutable and thread-safe. All data is stored only once on the heap, and the engine works by passing around references rather than cloning or reallocating existing structures.

Entities such as stops, areas, and others are stored as Arc<[T]> slices, and all strings are held as Arc<str>. This ensures thread safety and keeps the memory footprint low, since no entity is ever allocated more than once.

The only time the engine allocates new memory is when performing request-driven operations such as search operations. In those cases, the newly allocated memory is owned entirely by the consumer (i.e., you). In a scenario like a web server, this means the allocated data exists only for the duration of the request and is freed immediately afterward.

Usage

Simple program for finding areas (An area is a geografic area/collection of stops).

use std::{env, path::Path, process::exit, time::Instant};

use ontrack::{
    engine::{self, Identifiable},
    gtfs,
};

fn main() {
    let args: Vec<_> = env::args().collect();
    if args.len() < 3 {
        println!("Missing gtfs zip and/or search string");
        exit(1);
    }

    // Takes the search string from a possible local space like data/gtfs.zip into system i.e /home/user/data/gtfs.zip
    let path = Path::new(&args[1]).canonicalize().unwrap();

    // The gtfs module is used to read and parse gtfs data into in memory structs.
    // The structs that are held in the gtfs are supposed to be identical to the ones found in the gtfs spec.
    // We don't load the data until a load_from function is called.
    let data = gtfs::Gtfs::new(gtfs::Config::default())
        .load_from_zip(path)
        .unwrap();


    // This creates a new engine, good to note is that the engine is empty until a with function is called.
    let engine = engine::Engine::new().with_gtfs(data);

    let start = Instant::now();
    // The return of the search_areas_by_name is owned by the main function.
    let results = engine.search_areas_by_name(&args[2]);
    // Since the area struct is built using Arc<str> it's really cheap to copy but here we are using refrences.
    for value in results.iter().take(5) {
        println!("{}", value.name());
    }
    let duration = start.elapsed();
    // Note that building with --release tag improves performance alot.
    println!("Operation took: {:?}", duration);
}

Implemented

  • Load GTFS data directly from .zip archives.
  • In-memory GTFS engine for fast read/query operations.
  • Direct querying of entities by ID. 500ns to 500µs
  • Fuzzy search for stops and geographic areas.

Roadmap

  • Distance-based search for stops and areas.
  • Simple distance-based routing.
  • Time-based routing and schedule-aware journey planning.

Refrences