import numpy as np
import pytest
from numpy.testing import assert_array_equal
import shapely
from shapely import LinearRing, Polygon
from shapely.testing import assert_geometries_equal
from shapely.tests.common import empty_point, line_string, linear_ring, point, polygon
pnts = shapely.points
lstrs = shapely.linestrings
geom_coll = shapely.geometrycollections
@pytest.mark.parametrize(
"func", [shapely.points, shapely.linestrings, shapely.linearrings]
)
@pytest.mark.parametrize(
"coordinates",
[
np.empty((2,)), # not enough dimensions
np.empty((2, 4, 1)), # too many dimensions
np.empty((2, 4)), # wrong inner dimension size
None,
np.full((2, 2), "foo", dtype=object), # wrong type
],
)
def test_invalid_coordinates(func, coordinates):
with pytest.raises((TypeError, ValueError)):
func(coordinates, indices=[0, 1])
@pytest.mark.parametrize(
"func",
[
shapely.multipoints,
shapely.multilinestrings,
shapely.multipolygons,
shapely.geometrycollections,
],
)
@pytest.mark.parametrize(
"geometries", [np.array([1, 2], dtype=np.intp), None, np.array([[point]]), "hello"]
)
def test_invalid_geometries(func, geometries):
with pytest.raises((TypeError, ValueError)):
func(geometries, indices=[0, 1])
@pytest.mark.parametrize(
"func", [shapely.points, shapely.linestrings, shapely.linearrings]
)
@pytest.mark.parametrize("indices", [[point], " hello", [0, 1], [-1]])
def test_invalid_indices_simple(func, indices):
with pytest.raises((TypeError, ValueError)):
func([[0.2, 0.3]], indices=indices)
non_writeable = np.empty(3, dtype=object)
non_writeable.flags.writeable = False
@pytest.mark.parametrize(
"func", [shapely.points, shapely.linestrings, shapely.geometrycollections]
)
@pytest.mark.parametrize(
"out",
[
[None, None, None], # not an ndarray
np.empty(3), # wrong dtype
non_writeable, # not writeable
np.empty((3, 2), dtype=object), # too many dimensions
np.empty((), dtype=object), # too few dimensions
np.empty((2,), dtype=object), # too small
],
)
def test_invalid_out(func, out):
if func is shapely.points:
x = [[0.2, 0.3], [0.4, 0.5]]
indices = [0, 2]
elif func is shapely.linestrings:
x = [[1, 1], [2, 1], [2, 2], [3, 3], [3, 4], [4, 4]]
indices = [0, 0, 0, 2, 2, 2]
else:
x = [point, line_string]
indices = [0, 2]
with pytest.raises((TypeError, ValueError)):
func(x, indices=indices, out=out)
def test_points_invalid():
# attempt to construct a point with 2 coordinates
with pytest.raises(shapely.GEOSException):
shapely.points([[1, 1], [2, 2]], indices=[0, 0])
def test_points():
actual = shapely.points(
np.array([[2, 3], [2, 3]], dtype=float),
indices=np.array([0, 1], dtype=np.intp),
)
assert_geometries_equal(actual, [point, point])
def test_points_no_index_raises():
with pytest.raises(ValueError):
shapely.points(
np.array([[2, 3], [2, 3]], dtype=float),
indices=np.array([0, 2], dtype=np.intp),
)
@pytest.mark.parametrize(
"indices,expected",
[
([0, 1], [point, point, empty_point, None]),
([0, 3], [point, None, empty_point, point]),
([2, 3], [None, None, point, point]),
],
)
def test_points_out(indices, expected):
out = np.empty(4, dtype=object)
out[2] = empty_point
actual = shapely.points(
[[2, 3], [2, 3]],
indices=indices,
out=out,
)
assert_geometries_equal(out, expected)
assert actual is out
@pytest.mark.parametrize(
"coordinates,indices,expected",
[
([[1, 1], [2, 2]], [0, 0], [lstrs([[1, 1], [2, 2]])]),
([[1, 1, 1], [2, 2, 2]], [0, 0], [lstrs([[1, 1, 1], [2, 2, 2]])]),
(
[[1, 1], [2, 2], [2, 2], [3, 3]],
[0, 0, 1, 1],
[lstrs([[1, 1], [2, 2]]), lstrs([[2, 2], [3, 3]])],
),
],
)
def test_linestrings(coordinates, indices, expected):
actual = shapely.linestrings(
np.array(coordinates, dtype=float), indices=np.array(indices, dtype=np.intp)
)
assert_geometries_equal(actual, expected)
def test_linestrings_invalid():
# attempt to construct linestrings with 1 coordinate
with pytest.raises(shapely.GEOSException):
shapely.linestrings([[1, 1], [2, 2]], indices=[0, 1])
@pytest.mark.parametrize(
"indices,expected",
[
([0, 0, 0, 1, 1, 1], [line_string, line_string, empty_point, None]),
([0, 0, 0, 3, 3, 3], [line_string, None, empty_point, line_string]),
([2, 2, 2, 3, 3, 3], [None, None, line_string, line_string]),
],
)
def test_linestrings_out(indices, expected):
out = np.empty(4, dtype=object)
out[2] = empty_point
actual = shapely.linestrings(
[(0, 0), (1, 0), (1, 1), (0, 0), (1, 0), (1, 1)],
indices=indices,
out=out,
)
assert_geometries_equal(out, expected)
assert actual is out
@pytest.mark.parametrize(
"coordinates",
[
[[1, 1], [1, float("nan")], [2, 2]],
],
)
def test_linestrings_allow_nan(coordinates):
actual = shapely.linestrings(
np.array(coordinates, dtype=float),
indices=np.zeros(len(coordinates), dtype=np.intp),
)
assert_array_equal(shapely.get_coordinates(actual), coordinates)
@pytest.mark.parametrize(
"coordinates,indices,expected",
[
([[1, 1], [1, float("nan")], [2, 2]], [0, 0, 0], [lstrs([[1, 1], [2, 2]])]),
],
)
def test_linestrings_handle_nan_skip(coordinates, indices, expected):
actual = shapely.linestrings(
np.array(coordinates, dtype=float),
indices=np.array(indices, dtype=np.intp),
handle_nan="skip",
)
assert_geometries_equal(actual, expected)
def test_linestrings_handle_nan_skip_invalid():
# the NaN makes the linestring too short
with pytest.raises(shapely.GEOSException):
shapely.linestrings(
[[1, 1], [2, float("nan")]], indices=[0, 0], handle_nan="skip"
)
def test_linestrings_handle_nan_skip_only_nan():
actual = shapely.linestrings(
np.full((3, 2), fill_value=np.nan), indices=[0, 0, 0], handle_nan="skip"
)
assert actual[0].is_empty
def test_linestrings_handle_nan_error():
with pytest.raises(ValueError, match=".*NaN.*"):
shapely.linestrings(
[[0, 0], [float("nan"), 0], [1, 1]], indices=[0, 0, 0], handle_nan="error"
)
@pytest.mark.parametrize(
"coordinates", [([[1, 1], [2, 1], [2, 2], [1, 1]]), ([[1, 1], [2, 1], [2, 2]])]
)
def test_linearrings(coordinates):
actual = shapely.linearrings(
np.array(coordinates, dtype=np.float64),
indices=np.zeros(len(coordinates), dtype=np.intp),
)
assert_geometries_equal(actual, shapely.linearrings(coordinates))
@pytest.mark.parametrize(
"coordinates",
[
([[1, 1], [1, 1]]), # too short
([[1, np.nan], [2, 1], [2, 2], [1, 1]]), # starting with nan
],
)
def test_linearrings_invalid(coordinates):
with pytest.raises((shapely.GEOSException, ValueError)):
shapely.linearrings(coordinates, indices=np.zeros(len(coordinates)))
def test_linearrings_unclosed_all_coords_equal():
actual = shapely.linearrings([(0, 0), (0, 0), (0, 0)], indices=np.zeros(3))
assert_geometries_equal(actual, LinearRing([(0, 0), (0, 0), (0, 0), (0, 0)]))
@pytest.mark.parametrize(
"indices,expected",
[
([0, 0, 0, 0, 0], [linear_ring, None, None, empty_point]),
([1, 1, 1, 1, 1], [None, linear_ring, None, empty_point]),
([3, 3, 3, 3, 3], [None, None, None, linear_ring]),
],
)
def test_linearrings_out(indices, expected):
out = np.empty(4, dtype=object)
out[3] = empty_point
actual = shapely.linearrings(
[(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)],
indices=indices,
out=out,
)
assert_geometries_equal(out, expected)
assert actual is out
@pytest.mark.parametrize("dim", [2, 3])
@pytest.mark.parametrize("order", ["C", "F"])
def test_linearrings_buffer(dim, order):
coords = np.random.randn(10, 4, dim)
coords1 = np.asarray(coords.reshape(10 * 4, dim), order=order)
indices1 = np.repeat(range(10), 4)
result1 = shapely.linearrings(coords1, indices=indices1)
# with manual closure -> can directly copy from buffer if C order
coords2 = np.hstack((coords, coords[:, [0], :]))
coords2 = np.asarray(coords2.reshape(10 * 5, dim), order=order)
indices2 = np.repeat(range(10), 5)
result2 = shapely.linearrings(coords2, indices=indices2)
assert_geometries_equal(result1, result2)
@pytest.mark.parametrize(
"coordinates",
[
[[1, 1], [2, 1], [2, float("nan")], [1, 1]],
],
)
def test_linearrings_allow_nan(coordinates):
actual = shapely.linearrings(
np.array(coordinates, dtype=float),
indices=np.zeros(len(coordinates), dtype=np.intp),
)
assert_array_equal(shapely.get_coordinates(actual), coordinates)
@pytest.mark.parametrize(
"coordinates",
[
[[1, 1], [2, 1], [2, 2], [2, float("nan")], [1, 1]],
[[1, 1], [2, 1], [2, float("nan")], [2, 2]],
[[1, 1], [2, 1], [2, 2], [1, 1], [2, float("nan")]],
[[2, float("nan")], [1, 1], [2, 1], [2, 2], [1, 1]],
[[1, 1], [2, 1], [2, 2], [2, float("nan")]],
[[2, float("nan")], [1, 1], [2, 1], [2, 2]],
],
)
def test_linearrings_handle_nan_skip(coordinates):
actual = shapely.linearrings(
np.array(coordinates, dtype=np.float64),
indices=np.zeros(len(coordinates), dtype=np.intp),
handle_nan="skip",
)
assert_geometries_equal(actual, shapely.linearrings(coordinates, handle_nan="skip"))
def test_linearrings_handle_nan_skip_invalid():
# the NaN makes the linearring too short
with pytest.raises(ValueError):
shapely.linearrings(
[[1, 1], [float("nan"), 1], [1, 1]], indices=[0, 0, 0], handle_nan="skip"
)
def test_linearrings_handle_nan_skip_only_nan():
actual = shapely.linearrings(
np.full((5, 2), fill_value=np.nan), indices=[0] * 5, handle_nan="skip"
)
assert actual[0].is_empty
def test_linearrings_handle_nan_error():
with pytest.raises(ValueError, match=".*NaN.*"):
shapely.linearrings(
[[1, 1], [2, 1], [2, 2], [2, float("nan")], [1, 1]],
indices=[0, 0, 0, 0, 0],
handle_nan="error",
)
hole_1 = shapely.linearrings([(0.2, 0.2), (0.2, 0.4), (0.4, 0.4)])
hole_2 = shapely.linearrings([(0.6, 0.6), (0.6, 0.8), (0.8, 0.8)])
poly = shapely.polygons(linear_ring)
poly_empty = Polygon()
poly_hole_1 = shapely.polygons(linear_ring, holes=[hole_1])
poly_hole_2 = shapely.polygons(linear_ring, holes=[hole_2])
poly_hole_1_2 = shapely.polygons(linear_ring, holes=[hole_1, hole_2])
@pytest.mark.parametrize(
"rings,indices,expected",
[
([linear_ring, linear_ring], [0, 1], [poly, poly]),
([None, linear_ring], [0, 1], [poly_empty, poly]),
([None, linear_ring, None, None], [0, 0, 1, 1], [poly, poly_empty]),
([linear_ring, hole_1, linear_ring], [0, 0, 1], [poly_hole_1, poly]),
([linear_ring, linear_ring, hole_1], [0, 1, 1], [poly, poly_hole_1]),
([None, linear_ring, linear_ring, hole_1], [0, 0, 1, 1], [poly, poly_hole_1]),
([linear_ring, None, linear_ring, hole_1], [0, 0, 1, 1], [poly, poly_hole_1]),
([linear_ring, None, linear_ring, hole_1], [0, 1, 1, 1], [poly, poly_hole_1]),
([linear_ring, linear_ring, None, hole_1], [0, 1, 1, 1], [poly, poly_hole_1]),
([linear_ring, linear_ring, hole_1, None], [0, 1, 1, 1], [poly, poly_hole_1]),
(
[linear_ring, hole_1, hole_2, linear_ring],
[0, 0, 0, 1],
[poly_hole_1_2, poly],
),
(
[linear_ring, hole_1, linear_ring, hole_2],
[0, 0, 1, 1],
[poly_hole_1, poly_hole_2],
),
(
[linear_ring, linear_ring, hole_1, hole_2],
[0, 1, 1, 1],
[poly, poly_hole_1_2],
),
(
[linear_ring, hole_1, None, hole_2, linear_ring],
[0, 0, 0, 0, 1],
[poly_hole_1_2, poly],
),
(
[linear_ring, hole_1, None, linear_ring, hole_2],
[0, 0, 0, 1, 1],
[poly_hole_1, poly_hole_2],
),
(
[linear_ring, hole_1, linear_ring, None, hole_2],
[0, 0, 1, 1, 1],
[poly_hole_1, poly_hole_2],
),
],
)
def test_polygons(rings, indices, expected):
actual = shapely.polygons(
np.array(rings, dtype=object), indices=np.array(indices, dtype=np.intp)
)
assert_geometries_equal(actual, expected)
@pytest.mark.parametrize(
"indices,expected",
[
([0, 1], [poly, poly, empty_point, None]),
([0, 3], [poly, None, empty_point, poly]),
([2, 3], [None, None, poly, poly]),
],
)
def test_polygons_out(indices, expected):
out = np.empty(4, dtype=object)
out[2] = empty_point
actual = shapely.polygons([linear_ring, linear_ring], indices=indices, out=out)
assert_geometries_equal(out, expected)
assert actual is out
@pytest.mark.parametrize(
"func",
[
shapely.polygons,
shapely.multipoints,
shapely.multilinestrings,
shapely.multipolygons,
shapely.geometrycollections,
],
)
@pytest.mark.parametrize("indices", [np.array([point]), " hello", [0, 1], [-1]])
def test_invalid_indices_collections(func, indices):
with pytest.raises((TypeError, ValueError)):
func([point], indices=indices)
@pytest.mark.parametrize(
"geometries,indices,expected",
[
([point, line_string], [0, 0], [geom_coll([point, line_string])]),
([point, line_string], [0, 1], [geom_coll([point]), geom_coll([line_string])]),
([point, None], [0, 0], [geom_coll([point])]),
([point, None], [0, 1], [geom_coll([point]), geom_coll([])]),
([None, point, None, None], [0, 0, 1, 1], [geom_coll([point]), geom_coll([])]),
([point, None, line_string], [0, 0, 0], [geom_coll([point, line_string])]),
],
)
def test_geometrycollections(geometries, indices, expected):
actual = shapely.geometrycollections(
np.array(geometries, dtype=object), indices=indices
)
assert_geometries_equal(actual, expected)
def test_geometrycollections_no_index_raises():
with pytest.raises(ValueError):
shapely.geometrycollections(
np.array([point, line_string], dtype=object), indices=[0, 2]
)
@pytest.mark.parametrize(
"indices,expected",
[
([0, 0], [geom_coll([point, line_string]), None, None, empty_point]),
([3, 3], [None, None, None, geom_coll([point, line_string])]),
],
)
def test_geometrycollections_out(indices, expected):
out = np.empty(4, dtype=object)
out[3] = empty_point
actual = shapely.geometrycollections([point, line_string], indices=indices, out=out)
assert_geometries_equal(out, expected)
assert actual is out
def test_multipoints():
actual = shapely.multipoints(
np.array([point], dtype=object), indices=np.zeros(1, dtype=np.intp)
)
assert_geometries_equal(actual, shapely.multipoints([point]))
def test_multilinestrings():
actual = shapely.multilinestrings(
np.array([line_string], dtype=object), indices=np.zeros(1, dtype=np.intp)
)
assert_geometries_equal(actual, shapely.multilinestrings([line_string]))
def test_multilinearrings():
actual = shapely.multilinestrings(
np.array([linear_ring], dtype=object), indices=np.zeros(1, dtype=np.intp)
)
assert_geometries_equal(actual, shapely.multilinestrings([linear_ring]))
def test_multipolygons():
actual = shapely.multipolygons(
np.array([polygon], dtype=object), indices=np.zeros(1, dtype=np.intp)
)
assert_geometries_equal(actual, shapely.multipolygons([polygon]))
@pytest.mark.parametrize(
"geometries,func",
[
([point], shapely.polygons),
([line_string], shapely.polygons),
([polygon], shapely.polygons),
([line_string], shapely.multipoints),
([polygon], shapely.multipoints),
([point], shapely.multilinestrings),
([polygon], shapely.multilinestrings),
([point], shapely.multipolygons),
([line_string], shapely.multipolygons),
],
)
def test_incompatible_types(geometries, func):
with pytest.raises(TypeError):
func(geometries, indices=[0])
def test_points_deprecate_positional():
with pytest.deprecated_call(
match="positional argument `indices` for `points` is deprecated"
):
shapely.points([[0, 1], [2, 3]], None, None, [0, 1])
def test_linestrings_deprecate_positional():
with pytest.deprecated_call(
match="positional argument `indices` for `linestrings` is deprecated"
):
shapely.linestrings([[0, 1], [2, 3], [4, 5], [6, 7]], None, None, [0, 0, 1, 1])
def test_linearrings_deprecate_positional():
with pytest.deprecated_call(
match="positional argument `indices` for `linearrings` is deprecated"
):
shapely.linearrings([[0, 1], [1, 1], [1, 0]], None, None, [0, 0, 0])
def test_polygons_deprecate_positional():
with pytest.deprecated_call(
match="positional argument `indices` for `polygons` is deprecated"
):
shapely.polygons([linear_ring, linear_ring], None, [0, 1])
def test_multipoints_deprecate_positional():
with pytest.deprecated_call(
match="positional argument `indices` for `multipoints` is deprecated"
):
shapely.multipoints([point, point], [0, 1])
def test_multilinestrings_deprecate_positional():
with pytest.deprecated_call(
match="positional argument `indices` for `multilinestrings` is deprecated"
):
shapely.multilinestrings([line_string, line_string], [0, 1])
def test_multipolygons_deprecate_positional():
with pytest.deprecated_call(
match="positional argument `indices` for `multipolygons` is deprecated"
):
shapely.multipolygons([polygon, polygon], [0, 1])
def test_geometrycollections_deprecate_positional():
with pytest.deprecated_call(
match="positional argument `indices` for `geometrycollections` is deprecated"
):
shapely.geometrycollections([point, polygon], [0, 1])