Skip to content

Commit

Permalink
Allow multipolygons as clipping boundaries when converting from OSM
Browse files Browse the repository at this point in the history
  • Loading branch information
dabreegster committed Jul 16, 2024
1 parent 940f107 commit bdb5e99
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ changes.

## Unreleased

- The OSM importer can now handle MultiPolygon boundaries for clipping

## 0.4.1

- Removed `fetchWithProgress`, which is a general-purpose utility method that
Expand Down
27 changes: 20 additions & 7 deletions osm-to-route-snapper/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ use std::collections::HashMap;

use anyhow::Result;
use geo::{
BooleanOps, Contains, Coord, HaversineLength, Intersects, LineString, MultiLineString, Polygon,
BooleanOps, Contains, Coord, HaversineLength, Intersects, LineString, MultiLineString,
MultiPolygon,
};
use log::{debug, info};
use osm_reader::{Element, WayID};

use route_snapper_graph::{Edge, NodeID, RouteSnapperMap};

/// Convert input OSM PBF or XML data into a RouteSnapperMap, extracting all highway center-lines.
/// If a boundary polygon is specified, clips roads to this boundary.
/// If a boundary polygon or multipolygon is specified, clips roads to this boundary.
pub fn convert_osm(
input_bytes: Vec<u8>,
boundary_gj: Option<String>,
Expand All @@ -27,7 +28,14 @@ pub fn convert_osm(
let mut boundary = None;
if let Some(gj_string) = boundary_gj {
let gj: geojson::Feature = gj_string.parse()?;
let boundary_geo: Polygon = gj.try_into()?;
let boundary_geo: MultiPolygon = if matches!(
gj.geometry.as_ref().unwrap().value,
geojson::Value::Polygon(_)
) {
MultiPolygon(vec![gj.try_into()?])
} else {
gj.try_into()?
};
boundary = Some(boundary_geo);
}

Expand Down Expand Up @@ -83,7 +91,7 @@ fn scrape_elements(
fn split_edges(
nodes: HashMap<osm_reader::NodeID, Coord>,
ways: HashMap<WayID, Way>,
boundary: Option<&Polygon>,
boundary: Option<&MultiPolygon>,
) -> RouteSnapperMap {
let mut map = RouteSnapperMap {
nodes: Vec::new(),
Expand Down Expand Up @@ -118,7 +126,9 @@ fn split_edges(
let mut add_road = true;
if let Some(boundary) = boundary {
// If this road doesn't intersect the boundary at all, skip it
if !boundary.contains(&geometry) && !boundary.exterior().intersects(&geometry) {
if !boundary.contains(&geometry)
&& !boundary.iter().any(|p| p.exterior().intersects(&geometry))
{
add_road = false;
}
}
Expand Down Expand Up @@ -161,11 +171,14 @@ fn split_edges(
map
}

fn clip(map: &mut RouteSnapperMap, boundary: Polygon) {
fn clip(map: &mut RouteSnapperMap, boundary: MultiPolygon) {
// Fix edges crossing the boundary. Edges totally outside the boundary are skipped earlier
// during split_edges.
for edge in &mut map.edges {
if boundary.exterior().intersects(&edge.geometry) {
if boundary
.iter()
.any(|p| p.exterior().intersects(&edge.geometry))
{
let invert = false;
let mut multi_line_string =
boundary.clip(&MultiLineString::from(edge.geometry.clone()), invert);
Expand Down
5 changes: 3 additions & 2 deletions user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ file from OpenStreetMap data. The easiest way to do this for smaller areas is
[in your web browser](https://dabreegster.github.io/route_snapper/import.html).

For larger areas, you need an `.osm.xml` or `.osm.pbf` file, and optionally a
GeoJSON file with one polygon representing the boundary of your area. You'll
need to [install Rust](https://www.rust-lang.org/tools/install) to run this:
GeoJSON file with one polygon or multipolygon representing the boundary of your
area. You'll need to [install Rust](https://www.rust-lang.org/tools/install) to
run this:

```
cd osm-to-route-snapper
Expand Down

0 comments on commit bdb5e99

Please sign in to comment.