diff --git a/convert_osm/src/osm.rs b/convert_osm/src/osm.rs index 677e027a0a..3255bd1e7b 100644 --- a/convert_osm/src/osm.rs +++ b/convert_osm/src/osm.rs @@ -67,7 +67,7 @@ pub fn osm_to_raw_roads( } else if let Some(at) = get_area_type(&tags) { areas.push(raw_data::Area { area_type: at, - osm_way_id: way.id, + osm_id: way.id, points: pts, osm_tags: tags, }); @@ -83,35 +83,45 @@ pub fn osm_to_raw_roads( let tags = tags_to_map(&rel.tags); if let Some(at) = get_area_type(&tags) { if tags.get("type") == Some(&"multipolygon".to_string()) { + let mut ok = true; + let mut pts_per_way: Vec> = Vec::new(); for member in &rel.members { match *member { osm_xml::Member::Way(osm_xml::UnresolvedReference::Way(id), ref role) => { match id_to_way.get(&id) { Some(pts) => { if role == "outer" { - areas.push(raw_data::Area { - area_type: at, - osm_way_id: id, - points: pts.to_vec(), - osm_tags: tags.clone(), - }); + pts_per_way.push(pts.to_vec()); } else { println!( "Relation {} has unhandled member role {}", rel.id, role ); + ok = false; } } None => { println!("Relation {} refers to unknown way {}", rel.id, id); + ok = false; } } } _ => { println!("Relation {} refers to {:?}", rel.id, member); + ok = false; } } } + if ok { + if let Some(polygon) = glue_multipolygon(pts_per_way) { + areas.push(raw_data::Area { + area_type: at, + osm_id: rel.id, + points: polygon, + osm_tags: tags.clone(), + }); + } + } } } } @@ -175,11 +185,29 @@ fn get_area_type(tags: &BTreeMap) -> Option { if tags.get("natural") == Some(&"wood".to_string()) { return Some(AreaType::Park); } - if tags.get("natural") == Some(&"wetland".to_string()) { - return Some(AreaType::Swamp); - } - if tags.contains_key("waterway") { + if tags.contains_key("waterway") || tags.get("natural") == Some(&"water".to_string()) { return Some(AreaType::Water); } None } + +fn glue_multipolygon(mut pts_per_way: Vec>) -> Option> { + let mut result = pts_per_way.pop().unwrap(); + while !pts_per_way.is_empty() { + let glue_pt = *result.last().unwrap(); + if let Some(idx) = pts_per_way + .iter() + .position(|pts| pts[0] == glue_pt || *pts.last().unwrap() == glue_pt) + { + let mut append = pts_per_way.remove(idx); + if append[0] != glue_pt { + append.reverse(); + } + result.pop(); + result.extend(append); + } else { + return None; + } + } + Some(result) +} diff --git a/docs/TODO_quality.md b/docs/TODO_quality.md index f34c5b19ce..44e12bc472 100644 --- a/docs/TODO_quality.md +++ b/docs/TODO_quality.md @@ -39,8 +39,7 @@ - but theyre not marked everywhere - and theyre hard to associate with roads (sometimes need to infer a planter strip) - draw ALL water and greenery areas - - cant mouseover areas created from polylines, seemingly - - multipolygons seem broken + - how to handle multipolygons referring to clipped ways? - draw benches, bike racks - render trees - look for current stop sign priorities diff --git a/editor/src/plugins/debug/debug_objects.rs b/editor/src/plugins/debug/debug_objects.rs index 372aac9c9b..ed695bc85d 100644 --- a/editor/src/plugins/debug/debug_objects.rs +++ b/editor/src/plugins/debug/debug_objects.rs @@ -146,7 +146,7 @@ fn tooltip_lines(obj: ID, g: &mut GfxCtx, ctx: &DrawCtx) -> Text { } ID::Area(id) => { let a = map.get_a(id); - txt.add_line(format!("{} (from OSM way {})", id, a.osm_way_id)); + txt.add_line(format!("{} (from OSM {})", id, a.osm_id)); styled_kv(&mut txt, &a.osm_tags); } ID::Trip(_) => {} diff --git a/editor/src/plugins/debug/layers.rs b/editor/src/plugins/debug/layers.rs index ba4f3598e8..5dc9f755b5 100644 --- a/editor/src/plugins/debug/layers.rs +++ b/editor/src/plugins/debug/layers.rs @@ -26,7 +26,7 @@ impl ToggleableLayers { show_parcels: ToggleableLayer::new("parcels", Some(MIN_ZOOM_FOR_PARCELS)), show_extra_shapes: ToggleableLayer::new("extra shapes", Some(MIN_ZOOM_FOR_LANES)), show_all_turn_icons: ToggleableLayer::new("all turn icons", None), - show_areas: ToggleableLayer::new("areas", None), + show_areas: ToggleableLayer::new("areas", Some(0.0)), debug_mode: ToggleableLayer::new("geometry debug mode", None), } } diff --git a/editor/src/render/area.rs b/editor/src/render/area.rs index b74d893f4e..4e8cc72919 100644 --- a/editor/src/render/area.rs +++ b/editor/src/render/area.rs @@ -17,9 +17,8 @@ impl DrawArea { let fill_polygon = area.get_polygon(); let draw_default = prerender.upload_borrowed(vec![( match area.area_type { - AreaType::Park => cs.get_def("park area", Color::GREEN), - AreaType::Swamp => cs.get_def("swamp area", Color::rgb_f(0.0, 1.0, 0.6)), - AreaType::Water => cs.get_def("water area", Color::BLUE), + AreaType::Park => cs.get_def("park area", Color::rgb(200, 250, 204)), + AreaType::Water => cs.get_def("water area", Color::rgb(170, 211, 223)), }, &fill_polygon, )]); diff --git a/editor/src/ui.rs b/editor/src/ui.rs index 3d35686256..63d457ec0f 100644 --- a/editor/src/ui.rs +++ b/editor/src/ui.rs @@ -404,8 +404,8 @@ impl UI { let map = &state.primary.map; let draw_map = &state.primary.draw_map; - let mut areas: Vec> = Vec::new(); let mut parcels: Vec> = Vec::new(); + let mut areas: Vec> = Vec::new(); let mut lanes: Vec> = Vec::new(); let mut intersections: Vec> = Vec::new(); let mut buildings: Vec> = Vec::new(); @@ -419,8 +419,8 @@ impl UI { continue; } match id { - ID::Area(id) => areas.push(Box::new(draw_map.get_a(id))), ID::Parcel(id) => parcels.push(Box::new(draw_map.get_p(id))), + ID::Area(id) => areas.push(Box::new(draw_map.get_a(id))), ID::Lane(id) => { lanes.push(Box::new(draw_map.get_l(id))); if !state.show_icons_for(map.get_l(id).dst_i) { @@ -452,8 +452,8 @@ impl UI { // From background to foreground Z-order let mut borrows: Vec> = Vec::new(); - borrows.extend(areas); borrows.extend(parcels); + borrows.extend(areas); borrows.extend(lanes); borrows.extend(intersections); borrows.extend(buildings); diff --git a/map_model/src/area.rs b/map_model/src/area.rs index e4a8181b18..d4d0aa9db6 100644 --- a/map_model/src/area.rs +++ b/map_model/src/area.rs @@ -18,7 +18,6 @@ impl fmt::Display for AreaID { #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum AreaType { Park, - Swamp, Water, } @@ -29,7 +28,7 @@ pub struct Area { // Might be a closed loop or not -- waterways can be linear. pub points: Vec, pub osm_tags: BTreeMap, - pub osm_way_id: i64, + pub osm_id: i64, } impl Area { diff --git a/map_model/src/make/half_map.rs b/map_model/src/make/half_map.rs index ae1ceb2bb1..c99f961ce2 100644 --- a/map_model/src/make/half_map.rs +++ b/map_model/src/make/half_map.rs @@ -189,7 +189,7 @@ pub fn make_half_map( .map(|coord| Pt2D::from_gps(*coord, &gps_bounds).unwrap()) .collect(), osm_tags: a.osm_tags.clone(), - osm_way_id: a.osm_way_id, + osm_id: a.osm_id, }); } diff --git a/map_model/src/raw_data.rs b/map_model/src/raw_data.rs index 828be8de79..e0d0a84135 100644 --- a/map_model/src/raw_data.rs +++ b/map_model/src/raw_data.rs @@ -125,7 +125,7 @@ pub struct Area { // last point is always the same as the first pub points: Vec, pub osm_tags: BTreeMap, - pub osm_way_id: i64, + pub osm_id: i64, } #[derive(PartialEq, Debug, Serialize, Deserialize)]