1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use anyhow::Result;
use geojson::{Feature, GeoJson, Value};
use rand::{Rng, SeedableRng};
use rand_xorshift::XorShiftRng;
use geom::{Duration, LonLat, Time};
use map_model::Map;
use sim::{ExternalPerson, ExternalTrip, ExternalTripEndpoint, Scenario, TripMode};
use crate::configuration::ImporterConfiguration;
use crate::utils::download;
pub fn import_scenarios(map: &Map, config: &ImporterConfiguration) -> Result<()> {
download(
config,
map.get_city_name().input_path("desire_lines_disag.geojson"),
"https://raw.githubusercontent.com/cyipt/actdev/main/data-small/great-kneighton/desire_lines_disag.geojson",
);
let bytes = abstio::slurp_file(map.get_city_name().input_path("desire_lines_disag.geojson"))?;
let raw_string = std::str::from_utf8(&bytes)?;
let geojson = raw_string.parse::<GeoJson>()?;
let mut baseline = Vec::new();
let mut go_dutch = Vec::new();
if let GeoJson::FeatureCollection(collection) = geojson {
for feature in collection.features {
let (home, work) = match feature.geometry.as_ref().map(|g| &g.value) {
Some(Value::LineString(pts)) => {
if pts.len() != 2 {
bail!("Desire line doesn't have 2 points: {:?}", pts);
} else {
(
LonLat::new(pts[0][0], pts[0][1]),
LonLat::new(pts[1][0], pts[1][1]),
)
}
}
_ => {
bail!("Geometry isn't a line-string: {:?}", feature);
}
};
for (mode, baseline_key, go_dutch_key) in vec![
(TripMode::Walk, "foot", "walk_commute_godutch"),
(TripMode::Bike, "bicycle", "bicycle_commute_godutch"),
(TripMode::Drive, "car_driver", "car_commute_godutch"),
] {
baseline.push(ODSummary {
home,
work,
mode,
count: parse_usize(&feature, baseline_key)?,
});
go_dutch.push(ODSummary {
home,
work,
mode,
count: parse_usize(&feature, go_dutch_key)?,
});
}
}
}
generate_scenario("baseline", baseline, map);
generate_scenario("go_dutch", go_dutch, map);
Ok(())
}
fn parse_usize(feature: &Feature, key: &str) -> Result<usize> {
match feature.property(key).and_then(|value| value.as_f64()) {
Some(count) => Ok(count as usize),
None => bail!("{} missing or not a number", key),
}
}
struct ODSummary {
home: LonLat,
work: LonLat,
mode: TripMode,
count: usize,
}
fn generate_scenario(name: &str, input: Vec<ODSummary>, map: &Map) {
let mut rng = XorShiftRng::seed_from_u64(42);
let mut people = Vec::new();
for od in input {
for _ in 0..od.count {
let leave_time = rand_time(&mut rng, Duration::hours(7), Duration::hours(9));
let return_time = rand_time(&mut rng, Duration::hours(17), Duration::hours(19));
people.push(ExternalPerson {
origin: ExternalTripEndpoint::Position(od.home),
trips: vec![
ExternalTrip {
departure: leave_time,
destination: ExternalTripEndpoint::Position(od.work),
mode: od.mode,
},
ExternalTrip {
departure: return_time,
destination: ExternalTripEndpoint::Position(od.home),
mode: od.mode,
},
],
});
}
}
let mut scenario = Scenario::empty(map, name);
scenario.only_seed_buses = None;
scenario.people = ExternalPerson::import(map, people, false).unwrap();
scenario.save();
}
fn rand_duration(rng: &mut XorShiftRng, low: Duration, high: Duration) -> Duration {
assert!(high > low);
Duration::seconds(rng.gen_range(low.inner_seconds()..high.inner_seconds()))
}
fn rand_time(rng: &mut XorShiftRng, low: Duration, high: Duration) -> Time {
Time::START_OF_DAY + rand_duration(rng, low, high)
}