| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- 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
|