Skip to content

Commit 490dcba

Browse files
committed
Allow empty geometries. Fixes: #89
1 parent 49af995 commit 490dcba

File tree

3 files changed

+50
-12
lines changed

3 files changed

+50
-12
lines changed

geojson_pydantic/geometries.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,14 @@ def _wkt_coordinates(self) -> str:
207207
f"({_lines_wtk_coordinates(polygon)})" for polygon in self.coordinates
208208
)
209209

210+
@validator("coordinates")
211+
def check_closure(cls, coordinates: List) -> List:
212+
"""Validate that Polygon is closed (first and last coordinate are the same)."""
213+
if any([ring[-1] != ring[0] for polygon in coordinates for ring in polygon]):
214+
raise ValueError("All linear rings have the same start and end coordinates")
215+
216+
return coordinates
217+
210218

211219
Geometry = Union[Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon]
212220

geojson_pydantic/types.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
if TYPE_CHECKING:
1616
MultiPointCoords = List[Position]
1717
LineStringCoords = List[Position]
18-
MultiLineStringCoords = List[List[Position]]
1918
LinearRing = List[Position]
19+
MultiLineStringCoords = List[List[Position]]
2020
PolygonCoords = List[List[Position]]
2121
MultiPolygonCoords = List[List[List[Position]]]
2222
else:
23-
MultiPointCoords = conlist(Position, min_items=1)
23+
MultiPointCoords = conlist(Position)
2424
LineStringCoords = conlist(Position, min_items=2)
25-
MultiLineStringCoords = conlist(LineStringCoords, min_items=1)
2625
LinearRing = conlist(Position, min_items=4)
27-
PolygonCoords = conlist(LinearRing, min_items=1)
28-
MultiPolygonCoords = conlist(PolygonCoords, min_items=1)
26+
MultiLineStringCoords = conlist(LineStringCoords)
27+
PolygonCoords = conlist(LinearRing)
28+
MultiPolygonCoords = conlist(PolygonCoords)

tests/test_geometries.py

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def assert_wkt_equivalence(geom: Union[Geometry, GeometryCollection]):
2828
@pytest.mark.parametrize("coordinates", [(1.01, 2.01), (1.0, 2.0, 3.0), (1.0, 2.0)])
2929
def test_point_valid_coordinates(coordinates):
3030
"""
31-
Two or three number elements as coordinates shold be okay
31+
Two or three number elements as coordinates should be okay
3232
"""
3333
p = Point(type="Point", coordinates=coordinates)
3434
assert p.type == "Point"
@@ -38,7 +38,8 @@ def test_point_valid_coordinates(coordinates):
3838

3939

