from shapely.geometry import Polygon from leonida.heights import DISTRICT_RANGES, assign_heights SQUARE = Polygon([(0, 0), (20, 0), (20, 20), (0, 20)]) FAR = Polygon([(500, 500), (520, 500), (520, 520), (500, 520)]) def test_landmark_height_wins(): res = assign_heights([SQUARE], landmarks=[{"xyz": [10, 10, 0], "height": 180.0}], districts=[], seed=1) assert res[0]["height"] == 180.0 assert res[0]["source"] == "skyline" def test_district_range_is_used_without_landmark(): res = assign_heights([SQUARE], landmarks=[], districts=[{"name": "Downtown", "x": 5, "y": 5}], seed=1) lo, hi = DISTRICT_RANGES["Downtown"] assert lo <= res[0]["height"] <= hi assert res[0]["source"] == "district" def test_same_seed_gives_same_height(): args = dict(landmarks=[], districts=[{"name": "Downtown", "x": 5, "y": 5}], seed=42) assert assign_heights([SQUARE], **args)[0]["height"] == assign_heights([SQUARE], **args)[0]["height"] def test_unknown_district_falls_back_to_default(): res = assign_heights([FAR], landmarks=[], districts=[], seed=3) lo, hi = DISTRICT_RANGES["default"] assert lo <= res[0]["height"] <= hi from shapely.geometry import LineString, Point from leonida.heights import PALM_KERB, palm_points def test_no_palm_stands_on_the_carriageway(): """Der Versatz muss vom Fahrbahnrand her gedacht werden, nicht von einem festen Wert. Am echten Mosaik hat die Fahrbahn an den Mittellinienpunkten p50 5,4 m, p90 11,7 m und p95 14,0 m Halbbreite. Ein fester Versatz von 4 m setzt 60 % der Palmen auf die Fahrbahn, einer von 11 m immer noch 12,5 % – und auf der Hälfte der Straßen stünde die Palme dann im Nachbargebäude. """ road = Polygon([(0, -9), (200, -9), (200, 9), (0, 9)]) # 18 m breite Fahrbahn line = LineString([(0, 0), (200, 0)]) sand = Polygon([(0, 20), (200, 20), (200, 40), (0, 40)]) pts = palm_points([sand], [], [line], [], [road], [], seed=1) assert pts assert all(road.distance(Point(x, y)) >= PALM_KERB for x, y in pts) def test_palms_do_not_pile_up_where_centerlines_meet(): """Mittellinien sind an Kreuzungen getrennt (Task 4). Ein Start bei Position 0 setzt auf zwei gegenüberliegenden Ästen zwei Palmen in genau denselben Punkt.""" sand = Polygon([(-80, 20), (80, 20), (80, 40), (-80, 40)]) arms = [LineString([(0, 0), (60, 0)]), LineString([(0, 0), (-60, 0)])] pts = palm_points([sand], [], arms, [], [], [], seed=1) assert pts assert len(set(pts)) == len(pts) def test_no_palm_stands_in_the_water(): """Der Versatz nach außen schiebt Palmen an Küstenstraßen über die Uferlinie. Am echten Mosaik standen so 218 von 2.433 Palmen im Wasser.""" land = Polygon([(-100, -100), (100, -100), (100, 0), (-100, 0)]) # Ufer bei y = 0 road = Polygon([(-100, -12), (100, -12), (100, 0), (-100, 0)]) line = LineString([(-100, -6), (100, -6)]) sand = Polygon([(-100, 0), (100, 0), (100, 10), (-100, 10)]) pts = palm_points([sand], [], [line], [], [road], [land], seed=1) assert pts assert all(land.contains(Point(x, y)) for x, y in pts) def test_park_scatter_is_deterministic_and_inside_the_park(): """Ohne Sperrflächen liefert ein Park einfach verstreute, aber reproduzierbare Punkte: derselbe Seed muss dieselbe Punktliste geben.""" park = Polygon([(0, 0), (50, 0), (50, 50), (0, 50)]) # 2500 m^2 -> 10 Punkte pts_a = palm_points([], [park], [], [], [], [], seed=7) pts_b = palm_points([], [park], [], [], [], [], seed=7) assert pts_a assert all(park.contains(Point(x, y)) for x, y in pts_a) assert pts_a == pts_b