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