@@ -5,17 +5,19 @@ use std::borrow::Borrow;
5
5
use std:: collections:: BTreeMap ;
6
6
7
7
#[ cfg( test) ]
8
- use osm_builder;
8
+ use crate :: osm_builder;
9
9
#[ 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. ;
11
13
12
14
struct BoundaryPart {
13
15
nodes : Vec < osmpbfreader:: Node > ,
14
16
}
15
17
16
18
impl BoundaryPart {
17
19
pub fn new ( nodes : Vec < osmpbfreader:: Node > ) -> BoundaryPart {
18
- BoundaryPart { nodes : nodes }
20
+ BoundaryPart { nodes }
19
21
}
20
22
pub fn first ( & self ) -> osmpbfreader:: NodeId {
21
23
self . nodes . first ( ) . unwrap ( ) . id
@@ -160,51 +162,72 @@ pub fn build_boundary_parts<T: Borrow<osmpbfreader::OsmObj>>(
160
162
. map ( BoundaryPart :: new)
161
163
. collect ( ) ;
162
164
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
+
164
179
while !boundary_parts. is_empty ( ) {
165
180
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
+
167
206
let mut current = first_part. last ( ) ;
168
- let mut poly_geom = first_part. nodes ;
207
+ add_part ( first_part) ;
169
208
170
209
loop {
171
210
let mut added_part = false ;
172
211
let mut i = 0 ;
173
- while i < boundary_parts. len ( ) && current != first {
212
+ while i < boundary_parts. len ( ) {
174
213
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
177
216
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) ) ;
180
218
added_part = true ;
181
219
} 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
183
221
current = boundary_parts[ i] . first ( ) ;
184
222
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) ) ;
187
224
added_part = true ;
188
225
} else {
189
226
i += 1 ;
190
227
// didn't do anything, we want to explore the next way, if we had do something we
191
228
// will have removed the current way and there will be no need to increment
192
229
}
193
230
}
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
- }
208
231
if !added_part {
209
232
use geo:: haversine_distance:: HaversineDistance ;
210
233
let p = |n : & osmpbfreader:: Node | {
@@ -213,17 +236,20 @@ pub fn build_boundary_parts<T: Borrow<osmpbfreader::OsmObj>>(
213
236
y : n. lat ( ) ,
214
237
} )
215
238
} ;
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
+ }
227
253
}
228
254
break ;
229
255
}
@@ -619,3 +645,44 @@ fn test_build_inner_touching_outer_at_one_point() {
619
645
assert ! ( false ) ; //this should not happen
620
646
}
621
647
}
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
+ }
0 commit comments