Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: either support MultiIndex or raise an error when one is given #622

Merged
merged 9 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 35 additions & 6 deletions momepy/functional/_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from libpysal.cg import voronoi_frames
from libpysal.graph import Graph
from packaging.version import Version
from pandas import Series
from pandas import MultiIndex, Series

GPD_GE_013 = Version(gpd.__version__) >= Version("0.13.0")
GPD_GE_10 = Version(gpd.__version__) >= Version("1.0dev")
Expand Down Expand Up @@ -103,6 +103,10 @@ def morphological_tessellation(
4 POLYGON ((1603084.231 6464104.386, 1603083.773...

"""

if isinstance(geometry.index, MultiIndex):
raise ValueError("MultiIndex is not supported for tessellation.")
u3ks marked this conversation as resolved.
Show resolved Hide resolved

if isinstance(clip, GeoSeries | GeoDataFrame):
clip = clip.union_all() if GPD_GE_10 else clip.unary_union

Expand Down Expand Up @@ -218,6 +222,9 @@ def enclosed_tessellation(
126 POLYGON ((1603528.593 6464221.033, 1603527.796... 0
"""

if isinstance(geometry.index, MultiIndex):
raise ValueError("MultiIndex is not supported for tessellation.")
u3ks marked this conversation as resolved.
Show resolved Hide resolved

# convert to GeoDataFrame and add position (we will need it later)
enclosures = enclosures.geometry.to_frame()
enclosures["position"] = range(len(enclosures))
Expand Down Expand Up @@ -360,6 +367,12 @@ def verify_tessellation(tessellation, geometry):

>>> excluded, multipolygons = momepy.verify_tessellation(tessellation, buildings)
"""

if isinstance(geometry.index, MultiIndex) or isinstance(
tessellation.index, MultiIndex
):
raise ValueError("MultiIndex is not supported for tessellation.")
u3ks marked this conversation as resolved.
Show resolved Hide resolved

# check against input layer
ids_original = geometry.index
ids_generated = tessellation.index
Expand Down Expand Up @@ -397,7 +410,7 @@ def get_nearest_street(
buildings: GeoSeries | GeoDataFrame,
streets: GeoSeries | GeoDataFrame,
max_distance: float | None = None,
) -> np.ndarray:
) -> Series:
martinfleis marked this conversation as resolved.
Show resolved Hide resolved
"""Identify the nearest street for each building.

Parameters
Expand Down Expand Up @@ -447,12 +460,11 @@ def get_nearest_street(
)

if streets.index.dtype == "object":
ids = np.empty(len(buildings), dtype=object)
ids = pd.Series(None, index=buildings.index)
else:
ids = np.empty(len(buildings), dtype=np.float32)
ids[:] = np.nan
ids = pd.Series(np.nan, index=buildings.index)

ids[blg_idx] = streets.index[str_idx]
ids.iloc[blg_idx] = streets.index[str_idx]
return ids


Expand Down Expand Up @@ -522,6 +534,17 @@ def get_nearest_node(
143 22.0
Length: 144, dtype: float64
"""

if (
isinstance(buildings.index, MultiIndex)
or isinstance(nearest_edge.index, MultiIndex)
or isinstance(nodes.index, MultiIndex)
or isinstance(edges.index, MultiIndex)
):
raise ValueError(
"MultiIndex is not supported for calculating the nearest node."
u3ks marked this conversation as resolved.
Show resolved Hide resolved
)

# treat possibly missing edge index
a = np.empty(len(buildings))
na_mask = np.isnan(nearest_edge)
Expand Down Expand Up @@ -606,6 +629,12 @@ def generate_blocks(
>>> tessellation["block_id"] = tessellation_id
"""

if (
isinstance(buildings.index, MultiIndex)
or isinstance(tessellation.index, MultiIndex)
or isinstance(edges.index, MultiIndex)
):
raise ValueError("MultiIndex is not supported for generating blocks.")
u3ks marked this conversation as resolved.
Show resolved Hide resolved
id_name: str = "bID"

# slice the tessellations by the street network
Expand Down
8 changes: 6 additions & 2 deletions momepy/functional/_shape.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from geopandas import GeoDataFrame, GeoSeries
from numpy.typing import NDArray
from packaging.version import Version
from pandas import DataFrame, Series
from pandas import DataFrame, MultiIndex, Series

from momepy.functional import _dimension

Expand Down Expand Up @@ -724,6 +724,10 @@
"momepy.centroid_corner_distance requires geopandas 0.13 or later. "
)

result_index = geometry.index
if isinstance(geometry.index, MultiIndex):
geometry = geometry.reset_index(drop=True)

Check warning on line 729 in momepy/functional/_shape.py

View check run for this annotation

Codecov / codecov/patch

momepy/functional/_shape.py#L729

Added line #L729 was not covered by tests

def _ccd(points: DataFrame, eps: float) -> Series:
centroid = points.values[0, 2:]
pts = points.values[:-1, :2]
Expand All @@ -738,7 +742,7 @@
coords = geometry.exterior.get_coordinates(index_parts=False)
coords[["cent_x", "cent_y"]] = geometry.centroid.get_coordinates(index_parts=False)
ccd = coords.groupby(level=0).apply(_ccd, eps=eps)
ccd.index = geometry.index
ccd.index = result_index
return ccd


Expand Down
30 changes: 29 additions & 1 deletion momepy/functional/tests/test_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ def test_get_nearest_street(self):

streets.index = streets.index.astype(str)
nearest = mm.get_nearest_street(self.df_buildings, streets, 10)
assert (nearest == None).sum() == 137 # noqa: E711
assert pd.isna(nearest).sum() == 137 # noqa: E711

def test_get_nearest_node(self):
nodes, edges = mm.nx_to_gdf(mm.gdf_to_nx(self.df_streets))
Expand Down Expand Up @@ -332,6 +332,34 @@ def test_blocks_inner(self):
else:
assert len(blocks.sindex.query_bulk(blocks.geometry, "overlaps")[0]) == 0

def test_multi_index(self):
buildings = self.df_buildings.set_index(["uID", "uID"])
with pytest.raises(
ValueError, match="MultiIndex is not supported for tessellation."
):
mm.morphological_tessellation(buildings)
with pytest.raises(
ValueError, match="MultiIndex is not supported for tessellation."
):
mm.enclosed_tessellation(buildings, self.enclosures)
with pytest.raises(
ValueError, match="MultiIndex is not supported for tessellation."
):
mm.verify_tessellation(buildings, self.enclosures)

with pytest.raises(
ValueError,
match="MultiIndex is not supported for calculating the nearest node.",
):
mm.get_nearest_node(
buildings, self.enclosures, self.enclosures, self.enclosures
)

with pytest.raises(
ValueError, match="MultiIndex is not supported for generating blocks."
):
mm.generate_blocks(buildings, self.enclosures, self.enclosures)

def test_tess_single_building_edge_case(self):
tessellations = mm.enclosed_tessellation(
self.df_buildings, self.enclosures.geometry, n_jobs=-1
Expand Down
Loading