diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 10902aec..335e8c0a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,5 +1,3 @@ -# Set update schedule for GitHub Actions - version: 2 updates: - package-ecosystem: "github-actions" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 2721ebe9..e94c741a 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -43,7 +43,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -54,7 +54,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -68,5 +68,5 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 ... diff --git a/.github/workflows/pr_agent.yml b/.github/workflows/pr_agent.yml new file mode 100644 index 00000000..74742d7b --- /dev/null +++ b/.github/workflows/pr_agent.yml @@ -0,0 +1,15 @@ +on: [pull_request, issue_comment] +jobs: + pr_agent_job: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + contents: write + name: Run pr agent on every pull request, respond to user comments + steps: + - name: PR Agent action step + uses: Codium-ai/pr-agent@main + env: + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/run-all-tests.yml b/.github/workflows/run-all-tests.yml index 75891c36..a5baf17b 100644 --- a/.github/workflows/run-all-tests.yml +++ b/.github/workflows/run-all-tests.yml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -26,11 +26,11 @@ jobs: pytest tests --cov=tests --cov=pygeoif --cov-report=xml - name: "Upload coverage to Codecov" if: ${{ matrix.python-version==3.11 }} - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: fail_ci_if_error: true verbose: true - token: ${{ env.CODECOV_TOKEN }} + token: ${{ secrets.CODECOV_TOKEN }} static-tests: runs-on: ubuntu-latest @@ -41,7 +41,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -71,7 +71,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.pypy-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.pypy-version }} - name: Install dependencies @@ -90,7 +90,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python 3.12 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.12 - name: Install pypa/build diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 258362e7..a93bf37f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,20 +32,20 @@ repos: hooks: - id: absolufy-imports - repo: https://github.com/pycqa/isort - rev: 5.12.0 + rev: 5.13.2 hooks: - id: isort - repo: https://github.com/psf/black - rev: 23.11.0 + rev: 24.1.1 hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.1.6' + rev: 'v0.1.14' hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/PyCQA/flake8 - rev: 6.1.0 + rev: 7.0.0 hooks: - id: flake8 additional_dependencies: @@ -68,7 +68,7 @@ repos: - flake8-typing-imports - flake8-use-fstring - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.7.1 + rev: v1.8.0 hooks: - id: mypy # - repo: https://github.com/Lucas-C/pre-commit-hooks-markup @@ -76,7 +76,7 @@ repos: # hooks: # - id: rst-linter - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.15 + rev: v0.16 hooks: - id: validate-pyproject - repo: https://github.com/kieran-ryan/pyprojectsort @@ -84,7 +84,7 @@ repos: hooks: - id: pyprojectsort - repo: https://github.com/python-jsonschema/check-jsonschema - rev: "0.27.2" + rev: "0.27.3" hooks: - id: check-github-workflows - id: check-github-actions diff --git a/README.rst b/README.rst index 64873415..a32bbdd1 100644 --- a/README.rst +++ b/README.rst @@ -76,6 +76,18 @@ It was written to provide clean and python only geometries for fastkml_ :target: https://pypi.python.org/pypi/pygeoif/ :alt: Supported Python implementations +.. image:: https://img.shields.io/pypi/v/pygeoif.svg + :target: https://pypi.python.org/pypi/pygeoif/ + :alt: Latest Version + +.. image:: https://img.shields.io/pypi/l/pygeoif.svg + :target: https://pypi.python.org/pypi/pygeoif/ + :alt: License + +.. image:: https://img.shields.io/pypi/dm/pygeoif.svg + :target: https://pypi.python.org/pypi/pygeoif/ + :alt: Downloads + Installation ------------ @@ -103,7 +115,7 @@ Example You find more examples in the -`tests `_ +`tests `_ directory which cover every aspect of pygeoif or in fastkml_. Classes diff --git a/docs/HISTORY.rst b/docs/HISTORY.rst index 6e5c9718..0f0331ed 100644 --- a/docs/HISTORY.rst +++ b/docs/HISTORY.rst @@ -1,6 +1,10 @@ Changelog ========= +1.3.0 (unreleased) +------------------ + + 1.2.0 (2023/11/27) ------------------ diff --git a/mutmut_config.py b/mutmut_config.py index d99f99de..9436f1e0 100644 --- a/mutmut_config.py +++ b/mutmut_config.py @@ -1,4 +1,5 @@ """Mutmut configuration.""" + from typing import Protocol files_to_mutate = [ diff --git a/pygeoif/about.py b/pygeoif/about.py index 10c86489..29e2e3a5 100644 --- a/pygeoif/about.py +++ b/pygeoif/about.py @@ -3,4 +3,5 @@ The only purpose of this module is to provide a version number for the package. """ -__version__ = "1.2.0" + +__version__ = "1.3.0" diff --git a/pygeoif/factories.py b/pygeoif/factories.py index b0f61450..ff8e32ee 100644 --- a/pygeoif/factories.py +++ b/pygeoif/factories.py @@ -130,6 +130,7 @@ def shape( >>> geom2 = shape(geom) >>> geom == geom2 True + """ type_map = { "Point": Point, @@ -173,6 +174,7 @@ def num(number: str) -> float: Returns ------- float or an integer if the string can be converted to an integer + """ f = float(number) return int(f) if int(f) == f else f @@ -328,6 +330,7 @@ def mapping( >>> pt = Point(0, 0) >>> mapping(pt) {'type': 'Point', 'bbox': (0, 0, 0, 0), 'coordinates': (0, 0)} + """ return ob.__geo_interface__ diff --git a/pygeoif/feature.py b/pygeoif/feature.py index 633d51d6..90747f29 100644 --- a/pygeoif/feature.py +++ b/pygeoif/feature.py @@ -75,6 +75,7 @@ class Feature: {'Name': 'Sample Point', 'Other': 'Other Data'} >>> a.properties['Name'] 'Sample Point' + """ def __init__( @@ -110,7 +111,7 @@ def __repr__(self) -> str: ) @property - def id(self) -> Optional[Union[str, int]]: # noqa: A003 + def id(self) -> Optional[Union[str, int]]: """Return the id of the feature.""" return self._feature_id @@ -168,6 +169,7 @@ class FeatureCollection: {'geometry': {'type': 'Point', 'coordinates': (1.0, -1.0)}, 'type': 'Feature', 'properties': {'Other': 'Other Data2', 'Name': 'Sample Point2'}}]} + """ def __init__(self, features: Sequence[Feature]) -> None: diff --git a/pygeoif/geometry.py b/pygeoif/geometry.py index 9b862777..4586212f 100644 --- a/pygeoif/geometry.py +++ b/pygeoif/geometry.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2012 -2023 Christian Ledermann +# Copyright (C) 2012 -2024 Christian Ledermann # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -233,6 +233,7 @@ class Point(_Geometry): -1.0 >>> p.x 1.0 + """ _geoms: PointType @@ -245,6 +246,7 @@ def __init__(self, x: float, y: float, z: Optional[float] = None) -> None: ---------- 2 or 3 coordinate parameters: x, y, [z] : float Easting, northing, and elevation. + """ object.__setattr__( self, @@ -339,6 +341,7 @@ class LineString(_Geometry): ---------- geoms : sequence A sequence of Points + """ _geoms: Tuple[Point, ...] @@ -357,6 +360,7 @@ def __init__(self, coordinates: LineType) -> None: Create a line with two segments >>> a = LineString([(0, 0), (1, 0), (1, 1)]) + """ object.__setattr__(self, "_geoms", self._set_geoms(coordinates)) @@ -379,26 +383,15 @@ def is_empty(self) -> bool: """ Return if this geometry is empty. - A Linestring is considered empty when it has less than 2 points. + A Linestring is considered empty when it has no points. """ - return len(self._geoms) < 2 # noqa: PLR2004 + return len(self._geoms) == 0 @property def has_z(self) -> Optional[bool]: """Return True if the geometry's coordinate sequence(s) have z values.""" return self._geoms[0].has_z if self.geoms else None - @property - def maybe_valid(self) -> bool: - """ - Check validity of the coordinates. - - Returns False if the coordinates collapse to a single Point. - This only highlights obvious problems with this geometry. - Even if this test passes the geometry may still be invalid. - """ - return len({p.coords[0] for p in self._geoms}) > 1 - @property def _wkt_coords(self) -> str: return ", ".join(point._wkt_coords for point in self.geoms) # noqa: SLF001 @@ -475,6 +468,7 @@ def __init__(self, coordinates: LineType) -> None: ---- coordinates (Sequence): A sequence of (x, y [,z]) numeric coordinate pairs or triples + """ super().__init__(coordinates) if not self.is_empty and self._geoms[0].coords != self._geoms[-1].coords: @@ -501,26 +495,6 @@ def is_ccw(self) -> bool: """Return True if the ring is oriented counter clock-wise.""" return signed_area(self.coords) >= 0 - @property - def maybe_valid(self) -> bool: - """ - Check validity of the coordinates. - - This only highlights obvious problems with this geometry. - Even if this test passes the geometry may still be invalid. - """ - if self.has_z: - msg = "Validation is only implemented for 2D coordinates" - raise DimensionError(msg) - min_x, min_y, max_x, max_y = self.bounds # type: ignore [misc] - if min_x == max_x or min_y == max_y: - return False - try: - _, area = centroid(self.coords) - except ZeroDivisionError: - return False - return math.isclose(a=area, b=signed_area(self.coords)) - class Polygon(_Geometry): """ @@ -536,6 +510,7 @@ class Polygon(_Geometry): The ring which bounds the positive space of the polygon. interiors : sequence A sequence of rings which bound all existing holes. + """ _geoms: Tuple[LinearRing, ...] @@ -562,6 +537,7 @@ def __init__( >>> coords = ((0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.)) >>> polygon = Polygon(coords) + """ interiors = tuple(LinearRing(hole) for hole in holes) if holes else () exterior = LinearRing(shell) @@ -612,22 +588,6 @@ def has_z(self) -> Optional[bool]: """Return True if the geometry's coordinate sequence(s) have z values.""" return self._geoms[0].has_z - @property - def maybe_valid(self) -> bool: - """ - Check validity of the coordinates. - - This only highlights obvious problems with this geometry. - Even if this test passes the geometry may still be invalid. - """ - if not self._check_interior_bounds(): - return False - return ( - all(interior.maybe_valid for interior in self.interiors) - if self.exterior.maybe_valid - else False - ) - @property def _wkt_coords(self) -> str: ec = self.exterior._wkt_coords # noqa: SLF001 @@ -665,22 +625,6 @@ def _from_dict(cls, geo_interface: GeoInterface) -> "Polygon": holes=cast(Tuple[LineType], geo_interface["coordinates"][1:]), ) - def _check_interior_bounds(self) -> bool: - """Check that the bounding boxes of holes are inside the bounds of the shell.""" - bounds = self.bounds - if not bounds: - return False - for interior in self.interiors: - i_box = cast(Bounds, interior.bounds) - if ( - bounds[0] > i_box[0] - or bounds[1] > i_box[1] - or bounds[2] < i_box[2] - or bounds[3] < i_box[3] - ): - return False - return True - def _get_bounds(self) -> Bounds: return self.exterior._get_bounds() # noqa: SLF001 @@ -747,6 +691,7 @@ class MultiPoint(_MultiGeometry): ---------- geoms : sequence A sequence of Points + """ _geoms: Tuple[Point, ...] @@ -772,6 +717,7 @@ def __init__(self, points: Sequence[PointType], unique: bool = False) -> None: 2 >>> type(ob.geoms[0]) == Point True + """ if unique: points = set(points) # type: ignore [assignment] @@ -825,6 +771,7 @@ class MultiLineString(_MultiGeometry): ---------- geoms : sequence A sequence of LineStrings + """ _geoms: Tuple[LineString, ...] @@ -846,6 +793,7 @@ def __init__(self, lines: Sequence[LineType], unique: bool = False) -> None: Construct a collection containing one line string. >>> lines = MultiLineString( [[[0.0, 0.0], [1.0, 2.0]]] ) + """ if unique: lines = {tuple(line) for line in lines} # type: ignore [assignment] @@ -909,6 +857,7 @@ class MultiPolygon(_MultiGeometry): ---------- geoms : sequence A sequence of `Polygon` instances + """ _geoms: Tuple[Polygon, ...] @@ -942,6 +891,7 @@ def __init__(self, polygons: Sequence[PolygonType], unique: bool = False) -> Non 1 >>> type(ob.geoms[0]) == Polygon True + """ if unique: polygons = set(polygons) # type: ignore [assignment] @@ -1045,6 +995,7 @@ class isn't generally supported by ordinary GIS sw (viewers and so on). So {'type': 'GeometryCollection', 'geometries': [{'type': 'Point', 'coordinates': (1.0, -1.0)}, {'type': 'Point', 'coordinates': (1.0, -1.0)}]} + """ _geoms: Tuple[Union[Geometry, "GeometryCollection"], ...] @@ -1059,6 +1010,7 @@ def __init__( Args: ---- geometries (Iterable[Geometry] + """ object.__setattr__(self, "_geoms", tuple(geom for geom in geometries if geom)) @@ -1099,6 +1051,7 @@ def __len__(self) -> int: Returns ------- int: Number of geometries in the collection. + """ return len(self._geoms) diff --git a/pygeoif/types.py b/pygeoif/types.py index e9de16f6..9738b64f 100644 --- a/pygeoif/types.py +++ b/pygeoif/types.py @@ -87,13 +87,17 @@ class GeoFeatureCollectionInterface(TypedDict): class GeoType(Protocol): """Any compatible type that implements the __geo_interface__.""" - __geo_interface__: GeoInterface + @property + def __geo_interface__(self) -> GeoInterface: + """Return the GeoInterface.""" class GeoCollectionType(Protocol): """Any compatible type that implements the __geo_interface__.""" - __geo_interface__: GeoCollectionInterface + @property + def __geo_interface__(self) -> GeoCollectionInterface: + """Return the GeoInterface.""" __all__ = [ diff --git a/pyproject.toml b/pyproject.toml index 7a3bee81..8de74719 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ classifiers = [ "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Scientific/Engineering :: GIS", + "Typing :: Typed", ] dependencies = [ "typing_extensions", @@ -153,6 +154,9 @@ reportMissingTypeStubs = true [tool.ruff] fix = true +target-version = "py38" + +[tool.ruff.lint] ignore = [ "ANN101", "ANN102", @@ -220,12 +224,11 @@ select = [ "W", "YTT", ] -target-version = "py38" -[tool.ruff.isort] +[tool.ruff.lint.isort] force-single-line = true -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "pygeoif/factories.py" = [ "SLF001", ] diff --git a/tests/test_base.py b/tests/test_base.py index af98c21b..961f4beb 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,4 +1,5 @@ """Test Baseclass.""" + from unittest import mock import pytest diff --git a/tests/test_factories.py b/tests/test_factories.py index 71994ac1..f56528bc 100644 --- a/tests/test_factories.py +++ b/tests/test_factories.py @@ -94,7 +94,6 @@ def test_force_2d_polygon() -> None: ((0.5, 0.5), (0.5, 1.5), (1.5, 1.5), (1.5, 0.5), (0.5, 0.5)), ) assert not p2d.has_z - assert p.maybe_valid == p2d.maybe_valid # 3d to 2d external = [(0, 0, 1), (0, 2, 1), (2, 2, 1), (2, 0, 1), (0, 0, 1)] diff --git a/tests/test_feature.py b/tests/test_feature.py index 6e3fa3ac..641dcae6 100644 --- a/tests/test_feature.py +++ b/tests/test_feature.py @@ -1,4 +1,5 @@ """Test Feature and FeatureCollection.""" + import unittest import pytest diff --git a/tests/test_functions.py b/tests/test_functions.py index e1441a22..23715543 100644 --- a/tests/test_functions.py +++ b/tests/test_functions.py @@ -1,4 +1,5 @@ """Test geometric functions.""" + import itertools import math import random diff --git a/tests/test_line.py b/tests/test_line.py index 6cc60f40..0665a430 100644 --- a/tests/test_line.py +++ b/tests/test_line.py @@ -1,4 +1,5 @@ """Test LineString.""" + from unittest import mock import pytest @@ -190,24 +191,6 @@ def test_from_coordinates() -> None: assert geometry.LineString.from_coordinates(line.coords) == line -def test_maybe_valid() -> None: - line = geometry.LineString([(0, 0), (1, 0)]) - - assert line.maybe_valid - - -def test_maybe_valid_point() -> None: - line = geometry.LineString([(0, 0), (0, 0)]) - - assert not line.maybe_valid - - -def test_maybe_empty() -> None: - line = geometry.LineString([]) - - assert not line.maybe_valid - - def test_empty() -> None: line = geometry.LineString([]) @@ -217,7 +200,7 @@ def test_empty() -> None: def test_empty_1_pt() -> None: line = geometry.LineString([(0, 0)]) - assert line.is_empty + assert not line.is_empty def test_repr_empty() -> None: diff --git a/tests/test_linear_ring.py b/tests/test_linear_ring.py index 8e4d3c9b..8e59f68c 100644 --- a/tests/test_linear_ring.py +++ b/tests/test_linear_ring.py @@ -1,4 +1,5 @@ """Test LinearRing.""" + from unittest import mock import pytest @@ -150,46 +151,6 @@ def test_convex_hull_linear_ring() -> None: assert line.convex_hull == geometry.Polygon([(0, 0), (1, 0), (2, 2), (0, 0)]) -def test_maybe_valid_crossing() -> None: - line = geometry.LinearRing([(0, 0), (1, 0), (1, 1), (0, -1)]) - - assert not line.maybe_valid - - -def test_maybe_valid_no_area() -> None: - line = geometry.LinearRing([(0, 0), (1, 1)]) - - assert not line.maybe_valid - - -def test_maybe_valid_x_line() -> None: - line = geometry.LinearRing([(0, 2), (1, 2)]) - - assert not line.maybe_valid - - -def test_maybe_valid_y_line() -> None: - line = geometry.LinearRing([(3, 0), (3, 1)]) - - assert not line.maybe_valid - - -def test_maybe_valid_happy() -> None: - line = geometry.LinearRing([(0, 0), (1, 0), (1, 1), (0, 0)]) - - assert line.maybe_valid - - -def test_valid_3d() -> None: - line = geometry.LinearRing([(0, 0, 1), (2, 0, 2), (2, 2, 0), (0, 2, 0)]) - - with pytest.raises( - exceptions.DimensionError, - match="^Validation is only implemented for 2D coordinates$", - ): - assert line.maybe_valid - - def test_is_ccw() -> None: line = geometry.LinearRing([(0, 0), (1, 0), (1, 1), (0, 0)]) diff --git a/tests/test_multiline.py b/tests/test_multiline.py index 40d14280..46f396e9 100644 --- a/tests/test_multiline.py +++ b/tests/test_multiline.py @@ -1,4 +1,5 @@ """Test MultiLineString.""" + from pygeoif import geometry diff --git a/tests/test_multipoint.py b/tests/test_multipoint.py index 693bad36..04c3705d 100644 --- a/tests/test_multipoint.py +++ b/tests/test_multipoint.py @@ -1,4 +1,5 @@ """Test MultiPoint.""" + import pytest from pygeoif import geometry diff --git a/tests/test_multipolygon.py b/tests/test_multipolygon.py index 967b8fee..c0322bc1 100644 --- a/tests/test_multipolygon.py +++ b/tests/test_multipolygon.py @@ -1,4 +1,5 @@ """Test MultiPolygon.""" + from pygeoif import geometry diff --git a/tests/test_point.py b/tests/test_point.py index 33a3fb93..9d91682d 100644 --- a/tests/test_point.py +++ b/tests/test_point.py @@ -1,4 +1,5 @@ """Test Point.""" + import math from unittest import mock diff --git a/tests/test_polygon.py b/tests/test_polygon.py index 4ef462d3..8b17c1c2 100644 --- a/tests/test_polygon.py +++ b/tests/test_polygon.py @@ -1,4 +1,5 @@ """Test Polygon.""" + from unittest import mock from pygeoif import geometry @@ -249,77 +250,6 @@ def test_from_coordinates_with_holes() -> None: assert geometry.Polygon.from_coordinates(polygon.coords) == polygon -def test_maybe_valid() -> None: - e = [(0, 0), (0, 2), (2, 2), (2, 0), (0, 0)] - i = [(0.5, 0.5), (1, 1), (0.5, 1)] - polygon = geometry.Polygon(e, [i]) - - assert polygon.maybe_valid - - -def test_maybe_valid_touching_hole() -> None: - """A Hole may touch an exterior at one point.""" - e = [(0, 0), (0, 4), (4, 4), (4, 0)] - interiors_gen = (((1, 1), (2, 3), e[pt]) for pt in range(len(e))) - for polygon in (geometry.Polygon(e, [interior]) for interior in interiors_gen): - assert polygon.maybe_valid - - -def test_is_invalid_hole_too_big_y() -> None: - """A Hole may not cross an exterior.""" - e = [(0, 0), (0, 4), (4, 4), (4, 0)] - outside = ( - (-1, -1), - (-1, 5), - (5, 5), - (5, -1), - (-1, 0), - (-1, 4), - (5, 4), - (5, 0), - (0, -1), - (0, 5), - (4, 5), - (4, -1), - ) - interiors_gen = ( - ((1 + (i & 1), 1), (3, 3 - (i & 1)), outside[i]) for i in range(len(e)) - ) - for polygon in (geometry.Polygon(e, [interior]) for interior in interiors_gen): - assert not polygon.maybe_valid - - -def test_is_invalid_hole_too_big_x() -> None: - e = [(0, 0), (0, 2), (2, 2), (2, 0), (0, 0)] - i = [(0.5, 0.5), (3, 1), (0.5, 1)] - polygon = geometry.Polygon(e, [i]) - - assert not polygon.maybe_valid - - -def test_is_invalid_hole_too_big_min() -> None: - e = [(0, 0), (0, 2), (2, 2), (2, 0), (0, 0)] - i = [(-0.5, -0.5), (3, 1), (0.5, 1)] - polygon = geometry.Polygon(e, [i]) - - assert not polygon.maybe_valid - - -def test_is_invalid_exterior() -> None: - e = [(0, 0), (1, 0), (1, 1), (0, -1), (0, 0)] - polygon = geometry.Polygon(e) - - assert not polygon.maybe_valid - - -def test_is_invalid_interior() -> None: - e = [(-2, -2), (-2, 2), (2, 2), (2, -2), (-2, -2)] - i = [(0, 0), (1, 0), (1, 1), (0, -1), (0, 0)] - polygon = geometry.Polygon(e, [i]) - - assert not polygon.maybe_valid - - def test_empty() -> None: polygon = geometry.Polygon([]) @@ -342,9 +272,3 @@ def test_empty_bounds() -> None: polygon = geometry.Polygon([]) assert polygon.bounds == () - - -def test_maybe_valid_empty() -> None: - polygon = geometry.Polygon([]) - - assert not polygon.maybe_valid