| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- import numpy as np
- import pytest
- import shapely
- from shapely import (
- Geometry,
- LineString,
- MultiPolygon,
- Polygon,
- )
- from shapely.errors import UnsupportedGEOSVersionError
- from shapely.testing import assert_geometries_equal
- from shapely.tests.common import (
- all_types,
- all_types_z,
- empty_line_string,
- )
- @pytest.mark.skipif(shapely.geos_version < (3, 12, 0), reason="requires >= 3.12")
- @pytest.mark.parametrize("geometry", all_types + all_types_z)
- def test_coverage_is_valid(geometry):
- actual = shapely.coverage_is_valid([geometry])
- assert actual.ndim == 0
- assert actual.dtype == np.bool_
- assert actual.item() is True
- actual = shapely.coverage_invalid_edges([geometry])
- expected = np.array([empty_line_string], dtype=object)
- assert_geometries_equal(actual, expected)
- @pytest.mark.skipif(shapely.geos_version < (3, 12, 0), reason="requires >= 3.12")
- def test_coverage_is_valid_non_polygonal():
- # non-polygonal geometries are ignored to validate the coverage
- # (e.g. even if you have crossing linestrings)
- geoms = [
- LineString([(0, 0), (1, 1)]),
- LineString([(1, 0), (0, 1)]),
- Polygon([(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)]),
- ]
- assert shapely.coverage_is_valid(geoms)
- assert (shapely.coverage_invalid_edges(geoms) == empty_line_string).all()
- @pytest.mark.skipif(shapely.geos_version < (3, 12, 0), reason="requires >= 3.12")
- def test_coverage_is_valid_polygonal():
- # adjacent triangles
- poly1 = Polygon([(0, 0), (1, 1), (1, 0), (0, 0)])
- poly2 = Polygon([(0, 0), (1, 1), (0, 1), (0, 0)])
- assert shapely.coverage_is_valid([poly1, poly2])
- assert shapely.is_empty(shapely.coverage_invalid_edges([poly1, poly2])).all()
- # shared egde but without identical vertices
- poly2b = Polygon([(0, 0), (0.5, 0.5), (1, 1), (0, 1), (0, 0)])
- assert not shapely.coverage_is_valid([poly1, poly2b])
- result = shapely.coverage_invalid_edges([poly1, poly2b])
- expected = [LineString([(0, 0), (1, 1)]), LineString([(0, 0), (0.5, 0.5), (1, 1)])]
- assert_geometries_equal(result, expected)
- # overlap
- poly3 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)])
- assert not shapely.coverage_is_valid([poly1, poly3])
- result = shapely.coverage_invalid_edges([poly1, poly3])
- assert not shapely.is_empty(result).any()
- @pytest.mark.skipif(shapely.geos_version < (3, 12, 0), reason="requires >= 3.12")
- def test_coverage_is_valid_gap_width():
- # shared edge of boxes with multiple vertices
- poly1 = shapely.from_wkt("POLYGON ((0 10, 10 10, 10 7, 10 3, 10 0, 0 0, 0 10))")
- poly2 = shapely.from_wkt("POLYGON ((10 10, 20 10, 20 0, 10 0, 10 3, 10 7, 10 10))")
- # extra vertex in middle of shared edge
- poly2_extra = shapely.from_wkt(
- "POLYGON ((10 10, 20 10, 20 0, 10 0, 10 3, 10 5, 10 7, 10 10))"
- )
- # extra vertex shifted -> gap of max 1 wide
- poly2_shift = shapely.from_wkt(
- "POLYGON ((10 10, 20 10, 20 0, 10 0, 10 3, 11 5, 10 7, 10 10))"
- )
- # valid coverage -> gap_width value does not matter
- assert shapely.coverage_is_valid([poly1, poly2], gap_width=0.0)
- assert shapely.coverage_is_valid([poly1, poly2], gap_width=2.0)
- result = shapely.coverage_invalid_edges([poly1, poly2], gap_width=0.0)
- assert_geometries_equal(result, [empty_line_string] * 2)
- result = shapely.coverage_invalid_edges([poly1, poly2], gap_width=2.0)
- assert_geometries_equal(result, [empty_line_string] * 2)
- # invalid coverage -> gap_width value does not matter
- assert not shapely.coverage_is_valid([poly1, poly2_extra], gap_width=0.0)
- assert not shapely.coverage_is_valid([poly1, poly2_extra], gap_width=2.0)
- expected = shapely.from_wkt(
- ["LINESTRING (10 7, 10 3)", "LINESTRING (10 3, 10 5, 10 7)"]
- )
- result = shapely.coverage_invalid_edges([poly1, poly2_extra], gap_width=0.0)
- assert_geometries_equal(result, expected)
- result = shapely.coverage_invalid_edges([poly1, poly2_extra], gap_width=2.0)
- assert_geometries_equal(result, expected)
- # coverage with gap of 1 unit wide
- assert shapely.coverage_is_valid([poly1, poly2_shift], gap_width=0.0)
- assert shapely.coverage_is_valid([poly1, poly2_shift], gap_width=0.5)
- assert not shapely.coverage_is_valid([poly1, poly2_shift], gap_width=1.0)
- assert not shapely.coverage_is_valid([poly1, poly2_shift], gap_width=1.5)
- # TODO why this behaviour?
- assert shapely.coverage_is_valid([poly1, poly2_shift], gap_width=2.0)
- assert_geometries_equal(
- shapely.coverage_invalid_edges([poly1, poly2_shift], gap_width=0.0),
- [empty_line_string] * 2,
- )
- assert_geometries_equal(
- shapely.coverage_invalid_edges([poly1, poly2_shift], gap_width=1.0),
- shapely.from_wkt(["LINESTRING (10 7, 10 3)", "LINESTRING (10 3, 11 5, 10 7)"]),
- )
- @pytest.mark.skipif(shapely.geos_version < (3, 12, 0), reason="requires >= 3.12")
- def test_coverage_invalid_edges_gufunc():
- poly1 = shapely.from_wkt("POLYGON ((0 10, 10 10, 10 7, 10 3, 10 0, 0 0, 0 10))")
- poly2 = shapely.from_wkt("POLYGON ((10 10, 20 10, 20 0, 10 0, 10 3, 10 7, 10 10))")
- poly2_extra = shapely.from_wkt(
- "POLYGON ((10 10, 20 10, 20 0, 10 0, 10 3, 10 5, 10 7, 10 10))"
- )
- poly3 = shapely.from_wkt("POLYGON ((20 10, 30 10, 30 7, 30 3, 30 0, 20 0, 20 10))")
- arr = np.array([[poly1, poly2, poly3], [poly1, poly2_extra, poly3]])
- result = shapely.lib.coverage_invalid_edges(arr, 0.0)
- expected = shapely.from_wkt(
- [
- ["LINESTRING EMPTY"] * 3,
- [
- "LINESTRING (10 7, 10 3)",
- "LINESTRING (10 3, 10 5, 10 7)",
- "LINESTRING EMPTY",
- ],
- ]
- )
- assert_geometries_equal(result, expected)
- arr2 = np.array(arr, order="F")
- result = shapely.lib.coverage_invalid_edges(arr2, 0.0)
- assert_geometries_equal(result, expected)
- @pytest.mark.skipif(shapely.geos_version < (3, 12, 0), reason="GEOS < 3.12")
- @pytest.mark.parametrize("geometry", all_types)
- def test_coverage_simplify_scalars(geometry):
- if geometry.geom_type in ("Polygon", "MultiPolygon"):
- actual = shapely.coverage_simplify(geometry, 0.0)
- assert isinstance(actual, Geometry)
- assert shapely.get_type_id(actual) == shapely.get_type_id(geometry)
- assert actual.equals(geometry)
- else:
- with pytest.raises(TypeError, match="incorrect geometry type"):
- shapely.coverage_simplify(geometry, 0.0)
- @pytest.mark.skipif(shapely.geos_version < (3, 12, 0), reason="GEOS < 3.12")
- @pytest.mark.parametrize("geometry", all_types)
- def test_coverage_simplify_geom_types(geometry):
- if geometry.geom_type in ("Polygon", "MultiPolygon"):
- actual = shapely.coverage_simplify([geometry, geometry], 0.0)
- assert isinstance(actual, np.ndarray)
- assert actual.shape == (2,)
- assert (shapely.get_type_id(actual) == shapely.get_type_id(geometry)).all()
- else:
- with pytest.raises(TypeError, match="incorrect geometry type"):
- shapely.coverage_simplify([geometry, geometry], 0.0)
- @pytest.mark.skipif(shapely.geos_version < (3, 12, 0), reason="GEOS < 3.12")
- def test_coverage_simplify_multipolygon():
- mp = MultiPolygon(
- [
- Polygon([(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)]),
- Polygon([(2, 2), (2, 3), (3, 3), (3, 2), (2, 2)]),
- ]
- )
- actual = shapely.coverage_simplify(mp, 1)
- assert actual.equals(
- shapely.from_wkt(
- "MULTIPOLYGON (((0 1, 1 1, 1 0, 0 1)), ((2 3, 3 3, 3 2, 2 3)))"
- )
- )
- @pytest.mark.skipif(shapely.geos_version < (3, 12, 0), reason="GEOS < 3.12")
- def test_coverage_simplify_array():
- polygons = np.array(
- [
- shapely.Polygon([(0, 0), (20, 0), (20, 10), (10, 5), (0, 10), (0, 0)]),
- shapely.Polygon([(0, 10), (10, 5), (20, 10), (20, 20), (0, 20), (0, 10)]),
- ]
- )
- low_tolerance = shapely.coverage_simplify(polygons, 1)
- mid_tolerance = shapely.coverage_simplify(polygons, 8)
- high_tolerance = shapely.coverage_simplify(polygons, 10)
- assert shapely.equals(low_tolerance, shapely.normalize(polygons)).all()
- assert shapely.equals(
- mid_tolerance,
- shapely.from_wkt(
- [
- "POLYGON ((20 10, 0 10, 0 0, 20 0, 20 10))",
- "POLYGON ((20 10, 0 10, 0 20, 20 20, 20 10))",
- ]
- ),
- ).all()
- assert shapely.equals(
- high_tolerance,
- shapely.from_wkt(
- [
- "POLYGON ((20 10, 0 10, 20 0, 20 10))",
- "POLYGON ((20 10, 0 10, 0 20, 20 10))",
- ]
- ),
- ).all()
- no_boundary = shapely.coverage_simplify(polygons, 10, simplify_boundary=False)
- assert shapely.equals(
- no_boundary,
- shapely.from_wkt(
- [
- "POLYGON ((20 10, 0 10, 0 0, 20 0, 20 10))",
- "POLYGON ((20 10, 0 10, 0 20, 20 20, 20 10))",
- ]
- ),
- ).all()
- @pytest.mark.skipif(shapely.geos_version >= (3, 12, 0), reason="requires >= 3.12")
- def test_coverage_unsupported_geos():
- geoms = [
- Polygon([(0, 0), (1, 1), (1, 0), (0, 0)]),
- Polygon([(0, 0), (1, 1), (0, 1), (0, 0)]),
- ]
- with pytest.raises(UnsupportedGEOSVersionError):
- shapely.coverage_is_valid(geoms)
- with pytest.raises(UnsupportedGEOSVersionError):
- shapely.coverage_invalid_edges(geoms)
- with pytest.raises(UnsupportedGEOSVersionError):
- shapely.coverage_simplify(geoms, 1.0)
|