import hashlib import json from pathlib import Path from PIL import Image import overview from leonida.coords import SLICE def _ring(x0, z0, x1, z1): return [[x0, z0], [x1, z0], [x1, z1], [x0, z1], [x0, z0]] def _fixture(tmp_path: Path) -> Path: """Ein einziger Chunk A1 (Nordwest): Land, eine Straße, ein Gebäude, eine Palme. Chunk-lokale Koordinaten von chunk_0_0 fallen bei scale 1 genau auf die Kartenpixel, weil der Chunk-Ursprung die Nordwest-Ecke des Ausschnitts ist. """ chunk = { "chunk": [0, 0], "origin": [SLICE["x0"], -SLICE["y1"]], "land": [[_ring(0, 0, 250, 250)]], "surfaces": [], "roads": [{"poly": [_ring(100, 0, 120, 250)], "bridge": False}], "centerlines": [], "buildings": [{"poly": [_ring(20, 20, 60, 60)], "height": 12.0, "source": "district", "speculative": False}], "palms": [[200.0, 200.0]], } (tmp_path / "chunk_0_0.json").write_text(json.dumps(chunk)) (tmp_path / "manifest.json").write_text('{"slice": "vice-a"}\n') return tmp_path def test_map_picture_has_one_pixel_per_metre_and_no_palms(tmp_path): src = tmp_path / "chunks" src.mkdir() _fixture(src) dst = tmp_path / "out" overview.render_map(src, dst) picture = Image.open(dst / "map.png") assert picture.size == (3000, 3000) assert picture.getpixel((110, 125)) == overview.ROAD assert picture.getpixel((40, 40)) == overview._building_shade(12.0) assert picture.getpixel((200, 200)) == overview.LAND # Palme wird nicht gezeichnet assert picture.getpixel((1500, 1500)) == overview.WATER def test_walkable_mask_blocks_buildings_and_water(tmp_path): src = tmp_path / "chunks" src.mkdir() _fixture(src) dst = tmp_path / "out" overview.render_map(src, dst) mask = Image.open(dst / "map-walkable.png") assert mask.mode == "L" assert mask.size == (750, 750) assert mask.getpixel((10, 10)) == overview.BLOCKED # Gebäude bei 40 m / 40 m assert mask.getpixel((27, 31)) == overview.WALKABLE # Straße bei 110 m / 125 m assert mask.getpixel((50, 50)) == overview.WALKABLE # Land bei 200 m / 200 m assert mask.getpixel((375, 375)) == overview.BLOCKED # Wasser def test_map_json_carries_scale_slice_and_manifest_fingerprint(tmp_path): src = tmp_path / "chunks" src.mkdir() _fixture(src) dst = tmp_path / "out" overview.render_map(src, dst) info = json.loads((dst / "map.json").read_text()) assert info["scale"] == 1.0 assert info["walkable_scale"] == 0.25 assert info["slice"] == SLICE assert info["manifest_sha256"] == hashlib.sha256((src / "manifest.json").read_bytes()).hexdigest() def test_debug_overview_still_draws_palms(tmp_path): src = tmp_path / "chunks" src.mkdir() _fixture(src) dst = tmp_path / "debug.png" stats = overview.render(src, dst) assert stats["palmen"] == 1 assert Image.open(dst).getpixel((100, 100)) == overview.PALM # 200 m bei 0,5 px/m