| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- 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))
|