blockfinding/
lib.rs

1#[macro_use]
2extern crate anyhow;
3#[macro_use]
4extern crate log;
5
6use std::collections::{BTreeSet, HashMap, HashSet};
7use std::fmt;
8
9use anyhow::Result;
10use serde::{Deserialize, Serialize};
11
12use abstutil::wraparound_get;
13use geom::{Polygon, Pt2D, Ring};
14
15use map_model::{
16    osm, CommonEndpoint, Direction, LaneID, Map, PathConstraints, RoadID, RoadSideID, SideOfRoad,
17};
18
19/// A block is defined by a perimeter that traces along the sides of roads. Inside the perimeter,
20/// the block may contain buildings and interior roads. In the simple case, a block represents a
21/// single "city block", with no interior roads. It may also cover a "neighborhood", where the
22/// perimeter contains some "major" and the interior consists only of "minor" roads.
23// TODO Maybe "block" is a misleading term. "Contiguous road trace area"?
24#[derive(Clone, Serialize, Deserialize)]
25pub struct Block {
26    pub perimeter: Perimeter,
27    /// The polygon covers the interior of the block.
28    pub polygon: Polygon,
29}
30
31/// A sequence of roads in order, beginning and ending at the same place. No "crossings" -- tracing
32/// along this sequence should geometrically yield a simple polygon.
33// TODO Handle the map boundary. Sometimes this perimeter should be broken up by border
34// intersections or possibly by water/park areas.
35#[derive(Clone, Serialize, Deserialize)]
36pub struct Perimeter {
37    pub roads: Vec<RoadSideID>,
38    /// These roads exist entirely within the perimeter
39    pub interior: BTreeSet<RoadID>,
40}
41
42impl Perimeter {
43    /// Starting at any lane, snap to the nearest side of that road, then begin tracing a single
44    /// block, with no interior roads. This will fail if a map boundary is reached. The results are
45    /// unusual when crossing the entrance to a tunnel or bridge, and so `skip` is used to avoid
46    /// tracing there.
47    pub fn single_block(map: &Map, start: LaneID, skip: &HashSet<RoadID>) -> Result<Perimeter> {
48        let mut roads = Vec::new();
49        let start_road_side = map.get_l(start).get_nearest_side_of_road(map);
50
51        if skip.contains(&start_road_side.road) {
52            bail!("Started on a road we shouldn't trace");
53        }
54
55        // We may start on a loop road on the "inner" direction
56        {
57            let start_r = map.get_parent(start);
58            if start_r.src_i == start_r.dst_i {
59                let i = map.get_i(start_r.src_i);
60                if !i.get_road_sides_sorted(map).contains(&start_road_side) {
61                    bail!("Starting on inner piece of a loop road");
62                }
63            }
64        }
65
66        // We need to track which side of the road we're at, but also which direction we're facing
67        let mut current_road_side = start_road_side;
68        let mut current_intersection = map.get_l(start).dst_i;
69        loop {
70            let i = map.get_i(current_intersection);
71            if i.is_border() {
72                bail!("hit the map boundary at {}", i.orig_id);
73            }
74            let mut sorted_roads = i.get_road_sides_sorted(map);
75            sorted_roads.retain(|id| !skip.contains(&id.road));
76
77            let idx = sorted_roads
78                .iter()
79                .position(|x| *x == current_road_side)
80                .unwrap() as isize;
81            // Do we go clockwise or counter-clockwise around the intersection? Well, unless we're
82            // at a dead-end, we want to avoid the other side of the same road.
83            let mut next = *wraparound_get(&sorted_roads, idx + 1);
84            assert_ne!(next, current_road_side);
85            if next.road == current_road_side.road {
86                next = *wraparound_get(&sorted_roads, idx - 1);
87                assert_ne!(next, current_road_side);
88                if next.road == current_road_side.road {
89                    if sorted_roads.len() != 2 {
90                        bail!("Looped back on the same road, but not at a dead-end");
91                    }
92                }
93            }
94            roads.push(current_road_side);
95            current_road_side = next;
96            current_intersection = map
97                .get_r(current_road_side.road)
98                .other_endpt(current_intersection);
99
100            if current_road_side == start_road_side {
101                roads.push(start_road_side);
102                break;
103            }
104
105            if roads.len() > map.all_roads().len() {
106                bail!(
107                    "Infinite loop starting from {start} ({})",
108                    map.get_parent(start).orig_id
109                );
110            }
111        }
112        assert_eq!(roads[0], *roads.last().unwrap());
113        Ok(Perimeter {
114            roads,
115            interior: BTreeSet::new(),
116        })
117    }
118
119    /// This calculates all single block perimeters for the entire map. The resulting list does not
120    /// cover roads near the map boundary.
121    pub fn find_all_single_blocks(map: &Map) -> Vec<Perimeter> {
122        let skip = Perimeter::find_roads_to_skip_tracing(map);
123
124        let mut seen = HashSet::new();
125        let mut perimeters = Vec::new();
126        for lane in map.all_lanes() {
127            let side = lane.get_nearest_side_of_road(map);
128            if seen.contains(&side) {
129                continue;
130            }
131            match Perimeter::single_block(map, lane.id, &skip) {
132                Ok(perimeter) => {
133                    seen.extend(perimeter.roads.clone());
134                    perimeters.push(perimeter);
135                }
136                Err(err) => {
137                    // The logs are quite spammy and not helpful yet, since they're all expected
138                    // cases near the map boundary
139                    if false {
140                        warn!("Failed from {}: {}", lane.id, err);
141                    }
142                    // Don't try again
143                    seen.insert(side);
144                }
145            }
146        }
147        perimeters
148    }
149
150    /// Blockfinding is specialized for the LTN tool, so non-driveable roads (cycleways and light
151    /// rail) are considered invisible and can't be on a perimeter. Previously, there were also
152    /// some heuristics here to try to skip certain bridges/tunnels that break the planarity of
153    /// blocks.
154    pub fn find_roads_to_skip_tracing(map: &Map) -> HashSet<RoadID> {
155        let mut skip = HashSet::new();
156        for r in map.all_roads() {
157            // TODO Redundant
158            if r.is_light_rail() {
159                skip.insert(r.id);
160            } else if !PathConstraints::Car.can_use_road(r, map) {
161                skip.insert(r.id);
162            } else if r.orig_id.osm_way_id == osm::WayID(700169412)
163                || r.orig_id.osm_way_id == osm::WayID(2800303)
164            {
165                // TODO Hack to avoid the A27 bridge in Chichester
166                // Note that https://www.openstreetmap.org/way/700169409 isn't the ID to skip,
167                // because osm2streets merges roads, and this ID arbitrarily disappears here
168                skip.insert(r.id);
169            }
170        }
171        skip
172    }
173
174    /// A perimeter has the first and last road matching up, but that's confusing to
175    /// work with. Temporarily undo that.
176    fn undo_invariant(&mut self) {
177        assert_eq!(Some(self.roads[0]), self.roads.pop());
178    }
179
180    /// Restore the first=last invariant. Methods may temporarily break this, but must restore it
181    /// before returning.
182    fn restore_invariant(&mut self) {
183        self.roads.push(self.roads[0]);
184    }
185
186    /// Try to merge two blocks. This'll only succeed when the blocks are adjacent, but the merge
187    /// wouldn't create an interior "hole".
188    ///
189    /// Note this always modifies both perimeters, even upon failure. The caller should copy the
190    /// input and only use the output upon success.
191    fn try_to_merge(
192        &mut self,
193        map: &Map,
194        other: &mut Perimeter,
195        debug_failures: bool,
196    ) -> Result<()> {
197        for reverse_to_fix_winding_order in [false, true] {
198            self.undo_invariant();
199            other.undo_invariant();
200
201            // Calculate common roads
202            let roads1: HashSet<RoadID> = self.roads.iter().map(|id| id.road).collect();
203            let roads2: HashSet<RoadID> = other.roads.iter().map(|id| id.road).collect();
204            let common: HashSet<RoadID> = roads1.intersection(&roads2).cloned().collect();
205            if common.is_empty() {
206                if debug_failures {
207                    warn!("No common roads");
208                }
209                bail!("No common roads");
210            }
211
212            // "Rotate" the order of roads, so that all of the overlapping roads are at the end of the
213            // list. If the entire perimeter is surrounded by the other, then no rotation needed.
214            if self.roads.len() != common.len() {
215                let mut i = 0;
216                while common.contains(&self.roads[0].road)
217                    || !common.contains(&self.roads.last().unwrap().road)
218                {
219                    self.roads.rotate_left(1);
220
221                    i += 1;
222                    if i == self.roads.len() {
223                        bail!(
224                            "Rotating {:?} against common {:?} infinite-looped",
225                            self.roads,
226                            common
227                        );
228                    }
229                }
230            }
231            // Same thing with the other
232            if other.roads.len() != common.len() {
233                let mut i = 0;
234                while common.contains(&other.roads[0].road)
235                    || !common.contains(&other.roads.last().unwrap().road)
236                {
237                    other.roads.rotate_left(1);
238
239                    i += 1;
240                    if i == other.roads.len() {
241                        bail!(
242                            "Rotating {:?} against common {:?} infinite-looped",
243                            self.roads,
244                            common
245                        );
246                    }
247                }
248            }
249
250            if debug_failures {
251                println!("\nCommon: {:?}\n{:?}\n{:?}", common, self, other);
252            }
253
254            if !reverse_to_fix_winding_order && self.reverse_to_fix_winding_order(map, other) {
255                // Revert, reverse one, and try again.
256                self.restore_invariant();
257                other.restore_invariant();
258                self.roads.reverse();
259                continue;
260            }
261
262            // Check if all of the common roads are at the end of each perimeter, so we can
263            // "blindly" do this snipping. If this isn't true, then the overlapping portions are
264            // split by non-overlapping roads. This happens when merging the two blocks would
265            // result in a "hole."
266            for id in self.roads.iter().rev().take(common.len()) {
267                if !common.contains(&id.road) {
268                    if debug_failures {
269                        warn!(
270                            "The common roads on the first aren't consecutive, near {:?}",
271                            id
272                        );
273                    }
274                    bail!(
275                        "The common roads on the first aren't consecutive, near {:?}",
276                        id
277                    );
278                }
279            }
280            for id in other.roads.iter().rev().take(common.len()) {
281                if !common.contains(&id.road) {
282                    if debug_failures {
283                        warn!(
284                            "The common roads on the second aren't consecutive, near {:?}",
285                            id
286                        );
287                    }
288                    bail!(
289                        "The common roads on the first aren't consecutive, near {:?}",
290                        id
291                    );
292                }
293            }
294
295            // Very straightforward snipping now
296            for _ in 0..common.len() {
297                self.roads.pop().unwrap();
298                other.roads.pop().unwrap();
299            }
300
301            // This order assumes everything is clockwise to start with.
302            self.roads.append(&mut other.roads);
303
304            // TODO This case was introduced with find_roads_to_skip_tracing. Not sure why.
305            if self.roads.is_empty() {
306                if debug_failures {
307                    warn!("Two perimeters had every road in common: {:?}", common);
308                }
309                bail!("Two perimeters had every road in common: {:?}", common);
310            }
311
312            self.interior.extend(common);
313            self.interior.append(&mut other.interior);
314
315            // Restore the first=last invariant
316            self.restore_invariant();
317
318            // Make sure we didn't wind up with any internal dead-ends
319            self.collapse_deadends();
320
321            if let Err(err) = self.check_continuity(map) {
322                debug!(
323                    "A merged perimeter couldn't be blockified: {}. {:?}",
324                    err, self
325                );
326                bail!(
327                    "A merged perimeter couldn't be blockified: {}. {:?}",
328                    err,
329                    self
330                );
331            }
332
333            return Ok(());
334        }
335        unreachable!()
336    }
337
338    fn check_continuity(&self, map: &Map) -> Result<()> {
339        for pair in self.roads.windows(2) {
340            let r1 = map.get_r(pair[0].road);
341            let r2 = map.get_r(pair[1].road);
342            if r1.common_endpoint(r2) == CommonEndpoint::None {
343                bail!("Part of the perimeter goes from {:?} to {:?}, but they don't share a common endpoint", pair[0], pair[1]);
344            }
345        }
346        Ok(())
347    }
348
349    /// Should we reverse one perimeter to match the winding order?
350    ///
351    /// This is only meant to be called in the middle of try_to_merge. It assumes both perimeters
352    /// have already been rotated so the common roads are at the end. The invariant of first=last
353    /// is not true.
354    fn reverse_to_fix_winding_order(&self, map: &Map, other: &Perimeter) -> bool {
355        // Using geometry to determine winding order is brittle. Look for any common road, and see
356        // where it points.
357        let common_example = self.roads.last().unwrap().road;
358        let last_common_for_self = match map
359            .get_r(common_example)
360            .common_endpoint(map.get_r(wraparound_get(&self.roads, self.roads.len() as isize).road))
361        {
362            CommonEndpoint::One(i) => i,
363            CommonEndpoint::Both => {
364                // If the common road is a loop on the intersection, then this perimeter must be of
365                // length 2 (or 3 with the invariant), and reversing it is meaningless.
366                return false;
367            }
368            CommonEndpoint::None => unreachable!(),
369        };
370
371        // Find the same road in the other perimeter
372        let other_idx = other
373            .roads
374            .iter()
375            .position(|x| x.road == common_example)
376            .unwrap() as isize;
377        let last_common_for_other = match map
378            .get_r(common_example)
379            .common_endpoint(map.get_r(wraparound_get(&other.roads, other_idx + 1).road))
380        {
381            CommonEndpoint::One(i) => i,
382            CommonEndpoint::Both => {
383                return false;
384            }
385            CommonEndpoint::None => unreachable!(),
386        };
387        last_common_for_self == last_common_for_other
388    }
389
390    /// Try to merge all given perimeters. If successful, only one perimeter will be returned.
391    /// Perimeters are never "destroyed" -- if not merged, they'll appear in the results. If
392    /// `stepwise_debug` is true, returns after performing just one merge.
393    pub fn merge_all(map: &Map, mut input: Vec<Perimeter>, stepwise_debug: bool) -> Vec<Perimeter> {
394        // Internal dead-ends break merging, so first collapse of those. Do this before even
395        // looking for neighbors, since find_common_roads doesn't understand dead-ends.
396        for p in &mut input {
397            p.collapse_deadends();
398        }
399
400        loop {
401            let mut debug = false;
402            let mut results: Vec<Perimeter> = Vec::new();
403            let num_input = input.len();
404            'INPUT: for perimeter in input {
405                if debug {
406                    results.push(perimeter);
407                    continue;
408                }
409
410                for other in &mut results {
411                    // TODO Due to https://github.com/a-b-street/abstreet/issues/841, it seems like
412                    // rotation sometimes breaks `to_block`, so for now, always revert to the
413                    // original upon failure.
414                    let mut copy_a = other.clone();
415                    let mut copy_b = perimeter.clone();
416                    if let Ok(()) = copy_a.try_to_merge(map, &mut copy_b, stepwise_debug) {
417                        *other = copy_a;
418
419                        // To debug, return after any single change
420                        debug = stepwise_debug;
421                        continue 'INPUT;
422                    }
423                }
424
425                // No match
426                results.push(perimeter);
427            }
428
429            // Should we try merging again?
430            if results.len() > 1 && results.len() < num_input && !stepwise_debug {
431                input = results;
432                continue;
433            }
434            return results;
435        }
436    }
437
438    /// If the perimeter follows any dead-end roads, "collapse" them and instead make the perimeter
439    /// contain the dead-end.
440    pub fn collapse_deadends(&mut self) {
441        // Repeatedly try to do this as long as something is changing.
442        loop {
443            let orig = self.clone();
444            self.undo_invariant();
445
446            // TODO Workaround https://github.com/a-b-street/abstreet/issues/834. If this is a loop
447            // around a disconnected fragment of road, don't touch it
448            if self.roads.len() == 2 && self.roads[0].road == self.roads[1].road {
449                self.restore_invariant();
450                return;
451            }
452
453            // If the dead-end straddles the loop, it's confusing. Just rotate until that's not true.
454            while self.roads[0].road == self.roads.last().unwrap().road {
455                self.roads.rotate_left(1);
456            }
457
458            // TODO This won't handle a deadend that's more than 1 segment long
459            let mut roads: Vec<RoadSideID> = Vec::new();
460            let mut changed = false;
461            for id in self.roads.drain(..) {
462                if Some(id.road) == roads.last().map(|id| id.road) {
463                    changed = true;
464                    roads.pop();
465                    self.interior.insert(id.road);
466                } else {
467                    roads.push(id);
468                }
469            }
470
471            self.roads = roads;
472            if self.roads.is_empty() {
473                // TODO This case was introduced with find_roads_to_skip_tracing. Not sure why.
474                *self = orig;
475                return;
476            }
477            self.restore_invariant();
478
479            if !changed {
480                return;
481            }
482        }
483    }
484
485    /// Consider the perimeters as a graph, with adjacency determined by sharing any road in common.
486    /// Partition adjacent perimeters, subject to the predicate. Each partition should produce a
487    /// single result with `merge_all`.
488    pub fn partition_by_predicate<F: Fn(RoadID) -> bool>(
489        input: Vec<Perimeter>,
490        predicate: F,
491    ) -> Vec<Vec<Perimeter>> {
492        let mut road_to_perimeters: HashMap<RoadID, Vec<usize>> = HashMap::new();
493        for (idx, perimeter) in input.iter().enumerate() {
494            for id in &perimeter.roads {
495                road_to_perimeters
496                    .entry(id.road)
497                    .or_insert_with(Vec::new)
498                    .push(idx);
499            }
500        }
501
502        // Start at one perimeter, floodfill to adjacent perimeters, subject to the predicate.
503        // Returns the indices of everything in that component.
504        let floodfill = |start: usize| -> BTreeSet<usize> {
505            let mut visited = BTreeSet::new();
506            let mut queue = vec![start];
507            while !queue.is_empty() {
508                let current = queue.pop().unwrap();
509                if visited.contains(&current) {
510                    continue;
511                }
512                visited.insert(current);
513                for id in &input[current].roads {
514                    if predicate(id.road) {
515                        queue.extend(road_to_perimeters[&id.road].clone());
516                    }
517                }
518            }
519            visited
520        };
521
522        let mut partitions: Vec<BTreeSet<usize>> = Vec::new();
523        let mut finished: HashSet<usize> = HashSet::new();
524        for start in 0..input.len() {
525            if finished.contains(&start) {
526                continue;
527            }
528            let partition = floodfill(start);
529            finished.extend(partition.clone());
530            partitions.push(partition);
531        }
532
533        // Map the indices back to the actual perimeters.
534        let mut perimeters: Vec<Option<Perimeter>> = input.into_iter().map(Some).collect();
535        let mut results = Vec::new();
536        for indices in partitions {
537            let mut partition = Vec::new();
538            for idx in indices {
539                partition.push(perimeters[idx].take().unwrap());
540            }
541            results.push(partition);
542        }
543        // Sanity check
544        for maybe_perimeter in perimeters {
545            assert!(maybe_perimeter.is_none());
546        }
547        results
548    }
549
550    /// Assign each perimeter one of `num_colors`, such that no two adjacent perimeters share the
551    /// same color. May fail. The resulting colors are expressed as `[0, num_colors)`.
552    pub fn calculate_coloring(input: &[Perimeter], num_colors: usize) -> Option<Vec<usize>> {
553        let mut road_to_perimeters: HashMap<RoadID, Vec<usize>> = HashMap::new();
554        for (idx, perimeter) in input.iter().enumerate() {
555            for id in &perimeter.roads {
556                road_to_perimeters
557                    .entry(id.road)
558                    .or_insert_with(Vec::new)
559                    .push(idx);
560            }
561        }
562
563        // Greedily fill out a color for each perimeter, in the same order as the input
564        let mut assigned_colors = Vec::new();
565        for (this_idx, perimeter) in input.iter().enumerate() {
566            let mut available_colors: Vec<bool> =
567                std::iter::repeat(true).take(num_colors).collect();
568            // Find all neighbors
569            for id in &perimeter.roads {
570                for other_idx in &road_to_perimeters[&id.road] {
571                    // We assign colors in order, so any neighbor index smaller than us has been
572                    // chosen
573                    if *other_idx < this_idx {
574                        available_colors[assigned_colors[*other_idx]] = false;
575                    }
576                }
577            }
578            if let Some(color) = available_colors.iter().position(|x| *x) {
579                assigned_colors.push(color);
580            } else {
581                // Too few colors?
582                return None;
583            }
584        }
585        Some(assigned_colors)
586    }
587
588    pub fn to_block(self, map: &Map) -> Result<Block> {
589        // Trace along the perimeter and build the polygon
590        let mut pts: Vec<Pt2D> = Vec::new();
591        let mut first_intersection = None;
592        for pair in self.roads.windows(2) {
593            let lane1 = pair[0].get_outermost_lane(map);
594            let road1 = map.get_parent(lane1.id);
595            let lane2 = pair[1].get_outermost_lane(map);
596            // If lane1 and lane2 are the same, then it just means we found a dead-end road with
597            // exactly one lane, which is usually a footway or cycleway that legitimately is a
598            // dead-end, or connects to some other road we didn't import. We'll just trace around
599            // it like a normal dead-end road.
600            let mut pl = match pair[0].side {
601                SideOfRoad::Right => road1
602                    .center_pts
603                    .shift_right(road1.get_half_width())
604                    // TODO Remove after fixing whatever map import error allows a bad PolyLine to
605                    // wind up here at all
606                    .unwrap_or_else(|err| {
607                        warn!(
608                            "Can't get right edge of {} ({}): {}",
609                            road1.id, err, road1.orig_id
610                        );
611                        road1.center_pts.clone()
612                    }),
613                SideOfRoad::Left => road1
614                    .center_pts
615                    .shift_left(road1.get_half_width())
616                    .unwrap_or_else(|err| {
617                        warn!(
618                            "Can't get left edge of {} ({}): {}",
619                            road1.id, err, road1.orig_id
620                        );
621                        road1.center_pts.clone()
622                    }),
623            };
624            if lane1.dir == Direction::Back {
625                pl = pl.reversed();
626            }
627            let keep_lane_orientation = if pair[0].road == pair[1].road {
628                // We're doubling back at a dead-end. Always follow the orientation of the lane.
629                true
630            } else {
631                match lane1.common_endpoint(lane2) {
632                    CommonEndpoint::One(i) => i == lane1.dst_i,
633                    CommonEndpoint::Both => {
634                        // Two different roads link the same two intersections. I don't think we
635                        // can decide the order of points other than seeing which endpoint is
636                        // closest to our last point.
637                        if let Some(last) = pts.last() {
638                            last.dist_to(pl.first_pt()) < last.dist_to(pl.last_pt())
639                        } else {
640                            // The orientation doesn't matter
641                            true
642                        }
643                    }
644                    CommonEndpoint::None => bail!(
645                        "{} and {} don't share a common endpoint",
646                        lane1.id,
647                        lane2.id
648                    ),
649                }
650            };
651            if !keep_lane_orientation {
652                pl = pl.reversed();
653            }
654
655            // Before we add this road's points, try to trace along the polygon's boundary. Usually
656            // this has no effect (we'll dedupe points), but sometimes there's an extra curve.
657            //
658            // Note this logic is similar to how we find SharedSidewalkCorners. Don't rely on that
659            // existing, since the outermost lane mightn't be a sidewalk.
660            //
661            // If the ring.doubles_back(), don't bother. If we tried to trace the boundary, it
662            // usually breaks the final Ring we produce. Better to skip bad intersection polygons
663            // and still produce a reasonable looking block.
664            let prev_i = if keep_lane_orientation {
665                lane1.src_i
666            } else {
667                lane1.dst_i
668            };
669            if first_intersection.is_none() {
670                first_intersection = Some(prev_i);
671            }
672            if let Some(last_pt) = pts.last() {
673                let prev_i = map.get_i(prev_i);
674                let ring = prev_i.polygon.get_outer_ring();
675                if !ring.doubles_back() {
676                    // At dead-ends, trace around the intersection on the longer side
677                    let longer = prev_i.is_deadend_for_driving(map);
678                    if let Some(slice) = ring.get_slice_between(*last_pt, pl.first_pt(), longer) {
679                        pts.extend(slice.into_points());
680                    }
681                }
682            }
683
684            pts.extend(pl.into_points());
685        }
686        // Do the intersection boundary tracing for the last piece. We didn't know enough to do it
687        // the first time.
688        let first_intersection = map.get_i(first_intersection.unwrap());
689        let ring = first_intersection.polygon.get_outer_ring();
690        if !ring.doubles_back() {
691            let longer = first_intersection.is_deadend_for_driving(map);
692            if let Some(slice) = ring.get_slice_between(*pts.last().unwrap(), pts[0], longer) {
693                pts.extend(slice.into_points());
694            }
695        }
696        pts.push(pts[0]);
697        pts.dedup();
698        let polygon = Ring::unsafe_deduping_new(pts)?.into_polygon();
699        // TODO To debug anyway, we could plumb through a Tessellation, but there's pretty much
700        // always a root problem in the map geometry that should be properly fixed.
701
702        Ok(Block {
703            perimeter: self,
704            polygon,
705        })
706    }
707
708    /// Does this perimeter completely enclose the other?
709    pub fn contains(&self, other: &Perimeter) -> bool {
710        other
711            .roads
712            .iter()
713            .all(|id| self.interior.contains(&id.road) || self.roads.contains(id))
714    }
715
716    /// Shrinks or expands the perimeter by tracing the opposite side of the road.
717    pub fn flip_side_of_road(mut self) -> Self {
718        for road_side in &mut self.roads {
719            *road_side = road_side.other_side();
720        }
721        self
722    }
723
724    /// Looks for perimeters that're completely surrounded by other perimeters, aka, holes.
725    /// Attempts to merge them with the surrounding perimeter. This can be useful for applications
726    /// trying to incrementally merge adjacent blocks without creating splits, because it's often
727    /// impossible to do this in one merge when there are holes.
728    ///
729    /// This should never "lose" any of the input. It may not be fast or guaranteed to find and fix
730    /// every hole.
731    pub fn merge_holes(map: &Map, mut perims: Vec<Perimeter>) -> Vec<Perimeter> {
732        // Fixed-point for now -- find and fix one hole at a time. Slow, but simple.
733        loop {
734            let num_before = perims.len();
735
736            // Look for one hole
737            let mut hole = None;
738            for (idx, perim) in perims.iter().enumerate() {
739                let copy = perim.clone().flip_side_of_road();
740                // Now that we've "expanded" the perimeter to the other side of the road, is there
741                // another perimeter that completely contains it?
742                if let Some(surrounding) = perims.iter().position(|p| p.contains(&copy)) {
743                    hole = Some((idx, surrounding));
744                    // TODO If the first hole found doesn't merge for some reason, then we'll get
745                    // stuck and just give up, even if there are other holes that might be fixed
746                    // later. The indices just get too tricky.
747                    break;
748                }
749            }
750            if let Some((mut idx1, mut idx2)) = hole {
751                // Merge these two
752                if idx2 < idx1 {
753                    std::mem::swap(&mut idx1, &mut idx2);
754                }
755                let perim1 = perims.remove(idx2);
756                let perim2 = perims.remove(idx1);
757
758                let stepwise_debug = false;
759                perims.extend(Self::merge_all(map, vec![perim1, perim2], stepwise_debug));
760            }
761
762            if perims.len() == num_before {
763                // We didn't change anything, so stop
764                break;
765            }
766        }
767
768        perims
769    }
770}
771
772impl fmt::Debug for Perimeter {
773    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
774        writeln!(f, "Perimeter:")?;
775        for id in &self.roads {
776            writeln!(f, "- {:?} of {}", id.side, id.road)?;
777        }
778        Ok(())
779    }
780}