285 lines
9.2 KiB
Python
285 lines
9.2 KiB
Python
import numpy as np
|
|
import pytest
|
|
|
|
import shapely
|
|
from shapely import LinearRing, LineString, MultiLineString, Point, Polygon
|
|
from shapely.tests.common import all_types, all_types_z, empty_point, ignore_invalid
|
|
|
|
all_non_empty_types = np.array(all_types + all_types_z)[
|
|
~shapely.is_empty(all_types + all_types_z)
|
|
]
|
|
|
|
|
|
# TODO add all_types_m and all_types_zm once tranform supports M coordinates
|
|
@pytest.mark.parametrize("geom", all_types + all_types_z)
|
|
def test_equality(geom):
|
|
assert geom == geom # noqa: PLR0124
|
|
transformed = shapely.transform(geom, lambda x: x, include_z=True)
|
|
if (
|
|
shapely.geos_version < (3, 9, 0)
|
|
and isinstance(geom, Point)
|
|
and geom.is_empty
|
|
and not geom.has_z
|
|
):
|
|
# the transformed empty 2D point has become 3D on GEOS 3.8
|
|
transformed = shapely.force_2d(geom)
|
|
assert geom == transformed
|
|
assert not (geom != transformed)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"left, right",
|
|
# automated test cases with transformed coordinate values
|
|
[(geom, shapely.transform(geom, lambda x: x + 1)) for geom in all_non_empty_types]
|
|
+ [
|
|
# (slightly) different coordinate values
|
|
(LineString([(0, 0), (1, 1)]), LineString([(0, 0), (1, 2)])),
|
|
(LineString([(0, 0), (1, 1)]), LineString([(0, 0), (1, 1 + 1e-12)])),
|
|
# different coordinate order
|
|
(LineString([(0, 0), (1, 1)]), LineString([(1, 1), (0, 0)])),
|
|
# different number of coordinates (but spatially equal)
|
|
(LineString([(0, 0), (1, 1)]), LineString([(0, 0), (1, 1), (1, 1)])),
|
|
(LineString([(0, 0), (1, 1)]), LineString([(0, 0), (0.5, 0.5), (1, 1)])),
|
|
# different order of sub-geometries
|
|
(
|
|
MultiLineString([[(1, 1), (2, 2)], [(2, 2), (3, 3)]]),
|
|
MultiLineString([[(2, 2), (3, 3)], [(1, 1), (2, 2)]]),
|
|
),
|
|
# M coordinates (don't work yet with automated cases)
|
|
pytest.param(
|
|
shapely.from_wkt("POINT M (0 0 0)"),
|
|
shapely.from_wkt("POINT M (0 0 1)"),
|
|
marks=pytest.mark.skipif(
|
|
shapely.geos_version < (3, 12, 0), reason="GEOS < 3.12"
|
|
),
|
|
),
|
|
pytest.param(
|
|
shapely.from_wkt("POINT ZM (0 0 0 0)"),
|
|
shapely.from_wkt("POINT ZM (0 0 0 1)"),
|
|
marks=pytest.mark.skipif(
|
|
shapely.geos_version < (3, 12, 0), reason="GEOS < 3.12"
|
|
),
|
|
),
|
|
],
|
|
)
|
|
def test_equality_false(left, right):
|
|
assert left != right
|
|
assert not (left == right)
|
|
|
|
|
|
with ignore_invalid():
|
|
cases1 = [
|
|
(LineString([(0, 1), (2, np.nan)]), LineString([(0, 1), (2, np.nan)])),
|
|
(
|
|
LineString([(0, 1), (np.nan, np.nan)]),
|
|
LineString([(0, 1), (np.nan, np.nan)]),
|
|
),
|
|
(LineString([(np.nan, 1), (2, 3)]), LineString([(np.nan, 1), (2, 3)])),
|
|
(LineString([(0, np.nan), (2, 3)]), LineString([(0, np.nan), (2, 3)])),
|
|
(
|
|
LineString([(np.nan, np.nan), (np.nan, np.nan)]),
|
|
LineString([(np.nan, np.nan), (np.nan, np.nan)]),
|
|
),
|
|
# NaN as explicit Z coordinate
|
|
# TODO: if first z is NaN -> considered as 2D -> tested below explicitly
|
|
# (
|
|
# LineString([(0, 1, np.nan), (2, 3, np.nan)]),
|
|
# LineString([(0, 1, np.nan), (2, 3, np.nan)]),
|
|
# ),
|
|
(
|
|
LineString([(0, 1, 2), (2, 3, np.nan)]),
|
|
LineString([(0, 1, 2), (2, 3, np.nan)]),
|
|
),
|
|
# (
|
|
# LineString([(0, 1, np.nan), (2, 3, 4)]),
|
|
# LineString([(0, 1, np.nan), (2, 3, 4)]),
|
|
# ),
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize("left, right", cases1)
|
|
def test_equality_with_nan(left, right):
|
|
assert left == right
|
|
assert not (left != right)
|
|
|
|
|
|
with ignore_invalid():
|
|
cases2 = [
|
|
(
|
|
LineString([(0, 1, np.nan), (2, 3, np.nan)]),
|
|
LineString([(0, 1, np.nan), (2, 3, np.nan)]),
|
|
),
|
|
(
|
|
LineString([(0, 1, np.nan), (2, 3, 4)]),
|
|
LineString([(0, 1, np.nan), (2, 3, 4)]),
|
|
),
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize("left, right", cases2)
|
|
def test_equality_with_nan_z(left, right):
|
|
assert left == right
|
|
assert not (left != right)
|
|
|
|
|
|
with ignore_invalid():
|
|
cases3 = [
|
|
(LineString([(0, np.nan), (2, 3)]), LineString([(0, 1), (2, 3)])),
|
|
(LineString([(0, 1), (2, np.nan)]), LineString([(0, 1), (2, 3)])),
|
|
(LineString([(0, 1, np.nan), (2, 3, 4)]), LineString([(0, 1, 2), (2, 3, 4)])),
|
|
(LineString([(0, 1, 2), (2, 3, np.nan)]), LineString([(0, 1, 2), (2, 3, 4)])),
|
|
pytest.param(
|
|
shapely.from_wkt("POINT M (0 0 0)"),
|
|
shapely.from_wkt("POINT M (0 0 NaN)"),
|
|
marks=pytest.mark.skipif(
|
|
shapely.geos_version < (3, 12, 0), reason="GEOS < 3.12"
|
|
),
|
|
),
|
|
pytest.param(
|
|
shapely.from_wkt("POINT ZM (0 0 0 0)"),
|
|
shapely.from_wkt("POINT ZM (0 0 0 NaN)"),
|
|
marks=pytest.mark.skipif(
|
|
shapely.geos_version < (3, 12, 0), reason="GEOS < 3.12"
|
|
),
|
|
),
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize("left, right", cases3)
|
|
def test_equality_with_nan_false(left, right):
|
|
assert left != right
|
|
|
|
|
|
def test_equality_with_nan_z_false():
|
|
with ignore_invalid():
|
|
left = LineString([(0, 1, np.nan), (2, 3, np.nan)])
|
|
right = LineString([(0, 1, np.nan), (2, 3, 4)])
|
|
|
|
if shapely.geos_version < (3, 10, 0):
|
|
# GEOS <= 3.9 fill the NaN with 0, so the z dimension is different
|
|
assert left != right
|
|
elif shapely.geos_version < (3, 12, 0):
|
|
# GEOS 3.10-3.11 ignore NaN for Z also when explicitly created with 3D
|
|
# and so the geometries are considered as 2D (and thus z dimension is ignored)
|
|
assert left == right
|
|
else:
|
|
assert left != right
|
|
|
|
|
|
def test_equality_z():
|
|
# different dimensionality
|
|
geom1 = Point(0, 1)
|
|
geom2 = Point(0, 1, 0)
|
|
assert geom1 != geom2
|
|
|
|
# different dimensionality with NaN z
|
|
geom2 = Point(0, 1, np.nan)
|
|
if shapely.geos_version < (3, 12, 0):
|
|
# GEOS 3.10-3.11 ignore NaN for Z also when explicitly created with 3D
|
|
# and so the geometries are considered as 2D (and thus z dimension is ignored)
|
|
assert geom1 == geom2
|
|
else:
|
|
assert geom1 != geom2
|
|
|
|
|
|
def test_equality_exact_type():
|
|
# geometries with different type but same coord seq are not equal
|
|
geom1 = LineString([(0, 0), (1, 1), (0, 1), (0, 0)])
|
|
geom2 = LinearRing([(0, 0), (1, 1), (0, 1), (0, 0)])
|
|
geom3 = Polygon([(0, 0), (1, 1), (0, 1), (0, 0)])
|
|
assert geom1 != geom2
|
|
assert geom1 != geom3
|
|
assert geom2 != geom3
|
|
|
|
# empty with different type
|
|
geom1 = shapely.from_wkt("POINT EMPTY")
|
|
geom2 = shapely.from_wkt("LINESTRING EMPTY")
|
|
assert geom1 != geom2
|
|
|
|
|
|
def test_equality_polygon():
|
|
# different exterior rings
|
|
geom1 = shapely.from_wkt("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")
|
|
geom2 = shapely.from_wkt("POLYGON ((0 0, 10 0, 10 10, 0 15, 0 0))")
|
|
assert geom1 != geom2
|
|
|
|
# different number of holes
|
|
geom1 = shapely.from_wkt(
|
|
"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 1))"
|
|
)
|
|
geom2 = shapely.from_wkt(
|
|
"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 1), "
|
|
"(3 3, 4 3, 4 4, 3 3))"
|
|
)
|
|
assert geom1 != geom2
|
|
|
|
# different order of holes
|
|
geom1 = shapely.from_wkt(
|
|
"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 4 3, 4 4, 3 3), "
|
|
"(1 1, 2 1, 2 2, 1 1))"
|
|
)
|
|
geom2 = shapely.from_wkt(
|
|
"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 1), "
|
|
"(3 3, 4 3, 4 4, 3 3))"
|
|
)
|
|
assert geom1 != geom2
|
|
|
|
|
|
@pytest.mark.parametrize("geom", all_types)
|
|
def test_comparison_notimplemented(geom):
|
|
# comparing to a non-geometry class should return NotImplemented in __eq__
|
|
# to ensure proper delegation to other (eg to ensure comparison of scalar
|
|
# with array works)
|
|
# https://github.com/shapely/shapely/issues/1056
|
|
assert geom.__eq__(1) is NotImplemented
|
|
|
|
# with array
|
|
arr = np.array([geom, geom], dtype=object)
|
|
|
|
result = arr == geom
|
|
assert isinstance(result, np.ndarray)
|
|
assert result.all()
|
|
|
|
result = geom == arr
|
|
assert isinstance(result, np.ndarray)
|
|
assert result.all()
|
|
|
|
result = arr != geom
|
|
assert isinstance(result, np.ndarray)
|
|
assert not result.any()
|
|
|
|
result = geom != arr
|
|
assert isinstance(result, np.ndarray)
|
|
assert not result.any()
|
|
|
|
|
|
def test_comparison_not_supported():
|
|
geom1 = Point(1, 1)
|
|
geom2 = Point(2, 2)
|
|
|
|
with pytest.raises(TypeError, match="not supported between instances"):
|
|
assert geom1 > geom2
|
|
|
|
with pytest.raises(TypeError, match="not supported between instances"):
|
|
assert geom1 < geom2
|
|
|
|
with pytest.raises(TypeError, match="not supported between instances"):
|
|
assert geom1 >= geom2
|
|
|
|
with pytest.raises(TypeError, match="not supported between instances"):
|
|
assert geom1 <= geom2
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geom", all_types + (shapely.points(np.nan, np.nan), empty_point)
|
|
)
|
|
def test_hash_same_equal(geom):
|
|
hash1 = hash(geom)
|
|
hash2 = hash(shapely.transform(geom, lambda x: x))
|
|
assert hash1 == hash2, geom
|
|
|
|
|
|
@pytest.mark.parametrize("geom", all_non_empty_types)
|
|
def test_hash_same_not_equal(geom):
|
|
assert hash(geom) != hash(shapely.transform(geom, lambda x: x + 1))
|