The documentation on MultiPoint
says that I can pass to it a sequence of Point
s. But it doesn't warn us against passing empty Point
s. Interestingly enough, if I pass an empty Point
as the first element, it will throw an AssertionError
:
>>> from shapely.geometry import Point, MultiPoint
>>> MultiPoint([Point()])
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-30-8942eaebd9a2> in <module>
----> 1 MultiPoint([Point()])
~\Miniconda3\lib\site-packages\shapely\geometry\multipoint.py in __init__(self, points)
56 pass
57 else:
---> 58 self._geom, self._ndim = geos_multipoint_from_py(points)
59
60 def shape_factory(self, *args):
~\Miniconda3\lib\site-packages\shapely\geometry\multipoint.py in geos_multipoint_from_py(ob)
162 except TypeError:
163 n = ob[0]._ndim
--> 164 assert n == 2 or n == 3
165
166 # Array of pointers to point geometries
AssertionError:
md5-7f58c60ea833e3f4b523f7bc4e2c6cf6
```none
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-31-08a0b83c889a> in <module>
----> 1 MultiPoint([Point(), Point(1, 2)])
~\Miniconda3\lib\site-packages\shapely\geometry\multipoint.py in __init__(self, points)
56 pass
57 else:
---> 58 self._geom, self._ndim = geos_multipoint_from_py(points)
59
60 def shape_factory(self, *args):
~\Miniconda3\lib\site-packages\shapely\geometry\multipoint.py in geos_multipoint_from_py(ob)
162 except TypeError:
163 n = ob[0]._ndim
--> 164 assert n == 2 or n == 3
165
166 # Array of pointers to point geometries
AssertionError:
md5-b0713eaf20b3dbaf812d2b96e9059121
But the resulting object will throw an exception after trying to get its wkt:
md5-6cb5bf2f7d347dc7bcb3eb576c7452e1
```none
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
<ipython-input-38-c645b8b312e6> in <module>
----> 1 MultiPoint([Point(1, 2), Point()]).wkt
~\Miniconda3\lib\site-packages\shapely\geometry\base.py in wkt(self, **kw)
365 def wkt(self, **kw):
366 """WKT representation of the geometry"""
--> 367 return WKTWriter(lgeos, **kw).write(self)
368
369 @property
~\Miniconda3\lib\site-packages\shapely\geos.py in write(self, geom)
361 if geom is None or geom._geom is None:
362 raise ValueError("Null geometry supports no operations")
--> 363 result = self._lgeos.GEOSWKTWriter_write(self._writer, geom._geom)
364 text = string_at(result)
365 lgeos.GEOSFree(result)
OSError: exception: access violation reading 0x0000000000000000
I assume there needs to be a guard against a presence of empty points in the passed sequence, and the docs should be updated too.
Shapely version: 1.6.4.post1, installed from conda.
I've just discovered a similar issue with MultiLineString
. MultiPolygon
works fine though.
>>> from shapely.geometry import LineString, MultiLineString
>>> MultiLineString([LineString(), LineString([(0, 0), (1, 1)])])
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-55-0aca5a523169> in <module>
----> 1 MultiLineString([LineString(), LineString([(0, 0), (1, 1)])])
~\Miniconda3\lib\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):
~\Miniconda3\lib\site-packages\shapely\geometry\multilinestring.py in geos_multilinestring_from_py(ob)
125 N = exemplar._ndim
126 if N not in (2, 3):
--> 127 raise ValueError("Invalid coordinate dimensionality")
128
129 # Array of pointers to point geometries
ValueError: Invalid coordinate dimensionality
md5-8513a28055f286357bb8bf2e099ecb29
```none
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
<ipython-input-59-255eed31faa5> in <module>
----> 1 MultiLineString([LineString([(0, 0), (1, 1)]), LineString()]).wkt
~\Miniconda3\lib\site-packages\shapely\geometry\base.py in wkt(self, **kw)
365 def wkt(self, **kw):
366 """WKT representation of the geometry"""
--> 367 return WKTWriter(lgeos, **kw).write(self)
368
369 @property
~\Miniconda3\lib\site-packages\shapely\geos.py in write(self, geom)
361 if geom is None or geom._geom is None:
362 raise ValueError("Null geometry supports no operations")
--> 363 result = self._lgeos.GEOSWKTWriter_write(self._writer, geom._geom)
364 text = string_at(result)
365 lgeos.GEOSFree(result)
OSError: exception: access violation reading 0x0000000000000008
Not sure if I should open another issue for this.
@LostFan123 can you check with shapely 1.7.0? We're not supporting 1.6.x any longer.
@sgillies 1.7.0 has the same problem. The only difference I'm observing is that accessing .wkt
crashes Python this time instead of throwing the OSError
.
@sgillies, should Shapely raise an error when the user tries to create a Multi*
geometry with an empty component, since asking for the WKT of such a geometry crashes the interpreter? That seems like something we'd want to guard against at all costs. We could also remove empty parts while creating the geometry and print a warning (or not). I would appreciate your thoughts on ideal behavior.
The failing WKT might be a GEOS bug.
@jorisvandenbossche, it's a segfault so I bet it's coming from GEOS and is worth reporting back there. I still think it warrants some preventative action from Shapely, though.
I reported it in the meantime: https://github.com/libgeos/geos/issues/305
I still think it warrants some preventative action from Shapely, though.
For sure! The question might be if Shapely should raise an error upon creation, or when converting to WKT.
It's a good question, and I wasn't sure of the right fix myself. I guess stopping users from making geometries with empty parts might be a little heavy-handed, but my reasoning is that you can't use Shapely to create a multipart with an empty component using WKT, WKB, or GeoJSON (through the shape
adapters) as those inputs are invalid, so this felt like closing the loop there, as it were - forcing consistency across all geometry creation interfaces.
I'd like to avoid too much validation, because it's expensive, but in this case I think filtering out empty parts and warning that this has been done is a good approach. Warn instead of log, because user code could handle the warning and change its behavior if desired.