use rustsim_crowd::Pedestrian;
use rustsim_mobility::prelude::*;
use rustsim_modes::{AllowedModes, TravelMode};
use rustsim_traffic::{
LinkClass, LinkProperties, SignalTiming, TrafficControlType, TransportLinkMetadata,
TransportLinkOps, TransportLinkSpace,
};
use rustsim_transit::{Boarding, Route, Schedule, Stop, TransitVehicle, Waiter};
use rustsim_vehicle::{idm, Vehicle, VehicleModel};
const TRAVELLER: u64 = 42;
fn build_network() -> (
TransportLinkSpace,
Vec<TransportLinkMetadata>,
[usize; 4],
usize,
) {
let mut space = TransportLinkSpace::new();
let home = space.add_node();
let stop_a = space.add_node();
let stop_b = space.add_node();
let work = space.add_node();
let (walk_geom, walk_props) = LinkProperties::pedestrian(80.0, 3.0).unwrap();
let walk_in = space.add_link(home, stop_a, walk_geom, walk_props).unwrap();
let (bus_geom, bus_props) = LinkProperties::urban(600.0, 40.0, 1).unwrap();
let bus_link = space.add_link(stop_a, stop_b, bus_geom, bus_props).unwrap();
let (walk_out_geom, walk_out_props) = LinkProperties::pedestrian(80.0, 3.0).unwrap();
let walk_out = space
.add_link(stop_b, work, walk_out_geom, walk_out_props)
.unwrap();
let (direct_geom, direct_props) = LinkProperties::pedestrian(1_200.0, 3.0).unwrap();
let direct_walk = space
.add_link(home, work, direct_geom, direct_props)
.unwrap();
let metadata = vec![
TransportLinkMetadata::new(
walk_in,
home,
stop_a,
LinkClass::Walkway,
AllowedModes::pedestrian_only(),
),
TransportLinkMetadata::new(
bus_link,
stop_a,
stop_b,
LinkClass::Transitway,
AllowedModes::none().with_mode(TravelMode::Transit),
),
TransportLinkMetadata::new(
walk_out,
stop_b,
work,
LinkClass::Walkway,
AllowedModes::pedestrian_only(),
),
TransportLinkMetadata::new(
direct_walk,
home,
work,
LinkClass::Walkway,
AllowedModes::pedestrian_only(),
),
];
(space, metadata, [home, stop_a, stop_b, work], bus_link)
}
#[test]
fn multimodal_trip_exercises_routing_transit_vehicle_traffic_and_obstacles() {
let (mut space, metadata, [home, stop_a, stop_b, work], bus_link) = build_network();
let graph = ModalGraph::from_transport_links(&space, metadata).unwrap();
let walk_only = shortest_path(
&graph,
home as u64,
work as u64,
AllowedModes::pedestrian_only(),
)
.expect("direct walk should be reachable");
assert_eq!(walk_only.nodes, vec![home as u64, work as u64]);
let multimodal = AllowedModes::pedestrian_only().with_mode(TravelMode::Transit);
let transit_route = shortest_path(&graph, home as u64, work as u64, multimodal)
.expect("walk + transit + walk should be reachable");
assert_eq!(
transit_route.nodes,
vec![home as u64, stop_a as u64, stop_b as u64, work as u64]
);
assert!(transit_route.total_cost < walk_only.total_cost);
let stop_a_def = Stop::at_ground(10, "A", 80.0, 0.0, 120);
let stop_b_def = Stop::at_ground(20, "B", 680.0, 0.0, 120);
let route = Route::new(
5,
"Blue",
vec![stop_a_def.id, stop_b_def.id],
vec![space.link_free_flow_time(bus_link)],
Schedule::fixed_headway(300.0),
);
assert_eq!(route.index_of(stop_a_def.id), Some(0));
assert_eq!(route.index_of(stop_b_def.id), Some(1));
let plan = TripPlan {
id: 1,
legs: vec![
Leg::Walk {
from: Waypoint::ground(0.0, 0.0),
to: Waypoint::ground(stop_a_def.pos[0], stop_a_def.pos[1]),
},
Leg::Transit {
route: route.id,
board_at: stop_a_def.id,
alight_at: stop_b_def.id,
},
Leg::Walk {
from: Waypoint::ground(stop_b_def.pos[0], stop_b_def.pos[1]),
to: Waypoint::ground(760.0, 0.0),
},
],
};
let controller = ModeController;
let mut traveller = TravellerContext::new(TRAVELLER);
controller.enter_current_leg(&mut traveller, &plan);
assert_eq!(traveller.state, ModeState::Walking);
controller.complete_leg(&mut traveller, &plan);
assert_eq!(
traveller.state,
ModeState::WaitingAt(stop_a_def.id, route.id)
);
let mut queue = Boarding::new();
let waiter = Waiter {
passenger: TRAVELLER,
destination: stop_b_def.id,
arrived_at: 0.0,
};
assert!(CapacityStopQueuePolicy.can_enqueue(&stop_a_def, &queue, &waiter));
queue.enqueue(waiter);
assert_eq!(
ScheduledDispatchPolicy.decide(DispatchContext::new(&route, 0.0)),
DispatchDecision::Dispatch { departure_s: 0.0 }
);
let mut service = TransitVehicle::idle(99, route.id, 30);
let mut passenger_destinations = Vec::new();
let boarding = queue.board_vehicle_with_policy(
&mut service,
&route.stops,
&mut passenger_destinations,
&FifoBoardingPolicy::capped(4),
);
assert_eq!(boarding.boarded, 1);
assert!(queue.is_empty());
let dwell = LinearDwellPolicy::default().dwell_time(0, boarding.boarded);
assert!(dwell.total > 0.0);
controller.board_transit(&mut traveller, service.id, stop_b_def.id);
assert_eq!(
traveller.state,
ModeState::RidingTransit {
vehicle: service.id,
alight_at: stop_b_def.id,
}
);
space.add_agent_to_link(service.id, bus_link, 0.0).unwrap();
let speed = space
.agent_speed_with_policy(service.id, &FifoGapPolicy::default())
.unwrap();
assert!(speed > 0.0);
let signal = SignalTiming::new(60.0, 0.0, vec![30.0]);
let control = FixedControlPolicy::default()
.decide(ControlContext::new(45.0, TrafficControlType::Signal).with_signal_timing(&signal));
assert_eq!(control, ControlDecision::Hold { wait_s: 15.0 });
let advanced = space.advance_agent(service.id, speed * 10.0).unwrap();
assert!(advanced > 0.0);
let mut bus = Vehicle {
pos: [stop_a_def.pos[0], stop_a_def.pos[1]],
speed,
..Vehicle::default_bus()
};
rustsim_vehicle::IntelligentDriverModel.step(&mut [bus], &idm::Params::default(), 1.0);
let mut bus_after = [bus];
rustsim_vehicle::IntelligentDriverModel.step(&mut bus_after, &idm::Params::default(), 1.0);
bus = bus_after[0];
assert!(bus.pos[0] > stop_a_def.pos[0]);
let ped = Pedestrian::new(
[stop_a_def.pos[0] - 2.0, 0.0],
[1.0, 0.0],
0.25,
1.3,
[80.0, 0.0],
);
let ped_obstacle = PedestrianObstacle::ground(TRAVELLER, &ped).snapshot();
let bus_obstacle = VehicleObstacle::ground(service.id, &bus).snapshot();
assert_eq!(ped_obstacle.kind, ObstacleKind::Pedestrian);
assert_eq!(bus_obstacle.kind, ObstacleKind::Transit);
assert!(bus_obstacle.radius > ped_obstacle.radius);
let alighted = service.alight_at(stop_b_def.id, &passenger_destinations);
assert_eq!(alighted, vec![TRAVELLER]);
controller.alight_transit(&mut traveller, &plan);
assert_eq!(traveller.state, ModeState::Walking);
controller.complete_leg(&mut traveller, &plan);
assert_eq!(traveller.state, ModeState::Finished);
}