Skip to content

Commit

Permalink
[Done] Disallow linearrings with 3 coordinates in GEOS 3.10 (#378)
Browse files Browse the repository at this point in the history
  • Loading branch information
caspervdw committed Sep 28, 2021
1 parent ea76ac9 commit 26ad6d5
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 19 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ Version 0.11 (unreleased)

**API Changes**

* ...

* When constructing a linearring through ``pygeos.linearrings`` or a polygon through
``pygeos.polygons`` now a ``ValueError`` is raised (instead of a ``GEOSException``)
if the ring contains less than 4 coordinates including ring closure (#378).

**Added GEOS functions**

Expand Down
12 changes: 6 additions & 6 deletions pygeos/tests/test_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,12 @@ def test_linearrings_invalid_shape_scalar():
)
def test_linearrings_invalid_shape(shape):
coords = np.ones(shape)
with pytest.raises(pygeos.GEOSException):
with pytest.raises(ValueError):
pygeos.linearrings(coords)

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


Expand Down Expand Up @@ -245,12 +245,12 @@ def test_polygons_not_enough_points_in_shell_scalar():
)
def test_polygons_not_enough_points_in_shell(shape):
coords = np.ones(shape)
with pytest.raises(pygeos.GEOSException):
with pytest.raises(ValueError):
pygeos.polygons(coords)

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


Expand All @@ -275,12 +275,12 @@ def test_polygons_not_enough_points_in_holes_scalar():
)
def test_polygons_not_enough_points_in_holes(shape):
coords = np.ones(shape)
with pytest.raises(pygeos.GEOSException):
with pytest.raises(ValueError):
pygeos.polygons(np.ones((1, 4, 2)), coords)

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


Expand Down
5 changes: 5 additions & 0 deletions src/geos.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ enum {
PGERR_GEOMETRY_TYPE,
PGERR_MULTIPOINT_WITH_POINT_EMPTY,
PGERR_EMPTY_GEOMETRY,
PGERR_LINEARRING_NCOORDS,
PGWARN_INVALID_WKB, // raise the GEOS WKB error as a warning instead of exception
PGWARN_INVALID_WKT // raise the GEOS WKB error as a warning instead of exception
};
Expand Down Expand Up @@ -87,6 +88,10 @@ enum {
case PGERR_EMPTY_GEOMETRY: \
PyErr_SetString(PyExc_ValueError, "One of the Geometry inputs is empty."); \
break; \
case PGERR_LINEARRING_NCOORDS: \
PyErr_SetString(PyExc_ValueError, \
"A linearring requires at least 4 coordinates."); \
break; \
case PGWARN_INVALID_WKB: \
PyErr_WarnFormat(PyExc_Warning, 0, \
"Invalid WKB: geometry is returned as None. %s", last_error); \
Expand Down
28 changes: 17 additions & 11 deletions src/ufuncs.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@

// Fail if inputs output multiple times on the same place in memory. That would
// lead to segfaults as the same GEOSGeometry would be 'owned' by multiple PyObjects.
#define CHECK_NO_INPLACE_OUTPUT(N) \
if ((steps[N] == 0) && (dimensions[0] > 1)) { \
PyErr_Format(PyExc_NotImplementedError, \
"Zero-strided output detected. Ufunc mode with args[0]=%p, " \
"args[N]=%p, steps[0]=%ld, steps[N]=%ld, dimensions[0]=%ld.", \
args[0], args[N], steps[0], steps[N], dimensions[0]); \
return; \
#define CHECK_NO_INPLACE_OUTPUT(N) \
if ((steps[N] == 0) && (dimensions[0] > 1)) { \
PyErr_Format(PyExc_NotImplementedError, \
"Zero-strided output detected. Ufunc mode with args[0]=%p, " \
"args[N]=%p, steps[0]=%ld, steps[N]=%ld, dimensions[0]=%ld.", \
args[0], args[N], steps[0], steps[N], dimensions[0]); \
return; \
}

#define CHECK_ALLOC(ARR) \
Expand Down Expand Up @@ -1947,7 +1947,7 @@ static PyUFuncGenericFunction polygonize_full_funcs[1] = {&polygonize_full_func}

static char shortest_line_dtypes[3] = {NPY_OBJECT, NPY_OBJECT, NPY_OBJECT};
static void shortest_line_func(char** args, npy_intp* dimensions, npy_intp* steps,
void* data) {
void* data) {
GEOSGeometry* in1 = NULL;
GEOSGeometry* in2 = NULL;
GEOSPreparedGeometry* in1_prepared = NULL;
Expand Down Expand Up @@ -2196,6 +2196,12 @@ static void linearrings_func(char** args, npy_intp* dimensions, npy_intp* steps,
break;
}
}
/* the minimum number of coordinates in a linearring is 4 */
if (n_c1 + ring_closure < 4) {
errstate = PGERR_LINEARRING_NCOORDS;
destroy_geom_arr(ctx, geom_arr, i - 1);
goto finish;
}
/* fill the coordinate sequence */
coord_seq = GEOSCoordSeq_create_r(ctx, n_c1 + ring_closure, n_c2);
if (coord_seq == NULL) {
Expand Down Expand Up @@ -2932,13 +2938,13 @@ static void to_wkt_func(char** args, npy_intp* dimensions, npy_intp* steps, void
Py_INCREF(Py_None);
*out = Py_None;
} else {
// Before GEOS 3.9.0, there was as segfault on e.g. MULTIPOINT (1 1, EMPTY)
#if !GEOS_SINCE_3_9_0
// Before GEOS 3.9.0, there was as segfault on e.g. MULTIPOINT (1 1, EMPTY)
#if !GEOS_SINCE_3_9_0
errstate = check_to_wkt_compatible(ctx, in1);
if (errstate != PGERR_SUCCESS) {
goto finish;
}
#endif
#endif
wkt = GEOSWKTWriter_write_r(ctx, writer, in1);
if (wkt == NULL) {
errstate = PGERR_GEOS_EXCEPTION;
Expand Down

0 comments on commit 26ad6d5

Please sign in to comment.