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
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Write};
use std::process::Command;
use anyhow::Result;
use abstutil::{must_run_cmd, Timer};
use geom::{Distance, PolyLine};
use map_model::raw::{OriginalRoad, RawMap};
pub fn add_data(map: &mut RawMap, timer: &mut Timer) -> Result<()> {
timer.start("add elevation data");
timer.start("generate input");
let ids = generate_input(map)?;
timer.stop("generate input");
timer.start("run elevation_lookups");
std::fs::create_dir_all("elevation_output")?;
std::fs::create_dir_all("data/input/shared/elevation")?;
let pwd = std::env::current_dir()?.display().to_string();
must_run_cmd(
Command::new("docker")
.arg("run")
.arg("--mount")
.arg(format!(
"type=bind,source={}/elevation_input,target=/elevation/input,readonly",
pwd
))
.arg("--mount")
.arg(format!(
"type=bind,source={}/data/input/shared/elevation,target=/elevation/data",
pwd
))
.arg("--mount")
.arg(format!(
"type=bind,source={}/elevation_output,target=/elevation/output",
pwd
))
.arg("-t")
.arg("elevation_lookups")
.arg("python3")
.arg("main.py")
.arg("query"),
);
timer.stop("run elevation_lookups");
timer.start("grab output");
scrape_output(map, ids)?;
timer.stop("grab output");
std::fs::remove_file("elevation_input/query")?;
std::fs::remove_dir("elevation_input")?;
std::fs::remove_file("elevation_output/query")?;
std::fs::remove_dir("elevation_output")?;
timer.stop("add elevation data");
Ok(())
}
fn generate_input(map: &RawMap) -> Result<Vec<OriginalRoad>> {
std::fs::create_dir_all("elevation_input")?;
let mut f = BufWriter::new(File::create("elevation_input/query")?);
let mut ids = Vec::new();
for (id, r) in &map.roads {
if let Ok(pl) = PolyLine::new(r.center_points.clone()) {
ids.push(id.clone());
let mut pts = Vec::new();
let mut dist = Distance::ZERO;
while dist <= pl.length() {
let (pt, _) = pl.dist_along(dist).unwrap();
pts.push(pt);
dist += Distance::meters(1.0);
}
if *pts.last().unwrap() != pl.last_pt() {
pts.push(pl.last_pt());
}
for (idx, gps) in map.gps_bounds.convert_back(&pts).into_iter().enumerate() {
write!(f, "{},{}", gps.x(), gps.y())?;
if idx != pts.len() - 1 {
write!(f, " ")?;
}
}
writeln!(f)?;
}
}
Ok(ids)
}
fn scrape_output(map: &mut RawMap, ids: Vec<OriginalRoad>) -> Result<()> {
let num_ids = ids.len();
let mut cnt = 0;
for (line, id) in BufReader::new(File::open("elevation_output/query")?)
.lines()
.zip(ids)
{
let line = line?;
let mut values = Vec::new();
for x in line.split('\t') {
values.push(Distance::meters(x.parse::<f64>()?));
}
if values.len() != 4 {
bail!("Elevation output line \"{}\" doesn't have 4 numbers", line);
}
map.intersections.get_mut(&id.i1).unwrap().elevation = values[0];
map.intersections.get_mut(&id.i2).unwrap().elevation = values[1];
cnt += 1;
}
if cnt != num_ids {
bail!("Output had {} lines, but we made {} queries", cnt, num_ids);
}
Ok(())
}