diff --git a/tests/test_nodes.py b/tests/test_nodes.py index 53f08ababb..6eac56db70 100644 --- a/tests/test_nodes.py +++ b/tests/test_nodes.py @@ -342,6 +342,11 @@ def test_buildings_intersecting_plan_area(self): building_area = pd.Series([polygon1.area, polygon2.area]) crs = "epsg:3857" + wall_length_north = pd.Series([1.0, 1.0]) + wall_length_east = pd.Series([1.0, 1.0]) + wall_length_south = pd.Series([1.0, 1.0]) + wall_length_west = pd.Series([1.0, 1.0]) + total_plan_area_geometry_no_overlap = pd.Series([polygon1.buffer(1), polygon2.buffer(1)]) total_plan_area_geometry_some_overlap = pd.Series( [polygon1.buffer(3.5), polygon2.buffer(3.5)] @@ -356,10 +361,18 @@ def test_buildings_intersecting_plan_area(self): "building_area_target": building_area, "building_geometry": building_geometry, "building_buffered_target": gpd.GeoSeries(total_plan_area_geometry_no_overlap), + "wall_length_north_target": wall_length_north, + "wall_length_east_target": wall_length_east, + "wall_length_south_target": wall_length_south, + "wall_length_west_target": wall_length_west, "index_neighbor": building_id, "building_height_neighbor": building_height, "building_area_neighbor": building_area, "building_buffered_neighbor": total_plan_area_geometry_no_overlap, + "wall_length_north_neighbor": wall_length_north, + "wall_length_east_neighbor": wall_length_east, + "wall_length_south_neighbor": wall_length_south, + "wall_length_west_neighbor": wall_length_west, "building_geometry_neighbor": gpd.GeoSeries(building_geometry), }, geometry="building_geometry", @@ -387,6 +400,10 @@ def test_buildings_intersecting_plan_area(self): total_plan_area_geometry_some_overlap[1], ] ), + "wall_length_north_target": pd.Series([1.0, 1.0, 1.0, 1.0]), + "wall_length_east_target": pd.Series([1.0, 1.0, 1.0, 1.0]), + "wall_length_south_target": pd.Series([1.0, 1.0, 1.0, 1.0]), + "wall_length_west_target": pd.Series([1.0, 1.0, 1.0, 1.0]), "index_neighbor": pd.Series([0, 0, 1, 1]), "building_height_neighbor": pd.Series([5, 5, 10, 10]), "building_area_neighbor": pd.Series([1.0, 1.0, 1.0, 1.0]), @@ -398,6 +415,10 @@ def test_buildings_intersecting_plan_area(self): total_plan_area_geometry_some_overlap[1], ] ), + "wall_length_north_neighbor": pd.Series([1.0, 1.0, 1.0, 1.0]), + "wall_length_east_neighbor": pd.Series([1.0, 1.0, 1.0, 1.0]), + "wall_length_south_neighbor": pd.Series([1.0, 1.0, 1.0, 1.0]), + "wall_length_west_neighbor": pd.Series([1.0, 1.0, 1.0, 1.0]), "building_geometry_neighbor": gpd.GeoSeries( [ building_geometry[0], @@ -432,6 +453,10 @@ def test_buildings_intersecting_plan_area(self): total_plan_area_geometry_total_overlap[1], ] ), + "wall_length_north_target": pd.Series([1.0, 1.0, 1.0, 1.0]), + "wall_length_east_target": pd.Series([1.0, 1.0, 1.0, 1.0]), + "wall_length_south_target": pd.Series([1.0, 1.0, 1.0, 1.0]), + "wall_length_west_target": pd.Series([1.0, 1.0, 1.0, 1.0]), "index_neighbor": pd.Series([0, 0, 1, 1]), "building_height_neighbor": pd.Series([5, 5, 10, 10]), "building_area_neighbor": pd.Series([1.0, 1.0, 1.0, 1.0]), @@ -443,6 +468,10 @@ def test_buildings_intersecting_plan_area(self): total_plan_area_geometry_total_overlap[1], ] ), + "wall_length_north_neighbor": pd.Series([1.0, 1.0, 1.0, 1.0]), + "wall_length_east_neighbor": pd.Series([1.0, 1.0, 1.0, 1.0]), + "wall_length_south_neighbor": pd.Series([1.0, 1.0, 1.0, 1.0]), + "wall_length_west_neighbor": pd.Series([1.0, 1.0, 1.0, 1.0]), "building_geometry_neighbor": gpd.GeoSeries( [ building_geometry[0], @@ -481,7 +510,16 @@ class TestCase: for case in testcases: actual = nodes.buildings_intersecting_plan_area( - building_id, building_height, building_geometry, building_area, case.input, crs + building_id, + building_height, + building_geometry, + building_area, + case.input, + wall_length_north, + wall_length_east, + wall_length_south, + wall_length_west, + crs, ) expected = case.expected pd.testing.assert_frame_equal( @@ -637,6 +675,163 @@ class TestCase: "failed test {} expected {}, actual {}".format(case.name, expected, actual), ) + def test_wall_length(self): + """Test that the function wall_length returns the correct wall area.""" + + north = Settings.north + south = Settings.south + east = Settings.east + west = Settings.west + + wall_angle = Settings.wall_angle + wall_direction = Settings.wall_direction + wall_length = Settings.wall_length + + wall_length_north = Settings.wall_length_north + wall_length_south = Settings.wall_length_south + wall_length_east = Settings.wall_length_east + wall_length_west = Settings.wall_length_west + + square_root_one_half = 0.7071067811865476 + + square_input = pd.concat( + [ + pd.Series([[0.0, -90.0, 180.0, 90.0]], name=wall_angle), + pd.Series([[north, east, south, west]], name=wall_direction), + pd.Series([[1.0, 1.0, 1.0, 1.0]], name=wall_length), + ], + axis=1, + ) + triangle_input = pd.concat( + [ + pd.Series([[45.0, 180.0, -90.0]], name=wall_angle), + pd.Series([[west, south, east]], name=wall_direction), + pd.Series([[1.0, square_root_one_half, square_root_one_half]], name=wall_length), + ], + axis=1, + ) + eight_sided_input = pd.concat( + [ + pd.Series([[0.0, -90.0, 180.0, 90.0, -45.0, -135.0, 135.0, 45.0]], name=wall_angle), + pd.Series( + [[north, east, south, west, north, east, south, west]], name=wall_direction + ), + pd.Series([[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]], name=wall_length), + ], + axis=1, + ) + + @dataclass + class TestCase: + name: str + input: pd.DataFrame + expected: pd.DataFrame + + testcases = [ + TestCase( + name="square", + input=square_input, + expected=pd.concat( + [ + pd.Series([1.0], name=wall_length_north), + pd.Series([1.0], name=wall_length_east), + pd.Series([1.0], name=wall_length_south), + pd.Series([1.0], name=wall_length_west), + ], + axis=1, + ), + ), + TestCase( + name="triangle", + input=triangle_input, + expected=pd.concat( + [ + pd.Series([0], name=wall_length_north), + pd.Series([square_root_one_half], name=wall_length_east), + pd.Series([square_root_one_half], name=wall_length_south), + pd.Series([1.0], name=wall_length_west), + ], + axis=1, + ), + ), + TestCase( + name="eight sided", + input=eight_sided_input, + expected=pd.concat( + [ + pd.Series([6.0], name=wall_length_north), + pd.Series([8.0], name=wall_length_east), + pd.Series([10.0], name=wall_length_south), + pd.Series([12.0], name=wall_length_west), + ], + axis=1, + ), + ), + ] + + for case in testcases: + actual = nodes.wall_length(case.input) + expected = case.expected + pd.testing.assert_frame_equal( + expected, + actual, + "failed test {} expected {}, actual {}".format(case.name, expected, actual), + ) + + def test_frontal_length(self): + """Test that the function frontal_length returns the correct frontal length.""" + + polygon1 = Polygon([[0, 0], [0, 1], [1, 1], [1, 0]]) + polygon2 = Polygon([[3, 3], [3, 4], [4, 4], [4, 3]]) + building_id = pd.Series([0, 1]) + building_height = pd.Series([5, 10]) + building_geometry = pd.Series([polygon1, polygon2]) + building_area = pd.Series([polygon1.area, polygon2.area]) + crs = "epsg:3857" + + # The wall lengths listed below are the test cases. + # wall_length_north and wall_length_east test that the addition is working correctly. + # wall_length_south tests that addition works correctly when one building does not have a wall facing a given direction. + # wall_length_west tests that addition works correctly when neither building has a wall facing a given direction. + + wall_length_north = pd.Series([1.0, 1.0]) + wall_length_east = pd.Series([1.0, 3.0]) + wall_length_south = pd.Series([0, 1.0]) + wall_length_west = pd.Series([0, 0]) + + frontal_length_north = Settings.frontal_length_north + frontal_length_east = Settings.frontal_length_east + frontal_length_south = Settings.frontal_length_south + frontal_length_west = Settings.frontal_length_west + + total_plan_area_geometry = pd.Series([polygon1.buffer(5), polygon2.buffer(5)]) + + input_gdf = nodes.buildings_intersecting_plan_area( + building_id, + building_height, + building_geometry, + building_area, + total_plan_area_geometry, + wall_length_north, + wall_length_east, + wall_length_south, + wall_length_west, + crs, + ) + + actual = nodes.frontal_length(input_gdf) + expected = pd.concat( + [ + pd.Series([2.0, 2.0], name=frontal_length_north), + pd.Series([4.0, 4.0], name=frontal_length_east), + pd.Series([1.0, 1.0], name=frontal_length_south), + pd.Series([0, 0], name=frontal_length_west), + ], + axis=1, + ) + + pd.testing.assert_frame_equal(expected, actual) + if __name__ == "__main__": unittest.main()