Skip to content

Commit ddd0e32

Browse files
authored
[GH-2513] chore(python): Make 'assert_geometry_almost_equal' check for Z and M dimensions too (#2517)
1 parent 3d1ebde commit ddd0e32

File tree

2 files changed

+85
-2
lines changed

2 files changed

+85
-2
lines changed

python/tests/sql/test_dataframe_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,7 @@
916916
("line", 10.0),
917917
"4D_line",
918918
"ST_ReducePrecision(geom, 2)",
919-
"LINESTRING Z (1 -0.3 -1.383092639965822, 2 -0.59 -2.766185279931644, 3 -0.89 -4.149277919897466, -1 0.3 1.383092639965822)",
919+
"LINESTRING ZM (1 -0.3 -1.383092639965822 1, 2 -0.59 -2.766185279931644 2, 3 -0.89 -4.149277919897466 3, -1 0.3 1.383092639965822 -1)",
920920
),
921921
(
922922
stf.ST_RotateY,

python/tests/test_base.py

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@
2828
SPARK_REMOTE = os.getenv("SPARK_REMOTE")
2929
EXTRA_JARS = os.getenv("SEDONA_PYTHON_EXTRA_JARS")
3030

31+
import shapely
3132
from shapely import wkt
3233
from shapely.geometry.base import BaseGeometry
3334

35+
SHAPELY_GE_210 = shapely.__version__ >= "2.1.0"
36+
3437

3538
class TestBase:
3639

@@ -122,14 +125,31 @@ def assert_geometry_almost_equal(
122125
right_geom: Union[str, BaseGeometry],
123126
tolerance=1e-6,
124127
):
128+
"""
129+
Assert that two geometries are almost equal.
130+
131+
Note: this function will only check Z and M dimensions for shapely >= 2.1.0 (python >= 3.10)
132+
133+
When comparing geometries with Z or M dimensions, this function will ignore `tolerance` and check for exact equality.
134+
"""
125135
expected_geom = (
126136
wkt.loads(left_geom) if isinstance(left_geom, str) else left_geom
127137
)
128138
actual_geom = (
129139
wkt.loads(right_geom) if isinstance(right_geom, str) else right_geom
130140
)
131141

132-
if not actual_geom.equals_exact(expected_geom, tolerance=tolerance):
142+
# Note: only shapely >= 2.1.0 supports Z and M dimensions
143+
# If has Z or M dimension, use equals_identical to check the equality
144+
if SHAPELY_GE_210 and (has_zm(actual_geom) or has_zm(expected_geom)):
145+
if not shapely.equals_identical(actual_geom, expected_geom):
146+
raise ValueError(
147+
f"Geometry equality check failed for {left_geom} and {right_geom}"
148+
)
149+
150+
# Comparison for XY geometries
151+
# Note: equals_exact doesn't check for Z or M dimensions
152+
elif not actual_geom.equals_exact(expected_geom, tolerance=tolerance):
133153
# If the exact equals check fails, perform a buffer check with tolerance
134154
if (
135155
actual_geom.is_valid
@@ -143,3 +163,66 @@ def assert_geometry_almost_equal(
143163
raise ValueError(
144164
f"Geometry equality check failed for {left_geom} and {right_geom}"
145165
)
166+
167+
168+
def has_zm(geom: BaseGeometry):
169+
return geom.has_z or geom.has_m
170+
171+
172+
def test_assert_geometry_almost_equal():
173+
import pytest
174+
175+
TestBase.assert_geometry_almost_equal("POINT (1 1)", "POINT (1 1)")
176+
TestBase.assert_geometry_almost_equal("POINT (1 1)", "POINT (1.000001 1)")
177+
TestBase.assert_geometry_almost_equal("POINT (1 1)", "POINT (1.000001 1)")
178+
179+
with pytest.raises(ValueError):
180+
TestBase.assert_geometry_almost_equal("POINT (1 1)", "POINT (2 2)")
181+
182+
with pytest.raises(ValueError):
183+
TestBase.assert_geometry_almost_equal("POINT (1 1)", "POINT (2 2)")
184+
185+
# Check Z and M dimension compatibility (requires shapely >= 2.1.0)
186+
if SHAPELY_GE_210:
187+
# 2D vs 3D should fail
188+
with pytest.raises(ValueError):
189+
TestBase.assert_geometry_almost_equal("POINT (1 1)", "POINT (1 1 0)")
190+
191+
with pytest.raises(ValueError):
192+
TestBase.assert_geometry_almost_equal("POINT (1 1 0)", "POINT (1 1)")
193+
194+
# Different 3D should fail
195+
with pytest.raises(ValueError):
196+
TestBase.assert_geometry_almost_equal("POINT (1 1 1)", "POINT (1 1 2)")
197+
198+
with pytest.raises(ValueError):
199+
TestBase.assert_geometry_almost_equal("POINT (1 1 2)", "POINT (1 1 1)")
200+
201+
# Z vs M dimension should fail
202+
with pytest.raises(ValueError):
203+
TestBase.assert_geometry_almost_equal("POINT Z (1 1 1)", "POINT M (1 1 1)")
204+
205+
with pytest.raises(ValueError):
206+
TestBase.assert_geometry_almost_equal("POINT M (1 1 1)", "POINT Z (1 1 1)")
207+
208+
# 3D vs 4D should fail
209+
with pytest.raises(ValueError):
210+
TestBase.assert_geometry_almost_equal(
211+
"POINT Z (1 1 1)", "POINT ZM (1 1 1 1)"
212+
)
213+
214+
with pytest.raises(ValueError):
215+
TestBase.assert_geometry_almost_equal(
216+
"POINT ZM (1 1 1 1)", "POINT Z (1 1 1)"
217+
)
218+
219+
# Different 4D should fail
220+
with pytest.raises(ValueError):
221+
TestBase.assert_geometry_almost_equal(
222+
"POINT ZM (1 1 1 1)", "POINT ZM (1 1 1 2)"
223+
)
224+
225+
with pytest.raises(ValueError):
226+
TestBase.assert_geometry_almost_equal(
227+
"POINT ZM (1 1 1 2)", "POINT ZM (1 1 1 1)"
228+
)

0 commit comments

Comments
 (0)