Skip to content

Commit 74a8cf0

Browse files
Merge pull request #140 from eseglem/bugfix/139-WKT-tests
Tweak WKT generation and tests.
2 parents f89b588 + 567bd63 commit 74a8cf0

File tree

3 files changed

+60
-29
lines changed

3 files changed

+60
-29
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Note: Minor version `0.X.0` update might break the API, It's recommanded to pin
1111
### Fixed
1212

1313
* reduce validation error message verbosity when discriminating Geometry types
14+
* MultiPoint WKT now includes parentheses around each Point
1415

1516
### Added
1617

geojson_pydantic/geometries.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def _lines_has_z(lines: List[LineStringCoords]) -> bool:
6767
def _polygons_wkt_coordinates(
6868
coordinates: List[PolygonCoords], force_z: bool = False
6969
) -> str:
70-
return ",".join(
70+
return ", ".join(
7171
f"({_lines_wtk_coordinates(polygon, force_z)})" for polygon in coordinates
7272
)
7373

@@ -134,7 +134,10 @@ class MultiPoint(_GeometryBase):
134134

135135
def __wkt_coordinates__(self, coordinates: Any, force_z: bool) -> str:
136136
"""return WKT coordinates."""
137-
return _position_list_wkt_coordinates(coordinates, force_z)
137+
return ", ".join(
138+
f"({_position_wkt_coordinates(position, force_z)})"
139+
for position in coordinates
140+
)
138141

139142
@property
140143
def has_z(self) -> bool:
@@ -319,7 +322,7 @@ def check_geometries(cls, geometries: List) -> List:
319322
Field(discriminator="type"),
320323
]
321324

322-
GeometryCollection.update_forward_refs()
325+
GeometryCollection.model_rebuild()
323326

324327

325328
def parse_geometry_obj(obj: Any) -> Geometry:

tests/test_geometries.py

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
import re
2-
from typing import Union
3-
41
import pytest
2+
import shapely
53
from pydantic import ValidationError
6-
from shapely.geometry import shape
74

