convert_osm/
elevation.rs

1use std::collections::HashMap;
2use std::io::BufReader;
3
4use anyhow::Result;
5use elevation::GeoTiffElevation;
6use fs_err::File;
7use geom::Distance;
8
9use abstutil::Timer;
10use raw_map::RawMap;
11
12pub fn add_data(map: &mut RawMap, path: &str, timer: &mut Timer) -> Result<()> {
13    // Get intersection points from road endpoints, to reduce the number of elevation lookups
14    let mut intersection_points = HashMap::new();
15    for r in map.streets.roads.values() {
16        for (i, pt) in [
17            (r.src_i, r.reference_line.first_pt()),
18            (r.dst_i, r.reference_line.last_pt()),
19        ] {
20            intersection_points.insert(i, pt.to_gps(&map.streets.gps_bounds));
21        }
22    }
23
24    // TODO Download the file if needed?
25    let mut elevation = GeoTiffElevation::new(BufReader::new(File::open(path)?));
26
27    timer.start_iter("lookup elevation", intersection_points.len());
28    for (i, gps) in intersection_points {
29        timer.next();
30        if let Some(height) = elevation.get_height_for_lon_lat(gps.x() as f32, gps.y() as f32) {
31            if height < 0.0 {
32                continue;
33            }
34            map.elevation_per_intersection
35                .insert(i, Distance::meters(height.into()));
36        }
37    }
38
39    // Calculate the incline for each road here, before the road gets trimmed for intersection
40    // geometry. If we did this after trimming, we'd miss some of the horizontal distance.
41    for road in map.streets.roads.values() {
42        let rise = map.elevation_per_intersection[&road.dst_i]
43            - map.elevation_per_intersection[&road.src_i];
44        let run = road.untrimmed_length();
45        if !(rise / run).is_finite() {
46            // TODO Warn?
47            continue;
48        }
49        let data = map.extra_road_data.get_mut(&road.id).unwrap();
50        data.percent_incline = rise / run;
51        // Per https://wiki.openstreetmap.org/wiki/Key:incline#Common_.26_extreme_inclines, we
52        // shouldn't often see values outside a certain range. Adjust this when we import
53        // somewhere exceeding this...
54        if data.percent_incline.abs() > 0.3 {
55            error!(
56                "{} is unexpectedly steep! Incline is {}%",
57                road.id,
58                data.percent_incline * 100.0
59            );
60        }
61    }
62
63    Ok(())
64}