Add scrub edge bushes

This commit is contained in:
2026-07-31 16:53:28 +08:00
parent eb4a0d9b2a
commit 41a07a78ef
4 changed files with 319 additions and 10 deletions

View File

@@ -25,7 +25,10 @@ from osmassets.geom import (
geometry_rings,
point_in_polygon,
polygon_area,
sample_polygon_interior,
sample_ring_boundary,
sample_tree_row,
signed_polygon_area,
)
from osmassets.osm import Projector, parse_height, parse_osm, tags
@@ -84,11 +87,66 @@ class PolygonAreaTest(unittest.TestCase):
def test_winding_does_not_change_the_sign(self):
self.assertAlmostEqual(polygon_area(list(reversed(SQUARE))), 100.0)
self.assertGreater(signed_polygon_area(SQUARE), 0.0)
self.assertLess(signed_polygon_area(list(reversed(SQUARE))), 0.0)
def test_degenerate(self):
self.assertEqual(polygon_area([(0.0, 0.0), (1.0, 1.0)]), 0.0)
class SampleRingBoundaryTest(unittest.TestCase):
def test_samples_closed_boundary_evenly(self):
samples = sample_ring_boundary(SQUARE, spacing=10.0)
self.assertEqual(len(samples), 4)
self.assertEqual([(round(x, 6), round(y, 6)) for x, y, _, _ in samples],
[(5.0, 0.0), (10.0, 5.0), (5.0, 10.0), (0.0, 5.0)])
def test_repeated_closing_point_is_ignored(self):
open_samples = sample_ring_boundary(SQUARE, spacing=10.0)
closed_samples = sample_ring_boundary(SQUARE + [SQUARE[0]], spacing=10.0)
self.assertEqual(open_samples, closed_samples)
def test_inset_moves_samples_inside_for_either_winding(self):
ccw = sample_ring_boundary(SQUARE, spacing=10.0, inset=1.0)
cw = sample_ring_boundary(list(reversed(SQUARE)), spacing=10.0, inset=1.0)
self.assertTrue(all(point_in_polygon((x, y), SQUARE) for x, y, _, _ in ccw))
self.assertTrue(all(point_in_polygon((x, y), SQUARE) for x, y, _, _ in cw))
self.assertEqual([(round(x, 6), round(y, 6)) for x, y, _, _ in ccw],
[(5.0, 1.0), (9.0, 5.0), (5.0, 9.0), (1.0, 5.0)])
def test_max_samples_reduces_density(self):
samples = sample_ring_boundary(SQUARE, spacing=1.0, max_samples=5)
self.assertEqual(len(samples), 5)
def test_degenerate_input(self):
self.assertEqual(sample_ring_boundary([(0.0, 0.0)], spacing=1.0), [])
self.assertEqual(sample_ring_boundary(SQUARE, spacing=0.0), [])
class SamplePolygonInteriorTest(unittest.TestCase):
def test_samples_are_inside_and_clear_of_edges(self):
samples = sample_polygon_interior(SQUARE, spacing=3.0, edge_clearance=1.0,
seed=42)
self.assertTrue(samples)
for x, y, _ in samples:
self.assertTrue(point_in_polygon((x, y), SQUARE))
self.assertGreaterEqual(distance_to_ring((x, y), SQUARE), 1.0)
def test_seed_is_deterministic(self):
first = sample_polygon_interior(SQUARE, spacing=3.0, seed=7)
second = sample_polygon_interior(SQUARE, spacing=3.0, seed=7)
self.assertEqual(first, second)
def test_max_samples_reduces_density(self):
samples = sample_polygon_interior(SQUARE, spacing=1.0, max_samples=4,
seed=99)
self.assertEqual(len(samples), 4)
def test_degenerate_input(self):
self.assertEqual(sample_polygon_interior([(0.0, 0.0)], spacing=1.0), [])
self.assertEqual(sample_polygon_interior(SQUARE, spacing=0.0), [])
class PointInPolygonTest(unittest.TestCase):
def test_inside_and_outside(self):
self.assertTrue(point_in_polygon((5.0, 5.0), SQUARE))