from functools import partial import numpy as np import pytest import shapely from shapely import LinearRing, LineString, Point from shapely.tests.common import ( all_types, all_types_m, all_types_z, all_types_zm, empty, geometry_collection, ignore_invalid, line_string, linear_ring, point, polygon, ) UNARY_PREDICATES = ( shapely.has_z, pytest.param( shapely.has_m, marks=pytest.mark.skipif( shapely.geos_version < (3, 12, 0), reason="GEOS < 3.12" ), ), shapely.is_empty, shapely.is_simple, shapely.is_ring, shapely.is_closed, shapely.is_valid, shapely.is_missing, shapely.is_geometry, shapely.is_valid_input, shapely.is_prepared, shapely.is_ccw, ) BINARY_PREDICATES = ( shapely.disjoint, shapely.touches, shapely.intersects, shapely.crosses, shapely.within, shapely.contains, shapely.contains_properly, shapely.overlaps, shapely.covers, shapely.covered_by, pytest.param( partial(shapely.dwithin, distance=1.0), marks=pytest.mark.skipif( shapely.geos_version < (3, 10, 0), reason="GEOS < 3.10" ), ), shapely.equals, shapely.equals_exact, shapely.equals_identical, ) BINARY_PREPARED_PREDICATES = BINARY_PREDICATES[:-2] XY_PREDICATES = ( (shapely.contains_xy, shapely.contains), (shapely.intersects_xy, shapely.intersects), ) @pytest.mark.parametrize("geometry", all_types + all_types_z) @pytest.mark.parametrize("func", UNARY_PREDICATES) def test_unary_array(geometry, func): actual = func([geometry, geometry]) assert actual.shape == (2,) assert actual.dtype == np.bool_ @pytest.mark.parametrize("func", UNARY_PREDICATES) def test_unary_with_kwargs(func): out = np.empty((), dtype=np.uint8) actual = func(point, out=out) assert actual is out assert actual.dtype == np.uint8 @pytest.mark.parametrize("func", UNARY_PREDICATES) def test_unary_missing(func): if func in (shapely.is_valid_input, shapely.is_missing): assert func(None) else: assert not func(None) @pytest.mark.parametrize("a", all_types) @pytest.mark.parametrize("func", BINARY_PREDICATES) def test_binary_array(a, func): with ignore_invalid(shapely.is_empty(a) and shapely.geos_version < (3, 12, 0)): # Empty geometries give 'invalid value encountered' in all predicates # (see https://github.com/libgeos/geos/issues/515) actual = func([a, a], point) assert actual.shape == (2,) assert actual.dtype == np.bool_ @pytest.mark.parametrize("func", BINARY_PREDICATES) def test_binary_with_kwargs(func): out = np.empty((), dtype=np.uint8) actual = func(point, point, out=out) assert actual is out assert actual.dtype == np.uint8 @pytest.mark.parametrize("func", BINARY_PREDICATES) def test_binary_missing(func): actual = func(np.array([point, None, None]), np.array([None, point, None])) assert (~actual).all() def test_binary_empty_result(): a = LineString([(0, 0), (3, 0), (3, 3), (0, 3)]) b = LineString([(5, 1), (6, 1)]) with ignore_invalid(shapely.geos_version < (3, 12, 0)): # Intersection resulting in empty geometries give 'invalid value encountered' # (https://github.com/shapely/shapely/issues/1345) assert shapely.intersection(a, b).is_empty @pytest.mark.parametrize("a", all_types) @pytest.mark.parametrize("func, func_bin", XY_PREDICATES) def test_xy_array(a, func, func_bin): with ignore_invalid(shapely.is_empty(a) and shapely.geos_version < (3, 12, 0)): # Empty geometries give 'invalid value encountered' in all predicates # (see https://github.com/libgeos/geos/issues/515) actual = func([a, a], 2, 3) expected = func_bin([a, a], Point(2, 3)) assert actual.shape == (2,) assert actual.dtype == np.bool_ np.testing.assert_allclose(actual, expected) @pytest.mark.parametrize("a", all_types) @pytest.mark.parametrize("func, func_bin", XY_PREDICATES) def test_xy_array_broadcast(a, func, func_bin): a2 = shapely.transform(a, lambda x: x) # makes a copy with ignore_invalid(shapely.is_empty(a) and shapely.geos_version < (3, 12, 0)): # Empty geometries give 'invalid value encountered' in all predicates # (see https://github.com/libgeos/geos/issues/515) actual = func(a2, [0, 1, 2], [1, 2, 3]) expected = func_bin(a, [Point(0, 1), Point(1, 2), Point(2, 3)]) np.testing.assert_allclose(actual, expected) @pytest.mark.parametrize("func", [funcs[0] for funcs in XY_PREDICATES]) def test_xy_array_2D(func): polygon2 = shapely.transform(polygon, lambda x: x) # makes a copy actual = func(polygon2, [0, 1, 2], [1, 2, 3]) expected = func(polygon2, [[0, 1], [1, 2], [2, 3]]) np.testing.assert_allclose(actual, expected) @pytest.mark.parametrize("func, func_bin", XY_PREDICATES) def test_xy_prepared(func, func_bin): actual = func(_prepare_with_copy([polygon, line_string]), 2, 3) expected = func_bin([polygon, line_string], Point(2, 3)) np.testing.assert_allclose(actual, expected) @pytest.mark.parametrize("func", [funcs[0] for funcs in XY_PREDICATES]) def test_xy_with_kwargs(func): out = np.empty((), dtype=np.uint8) point2 = shapely.transform(point, lambda x: x) # makes a copy actual = func(point2, point2.x, point2.y, out=out) assert actual is out assert actual.dtype == np.uint8 @pytest.mark.parametrize("func", [funcs[0] for funcs in XY_PREDICATES]) def test_xy_missing(func): actual = func( np.array([point, point, point, None]), np.array([point.x, np.nan, point.x, point.x]), np.array([point.y, point.y, np.nan, point.y]), ) np.testing.assert_allclose(actual, [True, False, False, False]) def test_equals_exact_tolerance(): # specifying tolerance p1 = shapely.points(50, 4) p2 = shapely.points(50.1, 4.1) actual = shapely.equals_exact([p1, p2, None], p1, tolerance=0.05) np.testing.assert_allclose(actual, [True, False, False]) assert actual.dtype == np.bool_ actual = shapely.equals_exact([p1, p2, None], p1, tolerance=0.2) np.testing.assert_allclose(actual, [True, True, False]) assert actual.dtype == np.bool_ # default value for tolerance assert shapely.equals_exact(p1, p1).item() is True assert shapely.equals_exact(p1, p2).item() is False # an array of tolerances actual = shapely.equals_exact(p1, p2, tolerance=[0.05, 0.2, np.nan]) np.testing.assert_allclose(actual, [False, True, False]) def test_equals_exact_normalize(): l1 = LineString([(0, 0), (1, 1)]) l2 = LineString([(1, 1), (0, 0)]) # default requires same order of coordinates assert not shapely.equals_exact(l1, l2) assert shapely.equals_exact(l1, l2, normalize=True) def test_equals_identical(): # more elaborate tests are done at the Geometry.__eq__ level # requires same order of coordinates l1 = LineString([(0, 0), (1, 1)]) l2 = LineString([(1, 1), (0, 0)]) assert not shapely.equals_identical(l1, l2) # checks z-dimension (in contrast to equals_exact) l1 = LineString([(0, 0, 0), (1, 1, 0)]) l2 = LineString([(0, 0, 1), (1, 1, 1)]) assert not shapely.equals_identical(l1, l2) assert shapely.equals_exact(l1, l2) # NaNs in same place are equal (in contrast to equals_exact) with ignore_invalid(): l1 = LineString([(0, np.nan), (1, 1)]) l2 = LineString([(0, np.nan), (1, 1)]) assert shapely.equals_identical(l1, l2) assert not shapely.equals_exact(l1, l2) @pytest.mark.skipif(shapely.geos_version < (3, 10, 0), reason="GEOS < 3.10") def test_dwithin(): p1 = shapely.points(50, 4) p2 = shapely.points(50.1, 4.1) actual = shapely.dwithin([p1, p2, None], p1, distance=0.05) np.testing.assert_equal(actual, [True, False, False]) assert actual.dtype == np.bool_ actual = shapely.dwithin([p1, p2, None], p1, distance=0.2) np.testing.assert_allclose(actual, [True, True, False]) assert actual.dtype == np.bool_ # an array of distances actual = shapely.dwithin(p1, p2, distance=[0.05, 0.2, np.nan]) np.testing.assert_allclose(actual, [False, True, False]) @pytest.mark.parametrize("geometry", all_types) def test_has_z_has_m_all_types(geometry): assert not shapely.has_z(geometry) if shapely.geos_version >= (3, 12, 0): assert not shapely.has_m(geometry) # The next few tests skip has_z/has_m with empty geometries # See https://github.com/libgeos/geos/issues/888 @pytest.mark.parametrize("geometry", all_types_z) def test_has_z_has_m_all_types_z(geometry): if shapely.is_empty(geometry): pytest.skip("GEOSHasZ with EMPTY geometries is inconsistent") assert shapely.has_z(geometry) if shapely.geos_version >= (3, 12, 0): assert not shapely.has_m(geometry) @pytest.mark.skipif( shapely.geos_version < (3, 12, 0), reason="M coordinates not supported with GEOS < 3.12", ) @pytest.mark.parametrize("geometry", all_types_m) def test_has_m_all_types_m(geometry): if shapely.is_empty(geometry): pytest.skip("GEOSHasM with EMPTY geometries is inconsistent") assert not shapely.has_z(geometry) assert shapely.has_m(geometry) @pytest.mark.skipif( shapely.geos_version < (3, 12, 0), reason="M coordinates not supported with GEOS < 3.12", ) @pytest.mark.parametrize("geometry", all_types_zm) def test_has_z_has_m_all_types_zm(geometry): if shapely.is_empty(geometry): pytest.skip("GEOSHasZ with EMPTY geometries is inconsistent") assert shapely.has_z(geometry) assert shapely.has_m(geometry) @pytest.mark.parametrize( "geometry,expected", [ (point, False), (line_string, False), (linear_ring, True), (empty, False), ], ) def test_is_closed(geometry, expected): assert shapely.is_closed(geometry) == expected def test_relate(): p1 = shapely.points(0, 0) p2 = shapely.points(1, 1) actual = shapely.relate(p1, p2) assert isinstance(actual, str) assert actual == "FF0FFF0F2" @pytest.mark.parametrize("g1, g2", [(point, None), (None, point), (None, None)]) def test_relate_none(g1, g2): assert shapely.relate(g1, g2) is None def test_relate_pattern(): g = shapely.linestrings([(0, 0), (1, 0), (1, 1)]) polygon = shapely.box(0, 0, 2, 2) assert shapely.relate(g, polygon) == "11F00F212" assert shapely.relate_pattern(g, polygon, "11F00F212") assert shapely.relate_pattern(g, polygon, "*********") assert not shapely.relate_pattern(g, polygon, "F********") def test_relate_pattern_empty(): with ignore_invalid(shapely.geos_version < (3, 12, 0)): # Empty geometries give 'invalid value encountered' in all predicates # (see https://github.com/libgeos/geos/issues/515) assert shapely.relate_pattern(empty, empty, "*" * 9).item() is True @pytest.mark.parametrize("g1, g2", [(point, None), (None, point), (None, None)]) def test_relate_pattern_none(g1, g2): assert shapely.relate_pattern(g1, g2, "*" * 9).item() is False def test_relate_pattern_incorrect_length(): with pytest.raises(shapely.GEOSException, match="Should be length 9"): shapely.relate_pattern(point, polygon, "**") with pytest.raises(shapely.GEOSException, match="Should be length 9"): shapely.relate_pattern(point, polygon, "**********") @pytest.mark.parametrize("pattern", [b"*********", 10, None]) def test_relate_pattern_non_string(pattern): with pytest.raises(TypeError, match="expected string"): shapely.relate_pattern(point, polygon, pattern) def test_relate_pattern_non_scalar(): with pytest.raises(ValueError, match="only supports scalar"): shapely.relate_pattern([point] * 2, polygon, ["*********"] * 2) @pytest.mark.parametrize( "geom, expected", [ (LinearRing([(0, 0), (0, 1), (1, 1), (0, 0)]), False), (LinearRing([(0, 0), (1, 1), (0, 1), (0, 0)]), True), (LineString([(0, 0), (0, 1), (1, 1), (0, 0)]), False), (LineString([(0, 0), (1, 1), (0, 1), (0, 0)]), True), (LineString([(0, 0), (1, 1), (0, 1)]), False), (LineString([(0, 0), (0, 1), (1, 1)]), False), (point, False), (polygon, False), (geometry_collection, False), (None, False), ], ) def test_is_ccw(geom, expected): assert shapely.is_ccw(geom) == expected def _prepare_with_copy(geometry): """Prepare without modifying in-place""" geometry = shapely.transform(geometry, lambda x: x) # makes a copy shapely.prepare(geometry) return geometry @pytest.mark.parametrize("a", all_types) @pytest.mark.parametrize("func", BINARY_PREPARED_PREDICATES) def test_binary_prepared(a, func): with ignore_invalid(shapely.is_empty(a) and shapely.geos_version < (3, 12, 0)): # Empty geometries give 'invalid value encountered' in all predicates # (see https://github.com/libgeos/geos/issues/515) actual = func(a, point) result = func(_prepare_with_copy(a), point) assert actual == result @pytest.mark.parametrize("geometry", all_types) def test_is_prepared_true(geometry): assert shapely.is_prepared(_prepare_with_copy(geometry)) @pytest.mark.parametrize("geometry", all_types + (None,)) def test_is_prepared_false(geometry): assert not shapely.is_prepared(geometry) def test_contains_properly(): # polygon contains itself, but does not properly contains itself assert shapely.contains(polygon, polygon).item() is True assert shapely.contains_properly(polygon, polygon).item() is False
Memory