Skip to content

Commit

Permalink
[Done] Fix creation error handling and release GIL (#310)
Browse files Browse the repository at this point in the history
  • Loading branch information
caspervdw committed Mar 17, 2021
1 parent 03c2209 commit 46fb76f
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 81 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Version 0.10 (unreleased)

* Addition of ``nearest`` and ``nearest_all`` functions to ``STRtree`` for
GEOS >= 3.6 to find the nearest neighbors (#272).
* Released GIL for ``points``, ``linestrings``, ``linearrings``, and
``polygons`` (without holes) (#310).
* Updated ``box`` ufunc to use internal C function for creating polygon
(about 2x faster) and added ``ccw`` parameter to create polygon in
counterclockwise (default) or clockwise direction (#308).
Expand All @@ -20,6 +22,8 @@ Version 0.10 (unreleased)
* Deprecated ``VALID_PREDICATES`` set from ``pygeos.strtree`` package; these can be constructed
in downstream libraries using the ``pygeos.strtree.BinaryPredicate`` enum.
This will be removed in a future release.
* ``points``, ``linestrings``, ``linearrings``, and ``polygons`` now return a ``GEOSException``
instead of a ``ValueError`` for invalid input (#310).

**Added GEOS functions**

Expand All @@ -30,6 +34,8 @@ Version 0.10 (unreleased)
**Bug fixes**

* Fixed portability issue for ARM architecture (#293)
* Fixed segfault in ``linearrings`` and ``box`` when constructing a geometry with nan
coordinates (#310).

**Acknowledgments**

Expand Down
22 changes: 13 additions & 9 deletions pygeos/creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,19 @@
]


def _wrap_construct_ufunc(func, coords, y=None, z=None):
def _wrap_construct_ufunc(func, coords, y=None, z=None, **kwargs):
if y is None:
return func(coords)
return func(coords, **kwargs)
x = coords
if z is None:
coords = np.broadcast_arrays(x, y)
else:
coords = np.broadcast_arrays(x, y, z)
return func(np.stack(coords, axis=-1))
return func(np.stack(coords, axis=-1), **kwargs)


def points(coords, y=None, z=None):
@multithreading_enabled
def points(coords, y=None, z=None, **kwargs):
"""Create an array of points.
Note that GEOS >=3.10 automatically converts POINT (nan nan) to
Expand All @@ -43,10 +44,11 @@ def points(coords, y=None, z=None):
y : array_like
z : array_like
"""
return _wrap_construct_ufunc(lib.points, coords, y, z)
return _wrap_construct_ufunc(lib.points, coords, y, z, **kwargs)


def linestrings(coords, y=None, z=None):
@multithreading_enabled
def linestrings(coords, y=None, z=None, **kwargs):
"""Create an array of linestrings.
Parameters
Expand All @@ -57,10 +59,11 @@ def linestrings(coords, y=None, z=None):
y : array_like
z : array_like
"""
return _wrap_construct_ufunc(lib.linestrings, coords, y, z)
return _wrap_construct_ufunc(lib.linestrings, coords, y, z, **kwargs)


def linearrings(coords, y=None, z=None):
@multithreading_enabled
def linearrings(coords, y=None, z=None, **kwargs):
"""Create an array of linearrings.
If the provided coords do not constitute a closed linestring, the first
Expand All @@ -74,7 +77,8 @@ def linearrings(coords, y=None, z=None):
y : array_like
z : array_like
"""
return _wrap_construct_ufunc(lib.linearrings, coords, y, z)
return _wrap_construct_ufunc(lib.linearrings, coords, y, z, **kwargs)


@multithreading_enabled
def polygons(shells, holes=None):
Expand Down
44 changes: 33 additions & 11 deletions pygeos/test/test_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,21 @@ def test_linestrings_from_xyz():
assert str(actual) == "LINESTRING Z (0 2 0, 1 3 0)"


def test_linestrings_invalid_shape_scalar():
with pytest.raises(ValueError):
pygeos.linestrings((1, 1))


@pytest.mark.parametrize(
"shape",
[
(2, 1, 2), # 2 linestrings of 1 2D point
(1, 1, 2), # 1 linestring of 1 2D point
(1, 2), # 1 linestring of 1 2D point (scalar)
(2,), # 1 2D point (scalar)
],
)
def test_linestrings_invalid_shape(shape):
with pytest.raises(ValueError):
with pytest.raises(pygeos.GEOSException):
pygeos.linestrings(np.ones(shape))


Expand All @@ -99,6 +103,11 @@ def test_linearrings_unclosed():
assert str(actual) == "LINEARRING (1 0, 1 1, 0 1, 0 0, 1 0)"


def test_linearrings_invalid_shape_scalar():
with pytest.raises(ValueError):
pygeos.linearrings((1, 1))


@pytest.mark.parametrize(
"shape",
[
Expand All @@ -111,20 +120,25 @@ def test_linearrings_unclosed():
(2, 3, 2), # 2 linearrings of 3 2D points
(1, 3, 2), # 1 linearring of 3 2D points
(3, 2), # 1 linearring of 3 2D points (scalar)
(2,), # 1 2D point (scalar)
],
)
def test_linearrings_invalid_shape(shape):
coords = np.ones(shape)
with pytest.raises(ValueError):
with pytest.raises(pygeos.GEOSException):
pygeos.linearrings(coords)

# make sure the first coordinate != second coordinate
coords[..., 1] += 1
with pytest.raises(ValueError):
with pytest.raises(pygeos.GEOSException):
pygeos.linearrings(coords)


def test_linearrings_all_nan():
coords = np.full((4, 2), np.nan)
with pytest.raises(pygeos.GEOSException):
pygeos.linearrings(coords)


def test_polygon_from_linearring():
actual = pygeos.polygons(pygeos.linearrings(box_tpl(0, 0, 1, 1)))
assert str(actual) == "POLYGON ((1 0, 1 1, 0 1, 0 0, 1 0))"
Expand Down Expand Up @@ -175,6 +189,11 @@ def test_2_polygons_with_different_holes():
assert pygeos.area(actual).tolist() == [96.0, 24.0]


def test_polygons_not_enough_points_in_shell_scalar():
with pytest.raises(ValueError):
pygeos.polygons((1, 1))


@pytest.mark.parametrize(
"shape",
[
Expand All @@ -187,20 +206,24 @@ def test_2_polygons_with_different_holes():
(2, 3, 2), # 2 linearrings of 3 2D points
(1, 3, 2), # 1 linearring of 3 2D points
(3, 2), # 1 linearring of 3 2D points (scalar)
(2,), # 1 2D point (scalar)
],
)
def test_polygons_not_enough_points_in_shell(shape):
coords = np.ones(shape)
with pytest.raises(ValueError):
with pytest.raises(pygeos.GEOSException):
pygeos.polygons(coords)

# make sure the first coordinate != second coordinate
coords[..., 1] += 1
with pytest.raises(ValueError):
with pytest.raises(pygeos.GEOSException):
pygeos.polygons(coords)


def test_polygons_not_enough_points_in_holes_scalar():
with pytest.raises(ValueError):
pygeos.polygons(np.ones((1, 4, 2)), (1, 1))


@pytest.mark.parametrize(
"shape",
[
Expand All @@ -213,17 +236,16 @@ def test_polygons_not_enough_points_in_shell(shape):
(2, 3, 2), # 2 linearrings of 3 2D points
(1, 3, 2), # 1 linearring of 3 2D points
(3, 2), # 1 linearring of 3 2D points (scalar)
(2,), # 1 2D point (scalar)
],
)
def test_polygons_not_enough_points_in_holes(shape):
coords = np.ones(shape)
with pytest.raises(ValueError):
with pytest.raises(pygeos.GEOSException):
pygeos.polygons(np.ones((1, 4, 2)), coords)

# make sure the first coordinate != second coordinate
coords[..., 1] += 1
with pytest.raises(ValueError):
with pytest.raises(pygeos.GEOSException):
pygeos.polygons(np.ones((1, 4, 2)), coords)


Expand Down

0 comments on commit 46fb76f

Please sign in to comment.