85
from geojson_pydantic.geometries import (
9-
Geometry,
106
GeometryCollection,
117
LineString,
128
MultiLineString,
@@ -35,13 +31,6 @@ def test_pydantic_schema(obj):
3531
assert obj.model_json_schema()
3632

3733

38-
def assert_wkt_equivalence(geom: Union[Geometry, GeometryCollection]):
39-
"""Assert WKT equivalence with Shapely."""
40-
# Remove any trailing `.0` to match Shapely format
41-
clean_wkt = re.sub(r"\.0(\D)", r"\1", geom.wkt)
42-
assert shape(geom).wkt == clean_wkt
43-
44-
4534
@pytest.mark.parametrize("coordinates", [(1.01, 2.01), (1.0, 2.0, 3.0), (1.0, 2.0)])
4635
def test_point_valid_coordinates(coordinates):
4736
"""
@@ -51,7 +40,6 @@ def test_point_valid_coordinates(coordinates):
5140
assert p.type == "Point"
5241
assert p.coordinates == coordinates
5342
assert hasattr(p, "__geo_interface__")
54-
assert_wkt_equivalence(p)
5543

5644

5745
@pytest.mark.parametrize(
@@ -88,7 +76,6 @@ def test_multi_point_valid_coordinates(coordinates):
8876
assert p.type == "MultiPoint"
8977
assert p.coordinates == coordinates
9078
assert hasattr(p, "__geo_interface__")
91-
assert_wkt_equivalence(p)
9279

9380

9481
@pytest.mark.parametrize(
@@ -123,7 +110,6 @@ def test_line_string_valid_coordinates(coordinates):
123110
assert linestring.type == "LineString"
124111
assert linestring.coordinates == coordinates
125112
assert hasattr(linestring, "__geo_interface__")
126-
assert_wkt_equivalence(linestring)
127113

128114

129115
@pytest.mark.parametrize("coordinates", [None, "Foo", [], [(1.0, 2.0)], ["Foo", "Bar"]])
@@ -162,7 +148,6 @@ def test_multi_line_string_valid_coordinates(coordinates):
162148
assert multilinestring.type == "MultiLineString"
163149
assert multilinestring.coordinates == coordinates
164150
assert hasattr(multilinestring, "__geo_interface__")
165-
assert_wkt_equivalence(multilinestring)
166151

167152

168153
@pytest.mark.parametrize(
@@ -200,7 +185,6 @@ def test_polygon_valid_coordinates(coordinates):
200185
else:
201186
assert polygon.exterior is None
202187
assert not list(polygon.interiors)
203-
assert_wkt_equivalence(polygon)
204188

205189

206190
@pytest.mark.parametrize(
@@ -248,7 +232,6 @@ def test_polygon_with_holes(coordinates):
248232
assert hasattr(polygon, "__geo_interface__")
249233
assert polygon.exterior == polygon.coordinates[0]
250234
assert list(polygon.interiors) == [polygon.coordinates[1]]
251-
assert_wkt_equivalence(polygon)
252235

253236

254237
@pytest.mark.parametrize(
@@ -325,7 +308,6 @@ def test_multi_polygon(coordinates):
325308

326309
assert multi_polygon.type == "MultiPolygon"
327310
assert hasattr(multi_polygon, "__geo_interface__")
328-
assert_wkt_equivalence(multi_polygon)
329311

330312

331313
@pytest.mark.parametrize(
@@ -494,7 +476,6 @@ def test_geometry_collection_iteration(coordinates):
494476
type="GeometryCollection", geometries=[polygon, multipolygon]
495477
)
496478
assert hasattr(gc, "__geo_interface__")
497-
assert_wkt_equivalence(gc)
498479
iter(gc)
499480

500481

@@ -508,7 +489,6 @@ def test_len_geometry_collection(coordinates):
508489
gc = GeometryCollection(
509490
type="GeometryCollection", geometries=[polygon, multipolygon]
510491
)
511-
assert_wkt_equivalence(gc)
512492
assert len(gc) == 2
513493

514494

@@ -522,21 +502,26 @@ def test_getitem_geometry_collection(coordinates):
522502
gc = GeometryCollection(
523503
type="GeometryCollection", geometries=[polygon, multipolygon]
524504
)
525-
assert_wkt_equivalence(gc)
526505
assert polygon == gc[0]
527506
assert multipolygon == gc[1]
528507

529508

530509
def test_wkt_mixed_geometry_collection():
531510
point = Point(type="Point", coordinates=(0.0, 0.0, 0.0))
532511
line_string = LineString(type="LineString", coordinates=[(0.0, 0.0), (1.0, 1.0)])
533-
gc = GeometryCollection(type="GeometryCollection", geometries=[point, line_string])
534-
assert_wkt_equivalence(gc)
512+
assert (
513+
GeometryCollection(
514+
type="GeometryCollection", geometries=[point, line_string]
515+
).wkt
516+
== "GEOMETRYCOLLECTION Z (POINT Z (0.0 0.0 0.0), LINESTRING (0.0 0.0, 1.0 1.0))"
517+
)
535518

536519

537520
def test_wkt_empty_geometry_collection():
538-
gc = GeometryCollection(type="GeometryCollection", geometries=[])
539-
assert_wkt_equivalence(gc)
521+
assert (
522+
GeometryCollection(type="GeometryCollection", geometries=[]).wkt
523+
== "GEOMETRYCOLLECTION EMPTY"
524+
)
540525

541526

542527
def test_geometry_collection_warnings():
@@ -734,3 +719,45 @@ def test_wkt_empty_geometrycollection():
734719
assert GeometryCollection(type="GeometryCollection", geometries=[]).wkt.endswith(
735720
" EMPTY"
736721
)
722+
723+
724+
@pytest.mark.parametrize(
725+
"wkt",
726+
(
727+
"POINT (0.0 0.0)",
728+
# "POINT EMPTY" does not result in valid GeoJSON
729+
"POINT Z (0.0 0.0 0.0)",
730+
"MULTIPOINT ((0.0 0.0))",
731+
"MULTIPOINT Z ((0.0 0.0 0.0))",
732+
"MULTIPOINT ((0.0 0.0), (1.0 1.0))",
733+
"MULTIPOINT Z ((0.0 0.0 0.0), (1.0 1.0 1.0))",
734+
"MULTIPOINT EMPTY",
735+
"LINESTRING (0.0 0.0, 1.0 1.0, 2.0 2.0)",
736+
"LINESTRING Z (0.0 0.0 0.0, 1.0 1.0 1.0, 2.0 2.0 2.0)",
737+
# "LINESTRING EMPTY" does not result in valid GeoJSON
738+
"MULTILINESTRING ((0.0 0.0, 1.0 1.0))",
739+
"MULTILINESTRING ((0.0 0.0, 1.0 1.0), (1.0 1.0, 2.0 2.0))",
740+
"MULTILINESTRING Z ((0.0 0.0 0.0, 1.0 1.0 1.0))",
741+
"MULTILINESTRING Z ((0.0 0.0 0.0, 1.0 1.0 1.0), (1.0 1.0 1.0, 2.0 2.0 2.0))",
742+
"MULTILINESTRING EMPTY",
743+
"POLYGON ((0.0 0.0, 1.0 1.0, 2.0 2.0, 3.0 3.0, 0.0 0.0))",
744+
"POLYGON ((0.0 0.0, 4.0 0.0, 4.0 4.0, 0.0 4.0, 0.0 0.0), (1.0 1.0, 1.0 2.0, 2.0 2.0, 2.0 1.0, 1.0 1.0))",
745+
"POLYGON Z ((0.0 0.0 0.0, 1.0 1.0 0.0, 2.0 2.0 0.0, 3.0 3.0 0.0, 0.0 0.0 0.0))",
746+
"POLYGON Z ((0.0 0.0 0.0, 4.0 0.0 0.0, 4.0 4.0 0.0, 0.0 4.0 0.0, 0.0 0.0 0.0), (1.0 1.0 0.0, 1.0 2.0 0.0, 2.0 2.0 0.0, 2.0 1.0 0.0, 1.0 1.0 0.0))",
747+
"POLYGON EMPTY",
748+
"MULTIPOLYGON (((0.0 0.0, 1.0 1.0, 2.0 2.0, 3.0 3.0, 0.0 0.0)))",
749+
"MULTIPOLYGON (((0.0 0.0, 1.0 1.0, 2.0 2.0, 3.0 3.0, 0.0 0.0)), ((1.0 1.0, 2.0 2.0, 3.0 3.0, 4.0 4.0, 1.0 1.0)))",
750+
"MULTIPOLYGON Z (((0.0 0.0 0.0, 1.0 1.0 0.0, 2.0 2.0 0.0, 3.0 3.0 0.0, 0.0 0.0 0.0)))",
751+
"MULTIPOLYGON Z (((0.0 0.0 0.0, 1.0 1.0 0.0, 2.0 2.0 0.0, 3.0 3.0 0.0, 0.0 0.0 0.0)), ((1.0 1.0 0.0, 2.0 2.0 0.0, 3.0 3.0 0.0, 4.0 4.0 0.0, 1.0 1.0 0.0)))",
752+
"MULTIPOLYGON EMPTY",
753+
"GEOMETRYCOLLECTION (POINT (0.0 0.0))",
754+
"GEOMETRYCOLLECTION (POINT (0.0 0.0), MULTIPOINT ((0.0 0.0), (1.0 1.0)))",
755+
"GEOMETRYCOLLECTION Z (POLYGON EMPTY, MULTIPOLYGON Z (((0.0 0.0 0.0, 1.0 1.0 0.0, 2.0 2.0 0.0, 3.0 3.0 0.0, 0.0 0.0 0.0))))",
756+
"GEOMETRYCOLLECTION Z (LINESTRING Z (0.0 0.0 0.0, 1.0 1.0 1.0, 2.0 2.0 2.0), MULTILINESTRING ((0.0 0.0, 1.0 1.0), (1.0 1.0, 2.0 2.0)))",
757+
"GEOMETRYCOLLECTION EMPTY",
758+
),
759+
)
760+
def test_wkt(wkt: str):
761+
# Use Shapely to parse the input WKT so we know it is parsable by other tools.
762+
# Then load it into a Geometry and ensure the output WKT is the same as the input.
763+
assert parse_geometry_obj(shapely.from_wkt(wkt).__geo_interface__).wkt == wkt

0 commit comments

Comments
 (0)