Skip to content

Commit

Permalink
Overhaul StreetNetwork road splitting + clipping. #127
Browse files Browse the repository at this point in the history
Also has the side effect of getting rid of handling for tiny
roundabouts, because it's hard to reason about...
  • Loading branch information
dabreegster committed Dec 13, 2022
1 parent b60626d commit ff74cad
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 271 deletions.
4 changes: 4 additions & 0 deletions osm2streets/src/road.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,10 @@ impl Road {
pub fn common_endpoint(&self, other: &Road) -> CommonEndpoint {
CommonEndpoint::new((self.src_i, self.dst_i), (other.src_i, other.dst_i))
}

pub fn from_osm_way(&self, way: osm::WayID) -> bool {
self.osm_ids.iter().any(|id| id.osm_way_id == way)
}
}

impl StreetNetwork {
Expand Down
125 changes: 0 additions & 125 deletions streets_reader/src/clip.rs

This file was deleted.

3 changes: 1 addition & 2 deletions streets_reader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@ pub use self::extract::OsmExtract;
use osm_reader::Document;

// TODO Clean up the public API of all of this
mod clip;
pub mod extract;
pub mod osm_reader;
pub mod split_ways;

/// Create a `StreetNetwork` from the contents of an `.osm.xml` file. If `clip_pts` is specified,
/// use theese as a boundary polygon. (Use `LonLat::read_osmosis_polygon` or similar to produce
/// use theese as a boundary polygon. (Use `LonLat::read_geojson_polygon` or similar to produce
/// these.)
///
/// You probably want to do `StreetNetwork::apply_transformations` on the result to get a useful
Expand Down
48 changes: 31 additions & 17 deletions streets_reader/src/osm_reader/clip.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use geom::{PolyLine, Ring, Polygon};
use geom::{Distance, PolyLine, Polygon};

use super::Document;

Expand All @@ -9,12 +9,14 @@ impl Document {
pub fn clip(&mut self, boundary_polygon: &Polygon) {
// Remove all nodes that're out-of-bounds. Don't fix up ways and relations referring to
// these.
self.nodes.retain(|_, node| boundary_polygon.contains_pt(node.pt));
self.nodes
.retain(|_, node| boundary_polygon.contains_pt(node.pt));

// Remove ways that have no nodes within bounds.
// TODO If there's a way that geometrically crosses the boundary but only has nodes outside
// it, this'll remove it. Is that desirable?
self.ways.retain(|_, way| way.nodes.iter().any(|node| self.nodes.contains_key(node)));
self.ways
.retain(|_, way| way.nodes.iter().any(|node| self.nodes.contains_key(node)));

// For line-string ways (not areas), clip them to the boundary. way.pts and way.nodes
// become out-of-sync.
Expand All @@ -25,7 +27,7 @@ impl Document {
}

let pl = PolyLine::unchecked_new(way.pts.clone());
way.pts = clip_polyline_to_ring(pl, boundary_polygon.get_outer_ring()).into_points();
way.pts = clip_polyline_to_ring(pl, boundary_polygon).into_points();
}

// TODO Handle ways that're areas
Expand All @@ -34,20 +36,32 @@ impl Document {
}

// TODO Move to geom and test better
fn clip_polyline_to_ring(pl: PolyLine, ring: &Ring) -> PolyLine {
let hits = ring.all_intersections(&pl);
if hits.len() == 2 {
if let Some((mut dist1, _)) = pl.dist_along_of_point(hits[0]) {
if let Some((mut dist2, _)) = pl.dist_along_of_point(hits[1]) {
if dist1 > dist2 {
std::mem::swap(&mut dist1, &mut dist2);
}
if let Ok(slice) = pl.maybe_exact_slice(dist1, dist2) {
return slice;
}
}
// If this fails for any reason, just return the input untransformed
fn clip_polyline_to_ring(pl: PolyLine, polygon: &Polygon) -> PolyLine {
let mut hit_distances = Vec::new();
for pt in polygon.get_outer_ring().all_intersections(&pl) {
if let Some((dist, _)) = pl.dist_along_of_point(pt) {
hit_distances.push(dist);
} else {
return pl;
}
}

if hit_distances.len() == 1 {
// Does it start or end inside the ring?
if polygon.contains_pt(pl.first_pt()) {
return pl.exact_slice(Distance::ZERO, hit_distances[0]);
} else {
return pl.exact_slice(hit_distances[0], pl.length());
}
}
// If this fails for any reason, just return the input untransformed

if hit_distances.len() == 2 {
hit_distances.sort();
if let Ok(slice) = pl.maybe_exact_slice(hit_distances[0], hit_distances[1]) {
return slice;
}
}

pl
}
Loading

0 comments on commit ff74cad

Please sign in to comment.