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

Tess simplification #595

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
18 changes: 16 additions & 2 deletions momepy/functional/_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
GPD_GE_013 = Version(gpd.__version__) >= Version("0.13.0")
GPD_GE_10 = Version(gpd.__version__) >= Version("1.0dev")
LPS_GE_411 = Version(libpysal.__version__) >= Version("4.11.dev")
SHPLY_GE_250 = Version(shapely.__version__) >= Version("2.5.0dev")

__all__ = [
"morphological_tessellation",
Expand Down Expand Up @@ -128,6 +129,7 @@
shrink: float = 0.4,
segment: float = 0.5,
threshold: float = 0.05,
simplify: bool = False,
n_jobs: int = -1,
) -> GeoDataFrame:
"""Generate enclosed tessellation
Expand Down Expand Up @@ -175,6 +177,9 @@
inlude it in the tessellation of that enclosure. Resolves sliver geometry
issues. If None, the check is skipped and all intersecting buildings are
considered. By default 0.05
simplify: bool, optional
Whether to attempt to simplify the resulting tesselation boundaries with
``shapely.coverage_simplify``. By default False.
n_jobs : int, optional
The number of jobs to run in parallel. -1 means using all available cores.
By default -1
Expand Down Expand Up @@ -224,6 +229,9 @@
126 POLYGON ((1603528.593 6464221.033, 1603527.796... 0
"""

if simplify and not SHPLY_GE_250:
raise ImportError("Coverage simplification requires shapely 2.5 or higher.")

Check warning on line 233 in momepy/functional/_elements.py

View check run for this annotation

Codecov / codecov/patch

momepy/functional/_elements.py#L233

Added line #L233 was not covered by tests

if isinstance(geometry.index, MultiIndex):
raise ValueError(
"MultiIndex is not supported in `momepy.enclosed_tessellation`."
Expand Down Expand Up @@ -265,7 +273,8 @@

# generate tessellation in parallel
new = Parallel(n_jobs=n_jobs)(
delayed(_tess)(*t, threshold, shrink, segment, index_name) for t in tuples
delayed(_tess)(*t, threshold, shrink, segment, index_name, simplify)
for t in tuples
)

new_df = pd.concat(new, axis=0)
Expand Down Expand Up @@ -297,7 +306,7 @@
return pd.concat([new_df, singles.drop(columns="position"), clean_blocks])


def _tess(ix, poly, blg, threshold, shrink, segment, enclosure_id):
def _tess(ix, poly, blg, threshold, shrink, segment, enclosure_id, to_simplify):
"""Generate tessellation for a single enclosure. Helper for enclosed_tessellation"""
# check if threshold is set and filter buildings based on the threshold
if threshold:
Expand All @@ -315,6 +324,11 @@
return_input=False,
as_gdf=True,
)
if to_simplify:
simpl_collection = shapely.coverage_simplify(

Check warning on line 328 in momepy/functional/_elements.py

View check run for this annotation

Codecov / codecov/patch

momepy/functional/_elements.py#L328

Added line #L328 was not covered by tests
tess.geometry, tolerance=1e-1, simplify_boundary=False
)
tess.geometry = gpd.GeoSeries(simpl_collection.geoms).values

Check warning on line 331 in momepy/functional/_elements.py

View check run for this annotation

Codecov / codecov/patch

momepy/functional/_elements.py#L331

Added line #L331 was not covered by tests
tess[enclosure_id] = ix
return tess

Expand Down
49 changes: 47 additions & 2 deletions momepy/functional/tests/test_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import numpy as np
import pandas as pd
import pytest
import shapely
from geopandas.testing import assert_geodataframe_equal
from packaging.version import Version
from pandas.testing import assert_index_equal, assert_series_equal
Expand All @@ -13,6 +14,7 @@

GPD_GE_013 = Version(gpd.__version__) >= Version("0.13.0")
LPS_GE_411 = Version(libpysal.__version__) >= Version("4.11.dev")
SHPLY_GE_250 = Version(shapely.__version__) >= Version("2.5.0dev")


class TestElements:
Expand Down Expand Up @@ -330,6 +332,49 @@
else:
assert len(blocks.sindex.query_bulk(blocks.geometry, "overlaps")[0]) == 0

