Skip to content
This repository was archived by the owner on Oct 2, 2024. It is now read-only.

Commit 73d5f73

Browse files
authored
Fix build_boundary_part for touching rings (#12)
* Fix build_boundary_part for touching rings * travis: Include fmt check in rust stable job * corrections after review
1 parent df825ef commit 73d5f73

File tree

4 files changed

+112
-45
lines changed

4 files changed

+112
-45
lines changed

.travis.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ language: rust
22
cache: cargo
33
matrix:
44
include:
5-
- rust: nightly
6-
before_script: rustup component add rustfmt-preview
7-
script: cargo fmt --all -- --check
85
- rust: stable
6+
before_script:
7+
- rustup component add rustfmt
98
script:
9+
- cargo fmt --all -- --check
1010
- cargo test
1111
after_success:
1212
- |-

src/boundaries.rs

Lines changed: 106 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@ use std::borrow::Borrow;
55
use std::collections::BTreeMap;
66

77
#[cfg(test)]
8-
use osm_builder;
8+
use crate::osm_builder;
99
#[cfg(test)]
10-
use osm_builder::named_node;
10+
use crate::osm_builder::named_node;
11+
12+
const WARN_UNCLOSED_RING_MAX_DISTANCE: f64 = 10.;
1113

1214
struct BoundaryPart {
1315
nodes: Vec<osmpbfreader::Node>,
1416
}
1517

1618
impl BoundaryPart {
1719
pub fn new(nodes: Vec<osmpbfreader::Node>) -> BoundaryPart {
18-
BoundaryPart { nodes: nodes }
20+
BoundaryPart { nodes }
1921
}
2022
pub fn first(&self) -> osmpbfreader::NodeId {
2123
self.nodes.first().unwrap().id
@@ -160,51 +162,72 @@ pub fn build_boundary_parts<T: Borrow<osmpbfreader::OsmObj>>(
160162
.map(BoundaryPart::new)
161163
.collect();
162164
let mut multipoly = MultiPolygon(vec![]);
163-
// we want to try to build a polygon for a least each way
165+
166+
let mut append_ring = |nodes: &[osmpbfreader::Node]| {
167+
let poly_geom = nodes
168+
.iter()
169+
.map(|n| Coordinate {
170+
x: n.lon(),
171+
y: n.lat(),
172+
})
173+
.collect();
174+
multipoly
175+
.0
176+
.push(Polygon::new(LineString(poly_geom), vec![]));
177+
};
178+
164179
while !boundary_parts.is_empty() {
165180
let first_part = boundary_parts.remove(0);
166-
let first = first_part.first();
181+
let mut added_nodes: Vec<osmpbfreader::Node> = vec![];
182+
let mut node_to_idx: BTreeMap<osmpbfreader::NodeId, usize> = BTreeMap::new();
183+
184+
let mut add_part = |mut part: BoundaryPart| {
185+
let nodes = if added_nodes.is_empty() {
186+
part.nodes.drain(..)
187+
} else {
188+
part.nodes.drain(1..)
189+
};
190+
191+
for n in nodes {
192+
if let Some(start_idx) = node_to_idx.get(&n.id) {
193+
let ring = added_nodes.split_off(*start_idx);
194+
node_to_idx = added_nodes
195+
.iter()
196+
.enumerate()
197+
.map(|(i, n)| (n.id, i))
198+
.collect();
199+
append_ring(&ring);
200+
}
201+
node_to_idx.insert(n.id, added_nodes.len());
202+
added_nodes.push(n);
203+
}
204+
};
205+
167206
let mut current = first_part.last();
168-
let mut poly_geom = first_part.nodes;
207+
add_part(first_part);
169208

170209
loop {
171210
let mut added_part = false;
172211
let mut i = 0;
173-
while i < boundary_parts.len() && current != first {
212+
while i < boundary_parts.len() {
174213
if current == boundary_parts[i].first() {
175-
// the start of current way touch the polygon, we add it and remove it from the
176-
// pool
214+
// the start of current way touches the polygon,
215+
// we add it and remove it from the pool
177216
current = boundary_parts[i].last();
178-
poly_geom.append(&mut boundary_parts[i].nodes);
179-
boundary_parts.remove(i);
217+
add_part(boundary_parts.remove(i));
180218
added_part = true;
181219
} else if current == boundary_parts[i].last() {
182-
// the end of the current way touch the polygon, we reverse the way and add it
220+
// the end of the current way touches the polygon, we reverse the way and add it
183221
current = boundary_parts[i].first();
184222
boundary_parts[i].nodes.reverse();
185-
poly_geom.append(&mut boundary_parts[i].nodes);
186-
boundary_parts.remove(i);
223+
add_part(boundary_parts.remove(i));
187224
added_part = true;
188225
} else {
189226
i += 1;
190227
// didn't do anything, we want to explore the next way, if we had do something we
191228
// will have removed the current way and there will be no need to increment
192229
}
193230
}
194-
if current == first {
195-
// our polygon is closed, we create it and add it to the multipolygon
196-
let poly_geom = poly_geom
197-
.iter()
198-
.map(|n| Coordinate {
199-
x: n.lon(),
200-
y: n.lat(),
201-
})
202-
.collect();
203-
multipoly
204-
.0
205-
.push(Polygon::new(LineString(poly_geom), vec![]));
206-
break;
207-
}
208231
if !added_part {
209232
use geo::haversine_distance::HaversineDistance;
210233
let p = |n: &osmpbfreader::Node| {
@@ -213,17 +236,20 @@ pub fn build_boundary_parts<T: Borrow<osmpbfreader::OsmObj>>(
213236
y: n.lat(),
214237
})
215238
};
216-
let distance =
217-
p(poly_geom.first().unwrap()).haversine_distance(&p(poly_geom.last().unwrap()));
218-
if distance < 10. {
219-
warn!(
220-
"boundary: relation/{} ({}): unclosed polygon, dist({:?}, {:?}) = {}",
221-
relation.id.0,
222-
relation.tags.get("name").map_or("", |s| &s),
223-
poly_geom.first().unwrap().id,
224-
poly_geom.last().unwrap().id,
225-
distance
226-
);
239+
240+
if added_nodes.len() > 1 {
241+
let distance = p(added_nodes.first().unwrap())
242+
.haversine_distance(&p(added_nodes.last().unwrap()));
243+
if distance < WARN_UNCLOSED_RING_MAX_DISTANCE {
244+
warn!(
245+
"boundary: relation/{} ({}): unclosed polygon, dist({:?}, {:?}) = {}",
246+
relation.id.0,
247+
relation.tags.get("name").map_or("", |s| &s),
248+
added_nodes.first().unwrap().id,
249+
added_nodes.last().unwrap().id,
250+
distance
251+
);
252+
}
227253
}
228254
break;
229255
}
@@ -619,3 +645,44 @@ fn test_build_inner_touching_outer_at_one_point() {
619645
assert!(false); //this should not happen
620646
}
621647
}
648+
649+
#[test]
650+
fn test_build_two_touching_rings() {
651+
use geo::algorithm::area::Area;
652+
let mut builder = osm_builder::OsmBuilder::new();
653+
654+
let rel_id = builder
655+
.relation()
656+
.outer(vec![
657+
named_node(-1.0, -1.0, "A"),
658+
named_node(1.0, -1.0, "B"),
659+
])
660+
.outer(vec![
661+
named_node(0.0, 0.0, "touching"),
662+
named_node(-1.0, -1.0, "A"),
663+
])
664+
.outer(vec![
665+
named_node(1.0, -1.0, "B"),
666+
named_node(0.0, 0.0, "touching"),
667+
])
668+
.outer(vec![named_node(1.0, 1.0, "C"), named_node(-1.0, 1.0, "D")])
669+
.outer(vec![
670+
named_node(-1.0, 1.0, "D"),
671+
named_node(0.0, 0.0, "touching"),
672+
])
673+
.outer(vec![
674+
named_node(0.0, 0.0, "touching"),
675+
named_node(1.0, 1.0, "C"),
676+
])
677+
.relation_id
678+
.into();
679+
if let osmpbfreader::OsmObj::Relation(ref relation) = builder.objects[&rel_id] {
680+
let multipolygon = build_boundary(&relation, &builder.objects);
681+
assert!(multipolygon.is_some());
682+
let multipolygon = multipolygon.unwrap();
683+
assert_eq!(multipolygon.0.len(), 2);
684+
assert_eq!(multipolygon.unsigned_area(), 2.);
685+
} else {
686+
assert!(false); //this should not happen
687+
}
688+
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ extern crate osmpbfreader;
77
mod boundaries;
88
pub mod osm_builder;
99

10-
pub use boundaries::build_boundary;
10+
pub use crate::boundaries::build_boundary;

src/osm_builder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub struct Relation<'a> {
1414
impl<'a> Relation<'a> {
1515
pub fn outer(&mut self, coords: Vec<(Point<f64>, Option<String>)>) -> &'a mut Relation {
1616
let id = self.builder.way(coords);
17-
if let &mut osmpbfreader::OsmObj::Relation(ref mut rel) = self
17+
if let osmpbfreader::OsmObj::Relation(ref mut rel) = self
1818
.builder
1919
.objects
2020
.get_mut(&self.relation_id.into())
@@ -32,7 +32,7 @@ impl<'a> Relation<'a> {
3232
impl<'a> Relation<'a> {
3333
pub fn inner(&mut self, coords: Vec<(Point<f64>, Option<String>)>) -> &'a mut Relation {
3434
let id = self.builder.way(coords);
35-
if let &mut osmpbfreader::OsmObj::Relation(ref mut rel) = self
35+
if let osmpbfreader::OsmObj::Relation(ref mut rel) = self
3636
.builder
3737
.objects
3838
.get_mut(&self.relation_id.into())

0 commit comments

Comments
 (0)