use crate::{
model::Model,
objects::{StopPoint, Transfer},
Result,
};
use log::info;
use std::collections::HashMap;
use typed_index_collection::{Collection, CollectionWithId, Idx};
type TransferMap = HashMap<(Idx<StopPoint>, Idx<StopPoint>), Transfer>;
pub type NeedTransfer<'a> = Box<dyn 'a + Fn(&Model, Idx<StopPoint>, Idx<StopPoint>) -> bool>;
fn make_transfers_map(
transfers: Collection<Transfer>,
sp: &CollectionWithId<StopPoint>,
) -> TransferMap {
transfers
.into_iter()
.map(|t| {
(
(
sp.get_idx(&t.from_stop_id).unwrap(),
sp.get_idx(&t.to_stop_id).unwrap(),
),
t,
)
})
.collect()
}
fn generate_transfers_from_sp(
transfers_map: &mut TransferMap,
model: &Model,
max_distance: f64,
walking_speed: f64,
waiting_time: u32,
need_transfer: Option<NeedTransfer>,
) {
info!("Adding missing transfers from stop points.");
let sq_max_distance = max_distance * max_distance;
for (idx1, sp1) in model.stop_points.iter() {
let approx = sp1.coord.approx();
for (idx2, sp2) in model.stop_points.iter() {
if transfers_map.contains_key(&(idx1, idx2)) {
continue;
}
if let Some(ref f) = need_transfer {
if !f(model, idx1, idx2) {
continue;
}
}
let sq_distance = approx.sq_distance_to(&sp2.coord);
if sq_distance > sq_max_distance {
continue;
}
let transfer_time = (sq_distance.sqrt() / walking_speed) as u32;
transfers_map.insert(
(idx1, idx2),
Transfer {
from_stop_id: sp1.id.clone(),
to_stop_id: sp2.id.clone(),
min_transfer_time: Some(transfer_time),
real_min_transfer_time: Some(transfer_time + waiting_time),
equipment_id: None,
},
);
}
}
}
pub fn generates_transfers(
model: Model,
max_distance: f64,
walking_speed: f64,
waiting_time: u32,
need_transfer: Option<NeedTransfer>,
) -> Result<Model> {
info!("Generating transfers...");
let mut transfers_map = make_transfers_map(model.transfers.clone(), &model.stop_points);
generate_transfers_from_sp(
&mut transfers_map,
&model,
max_distance,
walking_speed,
waiting_time,
need_transfer,
);
let mut new_transfers: Vec<_> = transfers_map.into_iter().map(|(_, v)| v).collect();
new_transfers.sort_unstable_by(|t1, t2| {
(&t1.from_stop_id, &t1.to_stop_id).cmp(&(&t2.from_stop_id, &t2.to_stop_id))
});
let mut collections = model.into_collections();
collections.transfers = Collection::new(new_transfers);
Ok(Model::new(collections)?)
}