feat(extract): angeschnittene Gebäude schließen und verdeckte Blockteile bebauen
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
368ea90b3a
commit
ce93e57936
2 files changed
+258
-5
No files matched your search
@@ -161,4 +161,84 @@ def test_a_road_running_off_the_map_under_text_is_kept_to_the_edge():
|
||||
scribble(labels, mask)
|
||||
result = rebuild(labels, mask)
|
||||
assert result.stats.extended == 1
|
||||
assert (labels[0:30, 142:158] == ROAD).all()
|
||||
assert (labels[0:30, 142:158] == ROAD).all()
|
||||
|
||||
|
||||
def test_a_bitten_building_is_closed_but_not_extended():
|
||||
labels = city()
|
||||
labels[50:90, 50:110] = BUILDING
|
||||
mask = box_mask(labels.shape, slice(70, 110), slice(90, 150))
|
||||
scribble(labels, mask)
|
||||
result = rebuild(labels, mask)
|
||||
assert result.stats.closed == 1
|
||||
assert (labels[70:90, 90:110] == BUILDING).all()
|
||||
assert not (labels[92:110, 90:150] == BUILDING).any()
|
||||
assert not (labels[70:90, 112:150] == BUILDING).any()
|
||||
|
||||
|
||||
def block_city():
|
||||
"""Ein Block zwischen vier Straßen, links sichtbar bebaut, rechts verdeckt."""
|
||||
labels = city(300, 400)
|
||||
labels[20:40, :] = ROAD
|
||||
labels[260:280, :] = ROAD
|
||||
labels[:, 20:40] = ROAD
|
||||
labels[:, 360:380] = ROAD
|
||||
for top in (60, 100, 140, 180):
|
||||
for left in (60, 120):
|
||||
labels[top:top + 20, left:left + 30] = BUILDING
|
||||
mask = box_mask(labels.shape, slice(60, 241), slice(220, 341))
|
||||
return labels, mask
|
||||
|
||||
|
||||
def test_a_hidden_block_part_gets_the_density_of_the_visible_part():
|
||||
labels, mask = block_city()
|
||||
visible_density = 8 * 600 / (220 * 320 - mask.sum())
|
||||
scribble(labels, mask)
|
||||
result = rebuild(labels, mask)
|
||||
added = (labels[mask] == BUILDING).sum()
|
||||
assert result.stats.added == len(result.buildings) >= 3
|
||||
assert abs(added - visible_density * mask.sum()) <= 600 + 2 * (20 + 30) # ein Vorlage-Rechteck genau
|
||||
|
||||
|
||||
def test_added_buildings_keep_setback_gaps_and_stay_in_the_hidden_part():
|
||||
labels, mask = block_city()
|
||||
scribble(labels, mask)
|
||||
result = rebuild(labels, mask)
|
||||
new = (labels == BUILDING) & mask
|
||||
assert not (new & ~mask).any()
|
||||
road_distance = ndi.distance_transform_edt(~np.isin(labels, [ROAD, PATH]))
|
||||
assert road_distance[new].min() >= 15 # Straßenabstand der Nachbarn, auf 15 m begrenzt
|
||||
pieces, count = ndi.label(new)
|
||||
assert count == len(result.buildings)
|
||||
for k in range(1, count + 1):
|
||||
others = new & (pieces != k)
|
||||
assert ndi.distance_transform_edt(~others)[pieces == k].min() >= 3
|
||||
|
||||
|
||||
def test_no_buildings_are_added_in_a_park():
|
||||
labels, mask = block_city()
|
||||
labels[40:260, 200:360] = PARK
|
||||
scribble(labels, mask)
|
||||
result = rebuild(labels, mask)
|
||||
assert result.stats.added == 0
|
||||
|
||||
|
||||
def test_without_three_neighbours_nothing_is_added():
|
||||
labels, mask = block_city()
|
||||
labels[labels == BUILDING] = STADT
|
||||
labels[60:80, 60:90] = BUILDING
|
||||
labels[100:120, 60:90] = BUILDING
|
||||
scribble(labels, mask)
|
||||
result = rebuild(labels, mask)
|
||||
assert result.stats.added == 0
|
||||
assert result.stats.note == "zu wenig Nachbarn"
|
||||
|
||||
|
||||
def test_rebuild_is_deterministic():
|
||||
first, mask = block_city()
|
||||
second = first.copy()
|
||||
scribble(first, mask)
|
||||
scribble(second, mask)
|
||||
a, b = rebuild(first, mask), rebuild(second, mask)
|
||||
assert (first == second).all()
|
||||
assert a.buildings == b.buildings
|
||||
Reference in new issue
Block a user