pysal / momepy

Urban Morphology Measuring Toolkit
https://docs.momepy.org
BSD 3-Clause "New" or "Revised" License
496 stars 59 forks source link

fail gracefully when no initial polygons generated – `preprocessing.FaceArtifacts` #664

Closed jGaboardi closed 2 weeks ago

jGaboardi commented 2 weeks ago

This is an edge case, but currently preprocessing.FaceArtifacts() fails rather cryptically when an edge skeleton is passed which polygons can be generated from – see here.

import geopandas, shapely, momepy 

p10 = shapely.Point(1,0)
p20 = shapely.Point(2,0)
p30 = shapely.Point(3,0)
p21 = shapely.Point(2,1)

line1020 = shapely.LineString((p10, p20))
line2030 = shapely.LineString((p20, p30))
line2021 = shapely.LineString((p20, p21))

edges = geopandas.GeoDataFrame(geometry=[line1020, line2030, line2021])

momepy.FaceArtifacts(edges)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[1], line 14
     10 line2021 = shapely.LineString((p20, p21))
     12 edges = geopandas.GeoDataFrame(geometry=[line1020, line2030, line2021])
---> 14 momepy.FaceArtifacts(edges)

File [~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/momepy/preprocessing.py:1637](http://localhost:8888/~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/momepy/preprocessing.py#line=1636), in FaceArtifacts.__init__(self, gdf, index, height_mins, height_maxs, prominence)
   1626 peak_parameters = {
   1627     "height_mins": height_mins,
   1628     "height_maxs": height_maxs,
   1629     "prominence": prominence,
   1630 }
   1631 mylinspace = np.linspace(
   1632     self.polygons["face_artifact_index"].min(),
   1633     self.polygons["face_artifact_index"].max(),
   1634     1000,
   1635 )
-> 1637 self.kde = gaussian_kde(
   1638     self.polygons["face_artifact_index"], bw_method="silverman"
   1639 )
   1640 self.pdf = self.kde.pdf(mylinspace)
   1642 # find peaks

File [~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/scipy/stats/_kde.py:199](http://localhost:8888/~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/scipy/stats/_kde.py#line=198), in gaussian_kde.__init__(self, dataset, bw_method, weights)
    197 self.dataset = atleast_2d(asarray(dataset))
    198 if not self.dataset.size > 1:
--> 199     raise ValueError("`dataset` input should have multiple elements.")
    201 self.d, self.n = self.dataset.shape
    203 if weights is not None:

ValueError: `dataset` input should have multiple elements.

I propose adding a simple check following that step which either raises or warns+returns immediatley if the polygon result data frame is empty.

@martinfleis I think a raise is more appropriate. Thoughts?

martinfleis commented 2 weeks ago

I'd probably warn and return empty when there are no polygons.