mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 12:12:00 +03:00
simplify dataflow a bit in convert_osm
This commit is contained in:
parent
ead5593448
commit
a5fc3b092f
@ -1,13 +1,14 @@
|
|||||||
use abstutil::{retain_btreemap, Timer};
|
use abstutil::{retain_btreemap, Timer};
|
||||||
use geom::{LonLat, PolyLine, Polygon};
|
use geom::PolyLine;
|
||||||
use map_model::{raw_data, IntersectionType};
|
use map_model::{raw_data, IntersectionType};
|
||||||
|
|
||||||
pub fn clip_map(map: &mut raw_data::Map, boundary_poly_pts: Vec<LonLat>, timer: &mut Timer) {
|
pub fn clip_map(map: &mut raw_data::Map, timer: &mut Timer) {
|
||||||
timer.start("clipping map to boundary");
|
timer.start("clipping map to boundary");
|
||||||
|
|
||||||
let boundary_poly = Polygon::new(&map.gps_bounds.must_convert(&boundary_poly_pts));
|
// So we can use retain_btreemap without borrowing issues
|
||||||
map.boundary_polygon = boundary_poly.clone();
|
let boundary_polygon = map.boundary_polygon.clone();
|
||||||
let boundary_lines: Vec<PolyLine> = boundary_poly
|
let boundary_lines: Vec<PolyLine> = map
|
||||||
|
.boundary_polygon
|
||||||
.points()
|
.points()
|
||||||
.windows(2)
|
.windows(2)
|
||||||
.map(|pair| PolyLine::new(pair.to_vec()))
|
.map(|pair| PolyLine::new(pair.to_vec()))
|
||||||
@ -16,16 +17,18 @@ pub fn clip_map(map: &mut raw_data::Map, boundary_poly_pts: Vec<LonLat>, timer:
|
|||||||
// This is kind of indirect and slow, but first pass -- just remove roads that start or end
|
// This is kind of indirect and slow, but first pass -- just remove roads that start or end
|
||||||
// outside the boundary polygon.
|
// outside the boundary polygon.
|
||||||
retain_btreemap(&mut map.roads, |_, r| {
|
retain_btreemap(&mut map.roads, |_, r| {
|
||||||
let first_in = boundary_poly.contains_pt(r.center_points[0]);
|
let first_in = boundary_polygon.contains_pt(r.center_points[0]);
|
||||||
let last_in = boundary_poly.contains_pt(*r.center_points.last().unwrap());
|
let last_in = boundary_polygon.contains_pt(*r.center_points.last().unwrap());
|
||||||
first_in || last_in
|
first_in || last_in
|
||||||
});
|
});
|
||||||
|
|
||||||
let road_ids: Vec<raw_data::StableRoadID> = map.roads.keys().cloned().collect();
|
let road_ids: Vec<raw_data::StableRoadID> = map.roads.keys().cloned().collect();
|
||||||
for id in road_ids {
|
for id in road_ids {
|
||||||
let r = &map.roads[&id];
|
let r = &map.roads[&id];
|
||||||
let first_in = boundary_poly.contains_pt(r.center_points[0]);
|
let first_in = map.boundary_polygon.contains_pt(r.center_points[0]);
|
||||||
let last_in = boundary_poly.contains_pt(*r.center_points.last().unwrap());
|
let last_in = map
|
||||||
|
.boundary_polygon
|
||||||
|
.contains_pt(*r.center_points.last().unwrap());
|
||||||
|
|
||||||
// Some roads start and end in-bounds, but dip out of bounds. Leave those alone for now.
|
// Some roads start and end in-bounds, but dip out of bounds. Leave those alone for now.
|
||||||
if first_in && last_in {
|
if first_in && last_in {
|
||||||
@ -93,7 +96,7 @@ pub fn clip_map(map: &mut raw_data::Map, boundary_poly_pts: Vec<LonLat>, timer:
|
|||||||
b.polygon
|
b.polygon
|
||||||
.points()
|
.points()
|
||||||
.iter()
|
.iter()
|
||||||
.all(|pt| boundary_poly.contains_pt(*pt))
|
.all(|pt| boundary_polygon.contains_pt(*pt))
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut result_areas = Vec::new();
|
let mut result_areas = Vec::new();
|
||||||
|
@ -5,7 +5,7 @@ mod remove_disconnected;
|
|||||||
mod split_ways;
|
mod split_ways;
|
||||||
|
|
||||||
use abstutil::Timer;
|
use abstutil::Timer;
|
||||||
use geom::{Distance, FindClosest, GPSBounds, LonLat, PolyLine, Pt2D};
|
use geom::{Distance, FindClosest, GPSBounds, LonLat, PolyLine, Polygon, Pt2D};
|
||||||
use kml::ExtraShapes;
|
use kml::ExtraShapes;
|
||||||
use map_model::{raw_data, OffstreetParking, LANE_THICKNESS};
|
use map_model::{raw_data, OffstreetParking, LANE_THICKNESS};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@ -52,18 +52,11 @@ pub fn convert(flags: &Flags, timer: &mut abstutil::Timer) -> raw_data::Map {
|
|||||||
if flags.clip.is_empty() {
|
if flags.clip.is_empty() {
|
||||||
panic!("You must specify an Osmosis boundary polygon with --clip");
|
panic!("You must specify an Osmosis boundary polygon with --clip");
|
||||||
}
|
}
|
||||||
let boundary_polygon_pts = read_osmosis_polygon(&flags.clip);
|
|
||||||
let mut gps_bounds = GPSBounds::new();
|
|
||||||
for pt in &boundary_polygon_pts {
|
|
||||||
gps_bounds.update(*pt);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut map = split_ways::split_up_roads(
|
let mut map = split_ways::split_up_roads(
|
||||||
osm::osm_to_raw_roads(&flags.osm, &gps_bounds, timer),
|
osm::extract_osm(&flags.osm, read_osmosis_polygon(&flags.clip), timer),
|
||||||
gps_bounds,
|
|
||||||
timer,
|
timer,
|
||||||
);
|
);
|
||||||
clip::clip_map(&mut map, boundary_polygon_pts, timer);
|
clip::clip_map(&mut map, timer);
|
||||||
remove_disconnected::remove_disconnected_roads(&mut map, timer);
|
remove_disconnected::remove_disconnected_roads(&mut map, timer);
|
||||||
|
|
||||||
if flags.fast_dev {
|
if flags.fast_dev {
|
||||||
@ -143,8 +136,9 @@ fn use_parking_hints(map: &mut raw_data::Map, path: &str, timer: &mut Timer) {
|
|||||||
timer.stop("apply parking hints");
|
timer.stop("apply parking hints");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_osmosis_polygon(path: &str) -> Vec<LonLat> {
|
fn read_osmosis_polygon(path: &str) -> raw_data::Map {
|
||||||
let mut pts: Vec<LonLat> = Vec::new();
|
let mut pts: Vec<LonLat> = Vec::new();
|
||||||
|
let mut gps_bounds = GPSBounds::new();
|
||||||
for (idx, maybe_line) in BufReader::new(File::open(path).unwrap())
|
for (idx, maybe_line) in BufReader::new(File::open(path).unwrap())
|
||||||
.lines()
|
.lines()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@ -158,11 +152,18 @@ fn read_osmosis_polygon(path: &str) -> Vec<LonLat> {
|
|||||||
}
|
}
|
||||||
let parts: Vec<&str> = line.trim_start().split(" ").collect();
|
let parts: Vec<&str> = line.trim_start().split(" ").collect();
|
||||||
assert!(parts.len() == 2);
|
assert!(parts.len() == 2);
|
||||||
let lon = parts[0].parse::<f64>().unwrap();
|
let pt = LonLat::new(
|
||||||
let lat = parts[1].parse::<f64>().unwrap();
|
parts[0].parse::<f64>().unwrap(),
|
||||||
pts.push(LonLat::new(lon, lat));
|
parts[1].parse::<f64>().unwrap(),
|
||||||
|
);
|
||||||
|
pts.push(pt);
|
||||||
|
gps_bounds.update(pt);
|
||||||
}
|
}
|
||||||
pts
|
|
||||||
|
let mut map = raw_data::Map::blank();
|
||||||
|
map.boundary_polygon = Polygon::new(&gps_bounds.must_convert(&pts));
|
||||||
|
map.gps_bounds = gps_bounds;
|
||||||
|
map
|
||||||
}
|
}
|
||||||
|
|
||||||
fn use_offstreet_parking(map: &mut raw_data::Map, path: &str, timer: &mut Timer) {
|
fn use_offstreet_parking(map: &mut raw_data::Map, path: &str, timer: &mut Timer) {
|
||||||
|
@ -1,21 +1,19 @@
|
|||||||
use abstutil::{FileWithProgress, Timer};
|
use abstutil::{FileWithProgress, Timer};
|
||||||
use geom::{GPSBounds, HashablePt2D, LonLat, Polygon, Pt2D};
|
use geom::{HashablePt2D, LonLat, Polygon, Pt2D};
|
||||||
use map_model::{raw_data, AreaType};
|
use map_model::{raw_data, AreaType};
|
||||||
use osm_xml;
|
use osm_xml;
|
||||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
|
|
||||||
pub fn osm_to_raw_roads(
|
pub fn extract_osm(
|
||||||
osm_path: &str,
|
osm_path: &str,
|
||||||
gps_bounds: &GPSBounds,
|
mut map: raw_data::Map,
|
||||||
timer: &mut Timer,
|
timer: &mut Timer,
|
||||||
) -> (
|
) -> (
|
||||||
|
raw_data::Map,
|
||||||
|
// Un-split roads
|
||||||
Vec<raw_data::Road>,
|
Vec<raw_data::Road>,
|
||||||
Vec<raw_data::Building>,
|
// Traffic signals
|
||||||
Vec<raw_data::Area>,
|
|
||||||
// traffic signals as Pt2D
|
|
||||||
HashSet<HashablePt2D>,
|
HashSet<HashablePt2D>,
|
||||||
// turn restrictions
|
|
||||||
BTreeMap<i64, Vec<(String, i64)>>,
|
|
||||||
) {
|
) {
|
||||||
let (reader, done) = FileWithProgress::new(osm_path).unwrap();
|
let (reader, done) = FileWithProgress::new(osm_path).unwrap();
|
||||||
let doc = osm_xml::OSM::parse(reader).expect("OSM parsing failed");
|
let doc = osm_xml::OSM::parse(reader).expect("OSM parsing failed");
|
||||||
@ -27,11 +25,8 @@ pub fn osm_to_raw_roads(
|
|||||||
);
|
);
|
||||||
done(timer);
|
done(timer);
|
||||||
|
|
||||||
let mut id_to_way: HashMap<i64, Vec<LonLat>> = HashMap::new();
|
let mut id_to_way: HashMap<i64, Vec<Pt2D>> = HashMap::new();
|
||||||
let mut roads: Vec<raw_data::Road> = Vec::new();
|
let mut roads: Vec<raw_data::Road> = Vec::new();
|
||||||
let mut buildings: Vec<raw_data::Building> = Vec::new();
|
|
||||||
let mut areas: Vec<raw_data::Area> = Vec::new();
|
|
||||||
let mut turn_restrictions: BTreeMap<i64, Vec<(String, i64)>> = BTreeMap::new();
|
|
||||||
let mut traffic_signals: HashSet<HashablePt2D> = HashSet::new();
|
let mut traffic_signals: HashSet<HashablePt2D> = HashSet::new();
|
||||||
|
|
||||||
timer.start_iter("processing OSM nodes", doc.nodes.len());
|
timer.start_iter("processing OSM nodes", doc.nodes.len());
|
||||||
@ -40,7 +35,8 @@ pub fn osm_to_raw_roads(
|
|||||||
let tags = tags_to_map(&node.tags);
|
let tags = tags_to_map(&node.tags);
|
||||||
if tags.get("highway") == Some(&"traffic_signals".to_string()) {
|
if tags.get("highway") == Some(&"traffic_signals".to_string()) {
|
||||||
traffic_signals.insert(
|
traffic_signals.insert(
|
||||||
Pt2D::forcibly_from_gps(LonLat::new(node.lon, node.lat), gps_bounds).to_hashable(),
|
Pt2D::forcibly_from_gps(LonLat::new(node.lon, node.lat), &map.gps_bounds)
|
||||||
|
.to_hashable(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,11 +46,11 @@ pub fn osm_to_raw_roads(
|
|||||||
timer.next();
|
timer.next();
|
||||||
|
|
||||||
let mut valid = true;
|
let mut valid = true;
|
||||||
let mut pts = Vec::new();
|
let mut gps_pts = Vec::new();
|
||||||
for node_ref in &way.nodes {
|
for node_ref in &way.nodes {
|
||||||
match doc.resolve_reference(node_ref) {
|
match doc.resolve_reference(node_ref) {
|
||||||
osm_xml::Reference::Node(node) => {
|
osm_xml::Reference::Node(node) => {
|
||||||
pts.push(LonLat::new(node.lon, node.lat));
|
gps_pts.push(LonLat::new(node.lon, node.lat));
|
||||||
}
|
}
|
||||||
// Don't handle nested ways/relations yet
|
// Don't handle nested ways/relations yet
|
||||||
_ => {
|
_ => {
|
||||||
@ -65,14 +61,15 @@ pub fn osm_to_raw_roads(
|
|||||||
if !valid {
|
if !valid {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
let pts = map.gps_bounds.forcibly_convert(&gps_pts);
|
||||||
let tags = tags_to_map(&way.tags);
|
let tags = tags_to_map(&way.tags);
|
||||||
if is_road(&tags) {
|
if is_road(&tags) {
|
||||||
roads.push(raw_data::Road {
|
roads.push(raw_data::Road {
|
||||||
osm_way_id: way.id,
|
osm_way_id: way.id,
|
||||||
center_points: gps_bounds.forcibly_convert(&pts),
|
center_points: pts,
|
||||||
orig_id: raw_data::OriginalRoad {
|
orig_id: raw_data::OriginalRoad {
|
||||||
pt1: pts[0],
|
pt1: gps_pts[0],
|
||||||
pt2: *pts.last().unwrap(),
|
pt2: *gps_pts.last().unwrap(),
|
||||||
},
|
},
|
||||||
osm_tags: tags,
|
osm_tags: tags,
|
||||||
// We'll fill this out later
|
// We'll fill this out later
|
||||||
@ -82,20 +79,17 @@ pub fn osm_to_raw_roads(
|
|||||||
parking_lane_back: false,
|
parking_lane_back: false,
|
||||||
});
|
});
|
||||||
} else if is_bldg(&tags) {
|
} else if is_bldg(&tags) {
|
||||||
buildings.push(raw_data::Building {
|
map.buildings.push(raw_data::Building {
|
||||||
osm_way_id: way.id,
|
osm_way_id: way.id,
|
||||||
polygon: Polygon::new(&Pt2D::approx_dedupe(
|
polygon: Polygon::new(&Pt2D::approx_dedupe(pts, geom::EPSILON_DIST)),
|
||||||
gps_bounds.forcibly_convert(&pts),
|
|
||||||
geom::EPSILON_DIST,
|
|
||||||
)),
|
|
||||||
osm_tags: tags,
|
osm_tags: tags,
|
||||||
parking: None,
|
parking: None,
|
||||||
});
|
});
|
||||||
} else if let Some(at) = get_area_type(&tags) {
|
} else if let Some(at) = get_area_type(&tags) {
|
||||||
areas.push(raw_data::Area {
|
map.areas.push(raw_data::Area {
|
||||||
area_type: at,
|
area_type: at,
|
||||||
osm_id: way.id,
|
osm_id: way.id,
|
||||||
polygon: Polygon::new(&gps_bounds.forcibly_convert(&pts)),
|
polygon: Polygon::new(&pts),
|
||||||
osm_tags: tags,
|
osm_tags: tags,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -111,7 +105,7 @@ pub fn osm_to_raw_roads(
|
|||||||
if let Some(at) = get_area_type(&tags) {
|
if let Some(at) = get_area_type(&tags) {
|
||||||
if tags.get("type") == Some(&"multipolygon".to_string()) {
|
if tags.get("type") == Some(&"multipolygon".to_string()) {
|
||||||
let mut ok = true;
|
let mut ok = true;
|
||||||
let mut pts_per_way: Vec<Vec<LonLat>> = Vec::new();
|
let mut pts_per_way: Vec<Vec<Pt2D>> = Vec::new();
|
||||||
for member in &rel.members {
|
for member in &rel.members {
|
||||||
match member {
|
match member {
|
||||||
osm_xml::Member::Way(osm_xml::UnresolvedReference::Way(id), ref role) => {
|
osm_xml::Member::Way(osm_xml::UnresolvedReference::Way(id), ref role) => {
|
||||||
@ -138,11 +132,11 @@ pub fn osm_to_raw_roads(
|
|||||||
if polygons.is_empty() {
|
if polygons.is_empty() {
|
||||||
println!("Relation {} failed to glue multipolygon", rel.id);
|
println!("Relation {} failed to glue multipolygon", rel.id);
|
||||||
} else {
|
} else {
|
||||||
for points in polygons {
|
for polygon in polygons {
|
||||||
areas.push(raw_data::Area {
|
map.areas.push(raw_data::Area {
|
||||||
area_type: at,
|
area_type: at,
|
||||||
osm_id: rel.id,
|
osm_id: rel.id,
|
||||||
polygon: Polygon::new(&gps_bounds.forcibly_convert(&points)),
|
polygon,
|
||||||
osm_tags: tags.clone(),
|
osm_tags: tags.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -165,7 +159,7 @@ pub fn osm_to_raw_roads(
|
|||||||
}
|
}
|
||||||
if let (Some(from_way_id), Some(to_way_id)) = (from_way_id, to_way_id) {
|
if let (Some(from_way_id), Some(to_way_id)) = (from_way_id, to_way_id) {
|
||||||
if let Some(restriction) = tags.get("restriction") {
|
if let Some(restriction) = tags.get("restriction") {
|
||||||
turn_restrictions
|
map.turn_restrictions
|
||||||
.entry(from_way_id)
|
.entry(from_way_id)
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push((restriction.to_string(), to_way_id));
|
.push((restriction.to_string(), to_way_id));
|
||||||
@ -174,7 +168,7 @@ pub fn osm_to_raw_roads(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(roads, buildings, areas, traffic_signals, turn_restrictions)
|
(map, roads, traffic_signals)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tags_to_map(raw_tags: &[osm_xml::Tag]) -> BTreeMap<String, String> {
|
fn tags_to_map(raw_tags: &[osm_xml::Tag]) -> BTreeMap<String, String> {
|
||||||
@ -247,12 +241,12 @@ fn get_area_type(tags: &BTreeMap<String, String>) -> Option<AreaType> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The result could be more than one disjoint polygon.
|
// The result could be more than one disjoint polygon.
|
||||||
fn glue_multipolygon(mut pts_per_way: Vec<Vec<LonLat>>) -> Vec<Vec<LonLat>> {
|
fn glue_multipolygon(mut pts_per_way: Vec<Vec<Pt2D>>) -> Vec<Polygon> {
|
||||||
// First deal with all of the closed loops.
|
// First deal with all of the closed loops.
|
||||||
let mut polygons: Vec<Vec<LonLat>> = Vec::new();
|
let mut polygons: Vec<Polygon> = Vec::new();
|
||||||
pts_per_way.retain(|pts| {
|
pts_per_way.retain(|pts| {
|
||||||
if pts[0] == *pts.last().unwrap() {
|
if pts[0] == *pts.last().unwrap() {
|
||||||
polygons.push(pts.to_vec());
|
polygons.push(Polygon::new(pts));
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
@ -294,6 +288,6 @@ fn glue_multipolygon(mut pts_per_way: Vec<Vec<LonLat>>) -> Vec<Vec<LonLat>> {
|
|||||||
if result[0] != *result.last().unwrap() {
|
if result[0] != *result.last().unwrap() {
|
||||||
result.push(result[0]);
|
result.push(result[0]);
|
||||||
}
|
}
|
||||||
polygons.push(result);
|
polygons.push(Polygon::new(&result));
|
||||||
polygons
|
polygons
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
use abstutil::Timer;
|
use abstutil::Timer;
|
||||||
use geom::{GPSBounds, HashablePt2D, Pt2D};
|
use geom::{HashablePt2D, Pt2D};
|
||||||
use map_model::{raw_data, IntersectionType};
|
use map_model::{raw_data, IntersectionType};
|
||||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
pub fn split_up_roads(
|
pub fn split_up_roads(
|
||||||
(mut roads, buildings, areas, traffic_signals, turn_restrictions): (
|
(mut map, mut roads, traffic_signals): (
|
||||||
|
raw_data::Map,
|
||||||
Vec<raw_data::Road>,
|
Vec<raw_data::Road>,
|
||||||
Vec<raw_data::Building>,
|
|
||||||
Vec<raw_data::Area>,
|
|
||||||
HashSet<HashablePt2D>,
|
HashSet<HashablePt2D>,
|
||||||
BTreeMap<i64, Vec<(String, i64)>>,
|
|
||||||
),
|
),
|
||||||
gps_bounds: GPSBounds,
|
|
||||||
timer: &mut Timer,
|
timer: &mut Timer,
|
||||||
) -> raw_data::Map {
|
) -> raw_data::Map {
|
||||||
timer.start("splitting up roads");
|
timer.start("splitting up roads");
|
||||||
@ -60,12 +57,6 @@ pub fn split_up_roads(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut map = raw_data::Map::blank();
|
|
||||||
map.gps_bounds = gps_bounds;
|
|
||||||
map.buildings = buildings;
|
|
||||||
map.areas = areas;
|
|
||||||
map.turn_restrictions = turn_restrictions;
|
|
||||||
|
|
||||||
// All of the roundabout points will just keep moving the intersection
|
// All of the roundabout points will just keep moving the intersection
|
||||||
for (pt, id) in &pt_to_intersection {
|
for (pt, id) in &pt_to_intersection {
|
||||||
let point = pt.to_pt2d();
|
let point = pt.to_pt2d();
|
||||||
|
Loading…
Reference in New Issue
Block a user