@pytest.mark.skipif(not SHPLY_GE_250, reason="coverage_simplify required")
def test_simplified_tesselations(self):
n_workers = -1
tessellations = mm.enclosed_tessellation(

Check warning on line 338 in momepy/functional/tests/test_elements.py

View check run for this annotation

Codecov / codecov/patch

momepy/functional/tests/test_elements.py#L337-L338

Added lines #L337 - L338 were not covered by tests
self.df_buildings, self.enclosures.geometry, n_jobs=n_workers
)
simplified_tessellations = mm.enclosed_tessellation(

Check warning on line 341 in momepy/functional/tests/test_elements.py

View check run for this annotation

Codecov / codecov/patch

momepy/functional/tests/test_elements.py#L341

Added line #L341 was not covered by tests
self.df_buildings, self.enclosures.geometry, simplify=True, n_jobs=n_workers
)
## empty enclosures should be unmodified
assert_geodataframe_equal(

Check warning on line 345 in momepy/functional/tests/test_elements.py

View check run for this annotation

Codecov / codecov/patch

momepy/functional/tests/test_elements.py#L345

Added line #L345 was not covered by tests
tessellations[tessellations.index < 0],
simplified_tessellations[simplified_tessellations.index < 0],
)
## simplification should result in less total points
orig_points = shapely.get_coordinates(

Check warning on line 350 in momepy/functional/tests/test_elements.py

View check run for this annotation

Codecov / codecov/patch

momepy/functional/tests/test_elements.py#L350

Added line #L350 was not covered by tests
tessellations[tessellations.index >= 0].geometry
).shape
simpl_points = shapely.get_coordinates(

Check warning on line 353 in momepy/functional/tests/test_elements.py

View check run for this annotation

Codecov / codecov/patch

momepy/functional/tests/test_elements.py#L353

Added line #L353 was not covered by tests
simplified_tessellations[simplified_tessellations.index >= 0].geometry
).shape
assert orig_points > simpl_points

Check warning on line 356 in momepy/functional/tests/test_elements.py

View check run for this annotation

Codecov / codecov/patch

momepy/functional/tests/test_elements.py#L356

Added line #L356 was not covered by tests

## simplification should not modify the external borders of tesselation cells\
orig_grouper = tessellations.groupby("enclosure_index")
simpl_grouper = simplified_tessellations.groupby("enclosure_index")
for idx in np.union1d(

Check warning on line 361 in momepy/functional/tests/test_elements.py

View check run for this annotation

Codecov / codecov/patch

momepy/functional/tests/test_elements.py#L359-L361

Added lines #L359 - L361 were not covered by tests
tessellations["enclosure_index"].unique(),
simplified_tessellations["enclosure_index"].unique(),
):
orig_group = orig_grouper.get_group(idx).dissolve().boundary
enclosure = self.enclosures.loc[[idx]].dissolve().boundary

Check warning on line 366 in momepy/functional/tests/test_elements.py

View check run for this annotation

Codecov / codecov/patch

momepy/functional/tests/test_elements.py#L365-L366

Added lines #L365 - L366 were not covered by tests

simpl_group = simpl_grouper.get_group(idx).dissolve().boundary

Check warning on line 368 in momepy/functional/tests/test_elements.py

View check run for this annotation

Codecov / codecov/patch

momepy/functional/tests/test_elements.py#L368

Added line #L368 was not covered by tests

## simplified is not different to enclosure
## this needs to be redone
assert np.isclose(simpl_group.difference(enclosure).area, 0)

Check warning on line 372 in momepy/functional/tests/test_elements.py

View check run for this annotation

Codecov / codecov/patch

momepy/functional/tests/test_elements.py#L372

Added line #L372 was not covered by tests

# simplified is not different to original tess
## this needs to be redone
assert np.isclose(simpl_group.difference(orig_group).area, 0)

Check warning on line 376 in momepy/functional/tests/test_elements.py

View check run for this annotation

Codecov / codecov/patch

momepy/functional/tests/test_elements.py#L376

Added line #L376 was not covered by tests

def test_multi_index(self):
buildings = self.df_buildings.set_index(["uID", "uID"])
with pytest.raises(
Expand Down Expand Up @@ -375,13 +420,13 @@
new_blg.loc[22, "geometry"] = new_blg.loc[22, "geometry"].buffer(20)
new_tess = mm.enclosed_tessellation(new_blg, self.enclosures.geometry, n_jobs=1)

# assert that buildings 1 and 22 intersect the same enclosure
##assert that buildings 1 and 22 intersect the same enclosure
inp, res = self.enclosures.sindex.query(
new_blg.geometry, predicate="intersects"
)
assert np.isclose(new_blg.iloc[inp[res == 8]].index.values, [1, 22]).all()

# assert that there is a tessellation for building 1
### assert that there is a tessellation for building 1
assert 1 in new_tess.index


Expand Down
Loading