4040
@pytest.mark.parametrize(
41-
"coordinates", [(1.0,), (1.0, 2.0, 3.0, 4.0), "Foo", (None, 2.0), (1.0, (2.0,))]
41+
"coordinates",
42+
[(1.0,), (1.0, 2.0, 3.0, 4.0), "Foo", (None, 2.0), (1.0, (2.0,)), (), [], None],
4243
)
4344
def test_point_invalid_coordinates(coordinates):
4445
"""
@@ -51,6 +52,8 @@ def test_point_invalid_coordinates(coordinates):
5152
@pytest.mark.parametrize(
5253
"coordinates",
5354
[
55+
# Empty array
56+
[],
5457
# No Z
5558
[(1.0, 2.0)],
5659
[(1.0, 2.0), (1.0, 2.0)],
@@ -60,7 +63,7 @@ def test_point_invalid_coordinates(coordinates):
6063
)
6164
def test_multi_point_valid_coordinates(coordinates):
6265
"""
63-
Two or three number elements as coordinates shold be okay
66+
Two or three number elements as coordinates should be okay, as well as an empty array.
6467
"""
6568
p = MultiPoint(type="MultiPoint", coordinates=coordinates)
6669
assert p.type == "MultiPoint"
@@ -71,7 +74,7 @@ def test_multi_point_valid_coordinates(coordinates):
7174

7275
@pytest.mark.parametrize(
7376
"coordinates",
74-
[[(1.0,)], [(1.0, 2.0, 3.0, 4.0)], ["Foo"], [(None, 2.0)], [(1.0, (2.0,))]],
77+
[[(1.0,)], [(1.0, 2.0, 3.0, 4.0)], ["Foo"], [(None, 2.0)], [(1.0, (2.0,))], None],
7578
)
7679
def test_multi_point_invalid_coordinates(coordinates):
7780
"""
@@ -115,6 +118,8 @@ def test_line_string_invalid_coordinates(coordinates):
115118
@pytest.mark.parametrize(
116119
"coordinates",
117120
[
121+
# Empty array
122+
[],
118123
# One line, two points, no Z
119124
[[(1.0, 2.0), (3.0, 4.0)]],
120125
# One line, two points, has Z
@@ -139,7 +144,7 @@ def test_multi_line_string_valid_coordinates(coordinates):
139144

140145

141146
@pytest.mark.parametrize(
142-
"coordinates", [[None], ["Foo"], [[]], [[(1.0, 2.0)]], [["Foo", "Bar"]]]
147+
"coordinates", [None, [None], ["Foo"], [[]], [[(1.0, 2.0)]], [["Foo", "Bar"]]]
143148
)
144149
def test_multi_line_string_invalid_coordinates(coordinates):
145150
"""
@@ -152,6 +157,8 @@ def test_multi_line_string_invalid_coordinates(coordinates):
152157
@pytest.mark.parametrize(
153158
"coordinates",
154159
[
160+
# Empty array
161+
[],
155162
# Polygon, no Z
156163
[[(1.0, 2.0), (3.0, 4.0), (5.0, 6.0), (1.0, 2.0)]],
157164
# Polygon, has Z
@@ -166,7 +173,8 @@ def test_polygon_valid_coordinates(coordinates):
166173
assert polygon.type == "Polygon"
167174
assert polygon.coordinates == coordinates
168175
assert hasattr(polygon, "__geo_interface__")
169-
assert polygon.exterior == coordinates[0]
176+
if polygon.coordinates:
177+
assert polygon.exterior == coordinates[0]
170178
assert not list(polygon.interiors)
171179
assert_wkt_equivalence(polygon)
172180

@@ -212,10 +220,10 @@ def test_polygon_with_holes(coordinates):
212220
"coordinates",
213221
[
214222
"foo",
223+
None,
215224
[[(1.0, 2.0), (3.0, 4.0), (5.0, 6.0), (1.0, 2.0)], "foo", None],
216225
[[(1.0, 2.0), (3.0, 4.0), (1.0, 2.0)]],
217226
[[(1.0, 2.0), (3.0, 4.0), (5.0, 6.0), (7.0, 8.0)]],
218-
[],
219227
],
220228
)
221229
def test_polygon_invalid_coordinates(coordinates):
@@ -233,6 +241,8 @@ def test_polygon_invalid_coordinates(coordinates):
233241
@pytest.mark.parametrize(
234242
"coordinates",
235243
[
244+
# Empty array
245+
[],
236246
# Multipolygon, no Z
237247
[
238248
[
@@ -270,6 +280,26 @@ def test_multi_polygon(coordinates):
270280
assert_wkt_equivalence(multi_polygon)
271281

272282

283+
@pytest.mark.parametrize(
284+
"coordinates",
285+
[
286+
"foo",
287+
None,
288+
[
289+
[
290+
[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0), (0.0, 0.0)],
291+
],
292+
[
293+
[(2.1, 2.1), (2.2, 2.1), (2.2, 2.2), (2.1, 4.2)],
294+
],
295+
],
296+
],
297+
)
298+
def test_multipolygon_invalid_coordinates(coordinates):
299+
with pytest.raises(ValidationError):
300+
MultiPolygon(type="MultiPolygon", coordinates=coordinates)
301+
302+
273303
def test_parse_geometry_obj_point():
274304
assert parse_geometry_obj({"type": "Point", "coordinates": [102.0, 0.5]}) == Point(
275305
type="Point", coordinates=(102.0, 0.5)

0 commit comments

Comments
 (0)