From c6ea2c1438eb33d5446464c8090240be21e16749 Mon Sep 17 00:00:00 2001 From: Sam Cranford Date: Thu, 24 Sep 2026 16:12:33 +0000 Subject: [PATCH] FIX: Check CRS equality relying on string comparison --- podpac/core/coordinates/coordinates.py | 26 ++++++++++++-- .../core/coordinates/test/test_coordinates.py | 36 ++++++++++++++++++- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/podpac/core/coordinates/coordinates.py b/podpac/core/coordinates/coordinates.py index 6229ccf9..3abdc1a0 100644 --- a/podpac/core/coordinates/coordinates.py +++ b/podpac/core/coordinates/coordinates.py @@ -1691,7 +1691,7 @@ def merge_dims(coords_list, validate_crs=True): # check crs crs = coords_list[0].crs - if validate_crs and not all(coords.crs == crs for coords in coords_list): + if validate_crs and not all(_crs_equal(coords.crs, crs) for coords in coords_list): raise ValueError("Cannot merge Coordinates, crs mismatch") # merge @@ -1728,7 +1728,7 @@ def concat(coords_list): # check crs crs = coords_list[0].crs - if not all(coords.crs == crs for coords in coords_list): + if not all(_crs_equal(coords.crs, crs) for coords in coords_list): raise ValueError("Cannot concat Coordinates, crs mismatch") # concatenate @@ -1769,3 +1769,25 @@ def union(coords_list): """ return concat(coords_list).unique() + + +def _crs_equal(a: str | None, b: str | None) -> bool: + """Check if two CRS strings define the same coordinate reference system. + + Parameters + ---------- + a : str | None + The first CRS string or None. + b : str | None + The second CRS string or None. + + Returns + ------- + bool + True if the strings define the same CRS, otherwise False. + """ + if a == b: + return True + if a is None or b is None: + return False + return pyproj.CRS(a) == pyproj.CRS(b) diff --git a/podpac/core/coordinates/test/test_coordinates.py b/podpac/core/coordinates/test/test_coordinates.py index 996e15dd..94c71520 100644 --- a/podpac/core/coordinates/test/test_coordinates.py +++ b/podpac/core/coordinates/test/test_coordinates.py @@ -16,7 +16,7 @@ from podpac.core.coordinates.uniform_coordinates1d import UniformCoordinates1d from podpac.core.coordinates.cfunctions import crange, clinspace from podpac.core.coordinates.coordinates import Coordinates -from podpac.core.coordinates.coordinates import concat, union, merge_dims +from podpac.core.coordinates.coordinates import concat, union, merge_dims, _crs_equal _DIMENSION_MISMATCH = "Dimension mismatch" _MERCATOR_CRS = "+proj=merc +lat_ts=56.5 +ellps=GRS80" @@ -1822,6 +1822,40 @@ def test_concat_crs(self): with pytest.raises(ValueError, match="Cannot concat Coordinates"): concat([c1, c2]) + def test_crs_equal(self): + """Test CRS equality for WKT strings and shorthand codes.""" + wkt = pyproj.CRS("EPSG:4326").to_wkt() + assert _crs_equal("EPSG:4326", "EPSG:4326") + assert _crs_equal("EPSG:4326", wkt) + assert _crs_equal(None, None) + assert not _crs_equal("EPSG:4326", None) + assert not _crs_equal(None, "EPSG:4326") + assert not _crs_equal("EPSG:4326", "EPSG:2193") + + def test_merge_dims_equivalent_crs(self): + """Test coordinate merging with equivalent WKT strings and shorthand codes.""" + wkt = pyproj.CRS("EPSG:4326").to_wkt() + clat = Coordinates([[2, 4, 5]], dims=["lat"], crs=wkt) + clon = Coordinates([[3, -1, 5]], dims=["lon"], crs="EPSG:4326") + + c = merge_dims([clat, clon]) + assert c.dims == ("lat", "lon") + assert c.crs == wkt + + def test_concat_and_union_equivalent_crs(self): + """Test concatenation and union with equivalent WKT strings and shorthand codes.""" + wkt = pyproj.CRS("EPSG:4326").to_wkt() + c1 = Coordinates([[0, 1]], dims=["lat"], crs=wkt) + c2 = Coordinates([[1, 2]], dims=["lat"], crs="EPSG:4326") + + c = concat([c1, c2]) + assert c.shape == (4,) + assert c.crs == wkt + + c = union([c1, c2]) + assert c.shape == (3,) + assert c.crs == wkt + class TestCoordinatesGeoTransform(object): def test_uniform_working(self):