1extern crate rand;
2
3use std::collections::BTreeSet;
4
5use rand::Rng;
6use rand_xorshift::XorShiftRng;
7use serde::{Deserialize, Serialize};
8
9use abstutil::Timer;
10use geom::{Duration, Time};
11use map_model::Map;
12
13use crate::{Scenario, TripMode};
14
15#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
17pub enum ScenarioModifier {
18 RepeatDays(usize),
19 RepeatDaysNoise {
20 days: usize,
21 departure_time_noise: Duration,
22 },
23 ChangeMode {
24 pct_ppl: usize,
25 departure_filter: (Time, Time),
26 from_modes: BTreeSet<TripMode>,
27 to_mode: Option<TripMode>,
29 },
30 AddExtraTrips(String),
32}
33
34impl ScenarioModifier {
35 pub fn apply(&self, map: &Map, mut s: Scenario, rng: &mut XorShiftRng) -> Scenario {
38 match self {
39 ScenarioModifier::RepeatDays(n) => repeat_days(s, *n, None, rng),
40 ScenarioModifier::RepeatDaysNoise {
41 days,
42 departure_time_noise,
43 } => repeat_days(s, *days, Some(*departure_time_noise), rng),
44 ScenarioModifier::ChangeMode {
45 pct_ppl,
46 departure_filter,
47 from_modes,
48 to_mode,
49 } => {
50 for (idx, person) in s.people.iter_mut().enumerate() {
51 if idx % 100 > *pct_ppl {
55 continue;
56 }
57 let mut cancel_rest = false;
58 for trip in &mut person.trips {
59 if cancel_rest {
60 trip.modified = true;
61 trip.cancelled = true;
62 continue;
63 }
64
65 if trip.depart < departure_filter.0 || trip.depart > departure_filter.1 {
66 continue;
67 }
68 if !from_modes.contains(&trip.mode) {
69 continue;
70 }
71 if let Some(to_mode) = *to_mode {
72 trip.mode = to_mode;
73 trip.modified = true;
74 } else {
75 trip.modified = true;
76 trip.cancelled = true;
77 cancel_rest = true;
80 }
81 }
82 }
83 s
84 }
85 ScenarioModifier::AddExtraTrips(name) => {
87 let other: Scenario = abstio::must_read_object(
88 abstio::path_scenario(map.get_name(), name),
89 &mut Timer::throwaway(),
90 );
91 for mut p in other.people {
92 for trip in &mut p.trips {
93 trip.modified = true;
94 }
95 s.people.push(p);
96 }
97 s
98 }
99 }
100 }
101
102 pub fn describe(&self) -> String {
103 match self {
104 ScenarioModifier::RepeatDays(n) => format!("repeat the entire day {} times", n),
105 ScenarioModifier::RepeatDaysNoise {
106 days,
107 departure_time_noise,
108 } => format!(
109 "repeat the entire day {} times with +/- {} noise on each departure",
110 days, departure_time_noise
111 ),
112 ScenarioModifier::ChangeMode {
113 pct_ppl,
114 to_mode,
115 departure_filter,
116 from_modes,
117 } => format!(
118 "change all trips for {}% of people of types {:?} leaving between {} and {} to \
119 {:?}",
120 pct_ppl,
121 from_modes,
122 departure_filter.0.ampm_tostring(),
123 departure_filter.1.ampm_tostring(),
124 to_mode.map(|m| m.verb())
125 ),
126 ScenarioModifier::AddExtraTrips(name) => format!("Add extra trips from {}", name),
127 }
128 }
129}
130
131fn repeat_days(
140 mut s: Scenario,
141 days: usize,
142 noise: Option<Duration>,
143 rng: &mut XorShiftRng,
144) -> Scenario {
145 s.scenario_name = format!("{} (repeated {} days)", s.scenario_name, days);
146 for person in &mut s.people {
147 let mut trips = Vec::new();
148 let mut offset = Duration::ZERO;
149 for _ in 0..days {
150 for trip in &person.trips {
151 let mut new = trip.clone();
152 new.depart += offset;
153 if let Some(noise_v) = noise {
154 let noise_rnd = Duration::seconds(
156 rng.gen_range((0.0)..=(2.0 * noise_v.inner_seconds() as f64)),
157 ) - noise_v;
158 new.depart = new.depart.clamped_sub(noise_rnd);
159 }
160 new.modified = true;
161 trips.push(new);
162 }
163 offset += Duration::hours(24);
164 }
165 person.trips = trips;
166 }
167 s
168}