rustsim-mobility 0.0.1

Multi-modal mobility glue for rustsim: leg-based trips, mode transitions, shared obstacle interfaces between crowds, vehicles, and transit
Documentation
//! Trip plans made of per-mode [`Leg`]s.

use rustsim_geometry::vec3::Vec3;
use rustsim_modes::TravelMode;
use rustsim_transit::{RouteId, StopId};

/// A labelled point along a trip. Positions are 3-D so layered-building
/// transitions (floors, lifts) can be expressed natively.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Waypoint {
    /// Position in metres.
    pub pos: Vec3,
    /// Associated floor index (for layered 2.5-D); ignored otherwise.
    pub floor: i32,
}

impl Waypoint {
    /// Construct a ground-floor waypoint.
    pub fn ground(x: f64, y: f64) -> Self {
        Self {
            pos: [x, y, 0.0],
            floor: 0,
        }
    }
}

/// A single leg of a multi-modal trip.
#[derive(Debug, Clone)]
pub enum Leg {
    /// Walk between two waypoints (possibly across floors via connectors).
    Walk {
        /// Starting point.
        from: Waypoint,
        /// Ending point.
        to: Waypoint,
    },
    /// Ride a private vehicle (car, bicycle, motorcycle) between two waypoints.
    DriveSelf {
        /// Vehicle mode.
        mode: TravelMode,
        /// Starting point.
        from: Waypoint,
        /// Ending point.
        to: Waypoint,
    },
    /// Hail or board a shared ride (taxi, ride-hail) between two waypoints.
    Hail {
        /// Transport mode (usually `Vehicle`).
        mode: TravelMode,
        /// Starting point.
        from: Waypoint,
        /// Ending point.
        to: Waypoint,
    },
    /// Board a fixed-route transit service.
    Transit {
        /// Route to board.
        route: RouteId,
        /// Stop to board at.
        board_at: StopId,
        /// Stop to alight at.
        alight_at: StopId,
    },
    /// Ride a vertical connector (stair/escalator/ramp/lift).
    Connector {
        /// Connector identifier, opaque to this crate.
        connector_id: u64,
    },
}

impl Leg {
    /// Travel mode associated with this leg.
    pub fn mode(&self) -> TravelMode {
        match self {
            Leg::Walk { .. } | Leg::Connector { .. } => TravelMode::Pedestrian,
            Leg::DriveSelf { mode, .. } | Leg::Hail { mode, .. } => *mode,
            Leg::Transit { .. } => TravelMode::Transit,
        }
    }
}

/// A planned multi-modal trip.
#[derive(Debug, Clone)]
pub struct TripPlan {
    /// Stable trip identifier.
    pub id: u64,
    /// Ordered legs.
    pub legs: Vec<Leg>,
}

impl TripPlan {
    /// Number of legs.
    pub fn len(&self) -> usize {
        self.legs.len()
    }

    /// True if the trip has no legs.
    pub fn is_empty(&self) -> bool {
        self.legs.is_empty()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn leg_mode_dispatch() {
        let walk = Leg::Walk {
            from: Waypoint::ground(0.0, 0.0),
            to: Waypoint::ground(1.0, 0.0),
        };
        assert_eq!(walk.mode(), TravelMode::Pedestrian);

        let transit = Leg::Transit {
            route: 1,
            board_at: 10,
            alight_at: 20,
        };
        assert_eq!(transit.mode(), TravelMode::Transit);

        let drive = Leg::DriveSelf {
            mode: TravelMode::Vehicle,
            from: Waypoint::ground(0.0, 0.0),
            to: Waypoint::ground(5.0, 0.0),
        };
        assert_eq!(drive.mode(), TravelMode::Vehicle);
    }
}