Skip to content

Commit

Permalink
add hypothesis tests for functions
Browse files Browse the repository at this point in the history
  • Loading branch information
cleder committed May 1, 2024
1 parent 05d8d8c commit beae74a
Show file tree
Hide file tree
Showing 9 changed files with 354 additions and 18 deletions.
7 changes: 2 additions & 5 deletions pygeoif/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,12 @@ def shape(
)
raise TypeError(msg)

constructor = type_map.get(geometry["type"])
if constructor:
if constructor := type_map.get(geometry["type"]):
return constructor._from_dict( # type: ignore [attr-defined, no-any-return]
geometry,
)
if geometry["type"] == "GeometryCollection":
geometries = [
shape(fi) for fi in geometry["geometries"] # type: ignore [typeddict-item]
]
geometries = [shape(fi) for fi in geometry["geometries"]]
return GeometryCollection(geometries)
msg = f"[{geometry['type']} is not implemented"
raise NotImplementedError(msg)
Expand Down
17 changes: 11 additions & 6 deletions pygeoif/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ def signed_area(coords: LineType) -> float:
Linear time algorithm: http://www.cgafaq.info/wiki/Polygon_Area.
A value >= 0 indicates a counter-clockwise oriented ring.
"""
if len(coords) < 3: # noqa: PLR2004
return 0.0
xs, ys = map(list, zip(*(coord[:2] for coord in coords)))
xs.append(xs[1]) # pragma: no mutate
ys.append(ys[1]) # pragma: no mutate
Expand All @@ -53,7 +55,6 @@ def signed_area(coords: LineType) -> float:
def centroid(coords: LineType) -> Tuple[Point2D, float]:
"""Calculate the coordinates of the centroid and the area of a LineString."""
ans: List[float] = [0, 0]

n = len(coords)
signed_area = 0.0

Expand All @@ -68,6 +69,9 @@ def centroid(coords: LineType) -> Tuple[Point2D, float]:
ans[0] += (coord[0] + next_coord[0]) * area
ans[1] += (coord[1] + next_coord[1]) * area

if signed_area == 0 or math.isnan(signed_area):
return ((math.nan, math.nan), signed_area)

ans[0] = ans[0] / (3 * signed_area)
ans[1] = ans[1] / (3 * signed_area)

Expand Down Expand Up @@ -167,13 +171,13 @@ def compare_geo_interface(
return all(
compare_geo_interface(first=g1, second=g2) # type: ignore [arg-type]
for g1, g2 in zip_longest(
first["geometries"], # type: ignore [typeddict-item]
first["geometries"],
second["geometries"], # type: ignore [typeddict-item]
fillvalue={"type": None, "coordinates": ()},
)
)
return compare_coordinates(
coords=first["coordinates"], # type: ignore [typeddict-item]
coords=first["coordinates"],
other=second["coordinates"], # type: ignore [typeddict-item]
)
except KeyError:
Expand Down Expand Up @@ -220,6 +224,8 @@ def move_coordinates(
>>> move_coordinates(((0, 0), (-1, 1)), (-1, 1, 0))
((-1, 1, 0), (-2, 2, 0))
"""
if not coordinates:
return coordinates
if isinstance(coordinates[0], (int, float)):
return move_coordinate(cast(PointType, coordinates), move_by)
return cast(
Expand All @@ -237,14 +243,13 @@ def move_geo_interface(
return {
"type": "GeometryCollection",
"geometries": tuple(
move_geo_interface(g, move_by)
for g in interface["geometries"] # type: ignore [typeddict-item]
move_geo_interface(g, move_by) for g in interface["geometries"]
),
}
return {
"type": interface["type"],
"coordinates": move_coordinates(
interface["coordinates"], # type: ignore [typeddict-item, arg-type]
interface["coordinates"], # type: ignore [arg-type]
move_by,
),
}
Expand Down
11 changes: 7 additions & 4 deletions pygeoif/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from pygeoif.types import Bounds
from pygeoif.types import GeoCollectionInterface
from pygeoif.types import GeoInterface
from pygeoif.types import GeomType
from pygeoif.types import GeoType
from pygeoif.types import LineType
from pygeoif.types import Point2D
Expand Down Expand Up @@ -166,7 +167,7 @@ def __geo_interface__(self) -> GeoInterface:
msg = "Empty Geometry"
raise AttributeError(msg)
return {
"type": self.geom_type,
"type": cast(GeomType, self.geom_type),
"bbox": cast(Bounds, self.bounds),
"coordinates": (),
}
Expand Down Expand Up @@ -481,9 +482,9 @@ def centroid(self) -> Optional[Point]:
if self.has_z:
msg = "Centeroid is only implemented for 2D coordinates"
raise DimensionError(msg)
try:
cent, area = centroid(self.coords)
except ZeroDivisionError:

cent, area = centroid(self.coords)
if any(math.isnan(coord) for coord in cent):
return None
return (
Point(x=cent[0], y=cent[1])
Expand Down Expand Up @@ -625,6 +626,8 @@ def from_linear_rings(cls, shell: LinearRing, *args: LinearRing) -> "Polygon":
@classmethod
def _from_dict(cls, geo_interface: GeoInterface) -> "Polygon":
cls._check_dict(geo_interface)
if not geo_interface["coordinates"]:
return cls(shell=(), holes=())
return cls(
shell=cast(LineType, geo_interface["coordinates"][0]),
holes=cast(Tuple[LineType], geo_interface["coordinates"][1:]),
Expand Down
12 changes: 11 additions & 1 deletion pygeoif/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,21 @@
]
MultiCoordinatesType = Sequence[CoordinatesType]

GeomType = Literal[
"Point",
"LineString",
"LinearRing",
"Polygon",
"MultiPoint",
"MultiLineString",
"MultiPolygon",
]


class GeoInterface(TypedDict):
"""Required keys for the GeoInterface."""

type: str
type: GeomType
coordinates: Union[CoordinatesType, MultiCoordinatesType]
bbox: NotRequired[Bounds]

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ linting = [
]
tests = [
"hypothesis",
"more_itertools",
"pytest",
"pytest-cov",
]
Expand Down
Loading

0 comments on commit beae74a

Please sign in to comment.