diff --git a/momepy/functional/_dimension.py b/momepy/functional/_dimension.py index cc35f0da..ccf69e12 100644 --- a/momepy/functional/_dimension.py +++ b/momepy/functional/_dimension.py @@ -209,9 +209,9 @@ def longest_axis_length(geometry: GeoDataFrame | GeoSeries) -> Series: def perimeter_wall( - geometry: GeoDataFrame | GeoSeries, graph: Graph | None = None + geometry: GeoDataFrame | GeoSeries, graph: Graph | None = None, buffer: float = 0.01 ) -> Series: - """Calculate the perimeter wall length the joined structure. + """Calculate the perimeter wall length of the joined structure. Parameters ---------- @@ -220,6 +220,9 @@ def perimeter_wall( graph : Graph | None, optional Graph encoding Queen contiguity of ``geometry``. If ``None`` Queen contiguity is built on the fly. + buffer: float + Buffer value for the geometry. It can be used + to account for topological problems. Returns ------- @@ -276,7 +279,7 @@ def perimeter_wall( blocks = geometry.drop(isolates) component_perimeter = ( blocks[[blocks.geometry.name]] - .set_geometry(blocks.buffer(0.01)) # type: ignore + .set_geometry(blocks.buffer(buffer)) # type: ignore .dissolve(by=graph.component_labels.drop(isolates)) .exterior.length ) diff --git a/momepy/functional/_intensity.py b/momepy/functional/_intensity.py index 3ce55584..bed623ce 100644 --- a/momepy/functional/_intensity.py +++ b/momepy/functional/_intensity.py @@ -6,7 +6,9 @@ __all__ = ["courtyards"] -def courtyards(geometry: GeoDataFrame | GeoSeries, graph: Graph) -> Series: +def courtyards( + geometry: GeoDataFrame | GeoSeries, graph: Graph, buffer: float = 0.01 +) -> Series: """Calculate the number of courtyards within the joined structure. Adapted from :cite:`schirmer2015`. @@ -18,6 +20,9 @@ def courtyards(geometry: GeoDataFrame | GeoSeries, graph: Graph) -> Series: graph : libpysal.graph.Graph A spatial weights matrix for the geodataframe, it is used to denote adjacent buildings. + buffer: float + Buffer value for the geometry. It can be used + to account for topological problems. Returns ------- @@ -60,7 +65,7 @@ def courtyards(geometry: GeoDataFrame | GeoSeries, graph: Graph) -> Series: def _calculate_courtyards(group): """helper function to carry out the per group calculations""" return shapely.get_num_interior_rings( - shapely.union_all(shapely.buffer(group.values, 0.01)) + shapely.union_all(shapely.buffer(group.values, buffer)) ) # calculate per group courtyards diff --git a/momepy/functional/tests/test_dimension.py b/momepy/functional/tests/test_dimension.py index da91ebd0..5f7fe063 100644 --- a/momepy/functional/tests/test_dimension.py +++ b/momepy/functional/tests/test_dimension.py @@ -93,6 +93,17 @@ def test_perimeter_wall(self): pd.testing.assert_series_equal(result, result_given_graph) assert result[0] == pytest.approx(137.210, rel=1e-3) + def test_perimeter_wall_buffer(self): + buildings = self.df_buildings.copy() + buildings["geometry"] = buildings.simplify(0.10) + adj = Graph.build_contiguity(self.df_buildings) + new_perimeter = mm.perimeter_wall(buildings, adj) + old_perimeter = mm.perimeter_wall(self.df_buildings, adj) + assert (new_perimeter.values != old_perimeter.values).any() + + result = mm.perimeter_wall(buildings, adj, buffer=0.25) + assert result[0] == pytest.approx(137.210, rel=1e-3) + @pytest.mark.skipif(not GPD_013, reason="no attribute 'segmentize'") def test_street_profile(self): sp = mm.street_profile( diff --git a/momepy/functional/tests/test_intensity.py b/momepy/functional/tests/test_intensity.py index 94cfffb6..6db319df 100644 --- a/momepy/functional/tests/test_intensity.py +++ b/momepy/functional/tests/test_intensity.py @@ -44,6 +44,17 @@ def test_courtyards(self): expected = {"mean": 0.6805555555555556, "sum": 98, "min": 0, "max": 1} assert_result(courtyards, expected, self.df_buildings) + def test_courtyards_buffer(self): + buildings = self.df_buildings.copy() + buildings["geometry"] = buildings.simplify(0.10) + new_courtyards = mm.courtyards(buildings, self.buildings_graph) + old_courtyards = mm.courtyards(self.df_buildings, self.buildings_graph) + assert (new_courtyards.values != old_courtyards.values).any() + + courtyards = mm.courtyards(buildings, self.buildings_graph, buffer=0.25) + expected = {"mean": 0.6805555555555556, "sum": 98, "min": 0, "max": 1} + assert_result(courtyards, expected, self.df_buildings) + def test_node_density(self): g = mm.gdf_to_nx(self.df_streets, integer_labels=True) g = mm.node_degree(g)