"""Detections that straddle a tile seam must not become two half buildings. Tiling uses a fixed overlap. An object wider than that overlap is truncated by both tiles, so the two boxes barely intersect and plain IoU suppression keeps them both: two false positives plus one missed footprint for every seam building. The suppressor therefore also compares overlap against the smaller box, and truncated boxes that sit against an interior tile edge are dropped in favour of the neighbouring tile's complete view. """ from __future__ import annotations from shapely.geometry import box from app.services.detection_service import DetectionService def _candidate(name: str, geometry, confidence: float, *, tile_index: int = 0, tile_bounds=None): return { "class_name": "building", "confidence": confidence, "geometry": geometry, "bbox": [0.0, 0.0, 1.0, 1.0], "source_tile_path": f"/tiles/tile_{tile_index:04d}.tif", "properties": {"tile_index": tile_index, "name": name}, "tile_bounds": tile_bounds, } def test_identical_overlapping_predictions_are_still_suppressed() -> None: kept = DetectionService._suppress_duplicate_candidates( [ _candidate("a", box(0.0, 0.0, 1.0, 1.0), 0.7), _candidate("b", box(0.02, 0.02, 1.02, 1.02), 0.9), ], iou_threshold=0.5, ) assert [item["properties"]["name"] for item in kept] == ["b"] def test_a_box_contained_in_a_larger_one_is_suppressed() -> None: """A truncated seam half sits inside the complete box from the next tile.""" complete = box(0.0, 0.0, 10.0, 10.0) truncated_half = box(0.0, 0.0, 4.0, 10.0) # IoU with ``complete`` is 0.4 kept = DetectionService._suppress_duplicate_candidates( [ _candidate("complete", complete, 0.88), _candidate("truncated", truncated_half, 0.61), ], iou_threshold=0.5, ) assert [item["properties"]["name"] for item in kept] == ["complete"] def test_genuinely_adjacent_buildings_are_both_kept() -> None: """Terraced houses touch but do not contain one another.""" kept = DetectionService._suppress_duplicate_candidates( [ _candidate("left", box(0.0, 0.0, 10.0, 10.0), 0.9), _candidate("right", box(10.0, 0.0, 20.0, 10.0), 0.85), ], iou_threshold=0.5, ) assert sorted(item["properties"]["name"] for item in kept) == ["left", "right"] def test_different_classes_are_never_merged() -> None: first = _candidate("a", box(0.0, 0.0, 10.0, 10.0), 0.9) second = _candidate("b", box(0.0, 0.0, 10.0, 10.0), 0.8) second["class_name"] = "solar_panel" kept = DetectionService._suppress_duplicate_candidates([first, second], iou_threshold=0.5) assert len(kept) == 2 def test_boxes_clipped_by_an_interior_tile_edge_are_dropped() -> None: """The overlapping neighbour tile still sees the whole object.""" tile = box(0.0, 0.0, 10.0, 10.0) raster = box(0.0, 0.0, 30.0, 10.0) candidates = [ # Sits against the tile's right edge: truncated by the tile, not real. _candidate("edge", box(9.0, 2.0, 10.0, 4.0), 0.8, tile_bounds=tile.bounds), # Comfortably inside the tile. _candidate("interior", box(2.0, 2.0, 4.0, 4.0), 0.8, tile_bounds=tile.bounds), ] kept = DetectionService._drop_tile_edge_truncations( candidates, raster_bounds=raster.bounds, tolerance=0.001 ) assert [item["properties"]["name"] for item in kept] == ["interior"] def test_boxes_against_the_raster_edge_are_kept() -> None: """No neighbouring tile exists there, so the box is all the evidence there is.""" tile = box(0.0, 0.0, 10.0, 10.0) raster = box(0.0, 0.0, 10.0, 10.0) candidates = [_candidate("edge", box(9.0, 2.0, 10.0, 4.0), 0.8, tile_bounds=tile.bounds)] kept = DetectionService._drop_tile_edge_truncations( candidates, raster_bounds=raster.bounds, tolerance=0.001 ) assert [item["properties"]["name"] for item in kept] == ["edge"] def test_edge_filter_keeps_candidates_without_tile_bounds() -> None: candidates = [_candidate("unknown", box(2.0, 2.0, 4.0, 4.0), 0.8, tile_bounds=None)] kept = DetectionService._drop_tile_edge_truncations( candidates, raster_bounds=(0.0, 0.0, 30.0, 10.0), tolerance=0.001 ) assert len(kept) == 1