diff --git a/momepy/functional/_distribution.py b/momepy/functional/_distribution.py index e6ded9d0..e1fa3fe9 100644 --- a/momepy/functional/_distribution.py +++ b/momepy/functional/_distribution.py @@ -3,6 +3,7 @@ import shapely from geopandas import GeoDataFrame, GeoSeries from libpysal.graph import Graph +from numpy.typing import NDArray from packaging.version import Version from pandas import Series from scipy import sparse @@ -16,6 +17,7 @@ "building_adjacency", "neighbors", "street_alignment", + "cell_alignment", ] GPD_GE_013 = Version(gpd.__version__) >= Version("0.13.0") @@ -339,3 +341,38 @@ def street_alignment( Series """ return (building_orientation - street_orientation.loc[street_index].values).abs() + + +def cell_alignment( + left_orientation: NDArray[np.float64] | Series, + right_orientation: NDArray[np.float64] | Series, +) -> Series: + """ + Calculate the difference between cell orientation and the orientation of object. + + .. math:: + \\left|{\\textit{building orientation} - \\textit{cell orientation}}\\right| + + Notes + ----- + ``left_orientation`` and ``right_orientation`` must be aligned or have an index. + + Parameters + ---------- + left_orientation : np.array, pd.Series + The ``np.array``, or `pd.Series`` with orientation of cells. + This can be calculated using :func:`orientation`. + right_orientation : np.array, pd.Series + The ``np.array`` or ``pd.Series`` with orientation of objects. + This can be calculated using :func:`orientation`. + + Returns + ------- + Series + """ + + if not isinstance(left_orientation, Series): + left_orientation = Series(left_orientation) + if not isinstance(right_orientation, Series): + right_orientation = Series(right_orientation) + return (left_orientation - right_orientation).abs() diff --git a/momepy/functional/tests/test_distribution.py b/momepy/functional/tests/test_distribution.py index 162c8f46..f0a990a7 100644 --- a/momepy/functional/tests/test_distribution.py +++ b/momepy/functional/tests/test_distribution.py @@ -122,6 +122,23 @@ def test_street_alignment(self): r = mm.street_alignment(building_orientation, street_orientation, street_index) assert_result(r, expected, self.df_buildings) + def test_cell_alignment(self): + df_buildings = self.df_buildings.reset_index() + df_tessellation = self.df_tessellation.reset_index() + blgori = mm.orientation(df_buildings) + tessori = mm.orientation(df_tessellation) + + align = mm.cell_alignment(blgori, tessori) + align2 = mm.cell_alignment(blgori.values, tessori.values) + align_expected = { + "count": 144, + "mean": 2.604808585700, + "max": 33.201625570390746, + "min": 1.722848278973288e-05, + } + assert_result(align, align_expected, df_buildings) + assert_series_equal(align, align2, check_names=False) + class TestEquality: def setup_method(self): @@ -220,3 +237,17 @@ def test_street_alignment(self): right_network_id="index", ).series assert_series_equal(new, old, check_names=False, check_index=False) + + def test_cell_alignment(self): + df_buildings = self.df_buildings.reset_index() + df_tessellation = self.df_tessellation.reset_index() + df_buildings["orient"] = blgori = mm.orientation(df_buildings) + df_tessellation["orient"] = tessori = mm.orientation(df_tessellation) + + align_new = mm.cell_alignment(blgori, tessori) + + align_old = mm.CellAlignment( + df_buildings, df_tessellation, "orient", "orient", "uID", "uID" + ).series + + assert_series_equal(align_new, align_old, check_names=False, check_dtype=False)