diff --git a/geo_extensions/transformer.py b/geo_extensions/transformer.py index 75d52f1..9fd18fd 100644 --- a/geo_extensions/transformer.py +++ b/geo_extensions/transformer.py @@ -1,7 +1,7 @@ from typing import Iterable, List, Sequence, Tuple from shapely import Geometry, wkt -from shapely.geometry import MultiPolygon, Polygon +from shapely.geometry import MultiPolygon, Polygon, shape from geo_extensions.types import Transformation, TransformationResult @@ -12,6 +12,18 @@ class Transformer: def __init__(self, transformations: Sequence[Transformation]): self.transformations = transformations + def from_geo_json(self, geo_json: dict) -> List[Polygon]: + """Load and transform an object from a GeoJSON dict. + + :returns: a list of transformed polygons + :raises: ShapelyError, Exception + """ + + obj = shape(geo_json) + polygons = to_polygons(obj) + + return self.transform(polygons) + def from_wkt(self, wkt_str: str) -> List[Polygon]: """Load and transform an object from a WKT string. diff --git a/tests/test_transformer.py b/tests/test_transformer.py index 8e78c7f..ee11b20 100644 --- a/tests/test_transformer.py +++ b/tests/test_transformer.py @@ -71,6 +71,117 @@ def test_from_wkt_multipolygon(simplify_transformer): ] +def test_from_geo_json(simplify_transformer): + # Clockwise polygon remains clockwise + assert simplify_transformer.from_geo_json({ + "type": "Polygon", + "coordinates": [ + [ + [50, 20], + [50, 21], + [51, 21], + [51, 20], + [50, 20], + ], + ], + }) == [ + Polygon([ + (50.0, 20.0), + (50.0, 21.0), + (51.0, 21.0), + (51.0, 20.0), + (50.0, 20.0), + ]), + ] + assert simplify_transformer.from_geo_json({ + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [2, 1], + [1, 2], + [1, 1], + ], + ], + }) == [ + Polygon([ + (1, 1), + (2, 1), + (1, 2), + (1, 1), + ]), + ] + # Duplicate point is removed + assert simplify_transformer.from_geo_json({ + "type": "Polygon", + "coordinates": [ + [ + [50.0, 20.0], + [50.0, 21.0], + [51.0, 21.0], + [51.0, 20.0], + [50.0, 20.0], + [50.0, 20.0], + ], + ], + }) == [ + Polygon( + [ + (50.0, 20.0), + (50.0, 21.0), + (51.0, 21.0), + (51.0, 20.0), + (50.0, 20.0), + ], + ), + ] + + +def test_from_geo_json_multipolygon(simplify_transformer): + assert simplify_transformer.from_geo_json( + { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [30, 20], + [45, 40], + [10, 40], + [30, 20], + ], + ], + [ + [ + [15, 5], + [40, 10], + [10, 20], + [5, 10], + [15, 5], + ], + ], + ], + }, + ) == [ + Polygon( + [ + (30.0, 20.0), + (45.0, 40.0), + (10.0, 40.0), + (30.0, 20.0), + ], + ), + Polygon( + [ + (15.0, 5.0), + (40.0, 10.0), + (10.0, 20.0), + (5.0, 10.0), + (15.0, 5.0), + ], + ), + ] + + def test_from_wkt_bad_points(simplify_transformer): with pytest.raises( Exception, @@ -80,3 +191,17 @@ def test_from_wkt_bad_points(simplify_transformer): with pytest.raises(ShapelyError): simplify_transformer.from_wkt("") + + +def test_from_geo_json_bad_points(simplify_transformer): + with pytest.raises( + Exception, + match=r"WKT: 'POINT \(30 10\)' is not a Polygon or MultiPolygon", + ): + simplify_transformer.from_geo_json({ + "type": "Point", + "coordinates": [[30, 10]], + }) + + with pytest.raises(AttributeError): + simplify_transformer.from_geo_json({})