From 0461d9f16a59627adb878c209a0222b18b256fed Mon Sep 17 00:00:00 2001 From: Javier Jimenez Shaw Date: Thu, 29 Feb 2024 14:19:46 +0100 Subject: [PATCH] Add is_deprecated and get_non_deprecated() to CRS (#1383) --- docs/history.rst | 1 + pyproj/_crs.pyi | 3 +++ pyproj/_crs.pyx | 57 ++++++++++++++++++++++++++++++++++++++++++++ pyproj/crs/crs.py | 25 +++++++++++++++++++ pyproj/proj.pxi | 3 +++ test/crs/test_crs.py | 30 +++++++++++++++++++++++ 6 files changed, 119 insertions(+) diff --git a/docs/history.rst b/docs/history.rst index 8510a1aa5..34feecb6b 100644 --- a/docs/history.rst +++ b/docs/history.rst @@ -4,6 +4,7 @@ Change Log Latest ------ - DEP: Minimum supported Python version 3.10 (pull #1357) +- ENH: Add :meth:`CRS.is_deprecated` and :meth:`CRS.get_non_deprecated` (pull #1383) 3.6.1 ------ diff --git a/pyproj/_crs.pyi b/pyproj/_crs.pyi index 7d3bafdb9..b46618de4 100644 --- a/pyproj/_crs.pyi +++ b/pyproj/_crs.pyi @@ -235,6 +235,9 @@ class _CRS(Base): @property def is_geocentric(self) -> bool: ... def equals(self, other: Any, ignore_axis_order: bool) -> bool: ... + @property + def is_deprecated(self) -> bool: ... + def get_non_deprecated(self) -> list["_CRS"]: ... def is_proj(proj_string: str) -> bool: ... def is_wkt(proj_string: str) -> bool: ... diff --git a/pyproj/_crs.pyx b/pyproj/_crs.pyx index f6ea3b002..513149e1d 100644 --- a/pyproj/_crs.pyx +++ b/pyproj/_crs.pyx @@ -3203,3 +3203,60 @@ cdef class _CRS(Base): if not isinstance(other, _CRS): return False return self._equals(other, ignore_axis_order=ignore_axis_order) + + @property + def is_deprecated(self): + """ + .. versionadded:: 3.7.0 + + Check if the CRS is deprecated + + Returns + ------- + bool + """ + return bool(proj_is_deprecated(self.projobj)) + + def get_non_deprecated(self): + """ + .. versionadded:: 3.7.0 + + Return a list of non-deprecated objects related to this. + + Returns + ------- + list[_CRS] + """ + + non_deprecated = [] + + cdef PJ_OBJ_LIST *proj_list = NULL + cdef int num_proj_objects = 0 + + proj_list = proj_get_non_deprecated( + self.context, + self.projobj + ) + if proj_list != NULL: + num_proj_objects = proj_list_get_count(proj_list) + + cdef PJ* proj = NULL + try: + for iii in range(num_proj_objects): + proj = proj_list_get(self.context, proj_list, iii) + non_deprecated.append(_CRS(_to_wkt( + self.context, + proj, + version=WktVersion.WKT2_2019, + pretty=False, + ))) + proj_destroy(proj) + proj = NULL + finally: + # If there was an error we have to call proj_destroy + # If there was none, calling it on NULL does nothing + proj_destroy(proj) + proj_list_destroy(proj_list) + _clear_proj_error() + + return non_deprecated diff --git a/pyproj/crs/crs.py b/pyproj/crs/crs.py index ce48a1a52..9436cac35 100644 --- a/pyproj/crs/crs.py +++ b/pyproj/crs/crs.py @@ -1565,6 +1565,31 @@ def is_derived(self): """ return self._crs.is_derived + @property + def is_deprecated(self) -> bool: + """ + .. versionadded:: 3.7.0 + + Check if the CRS is deprecated + + Returns + ------- + bool + """ + return self._crs.is_deprecated + + def get_non_deprecated(self) -> list["CRS"]: + """ + .. versionadded:: 3.7.0 + + Return a list of non-deprecated objects related to this. + + Returns + ------- + list[CRS] + """ + return self._crs.get_non_deprecated() + def __eq__(self, other: Any) -> bool: return self.equals(other) diff --git a/pyproj/proj.pxi b/pyproj/proj.pxi index 96bab8c48..1502b3378 100644 --- a/pyproj/proj.pxi +++ b/pyproj/proj.pxi @@ -546,3 +546,6 @@ cdef extern from "proj.h" nogil: ) void proj_unit_list_destroy(PROJ_UNIT_INFO** list) const char *proj_context_get_url_endpoint(PJ_CONTEXT* ctx) + + int proj_is_deprecated(const PJ *obj) + PJ_OBJ_LIST *proj_get_non_deprecated(PJ_CONTEXT *ctx, const PJ *obj) diff --git a/test/crs/test_crs.py b/test/crs/test_crs.py index d37480c01..2224897a8 100644 --- a/test/crs/test_crs.py +++ b/test/crs/test_crs.py @@ -232,6 +232,36 @@ def test_to_wkt_pretty(): assert "\n" not in crs.to_wkt() +def test_no_non_deprecated(): + crs = CRS.from_epsg(4326) + assert not crs.is_deprecated + non_dep = crs.get_non_deprecated() + assert len(non_dep) == 0 + + +def test_non_deprecated(): + crs = CRS.from_epsg(28473) + assert crs.is_deprecated + non_dep = crs.get_non_deprecated() + assert len(non_dep) == 1 + assert "EPSG:2503" == ":".join(non_dep[0].to_authority()) + + +def test_non_deprecated_empty(): + crs = CRS.from_epsg(3151) + assert crs.is_deprecated + assert len(crs.get_non_deprecated()) == 0 + + +def test_non_deprecated_multiple(): + crs = CRS.from_epsg(3315) + assert crs.is_deprecated + non_dep = [":".join(el.to_authority()) for el in crs.get_non_deprecated()] + assert len(non_dep) == 4 + for elem in ["EPSG:3989", "EPSG:3988", "EPSG:3987", "EPSG:3986"]: + assert elem in non_dep + + @pytest.mark.parametrize( "version, expected", [