ltn/
export.rs

1use anyhow::Result;
2use geo::MapCoordsInPlace;
3use geojson::{Feature, FeatureCollection, GeoJson, Value};
4
5use geom::{PolyLine, Pt2D};
6use osm2streets::Direction;
7
8use crate::{render, App, Neighbourhood};
9
10pub fn geojson_string(app: &App) -> Result<String> {
11    let map = &app.per_map.map;
12    let gps_bounds = Some(map.get_gps_bounds());
13    let mut features = Vec::new();
14
15    // All neighbourhood boundaries
16    for (id, info) in app.partitioning().all_neighbourhoods() {
17        let mut feature = Feature::from(info.block.polygon.to_geojson(gps_bounds));
18        feature.set_property("type", "neighbourhood");
19        features.push(feature);
20
21        // Cells per neighbourhood
22        let render_cells = render::RenderCells::new(map, &Neighbourhood::new(app, *id));
23        for (idx, mut multipolygon) in render_cells.to_multipolygons().into_iter().enumerate() {
24            // Transform to WGS84
25            multipolygon.map_coords_in_place(|c| {
26                let gps = Pt2D::new(c.x, c.y).to_gps(map.get_gps_bounds());
27                (gps.x(), gps.y()).into()
28            });
29            let mut feature = Feature::from(Value::from(&multipolygon));
30            feature.set_property("type", "cell");
31            feature.set_property("fill", render_cells.colors[idx].as_hex());
32            features.push(feature);
33        }
34    }
35
36    // All modal filters
37    for (road, filter) in map.all_roads_with_modal_filter() {
38        if let Ok((pt, angle)) = road.center_pts.dist_along(filter.dist) {
39            let road_width = road.get_width();
40            let pl = PolyLine::must_new(vec![
41                pt.project_away(0.8 * road_width, angle.rotate_degs(90.0)),
42                pt.project_away(0.8 * road_width, angle.rotate_degs(-90.0)),
43            ]);
44            let mut feature = Feature::from(pl.to_geojson(gps_bounds));
45            feature.set_property("type", "road filter");
46            feature.set_property("filter_type", format!("{:?}", filter.filter_type));
47            feature.set_property("stroke", "red");
48            features.push(feature);
49        }
50    }
51    for i in map.all_intersections() {
52        if let Some(ref filter) = i.modal_filter {
53            let pl = filter.geometry(map).to_polyline();
54            let mut feature = Feature::from(pl.to_geojson(gps_bounds));
55            feature.set_property("type", "diagonal filter");
56            feature.set_property("filter_type", format!("{:?}", filter.filter_type));
57            feature.set_property("stroke", "red");
58            features.push(feature);
59        }
60    }
61
62    // This includes the direction of every driveable road, not just one-ways. Not sure who's using
63    // this export or how, so doesn't matter much.
64    for road in map.all_roads() {
65        if crate::is_driveable(road, map) {
66            let mut feature = Feature::from(road.center_pts.to_geojson(gps_bounds));
67            feature.set_property("type", "direction");
68            feature.set_property(
69                "direction",
70                match road.oneway_for_driving() {
71                    Some(Direction::Fwd) => "one-way forwards",
72                    Some(Direction::Back) => "one-way backwards",
73                    None => "two-ways",
74                },
75            );
76            feature.set_property("stroke", "blue");
77            features.push(feature);
78        }
79    }
80
81    for road in map.all_roads() {
82        for crossing in &road.crossings {
83            let mut feature = Feature::from(
84                road.center_pts
85                    .must_dist_along(crossing.dist)
86                    .0
87                    .to_geojson(gps_bounds),
88            );
89            feature.set_property("type", "crossing");
90            feature.set_property("crossing_type", format!("{:?}", crossing.kind));
91            features.push(feature);
92        }
93    }
94
95    let gj = GeoJson::FeatureCollection(FeatureCollection {
96        features,
97        bbox: None,
98        foreign_members: None,
99    });
100
101    let x = serde_json::to_string_pretty(&gj)?;
102    Ok(x)
103}