fitodic / centerline

Calculate the polygon's centerline
https://centerline.readthedocs.io
MIT License
266 stars 55 forks source link

Error when MultiLineString degenerates into LineString #14

Closed mxwell closed 6 years ago

mxwell commented 6 years ago

For some inputs _create_centerline() returns LineString, which breaks MultiLineString constructor. Not sure, if that is so by design.

Anyway, here is an example:

from centerline import Centerline
from shapely.geometry import Polygon

p = Polygon([(614, 1005), (614, 1010), (617, 1010), (617, 1009), (620, 1009), (620, 1010), (621, 1010), (621, 1011), (624, 1011), (624, 1010), (625, 1010), (625, 1006), (624, 1006), (624, 1005), (619, 1005), (619, 1006), (618, 1006), (618, 1005), (614, 1005)])

c = Centerline(p, 10)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-f6be4fb7d387> in <module>()
----> 1 c = Centerline(p, 10)

~/.local/lib/python3.6/site-packages/centerline/main.py in __init__(self, input_geom, interpolation_dist, **attributes)
     61             setattr(self, key, attributes.get(key))
     62 
---> 63         super(Centerline, self).__init__(lines=self._create_centerline())
     64 
     65     def _create_centerline(self):

/opt/conda/envs/pytorch-py3.6/lib/python3.6/site-packages/shapely/geometry/multilinestring.py in __init__(self, lines)
     50             pass
     51         else:
---> 52             self._geom, self._ndim = geos_multilinestring_from_py(lines)
     53 
     54     def shape_factory(self, *args):

/opt/conda/envs/pytorch-py3.6/lib/python3.6/site-packages/shapely/geometry/multilinestring.py in geos_multilinestring_from_py(ob)
    117 
    118     obs = getattr(ob, 'geoms', ob)
--> 119     L = len(obs)
    120     assert L >= 1
    121     exemplar = obs[0]

TypeError: object of type 'LineString' has no len()

If you consider this an issue, I can provide PR.

Thanks.

fitodic commented 6 years ago

@mxwell Thanks for pointing that out! 👍

I'm just curious, considering the polygon's structure, is there a reason for using such a large border density value (i.e. 10)? Have you tried reducing this value?

I'm asking because this library uses the scipy's Voronoi algorithm for creating centerlines. Depending on the polygon's structure and the border density parameter set by the user, the Voronoi algorithm might not be able to produce enough vertices that lie completely inside the polygon (i.e. do not intersect the polygon's boundary). In that case, the output centerline can be reduced to a single line (which you were able to reproduce) or even a single point (an edge case that's already discarded).

This brings us to the problem at hand. Is this the result you were looking for? screenshot_20180127_164838

mxwell commented 6 years ago

Sorry for late reply.

Regarding my use case, this particular polygon is much smaller than others in the input and probably should be discarded by some kind of filter. But if I failed to filter it out or tune the density appropriately, then the single line will do.

My point is that the program could either (a) produce warning about too large density, leading to degeneration of center line, or (b) raise an exception with the same message. Currently, the error initially looks like a bug in shapely, which doesn't help during troubleshooting.

fitodic commented 6 years ago

@mxwell I agree. I believe a RuntimeError with a description would suffice, but the create_centerlines function should simply log it and continue processing the data. Would you like to send a PR?

P.S. The density of the Voronoi grid is inversely proportional to the border density value.