[Git][debian-gis-team/python-pyproj][master] 10 commits: Update branch in gbp.conf & Vcs-Git URL.

Bas Couwenberg (@sebastic) gitlab at salsa.debian.org
Sat Sep 18 06:02:47 BST 2021



Bas Couwenberg pushed to branch master at Debian GIS Project / python-pyproj


Commits:
a53b1aa2 by Bas Couwenberg at 2021-09-15T07:28:45+02:00
Update branch in gbp.conf & Vcs-Git URL.

- - - - -
fa67bd07 by Bas Couwenberg at 2021-09-15T07:28:57+02:00
New upstream version 3.2.1~rc0
- - - - -
076e847c by Bas Couwenberg at 2021-09-15T07:29:01+02:00
Update upstream source from tag 'upstream/3.2.1_rc0'

Update to upstream version '3.2.1~rc0'
with Debian dir a27601f6a95c5dee4768b6c49a0e8c82795f9327
- - - - -
591bca38 by Bas Couwenberg at 2021-09-15T07:29:15+02:00
New upstream release candidate.

- - - - -
fae38b94 by Bas Couwenberg at 2021-09-15T07:30:59+02:00
Set distribution to experimental.

- - - - -
09dce063 by Bas Couwenberg at 2021-09-18T06:46:50+02:00
Revert "Update branch in gbp.conf & Vcs-Git URL."

This reverts commit a53b1aa23a08019be4ff2b3171f1cb1c07b1e2b4.

- - - - -
adbd3c36 by Bas Couwenberg at 2021-09-18T06:47:26+02:00
New upstream version 3.2.1
- - - - -
b708e701 by Bas Couwenberg at 2021-09-18T06:47:29+02:00
Update upstream source from tag 'upstream/3.2.1'

Update to upstream version '3.2.1'
with Debian dir 6d6aaa1304c108fb0fb014e33a632ac1aa722745
- - - - -
ee1797ef by Bas Couwenberg at 2021-09-18T06:47:45+02:00
New upstream release.

- - - - -
122ca032 by Bas Couwenberg at 2021-09-18T06:48:55+02:00
Set distribution to unstable.

- - - - -


29 changed files:

- debian/changelog
- docs/crs_compatibility.rst
- docs/history.rst
- docs/past_versions.rst
- pyproj/__init__.py
- pyproj/_compat.pxd
- + pyproj/_compat.pyi
- pyproj/_compat.pyx
- pyproj/_crs.pxd
- pyproj/_crs.pyi
- pyproj/_crs.pyx
- pyproj/_datadir.pyx
- pyproj/_geod.pyx
- pyproj/_network.pyx
- pyproj/_sync.pyx
- pyproj/_transformer.pxd
- pyproj/_transformer.pyi
- pyproj/_transformer.pyx
- − pyproj/compat.py
- pyproj/crs/_cf1x8.py
- pyproj/database.pyx
- pyproj/datadir.py
- pyproj/list.pyx
- pyproj/proj.py
- pyproj/transformer.py
- setup.cfg
- setup.py
- test/crs/test_crs.py
- test/crs/test_crs_cf.py


Changes:

=====================================
debian/changelog
=====================================
@@ -1,10 +1,18 @@
-python-pyproj (3.2.0-2) UNRELEASED; urgency=medium
+python-pyproj (3.2.1-1) unstable; urgency=medium
 
+  * New upstream release.
+  * Move from experimental to unstable.
+
+ -- Bas Couwenberg <sebastic at debian.org>  Sat, 18 Sep 2021 06:48:37 +0200
+
+python-pyproj (3.2.1~rc0-1~exp1) experimental; urgency=medium
+
+  * New upstream release candidate.
   * Bump Standards-Version to 4.6.0, no changes.
   * Bump debhelper compat to 12, changes:
     - Drop --list-missing from dh_install
 
- -- Bas Couwenberg <sebastic at debian.org>  Wed, 08 Sep 2021 17:43:10 +0200
+ -- Bas Couwenberg <sebastic at debian.org>  Wed, 15 Sep 2021 07:30:23 +0200
 
 python-pyproj (3.2.0-1) unstable; urgency=medium
 


=====================================
docs/crs_compatibility.rst
=====================================
@@ -208,31 +208,36 @@ cartopy
 
 https://github.com/SciTools/cartopy
 
-This may change in the future:
-`Port to use pyproj v2 <https://github.com/SciTools/cartopy/issues/1477>`__
+.. note:: These examples require cartopy 0.20+
 
 Preparing `pyproj.crs.CRS` for `cartopy.crs.CRS`
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+.. warning:: This only works for CRS created with WKT2,
+             PROJ JSON, or a spatial reference ID (i.e. EPSG)
+             with the area of use defined. Otherwise,
+             the x_limits and y_limits will not work.
+
 .. code-block:: python
 
     import cartopy.crs as ccrs
     from pyproj.crs import CRS
 
+    # geographic
     proj_crs = CRS.from_epsg(4326)
-    globe = ccrs.Globe(
-        ellipse=None,
-        semimajor_axis=proj_crs.ellipsoid.semi_major_metre,
-        semiminor_axis=proj_crs.ellipsoid.semi_minor_metre,
-        inverse_flattening=proj_crs.ellipsoid.inverse_flattening,
-    )
-    proj_dict = proj_crs.to_dict()
-    proj_dict["pm"] = proj_crs.prime_meridian.longitude
-    cart_crs = ccrs.CRS(proj_dict, globe=globe)
+    cart_crs = ccrs.CRS(proj_crs)
+
+    # projected
+    proj_crs = CRS.from_epsg(6933)
+    cart_crs = ccrs.Projection(proj_crs)
 
 
 Preparing `cartopy.crs.CRS` for `pyproj.crs.CRS`
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. note:: `cartopy.crs.CRS` inherits from `pyproj.crs.CRS`,
+          so it should behave like a `pyproj.crs.CRS`.
+
 .. code-block:: python
 
 
@@ -240,7 +245,7 @@ Preparing `cartopy.crs.CRS` for `pyproj.crs.CRS`
     from pyproj.crs import CRS
 
     cart_crs = PlateCarree()
-    proj_crs = CRS.from_dict(cart_crs.proj4_params)
+    proj_crs = CRS.from_user_input(cart_crs)
 
 
 pycrs


=====================================
docs/history.rst
=====================================
@@ -1,9 +1,17 @@
 Change Log
 ==========
 
+3.2.1
+------
+- REF: declare specific python types in cython (pull #928)
+- REF: Use cython string decoding (pull #929)
+- BUG: Return multiple authorities with :attr:`pyproj.crs.CRS.list_authority` (pull #943)
+- BUG: CRS CF conversions ensure lon_0 = north_pole_grid_longitude + 180 (issue #927)
+- BUG: CRS CF conversions ensure Pole rotation (netCDF CF convention) conversion works (issue #927)
+
 3.2.0
 ------
-* WHL: Wheels contain PROJ 8.1.1
+- WHL: Wheels contain PROJ 8.1.1
 - DOC: Add new pyproj logo (issue #700)
 - REF: Handle deprecation of proj_context_set_autoclose_database (issue #866)
 - REF: Make CRS methods inheritable (issue #847)


=====================================
docs/past_versions.rst
=====================================
@@ -1,6 +1,7 @@
 Documentation Archive
 =====================
 
+- `3.2.0 <https://pyproj4.github.io/pyproj/3.2.0/>`_
 - `3.1.0 <https://pyproj4.github.io/pyproj/3.1.0/>`_
 - `3.0.1 <https://pyproj4.github.io/pyproj/3.0.1/>`_
 - `2.6.1 <https://pyproj4.github.io/pyproj/v2.6.1rel/>`_


=====================================
pyproj/__init__.py
=====================================
@@ -28,7 +28,7 @@ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTIO
 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 """
-__version__ = "3.2.0"
+__version__ = "3.2.1"
 __all__ = [
     "Proj",
     "Geod",


=====================================
pyproj/_compat.pxd
=====================================
@@ -1,4 +1,10 @@
-from pyproj.compat import pystrdecode
+cdef str cstrdecode(const char *instring)
+cpdef bytes cstrencode(str pystr)
 
+IF CTE_PYTHON_IMPLEMENTATION == "CPython":
+    from cpython cimport array
+    cdef array.array empty_array(int npts)
 
-cdef cstrdecode(const char *instring)
+ELSE:
+    # https://github.com/pyproj4/pyproj/issues/854
+    cdef empty_array(int npts)


=====================================
pyproj/_compat.pyi
=====================================
@@ -0,0 +1 @@
+def cstrencode(pystr: str) -> bytes: ...


=====================================
pyproj/_compat.pyx
=====================================
@@ -1,11 +1,19 @@
 import array
 
-from pyproj.compat import pystrdecode
 
+cpdef bytes cstrencode(str pystr):
+    """
+    Encode a string into bytes.
+    """
+    try:
+        return pystr.encode("utf-8")
+    except UnicodeDecodeError:
+        return pystr.decode("utf-8").encode("utf-8")
 
-cdef cstrdecode(const char *instring):
+
+cdef str cstrdecode(const char *instring):
     if instring != NULL:
-        return pystrdecode(instring)
+        return instring
     return None
 
 
@@ -14,10 +22,10 @@ IF CTE_PYTHON_IMPLEMENTATION == "CPython":
 
     cdef array.array _ARRAY_TEMPLATE = array.array("d", [])
 
-    def empty_array(int npts):
+    cdef array.array empty_array(int npts):
         return array.clone(_ARRAY_TEMPLATE, npts, zero=False)
 
 ELSE:
     # https://github.com/pyproj4/pyproj/issues/854
-    def empty_array(int npts):
+    cdef empty_array(int npts):
         return array.array("d", [float("NaN")] * npts)


=====================================
pyproj/_crs.pxd
=====================================
@@ -6,22 +6,22 @@ cdef extern from "proj_experimental.h":
                                const PJ* crs_2D)
 
 
-cdef _get_concatenated_operations(PJ_CONTEXT*context, PJ*concatenated_operation)
+cdef tuple _get_concatenated_operations(PJ_CONTEXT*context, PJ*concatenated_operation)
 cdef _to_proj4(
     PJ_CONTEXT* context,
     PJ* projobj,
-    version,
-    pretty,
+    object version,
+    bint pretty,
 )
 
 cdef class Axis:
-    cdef readonly object name
-    cdef readonly object abbrev
-    cdef readonly object direction
+    cdef readonly str name
+    cdef readonly str abbrev
+    cdef readonly str direction
     cdef readonly double unit_conversion_factor
-    cdef readonly object unit_name
-    cdef readonly object unit_auth_code
-    cdef readonly object unit_code
+    cdef readonly str unit_name
+    cdef readonly str unit_auth_code
+    cdef readonly str unit_code
 
     @staticmethod
     cdef Axis create(PJ_CONTEXT* context, PJ* projobj, int index)
@@ -31,9 +31,9 @@ cdef create_area_of_use(PJ_CONTEXT* context, PJ* projobj)
 cdef class Base:
     cdef PJ *projobj
     cdef PJ_CONTEXT* context
-    cdef readonly object name
-    cdef readonly object _remarks
-    cdef readonly object _scope
+    cdef readonly str name
+    cdef readonly str _remarks
+    cdef readonly str _scope
     cdef _set_base_info(self)
 
 cdef class _CRSParts(Base):
@@ -43,7 +43,7 @@ cdef class _CRSParts(Base):
 cdef class Ellipsoid(_CRSParts):
     cdef readonly double semi_major_metre
     cdef readonly double semi_minor_metre
-    cdef readonly object is_semi_minor_computed
+    cdef readonly bint is_semi_minor_computed
     cdef readonly double inverse_flattening
 
     @staticmethod
@@ -53,14 +53,14 @@ cdef class Ellipsoid(_CRSParts):
 cdef class PrimeMeridian(_CRSParts):
     cdef readonly double longitude
     cdef readonly double unit_conversion_factor
-    cdef readonly object unit_name
+    cdef readonly str unit_name
 
     @staticmethod
     cdef PrimeMeridian create(PJ_CONTEXT* context, PJ* prime_meridian_pj)
 
 
 cdef class Datum(_CRSParts):
-    cdef readonly object type_name
+    cdef readonly str type_name
     cdef readonly object _ellipsoid
     cdef readonly object _prime_meridian
 
@@ -69,53 +69,53 @@ cdef class Datum(_CRSParts):
 
 
 cdef class CoordinateSystem(_CRSParts):
-    cdef readonly object _axis_list
+    cdef readonly list _axis_list
 
     @staticmethod
     cdef CoordinateSystem create(PJ_CONTEXT* context, PJ* coordinate_system_pj)
 
 
 cdef class Param:
-    cdef readonly object name
-    cdef readonly object auth_name
-    cdef readonly object code
+    cdef readonly str name
+    cdef readonly str auth_name
+    cdef readonly str code
     cdef readonly object value
     cdef readonly double unit_conversion_factor
-    cdef readonly object unit_name
-    cdef readonly object unit_auth_name
-    cdef readonly object unit_code
-    cdef readonly object unit_category
+    cdef readonly str unit_name
+    cdef readonly str unit_auth_name
+    cdef readonly str unit_code
+    cdef readonly str unit_category
 
     @staticmethod
     cdef Param create(PJ_CONTEXT* context, PJ* projobj, int param_idx)
 
 
 cdef class Grid:
-    cdef readonly object short_name
-    cdef readonly object full_name
-    cdef readonly object package_name
-    cdef readonly object url
-    cdef readonly object direct_download
-    cdef readonly object open_license
-    cdef readonly object available
+    cdef readonly str short_name
+    cdef readonly str full_name
+    cdef readonly str package_name
+    cdef readonly str url
+    cdef readonly bint direct_download
+    cdef readonly bint open_license
+    cdef readonly bint available
 
     @staticmethod
     cdef Grid create(PJ_CONTEXT* context, PJ* projobj, int grid_idx)
 
 
 cdef class CoordinateOperation(_CRSParts):
-    cdef readonly object _params
-    cdef readonly object _grids
+    cdef readonly list _params
+    cdef readonly list _grids
     cdef readonly object _area_of_use
-    cdef readonly object method_name
-    cdef readonly object method_auth_name
-    cdef readonly object method_code
+    cdef readonly str method_name
+    cdef readonly str method_auth_name
+    cdef readonly str method_code
     cdef readonly double accuracy
-    cdef readonly object is_instantiable
-    cdef readonly object has_ballpark_transformation
-    cdef readonly object _towgs84
-    cdef readonly object _operations
-    cdef readonly type_name
+    cdef readonly bint is_instantiable
+    cdef readonly bint has_ballpark_transformation
+    cdef readonly list _towgs84
+    cdef readonly tuple _operations
+    cdef readonly str type_name
 
     @staticmethod
     cdef CoordinateOperation create(PJ_CONTEXT* context, PJ* coordinate_operation_pj)
@@ -124,15 +124,15 @@ cdef class CoordinateOperation(_CRSParts):
 cdef class _CRS(Base):
     cdef PJ_TYPE _type
     cdef PJ_PROJ_INFO projpj_info
-    cdef readonly object srs
-    cdef readonly object type_name
-    cdef readonly object _ellipsoid
+    cdef readonly str srs
+    cdef readonly str type_name
+    cdef readonly Ellipsoid _ellipsoid
     cdef readonly object _area_of_use
-    cdef readonly object _prime_meridian
-    cdef readonly object _datum
-    cdef readonly object _sub_crs_list
-    cdef readonly object _source_crs
-    cdef readonly object _target_crs
-    cdef readonly object _geodetic_crs
-    cdef readonly object _coordinate_system
-    cdef readonly object _coordinate_operation
+    cdef readonly PrimeMeridian _prime_meridian
+    cdef readonly Datum _datum
+    cdef readonly list _sub_crs_list
+    cdef readonly _CRS _source_crs
+    cdef readonly _CRS _target_crs
+    cdef readonly _CRS _geodetic_crs
+    cdef readonly CoordinateSystem _coordinate_system
+    cdef readonly CoordinateOperation _coordinate_operation


=====================================
pyproj/_crs.pyi
=====================================
@@ -185,13 +185,11 @@ class CoordinateOperation(_CRSParts):
         ] = CoordinateOperationType.CONVERSION,
     ) -> "CoordinateOperation": ...
 
-
 class AuthorityMatchInfo(NamedTuple):
     auth_name: str
     code: str
     confidence: int
 
-
 class _CRS(Base):
     srs: str
     type_name: str


=====================================
pyproj/_crs.pyx
=====================================
@@ -3,11 +3,10 @@ import re
 import warnings
 from collections import OrderedDict, namedtuple
 
-from pyproj._compat cimport cstrdecode
+from pyproj._compat cimport cstrdecode, cstrencode
 from pyproj._datadir cimport pyproj_context_create, pyproj_context_destroy
 
 from pyproj.aoi import AreaOfUse
-from pyproj.compat import cstrencode, pystrdecode
 from pyproj.crs.datum import CustomEllipsoid
 from pyproj.crs.enums import CoordinateOperationType, DatumType
 from pyproj.enums import ProjVersion, WktVersion
@@ -15,21 +14,22 @@ from pyproj.exceptions import CRSError
 from pyproj.geod import pj_ellps
 from pyproj.utils import NumpyEncoder
 
+
 # This is for looking up the ellipsoid parameters
 # based on the long name
-_PJ_ELLPS_NAME_MAP = {
+cdef dict _PJ_ELLPS_NAME_MAP = {
     ellps["description"]: ellps_id for ellps_id, ellps in pj_ellps.items()
 }
 
 
-cdef decode_or_undefined(const char* instring):
+cdef str decode_or_undefined(const char* instring):
     pystr = cstrdecode(instring)
     if pystr is None:
         return "undefined"
     return pystr
 
 
-def is_wkt(proj_string):
+def is_wkt(str proj_string not None):
     """
     .. versionadded:: 2.0.0
 
@@ -44,11 +44,11 @@ def is_wkt(proj_string):
     -------
     bool: True if the string is in the Well-Known Text format
     """
-    tmp_string = cstrencode(proj_string)
-    return proj_context_guess_wkt_dialect(NULL, tmp_string) != PJ_GUESSED_NOT_WKT
+    cdef bytes b_proj_string = cstrencode(proj_string)
+    return proj_context_guess_wkt_dialect(NULL, b_proj_string) != PJ_GUESSED_NOT_WKT
 
 
-def is_proj(proj_string):
+def is_proj(str proj_string not None):
     """
     .. versionadded:: 2.2.2
 
@@ -69,8 +69,8 @@ def is_proj(proj_string):
 cdef _to_wkt(
     PJ_CONTEXT* context,
     PJ* projobj,
-    version=WktVersion.WKT2_2019,
-    pretty=False
+    object version=WktVersion.WKT2_2019,
+    bint pretty=False
 ):
     """
     Convert a PJ object to a wkt string.
@@ -101,7 +101,7 @@ cdef _to_wkt(
     wkt_out_type = supported_wkt_types[WktVersion.create(version)]
 
     cdef const char* options_wkt[2]
-    multiline = b"MULTILINE=NO"
+    cdef bytes multiline = b"MULTILINE=NO"
     if pretty:
         multiline = b"MULTILINE=YES"
     options_wkt[0] = multiline
@@ -120,8 +120,8 @@ cdef _to_wkt(
 cdef _to_proj4(
     PJ_CONTEXT* context,
     PJ* projobj,
-    version,
-    pretty,
+    object version,
+    bint pretty,
 ):
     """
     Convert the projection to a PROJ string.
@@ -147,7 +147,7 @@ cdef _to_proj4(
     proj_out_type = supported_prj_types[ProjVersion.create(version)]
 
     cdef const char* options[2]
-    multiline = b"MULTILINE=NO"
+    cdef bytes multiline = b"MULTILINE=NO"
     if pretty:
         multiline = b"MULTILINE=YES"
     options[0] = multiline
@@ -165,7 +165,9 @@ cdef _to_proj4(
     return cstrdecode(proj_string)
 
 
-cdef _get_concatenated_operations(PJ_CONTEXT* context, PJ* concatenated_operation):
+cdef tuple _get_concatenated_operations(
+    PJ_CONTEXT* context, PJ* concatenated_operation
+):
     """
     For a PJ* of type concatenated operation, get the operations
     """
@@ -190,8 +192,8 @@ cdef _get_concatenated_operations(PJ_CONTEXT* context, PJ* concatenated_operatio
 
 cdef PJ * _from_name(
         PJ_CONTEXT* context,
-        name_string,
-        auth_name,
+        str name_string,
+        str auth_name,
         PJ_TYPE pj_type,
     ):
         """
@@ -215,6 +217,7 @@ cdef PJ * _from_name(
         """
         cdef PJ_TYPE[1] pj_types = [pj_type]
         cdef char* c_auth_name = NULL
+        cdef bytes b_auth_name
         if auth_name is not None:
             b_auth_name = cstrencode(auth_name)
             c_auth_name = b_auth_name
@@ -237,7 +240,7 @@ cdef PJ * _from_name(
         return datum_pj
 
 
-def _load_proj_json(in_proj_json):
+def _load_proj_json(str in_proj_json):
     try:
         return json.loads(in_proj_json)
     except ValueError:
@@ -357,13 +360,11 @@ cdef class Base:
         cdef const char* proj_name = proj_get_name(self.projobj)
         self.name = decode_or_undefined(proj_name)
         cdef const char* scope = proj_get_scope(self.projobj)
-        if scope != NULL:
-            py_scope = pystrdecode(scope)
-            self._scope = py_scope if py_scope else self._scope
+        if scope != NULL and scope != "":
+            self._scope = scope
         cdef const char* remarks = proj_get_remarks(self.projobj)
-        if remarks != NULL:
-            py_remarks = pystrdecode(remarks)
-            self._remarks = py_remarks if py_remarks else self._remarks
+        if remarks != NULL and remarks != "":
+            self._remarks = remarks
 
     @property
     def remarks(self):
@@ -415,7 +416,7 @@ cdef class Base:
         """
         return _to_wkt(self.context, self.projobj, version, pretty=pretty)
 
-    def to_json(self, pretty=False, indentation=2):
+    def to_json(self, bint pretty=False, int indentation=2):
         """
         .. versionadded:: 2.4.0
 
@@ -536,7 +537,7 @@ cdef class _CRSParts(Base):
         return self._is_equivalent(other)
 
 
-_COORD_SYSTEM_TYPE_MAP = {
+cdef dict _COORD_SYSTEM_TYPE_MAP = {
     PJ_CS_TYPE_UNKNOWN: "unknown",
     PJ_CS_TYPE_CARTESIAN: "cartesian",
     PJ_CS_TYPE_ELLIPSOIDAL: "ellipsoidal",
@@ -607,7 +608,7 @@ cdef class CoordinateSystem(_CRSParts):
         return self._axis_list
 
     @staticmethod
-    def from_string(coordinate_system_string):
+    def from_string(str coordinate_system_string not None):
         """
         .. versionadded:: 2.5.0
 
@@ -637,13 +638,13 @@ cdef class CoordinateSystem(_CRSParts):
             pyproj_context_destroy(context)
             raise CRSError(
                 "Invalid coordinate system string: "
-                f"{pystrdecode(coordinate_system_string)}"
+                f"{coordinate_system_string}"
             )
         CRSError.clear()
         return CoordinateSystem.create(context, coordinate_system_pj)
 
     @staticmethod
-    def from_json_dict(coordinate_system_dict):
+    def from_json_dict(dict coordinate_system_dict not None):
         """
         .. versionadded:: 2.5.0
 
@@ -663,7 +664,7 @@ cdef class CoordinateSystem(_CRSParts):
         )
 
     @staticmethod
-    def from_json(coordinate_system_json_str):
+    def from_json(str coordinate_system_json_str not None):
         """
         .. versionadded:: 2.5.0
 
@@ -682,7 +683,7 @@ cdef class CoordinateSystem(_CRSParts):
             _load_proj_json(coordinate_system_json_str)
         )
 
-    def to_cf(self, rotated_pole=False):
+    def to_cf(self, bint rotated_pole=False):
         """
         .. versionadded:: 3.0.0
 
@@ -816,7 +817,7 @@ cdef class Ellipsoid(_CRSParts):
         return ellips
 
     @staticmethod
-    def from_authority(auth_name, code):
+    def from_authority(str auth_name not None, code not None):
         """
         .. versionadded:: 2.2.0
 
@@ -850,7 +851,7 @@ cdef class Ellipsoid(_CRSParts):
         return Ellipsoid.create(context, ellipsoid_pj)
 
     @staticmethod
-    def from_epsg(code):
+    def from_epsg(code not None):
         """
         .. versionadded:: 2.2.0
 
@@ -868,7 +869,7 @@ cdef class Ellipsoid(_CRSParts):
         return Ellipsoid.from_authority("EPSG", code)
 
     @staticmethod
-    def _from_string(ellipsoid_string):
+    def _from_string(str ellipsoid_string not None):
         """
         Create an Ellipsoid from a string.
 
@@ -896,13 +897,13 @@ cdef class Ellipsoid(_CRSParts):
             proj_destroy(ellipsoid_pj)
             pyproj_context_destroy(context)
             raise CRSError(
-                f"Invalid ellipsoid string: {pystrdecode(ellipsoid_string)}"
+                f"Invalid ellipsoid string: {ellipsoid_string}"
             )
         CRSError.clear()
         return Ellipsoid.create(context, ellipsoid_pj)
 
     @staticmethod
-    def from_string(ellipsoid_string):
+    def from_string(str ellipsoid_string not None):
         """
         .. versionadded:: 2.2.0
 
@@ -933,7 +934,7 @@ cdef class Ellipsoid(_CRSParts):
                 raise crs_err
 
     @staticmethod
-    def from_json_dict(ellipsoid_dict):
+    def from_json_dict(dict ellipsoid_dict not None):
         """
         .. versionadded:: 2.4.0
 
@@ -951,7 +952,7 @@ cdef class Ellipsoid(_CRSParts):
         return Ellipsoid._from_string(json.dumps(ellipsoid_dict, cls=NumpyEncoder))
 
     @staticmethod
-    def from_json(ellipsoid_json_str):
+    def from_json(str ellipsoid_json_str not None):
         """
         .. versionadded:: 2.4.0
 
@@ -970,8 +971,8 @@ cdef class Ellipsoid(_CRSParts):
 
     @staticmethod
     def _from_name(
-        ellipsoid_name,
-        auth_name,
+        str ellipsoid_name,
+        str auth_name,
     ):
         """
         .. versionadded:: 2.5.0
@@ -999,14 +1000,14 @@ cdef class Ellipsoid(_CRSParts):
         )
         if ellipsoid_pj == NULL:
             pyproj_context_destroy(context)
-            raise CRSError(f"Invalid ellipsoid name: {pystrdecode(ellipsoid_name)}")
+            raise CRSError(f"Invalid ellipsoid name: {ellipsoid_name}")
         CRSError.clear()
         return Ellipsoid.create(context, ellipsoid_pj)
 
     @staticmethod
     def from_name(
-        ellipsoid_name,
-        auth_name=None,
+        str ellipsoid_name not None,
+        str auth_name=None,
     ):
         """
         .. versionadded:: 2.5.0
@@ -1094,7 +1095,7 @@ cdef class PrimeMeridian(_CRSParts):
         return prime_meridian
 
     @staticmethod
-    def from_authority(auth_name, code):
+    def from_authority(str auth_name not None, code not None):
         """
         .. versionadded:: 2.2.0
 
@@ -1128,7 +1129,7 @@ cdef class PrimeMeridian(_CRSParts):
         return PrimeMeridian.create(context, prime_meridian_pj)
 
     @staticmethod
-    def from_epsg(code):
+    def from_epsg(code not None):
         """
         .. versionadded:: 2.2.0
 
@@ -1146,7 +1147,7 @@ cdef class PrimeMeridian(_CRSParts):
         return PrimeMeridian.from_authority("EPSG", code)
 
     @staticmethod
-    def _from_string(prime_meridian_string):
+    def _from_string(str prime_meridian_string not None):
         """
         Create an PrimeMeridian from a string.
 
@@ -1177,13 +1178,13 @@ cdef class PrimeMeridian(_CRSParts):
             proj_destroy(prime_meridian_pj)
             pyproj_context_destroy(context)
             raise CRSError(
-                f"Invalid prime meridian string: {pystrdecode(prime_meridian_string)}"
+                f"Invalid prime meridian string: {prime_meridian_string}"
             )
         CRSError.clear()
         return PrimeMeridian.create(context, prime_meridian_pj)
 
     @staticmethod
-    def from_string(prime_meridian_string):
+    def from_string(str prime_meridian_string not None):
         """
         .. versionadded:: 2.2.0
 
@@ -1214,7 +1215,7 @@ cdef class PrimeMeridian(_CRSParts):
                 raise crs_err
 
     @staticmethod
-    def from_json_dict(prime_meridian_dict):
+    def from_json_dict(dict prime_meridian_dict not None):
         """
         .. versionadded:: 2.4.0
 
@@ -1234,7 +1235,7 @@ cdef class PrimeMeridian(_CRSParts):
         )
 
     @staticmethod
-    def from_json(prime_meridian_json_str):
+    def from_json(str prime_meridian_json_str not None):
         """
         .. versionadded:: 2.4.0
 
@@ -1253,8 +1254,8 @@ cdef class PrimeMeridian(_CRSParts):
 
     @staticmethod
     def from_name(
-        prime_meridian_name,
-        auth_name=None,
+        str prime_meridian_name not None,
+        str auth_name=None,
     ):
         """
         .. versionadded:: 2.5.0
@@ -1286,13 +1287,13 @@ cdef class PrimeMeridian(_CRSParts):
         if prime_meridian_pj == NULL:
             pyproj_context_destroy(context)
             raise CRSError(
-                f"Invalid prime meridian name: {pystrdecode(prime_meridian_name)}"
+                f"Invalid prime meridian name: {prime_meridian_name}"
             )
         CRSError.clear()
         return PrimeMeridian.create(context, prime_meridian_pj)
 
 
-_DATUM_TYPE_MAP = {
+cdef dict _DATUM_TYPE_MAP = {
     PJ_TYPE_GEODETIC_REFERENCE_FRAME: "Geodetic Reference Frame",
     PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME: "Dynamic Geodetic Reference Frame",
     PJ_TYPE_VERTICAL_REFERENCE_FRAME: "Vertical Reference Frame",
@@ -1303,7 +1304,7 @@ _DATUM_TYPE_MAP = {
     PJ_TYPE_PARAMETRIC_DATUM: "Parametric Datum",
 }
 
-_PJ_DATUM_TYPE_MAP = {
+cdef dict _PJ_DATUM_TYPE_MAP = {
     DatumType.DATUM_ENSEMBLE: PJ_TYPE_DATUM_ENSEMBLE,
     DatumType.GEODETIC_REFERENCE_FRAME: PJ_TYPE_GEODETIC_REFERENCE_FRAME,
     DatumType.DYNAMIC_GEODETIC_REFERENCE_FRAME:
@@ -1345,7 +1346,7 @@ cdef class Datum(_CRSParts):
         return datum
 
     @staticmethod
-    def _from_authority(auth_name, code, PJ_CATEGORY category):
+    def _from_authority(str auth_name not None, code not None, PJ_CATEGORY category):
         """
         Create a Datum from an authority code.
 
@@ -1364,7 +1365,7 @@ cdef class Datum(_CRSParts):
 
         cdef PJ* datum_pj = proj_create_from_database(
             context,
-            cstrencode(str(auth_name)),
+            cstrencode(auth_name),
             cstrencode(str(code)),
             category,
             False,
@@ -1378,7 +1379,7 @@ cdef class Datum(_CRSParts):
         return Datum.create(context, datum_pj)
 
     @staticmethod
-    def from_authority(auth_name, code):
+    def from_authority(str auth_name not None, code not None):
         """
         Create a Datum from an authority code.
 
@@ -1399,7 +1400,7 @@ cdef class Datum(_CRSParts):
             return Datum._from_authority(auth_name, code, PJ_CATEGORY_DATUM)
 
     @staticmethod
-    def from_epsg(code):
+    def from_epsg(code not None):
         """
         Create a Datum from an EPSG code.
 
@@ -1415,7 +1416,7 @@ cdef class Datum(_CRSParts):
         return Datum.from_authority("EPSG", code)
 
     @staticmethod
-    def _from_string(datum_string):
+    def _from_string(str datum_string not None):
         """
         Create a Datum from a string.
 
@@ -1446,12 +1447,12 @@ cdef class Datum(_CRSParts):
         ):
             proj_destroy(datum_pj)
             pyproj_context_destroy(context)
-            raise CRSError(f"Invalid datum string: {pystrdecode(datum_string)}")
+            raise CRSError(f"Invalid datum string: {datum_string}")
         CRSError.clear()
         return Datum.create(context, datum_pj)
 
     @staticmethod
-    def from_string(datum_string):
+    def from_string(str datum_string not None):
         """
         Create a Datum from a string.
 
@@ -1482,9 +1483,9 @@ cdef class Datum(_CRSParts):
 
     @staticmethod
     def _from_name(
-        datum_name,
-        auth_name,
-        datum_type,
+        str datum_name,
+        str auth_name,
+        object datum_type,
     ):
         """
         .. versionadded:: 2.5.0
@@ -1515,14 +1516,14 @@ cdef class Datum(_CRSParts):
         )
         if datum_pj == NULL:
             pyproj_context_destroy(context)
-            raise CRSError(f"Invalid datum name: {pystrdecode(datum_name)}")
+            raise CRSError(f"Invalid datum name: {datum_name}")
         CRSError.clear()
         return Datum.create(context, datum_pj)
 
     @staticmethod
     def from_name(
-        datum_name,
-        auth_name=None,
+        str datum_name not None,
+        str auth_name=None,
         datum_type=None,
     ):
         """
@@ -1571,7 +1572,7 @@ cdef class Datum(_CRSParts):
         )
 
     @staticmethod
-    def from_json_dict(datum_dict):
+    def from_json_dict(dict datum_dict not None):
         """
         .. versionadded:: 2.4.0
 
@@ -1589,7 +1590,7 @@ cdef class Datum(_CRSParts):
         return Datum._from_string(json.dumps(datum_dict, cls=NumpyEncoder))
 
     @staticmethod
-    def from_json(datum_json_str):
+    def from_json(str datum_json_str not None):
         """
         .. versionadded:: 2.4.0
 
@@ -1824,7 +1825,7 @@ cdef class Grid:
         )
 
 
-_COORDINATE_OPERATION_TYPE_MAP = {
+cdef dict _COORDINATE_OPERATION_TYPE_MAP = {
     PJ_TYPE_UNKNOWN: "Unknown",
     PJ_TYPE_CONVERSION: "Conversion",
     PJ_TYPE_TRANSFORMATION: "Transformation",
@@ -1832,7 +1833,7 @@ _COORDINATE_OPERATION_TYPE_MAP = {
     PJ_TYPE_OTHER_COORDINATE_OPERATION: "Other Coordinate Operation",
 }
 
-_PJ_COORDINATE_OPERATION_TYPE_MAP = {
+cdef dict _PJ_COORDINATE_OPERATION_TYPE_MAP = {
     CoordinateOperationType.CONVERSION: PJ_TYPE_CONVERSION,
     CoordinateOperationType.TRANSFORMATION: PJ_TYPE_TRANSFORMATION,
     CoordinateOperationType.CONCATENATED_OPERATION: PJ_TYPE_CONCATENATED_OPERATION,
@@ -1925,7 +1926,11 @@ cdef class CoordinateOperation(_CRSParts):
         return coord_operation
 
     @staticmethod
-    def from_authority(auth_name, code, use_proj_alternative_grid_names=False):
+    def from_authority(
+        str auth_name not None,
+        code not None,
+        bint use_proj_alternative_grid_names=False,
+    ):
         """
         Create a CoordinateOperation from an authority code.
 
@@ -1959,7 +1964,7 @@ cdef class CoordinateOperation(_CRSParts):
         return CoordinateOperation.create(context, coord_operation_pj)
 
     @staticmethod
-    def from_epsg(code, use_proj_alternative_grid_names=False):
+    def from_epsg(code not None, bint use_proj_alternative_grid_names= False):
         """
         Create a CoordinateOperation from an EPSG code.
 
@@ -1979,7 +1984,7 @@ cdef class CoordinateOperation(_CRSParts):
         )
 
     @staticmethod
-    def _from_string(coordinate_operation_string):
+    def _from_string(str coordinate_operation_string not None):
         """
         Create a CoordinateOperation from a string.
 
@@ -2013,13 +2018,13 @@ cdef class CoordinateOperation(_CRSParts):
             pyproj_context_destroy(context)
             raise CRSError(
                 "Invalid coordinate operation string: "
-                f"{pystrdecode(coordinate_operation_string)}"
+                f"{coordinate_operation_string}"
             )
         CRSError.clear()
         return CoordinateOperation.create(context, coord_operation_pj)
 
     @staticmethod
-    def from_string(coordinate_operation_string):
+    def from_string(str coordinate_operation_string not None):
         """
         Create a CoordinateOperation from a string.
 
@@ -2046,7 +2051,7 @@ cdef class CoordinateOperation(_CRSParts):
                 raise crs_err
 
     @staticmethod
-    def from_json_dict(coordinate_operation_dict):
+    def from_json_dict(dict coordinate_operation_dict not None):
         """
         Create CoordinateOperation from a JSON dictionary.
 
@@ -2066,7 +2071,7 @@ cdef class CoordinateOperation(_CRSParts):
         )
 
     @staticmethod
-    def from_json(coordinate_operation_json_str):
+    def from_json(str coordinate_operation_json_str not None):
         """
         Create CoordinateOperation from a JSON string.
 
@@ -2087,9 +2092,9 @@ cdef class CoordinateOperation(_CRSParts):
 
     @staticmethod
     def from_name(
-        coordinate_operation_name,
-        auth_name=None,
-        coordinate_operation_type=CoordinateOperationType.CONVERSION,
+        str coordinate_operation_name not None,
+        str auth_name=None,
+        coordinate_operation_type not None=CoordinateOperationType.CONVERSION,
     ):
         """
         .. versionadded:: 2.5.0
@@ -2128,7 +2133,7 @@ cdef class CoordinateOperation(_CRSParts):
             pyproj_context_destroy(context)
             raise CRSError(
                 "Invalid coordinate operation name: "
-                f"{pystrdecode(coordinate_operation_name)}"
+                f"{coordinate_operation_name}"
             )
         CRSError.clear()
         return CoordinateOperation.create(context, coordinate_operation_pj)
@@ -2200,7 +2205,7 @@ cdef class CoordinateOperation(_CRSParts):
         self._area_of_use = create_area_of_use(self.context, self.projobj)
         return self._area_of_use
 
-    def to_proj4(self, version=ProjVersion.PROJ_5, pretty=False):
+    def to_proj4(self, version not None=ProjVersion.PROJ_5, bint pretty=False):
         """
         Convert the projection to a PROJ string.
 
@@ -2298,7 +2303,7 @@ confidence: int
 """
 
 
-_CRS_TYPE_MAP = {
+cdef dict _CRS_TYPE_MAP = {
     PJ_TYPE_UNKNOWN: "Unknown CRS",
     PJ_TYPE_CRS: "CRS",
     PJ_TYPE_GEODETIC_CRS: "Geodetic CRS",
@@ -2336,20 +2341,20 @@ cdef class _CRS(Base):
         self._coordinate_operation = None
         self.type_name = "undefined"
 
-    def __init__(self, proj_string):
+    def __init__(self, const char *proj_string):
         self.context = pyproj_context_create()
         # initialize projection
         self.projobj = proj_create(
             self.context,
-            cstrencode(proj_string),
+            proj_string,
         )
         if self.projobj == NULL:
-            raise CRSError(f"Invalid projection: {pystrdecode(proj_string)}")
+            raise CRSError(f"Invalid projection: {proj_string}")
         # make sure the input is a CRS
         if not proj_is_crs(self.projobj):
             raise CRSError(f"Input is not a CRS: {proj_string}")
         # set proj information
-        self.srs = pystrdecode(proj_string)
+        self.srs = proj_string
         self._type = proj_get_type(self.projobj)
         self.type_name = _CRS_TYPE_MAP[self._type]
         self._set_base_info()
@@ -2667,7 +2672,7 @@ cdef class _CRS(Base):
         )
         return _to_proj4(self.context, self.projobj, version=version, pretty=False)
 
-    def to_epsg(self, min_confidence=70):
+    def to_epsg(self, int min_confidence=70):
         """
         Return the EPSG code best matching the CRS
         or None if it a match is not found.
@@ -2710,7 +2715,7 @@ cdef class _CRS(Base):
             return int(auth_info[1])
         return None
 
-    def to_authority(self, auth_name=None, min_confidence=70):
+    def to_authority(self, str auth_name=None, int min_confidence=70):
         """
         .. versionadded:: 2.2.0
 
@@ -2756,7 +2761,7 @@ cdef class _CRS(Base):
         except IndexError:
             return None
 
-    def list_authority(self, auth_name=None, min_confidence=70):
+    def list_authority(self, str auth_name=None, int min_confidence=70):
         """
         .. versionadded:: 3.2.0
 
@@ -2797,8 +2802,8 @@ cdef class _CRS(Base):
         # get list of possible matching projections
         cdef PJ_OBJ_LIST *proj_list = NULL
         cdef int *c_out_confidence_list = NULL
-        cdef int out_confidence = -9999
         cdef int num_proj_objects = -9999
+        cdef bytes b_auth_name
         cdef char *user_auth_name = NULL
         cdef int iii = 0
 
@@ -2836,13 +2841,13 @@ cdef class _CRS(Base):
                 if out_confidence_list[iii] < min_confidence:
                     continue
                 proj = proj_list_get(self.context, proj_list, iii)
-                code = proj_get_id_code(proj, iii)
-                out_auth_name = proj_get_id_auth_name(proj, iii)
+                code = proj_get_id_code(proj, 0)
+                out_auth_name = proj_get_id_auth_name(proj, 0)
                 if out_auth_name != NULL and code != NULL:
                     authority_list.append(
                         AuthorityMatchInfo(
-                            pystrdecode(out_auth_name),
-                            pystrdecode(code),
+                            out_auth_name,
+                            code,
                             out_confidence_list[iii]
                         )
                     )
@@ -2853,7 +2858,7 @@ cdef class _CRS(Base):
             CRSError.clear()
         return authority_list
 
-    def to_3d(self, name=None):
+    def to_3d(self, str name=None):
         """
         .. versionadded:: 3.1.0
 
@@ -2873,13 +2878,14 @@ cdef class _CRS(Base):
         -------
         _CRS
         """
-        cdef char* name_3D = NULL
+        cdef char* c_name = NULL
+        cdef bytes b_name
         if name is not None:
             b_name = cstrencode(name)
-            name_3D = b_name
+            c_name = b_name
 
         cdef PJ * projobj = proj_crs_promote_to_3D(
-            self.context, name_3D, self.projobj
+            self.context, c_name, self.projobj
         )
         CRSError.clear()
         if projobj == NULL:
@@ -2890,7 +2896,9 @@ cdef class _CRS(Base):
             proj_destroy(projobj)
         return crs_3d
 
-    def _is_crs_property(self, property_name, property_types, sub_crs_index=0):
+    def _is_crs_property(
+        self, str property_name, tuple property_types, int sub_crs_index=0
+    ):
         """
         .. versionadded:: 2.2.0
 


=====================================
pyproj/_datadir.pyx
=====================================
@@ -2,12 +2,12 @@ import logging
 import os
 import warnings
 
-from pyproj.utils import strtobool
-
 from libc.stdlib cimport free, malloc
 
-from pyproj.compat import cstrencode, pystrdecode
+from pyproj._compat cimport cstrencode
+
 from pyproj.exceptions import DataDirError, ProjError
+from pyproj.utils import strtobool
 
 # for logging the internal PROJ messages
 # https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library
@@ -85,9 +85,9 @@ def get_user_data_dir(create=False):
     str:
         The user writable data directory.
     """
-    return pystrdecode(proj_context_get_user_writable_directory(
+    return proj_context_get_user_writable_directory(
         PYPROJ_GLOBAL_CONTEXT, bool(create)
-    ))
+    )
 
 
 cdef void pyproj_log_function(void *user_data, int level, const char *error_msg) nogil:
@@ -99,14 +99,14 @@ cdef void pyproj_log_function(void *user_data, int level, const char *error_msg)
     # PROJ_DEBUG environment variable.
     if level == PJ_LOG_ERROR:
         with gil:
-            ProjError.internal_proj_error = pystrdecode(error_msg)
+            ProjError.internal_proj_error = error_msg
             _LOGGER.debug(f"PROJ_ERROR: {ProjError.internal_proj_error}")
     elif level == PJ_LOG_DEBUG:
         with gil:
-            _LOGGER.debug(f"PROJ_DEBUG: {pystrdecode(error_msg)}")
+            _LOGGER.debug(f"PROJ_DEBUG: {error_msg}")
     elif level == PJ_LOG_TRACE:
         with gil:
-            _LOGGER.debug(f"PROJ_TRACE: {pystrdecode(error_msg)}")
+            _LOGGER.debug(f"PROJ_TRACE: {error_msg}")
 
 
 cdef void set_context_data_dir(PJ_CONTEXT* context) except *:
@@ -117,7 +117,7 @@ cdef void set_context_data_dir(PJ_CONTEXT* context) except *:
 
     data_dir_list = get_data_dir().split(os.pathsep)
     # the first path will always have the database
-    b_database_path = cstrencode(os.path.join(data_dir_list[0], "proj.db"))
+    cdef bytes b_database_path = cstrencode(os.path.join(data_dir_list[0], "proj.db"))
     cdef const char* c_database_path = b_database_path
     if not proj_context_set_database_path(context, c_database_path, NULL, NULL):
         warnings.warn("pyproj unable to set database path.")
@@ -125,6 +125,7 @@ cdef void set_context_data_dir(PJ_CONTEXT* context) except *:
     cdef const char **c_data_dir = <const char **>malloc(
         (dir_list_len + 1) * sizeof(const char*)
     )
+    cdef bytes b_data_dir
     try:
         for iii in range(dir_list_len):
             b_data_dir = cstrencode(data_dir_list[iii])
@@ -192,9 +193,9 @@ cdef void pyproj_context_destroy(PJ_CONTEXT* context) except *:
         proj_context_destroy(context)
 
 
-def _pyproj_global_context_initialize():
+cpdef _pyproj_global_context_initialize():
     pyproj_context_initialize(PYPROJ_GLOBAL_CONTEXT)
 
 
-def _global_context_set_data_dir():
+cpdef _global_context_set_data_dir():
     set_context_data_dir(PYPROJ_GLOBAL_CONTEXT)


=====================================
pyproj/_geod.pyx
=====================================
@@ -3,10 +3,10 @@ include "base.pxi"
 cimport cython
 from libc.math cimport ceil, isnan, round
 
+from pyproj._compat cimport cstrencode, empty_array
+
 from collections import namedtuple
 
-from pyproj._compat import empty_array
-from pyproj.compat import cstrencode, pystrdecode
 from pyproj.enums import GeodIntermediateFlag
 from pyproj.exceptions import GeodError
 
@@ -73,7 +73,7 @@ cdef class Geod:
         # convert 'a' only for initstring
         a_str = int(a) if a.is_integer() else a
         f_str = int(f) if f.is_integer() else f
-        self.initstring = pystrdecode(cstrencode(f"+a={a_str} +f={f_str}"))
+        self.initstring = f"+a={a_str} +f={f_str}"
         self.sphere = sphere
         self.b = b
         self.es = es


=====================================
pyproj/_network.pyx
=====================================
@@ -4,12 +4,11 @@ import os
 
 from pyproj.utils import strtobool
 
+from pyproj._compat cimport cstrencode
 from pyproj._datadir cimport PYPROJ_GLOBAL_CONTEXT
 
-from pyproj.compat import cstrencode
 
-
-def _set_ca_bundle_path(ca_bundle_path):
+def _set_ca_bundle_path(str ca_bundle_path):
     """
     Sets the path to the CA Bundle used by the `curl`
     built into PROJ.


=====================================
pyproj/_sync.pyx
=====================================
@@ -1,7 +1,5 @@
 include "proj.pxi"
 
-from pyproj.compat import pystrdecode
-
 from pyproj._datadir cimport PYPROJ_GLOBAL_CONTEXT
 
 
@@ -12,4 +10,4 @@ def get_proj_endpoint() -> str:
     str:
         URL of the endpoint where PROJ grids are stored.
     """
-    return pystrdecode(proj_context_get_url_endpoint(PYPROJ_GLOBAL_CONTEXT))
+    return proj_context_get_url_endpoint(PYPROJ_GLOBAL_CONTEXT)


=====================================
pyproj/_transformer.pxd
=====================================
@@ -1,23 +1,23 @@
 include "proj.pxi"
 
-from pyproj._crs cimport _CRS, Base
+from pyproj._crs cimport Base
 
 
 cdef class _TransformerGroup:
     cdef PJ_CONTEXT* context
-    cdef readonly _transformers
-    cdef readonly _unavailable_operations
-    cdef readonly _best_available
+    cdef readonly list _transformers
+    cdef readonly list _unavailable_operations
+    cdef readonly list _best_available
 
 cdef class _Transformer(Base):
     cdef PJ_PROJ_INFO proj_info
     cdef readonly _area_of_use
-    cdef readonly type_name
-    cdef readonly object _operations
+    cdef readonly str type_name
+    cdef readonly tuple _operations
 
     @staticmethod
     cdef _Transformer _from_pj(
         PJ_CONTEXT* context,
         PJ *transform_pj,
-        always_xy,
+        bint always_xy,
     )


=====================================
pyproj/_transformer.pyi
=====================================
@@ -66,16 +66,16 @@ class _Transformer(Base):
     ) -> str: ...
     @staticmethod
     def from_crs(
-        crs_from: str,
-        crs_to: str,
+        crs_from: bytes,
+        crs_to: bytes,
         always_xy: bool = False,
         area_of_interest: Optional[AreaOfInterest] = None,
         authority: Optional[str] = None,
-        accuracy: Optional[float] = None,
+        accuracy: Optional[str] = None,
         allow_ballpark: Optional[bool] = None,
     ) -> "_Transformer": ...
     @staticmethod
-    def from_pipeline(proj_pipeline: str) -> "_Transformer": ...
+    def from_pipeline(proj_pipeline: bytes) -> "_Transformer": ...
     def _transform(
         self,
         inx: Any,


=====================================
pyproj/_transformer.pyx
=====================================
@@ -8,6 +8,7 @@ import re
 import warnings
 from collections import namedtuple
 
+from pyproj._compat cimport cstrencode
 from pyproj._crs cimport (
     _CRS,
     Base,
@@ -20,7 +21,6 @@ from pyproj._datadir cimport pyproj_context_create, pyproj_context_destroy
 
 from pyproj._datadir import _LOGGER
 from pyproj.aoi import AreaOfInterest
-from pyproj.compat import cstrencode, pystrdecode
 from pyproj.enums import ProjVersion, TransformDirection
 from pyproj.exceptions import ProjError
 
@@ -29,21 +29,21 @@ proj_version_str = f"{PROJ_VERSION_MAJOR}.{PROJ_VERSION_MINOR}.{PROJ_VERSION_PAT
 _AUTH_CODE_RE = re.compile(r"(?P<authority>\w+)\:(?P<code>\w+)")
 
 
-cdef pyproj_errno_string(PJ_CONTEXT* ctx, int err):
+cdef str pyproj_errno_string(PJ_CONTEXT* ctx, int err):
     # https://github.com/pyproj4/pyproj/issues/760
     IF CTE_PROJ_VERSION_MAJOR >= 8:
-        return pystrdecode(proj_context_errno_string(ctx, err))
+        return proj_context_errno_string(ctx, err)
     ELSE:
-        return pystrdecode(proj_errno_string(err))
+        return proj_errno_string(err)
 
 
-_PJ_DIRECTION_MAP = {
+cdef dict _PJ_DIRECTION_MAP = {
     TransformDirection.FORWARD: PJ_FWD,
     TransformDirection.INVERSE: PJ_INV,
     TransformDirection.IDENT: PJ_IDENT,
 }
 
-_TRANSFORMER_TYPE_MAP = {
+cdef dict _TRANSFORMER_TYPE_MAP = {
     PJ_TYPE_UNKNOWN: "Unknown Transformer",
     PJ_TYPE_CONVERSION: "Conversion Transformer",
     PJ_TYPE_TRANSFORMATION: "Transformation Transformer",
@@ -120,9 +120,9 @@ cdef class _TransformerGroup:
 
     def __init__(
         self,
-        _CRS crs_from,
-        _CRS crs_to,
-        always_xy=False,
+        _CRS crs_from not None,
+        _CRS crs_to not None,
+        bint always_xy=False,
         area_of_interest=None,
     ):
         """
@@ -237,8 +237,8 @@ cdef PJ* proj_create_crs_to_crs(
     const char *source_crs_str,
     const char *target_crs_str,
     PJ_AREA *area,
-    authority,
-    accuracy,
+    str authority,
+    str accuracy,
     allow_ballpark,
 ):
     """
@@ -263,6 +263,8 @@ cdef PJ* proj_create_crs_to_crs(
         return NULL
 
     cdef const char* options[4]
+    cdef bytes b_authority
+    cdef bytes b_accuracy
     cdef int options_index = 0
     options[0] = NULL
     options[1] = NULL
@@ -512,15 +514,15 @@ cdef class _Transformer(Base):
 
     @property
     def id(self):
-        return pystrdecode(self.proj_info.id)
+        return self.proj_info.id
 
     @property
     def description(self):
-        return pystrdecode(self.proj_info.description)
+        return self.proj_info.description
 
     @property
     def definition(self):
-        return pystrdecode(self.proj_info.definition)
+        return self.proj_info.definition
 
     @property
     def has_inverse(self):
@@ -566,7 +568,7 @@ cdef class _Transformer(Base):
         """
         return proj_context_is_network_enabled(self.context) == 1
 
-    def to_proj4(self, version=ProjVersion.PROJ_5, pretty=False):
+    def to_proj4(self, version=ProjVersion.PROJ_5, bint pretty=False):
         """
         Convert the projection to a PROJ string.
 
@@ -591,10 +593,10 @@ cdef class _Transformer(Base):
     def from_crs(
         const char* crs_from,
         const char* crs_to,
-        always_xy=False,
+        bint always_xy=False,
         area_of_interest=None,
-        authority=None,
-        accuracy=None,
+        str authority=None,
+        str accuracy=None,
         allow_ballpark=None,
     ):
         """
@@ -649,7 +651,7 @@ cdef class _Transformer(Base):
     cdef _Transformer _from_pj(
         PJ_CONTEXT* context,
         PJ *transform_pj,
-        always_xy,
+        bint always_xy,
     ):
         """
         Create a Transformer from a PJ* object
@@ -672,7 +674,7 @@ cdef class _Transformer(Base):
         cdef _Transformer transformer = _Transformer()
         transformer.context = pyproj_context_create()
 
-        auth_match = _AUTH_CODE_RE.match(pystrdecode(proj_pipeline.strip()))
+        auth_match = _AUTH_CODE_RE.match(proj_pipeline.strip())
         if auth_match:
             # attempt to create coordinate operation from AUTH:CODE
             match_data = auth_match.groupdict()
@@ -706,7 +708,7 @@ cdef class _Transformer(Base):
         proj_destroy(self.projobj)
         self.projobj = always_xy_pj
 
-    def _init_from_crs(self, always_xy):
+    def _init_from_crs(self, bint always_xy):
         """
         Finish initializing transformer properties from CRS objects
         """


=====================================
pyproj/compat.py deleted
=====================================
@@ -1,25 +0,0 @@
-"""
-This module is for compatibility between string types
-"""
-
-
-def cstrencode(pystr):
-    """
-    encode a string into bytes.  If already bytes, do nothing.
-    """
-    try:
-        return pystr.encode("utf-8")
-    except UnicodeDecodeError:
-        return pystr.decode("utf-8").encode("utf-8")
-    except AttributeError:
-        return pystr  # already bytes
-
-
-def pystrdecode(cstr):
-    """
-    Decode a string to a python string.
-    """
-    try:
-        return cstr.decode("utf-8")
-    except AttributeError:
-        return cstr


=====================================
pyproj/crs/_cf1x8.py
=====================================
@@ -336,7 +336,8 @@ def _rotated_latitude_longitude(cf_params):
     return RotatedLatitudeLongitudeConversion(
         o_lat_p=cf_params["grid_north_pole_latitude"],
         o_lon_p=cf_params["grid_north_pole_longitude"],
-        lon_0=cf_params.get("north_pole_grid_longitude", 0.0),
+        # https://github.com/pyproj4/pyproj/issues/927
+        lon_0=cf_params.get("north_pole_grid_longitude", 0.0) + 180,
     )
 
 
@@ -626,7 +627,29 @@ def _rotated_latitude_longitude__to_cf(conversion):
         "grid_mapping_name": "rotated_latitude_longitude",
         "grid_north_pole_latitude": params["o_lat_p"],
         "grid_north_pole_longitude": params["o_lon_p"],
-        "north_pole_grid_longitude": params["lon_0"],
+        # https://github.com/pyproj4/pyproj/issues/927
+        "north_pole_grid_longitude": params["lon_0"] - 180,
+    }
+
+
+def _pole_rotation_netcdf__to_cf(conversion):
+    """
+    http://cfconventions.org/cf-conventions/cf-conventions.html#_rotated_pole
+
+    https://github.com/OSGeo/PROJ/pull/2835
+    """
+    params = _to_dict(conversion)
+    return {
+        "grid_mapping_name": "rotated_latitude_longitude",
+        "grid_north_pole_latitude": params[
+            "grid_north_pole_latitude_(netcdf_cf_convention)"
+        ],
+        "grid_north_pole_longitude": params[
+            "grid_north_pole_longitude_(netcdf_cf_convention)"
+        ],
+        "north_pole_grid_longitude": params[
+            "north_pole_grid_longitude_(netcdf_cf_convention)"
+        ],
     }
 
 
@@ -656,4 +679,5 @@ _INVERSE_GEOGRAPHIC_GRID_MAPPING_NAME_MAP = {
     "proj ob_tran o_proj=lonlat": _rotated_latitude_longitude__to_cf,
     "proj ob_tran o_proj=latlon": _rotated_latitude_longitude__to_cf,
     "proj ob_tran o_proj=latlong": _rotated_latitude_longitude__to_cf,
+    "pole rotation (netcdf cf convention)": _pole_rotation_netcdf__to_cf,
 }


=====================================
pyproj/database.pyx
=====================================
@@ -6,14 +6,14 @@ from typing import Optional
 
 from libc.stdlib cimport free, malloc
 
-from pyproj._compat cimport cstrdecode
+from pyproj._compat cimport cstrdecode, cstrencode
 from pyproj._datadir cimport pyproj_context_create, pyproj_context_destroy
 
 from pyproj.aoi import AreaOfUse
-from pyproj.compat import cstrencode, pystrdecode
 from pyproj.enums import PJType
 
-_PJ_TYPE_MAP = {
+
+cdef dict _PJ_TYPE_MAP = {
     PJType.UNKNOWN: PJ_TYPE_UNKNOWN,
     PJType.ELLIPSOID: PJ_TYPE_ELLIPSOID,
     PJType.PRIME_MERIDIAN: PJ_TYPE_PRIME_MERIDIAN,
@@ -40,7 +40,7 @@ _PJ_TYPE_MAP = {
     PJType.CONCATENATED_OPERATION: PJ_TYPE_CONCATENATED_OPERATION,
     PJType.OTHER_COORDINATE_OPERATION: PJ_TYPE_OTHER_COORDINATE_OPERATION,
 }
-_INV_PJ_TYPE_MAP = {value: key for key, value in _PJ_TYPE_MAP.items()}
+cdef dict _INV_PJ_TYPE_MAP = {value: key for key, value in _PJ_TYPE_MAP.items()}
 
 
 def get_authorities():
@@ -61,7 +61,7 @@ def get_authorities():
     try:
         auth_list = []
         while proj_auth_list[iii] != NULL:
-            auth_list.append(pystrdecode(proj_auth_list[iii]))
+            auth_list.append(proj_auth_list[iii])
             iii += 1
     finally:
         pyproj_context_destroy(context)
@@ -69,7 +69,7 @@ def get_authorities():
     return auth_list
 
 
-def get_codes(auth_name, pj_type, allow_deprecated=False):
+def get_codes(str auth_name not None, pj_type not None, bint allow_deprecated=False):
     """
     .. versionadded:: 2.4.0
 
@@ -106,7 +106,7 @@ def get_codes(auth_name, pj_type, allow_deprecated=False):
     try:
         code_list = []
         while proj_code_list[iii] != NULL:
-            code_list.append(pystrdecode(proj_code_list[iii]))
+            code_list.append(proj_code_list[iii])
             iii += 1
     finally:
         proj_string_list_destroy(proj_code_list)
@@ -150,11 +150,11 @@ projection_method_name: Optional[str]
 
 
 def query_crs_info(
-    auth_name=None,
+    str auth_name=None,
     pj_types=None,
     area_of_interest=None,
-    contains=False,
-    allow_deprecated=False,
+    bint contains=False,
+    bint allow_deprecated=False,
  ):
     """
     .. versionadded:: 3.0.0
@@ -191,6 +191,7 @@ def query_crs_info(
     cdef int result_count = 0
     cdef int pj_type_count = 0
     cdef int iii = 0
+    cdef bytes b_auth_name
     if auth_name is not None:
         b_auth_name = cstrencode(auth_name)
         c_auth_name = b_auth_name
@@ -245,9 +246,9 @@ def query_crs_info(
                     name=cstrdecode(crs_info_list[iii].area_name),
                 )
             code_list.append(CRSInfo(
-                auth_name=pystrdecode(crs_info_list[iii].auth_name),
-                code=pystrdecode(crs_info_list[iii].code),
-                name=pystrdecode(crs_info_list[iii].name),
+                auth_name=crs_info_list[iii].auth_name,
+                code=crs_info_list[iii].code,
+                name=crs_info_list[iii].name,
                 type=_INV_PJ_TYPE_MAP[crs_info_list[iii].type],
                 deprecated=bool(crs_info_list[iii].deprecated),
                 area_of_use=area_of_use,
@@ -262,9 +263,9 @@ def query_crs_info(
 
 
 def query_utm_crs_info(
-    datum_name=None,
+    str datum_name=None,
     area_of_interest=None,
-    contains=False,
+    bint contains=False,
  ):
     """
     .. versionadded:: 3.0.0
@@ -341,7 +342,7 @@ deprecated: bool
 """
 
 
-def get_units_map(auth_name=None, category=None, allow_deprecated=False):
+def get_units_map(str auth_name=None, str category=None, bint allow_deprecated=False):
     """
     .. versionadded:: 2.2.0
     .. versionadded:: 3.0.0 query PROJ database.
@@ -364,12 +365,14 @@ def get_units_map(auth_name=None, category=None, allow_deprecated=False):
     """
     cdef const char* c_auth_name = NULL
     cdef const char* c_category = NULL
+    cdef bytes b_auth_name
+    cdef bytes b_category
     if auth_name is not None:
-        auth_name = cstrencode(auth_name)
-        c_auth_name = auth_name
+        b_auth_name = cstrencode(auth_name)
+        c_auth_name = b_auth_name
     if category is not None:
-        category = cstrencode(category)
-        c_category = category
+        b_category = cstrencode(category)
+        c_category = b_category
 
     cdef int num_units = 0
     cdef PJ_CONTEXT* context = pyproj_context_create()
@@ -385,13 +388,13 @@ def get_units_map(auth_name=None, category=None, allow_deprecated=False):
         for iii in range(num_units):
             proj_short_name = None
             if db_unit_list[iii].proj_short_name != NULL:
-                proj_short_name = pystrdecode(db_unit_list[iii].proj_short_name)
-            name = pystrdecode(db_unit_list[iii].name)
+                proj_short_name = db_unit_list[iii].proj_short_name
+            name = db_unit_list[iii].name
             units_map[name] = Unit(
-                auth_name=pystrdecode(db_unit_list[iii].auth_name),
-                code=pystrdecode(db_unit_list[iii].code),
+                auth_name=db_unit_list[iii].auth_name,
+                code=db_unit_list[iii].code,
                 name=name,
-                category=pystrdecode(db_unit_list[iii].category),
+                category=db_unit_list[iii].category,
                 conv_factor=db_unit_list[iii].conv_factor,
                 proj_short_name=proj_short_name,
                 deprecated=bool(db_unit_list[iii].deprecated),


=====================================
pyproj/datadir.py
=====================================
@@ -70,7 +70,6 @@ def get_data_dir() -> str:
     global _VALIDATED_PROJ_DATA
     if _VALIDATED_PROJ_DATA is not None:
         return _VALIDATED_PROJ_DATA
-    global _USER_PROJ_DATA
     internal_datadir = Path(__file__).absolute().parent / "proj_dir" / "share" / "proj"
     proj_lib_dirs = os.environ.get("PROJ_LIB", "")
     prefix_datadir = Path(sys.prefix, "share", "proj")


=====================================
pyproj/list.pyx
=====================================
@@ -1,7 +1,5 @@
 include "proj.pxi"
 
-from pyproj.compat import pystrdecode
-
 
 def get_proj_operations_map():
     """
@@ -14,8 +12,8 @@ def get_proj_operations_map():
     cdef int iii = 0
     operations_map = {}
     while proj_operations[iii].id != NULL:
-        operations_map[pystrdecode(proj_operations[iii].id)] = \
-            pystrdecode(proj_operations[iii].descr[0]).split("\n\t")[0]
+        operations_map[proj_operations[iii].id] = \
+            proj_operations[iii].descr[0].split("\n\t")[0]
         iii += 1
     return operations_map
 
@@ -31,12 +29,12 @@ def get_ellps_map():
     cdef int iii = 0
     ellps_map = {}
     while proj_ellps[iii].id != NULL:
-        major_key, major_val = pystrdecode(proj_ellps[iii].major).split("=")
-        ell_key, ell_val = pystrdecode(proj_ellps[iii].ell).split("=")
-        ellps_map[pystrdecode(proj_ellps[iii].id)] = {
+        major_key, major_val = proj_ellps[iii].major.split("=")
+        ell_key, ell_val = proj_ellps[iii].ell.split("=")
+        ellps_map[proj_ellps[iii].id] = {
             major_key: float(major_val),
             ell_key: float(ell_val),
-            "description": pystrdecode(proj_ellps[iii].name)
+            "description": proj_ellps[iii].name
         }
         iii += 1
     return ellps_map
@@ -53,7 +51,7 @@ def get_prime_meridians_map():
     cdef int iii = 0
     prime_meridians_map = {}
     while prime_meridians[iii].id != NULL:
-        prime_meridians_map[pystrdecode(prime_meridians[iii].id)] = \
-            pystrdecode(prime_meridians[iii].defn)
+        prime_meridians_map[prime_meridians[iii].id] = \
+            prime_meridians[iii].defn
         iii += 1
     return prime_meridians_map


=====================================
pyproj/proj.py
=====================================
@@ -18,8 +18,8 @@ import re
 import warnings
 from typing import Any, Optional, Tuple, Type
 
+from pyproj._compat import cstrencode
 from pyproj._transformer import Factors
-from pyproj.compat import pystrdecode
 from pyproj.crs import CRS
 from pyproj.enums import TransformDirection
 from pyproj.list import get_proj_operations_map
@@ -131,7 +131,7 @@ class Proj(Transformer):
             projstring = self.crs.to_proj4() or self.crs.srs
 
         self.srs = re.sub(r"\s\+?type=crs", "", projstring).strip()
-        super().__init__(TransformerFromPipeline(self.srs))
+        super().__init__(TransformerFromPipeline(cstrencode(self.srs)))
 
     def __call__(
         self,
@@ -256,7 +256,7 @@ class Proj(Transformer):
         >>> Proj("epsg:4326").definition_string()
         'proj=longlat datum=WGS84 no_defs ellps=WGS84 towgs84=0,0,0'
         """
-        return pystrdecode(self.definition)
+        return self.definition
 
     def to_latlong_def(self) -> Optional[str]:
         """return the definition string of the geographic (lat/lon)


=====================================
pyproj/transformer.py
=====================================
@@ -18,6 +18,7 @@ from pathlib import Path
 from typing import Any, Iterable, Iterator, List, Optional, Tuple, Union, overload
 
 from pyproj import CRS
+from pyproj._compat import cstrencode
 from pyproj._crs import AreaOfUse, CoordinateOperation
 from pyproj._transformer import (  # noqa: F401 pylint: disable=unused-import
     AreaOfInterest,
@@ -25,7 +26,6 @@ from pyproj._transformer import (  # noqa: F401 pylint: disable=unused-import
     _TransformerGroup,
     proj_version_str,
 )
-from pyproj.compat import cstrencode
 from pyproj.datadir import get_user_data_dir
 from pyproj.enums import ProjVersion, TransformDirection, WktVersion
 from pyproj.exceptions import ProjError
@@ -80,12 +80,12 @@ class TransformerFromCRS(TransformerMaker):
     Generates a Cython _Transformer class from input CRS data.
     """
 
-    crs_from: str
-    crs_to: str
+    crs_from: bytes
+    crs_to: bytes
     always_xy: bool
     area_of_interest: Optional[AreaOfInterest]
     authority: Optional[str]
-    accuracy: Optional[float]
+    accuracy: Optional[str]
     allow_ballpark: Optional[bool]
 
     def __call__(self) -> _Transformer:
@@ -95,8 +95,8 @@ class TransformerFromCRS(TransformerMaker):
         _Transformer
         """
         return _Transformer.from_crs(
-            cstrencode(self.crs_from),
-            cstrencode(self.crs_to),
+            self.crs_from,
+            self.crs_to,
             always_xy=self.always_xy,
             area_of_interest=self.area_of_interest,
             authority=self.authority,
@@ -113,7 +113,7 @@ class TransformerFromPipeline(TransformerMaker):
     Generates a Cython _Transformer class from input pipeline data.
     """
 
-    proj_pipeline: str
+    proj_pipeline: bytes
 
     def __call__(self) -> _Transformer:
         """
@@ -121,7 +121,7 @@ class TransformerFromPipeline(TransformerMaker):
         -------
         _Transformer
         """
-        return _Transformer.from_pipeline(cstrencode(self.proj_pipeline))
+        return _Transformer.from_pipeline(self.proj_pipeline)
 
 
 class TransformerGroup(_TransformerGroup):
@@ -533,12 +533,12 @@ class Transformer:
 
         return Transformer(
             TransformerFromCRS(
-                CRS.from_user_input(crs_from).srs,
-                CRS.from_user_input(crs_to).srs,
+                cstrencode(CRS.from_user_input(crs_from).srs),
+                cstrencode(CRS.from_user_input(crs_to).srs),
                 always_xy=always_xy,
                 area_of_interest=area_of_interest,
                 authority=authority,
-                accuracy=accuracy,
+                accuracy=accuracy if accuracy is None else str(accuracy),
                 allow_ballpark=allow_ballpark,
             )
         )
@@ -574,7 +574,7 @@ class Transformer:
         Transformer
 
         """
-        return Transformer(TransformerFromPipeline(proj_pipeline))
+        return Transformer(TransformerFromPipeline(cstrencode(proj_pipeline)))
 
     @overload
     def transform(  # pylint: disable=invalid-name
@@ -659,7 +659,8 @@ class Transformer:
             instead of returning a new array. This will fail if the input
             is not an array in C order with the double data type.
 
-        Example:
+        Example
+        --------
 
         >>> from pyproj import Transformer
         >>> transformer = Transformer.from_crs("epsg:4326", "epsg:3857")
@@ -769,7 +770,8 @@ class Transformer:
             Default is :attr:`pyproj.enums.TransformDirection.FORWARD`.
 
 
-        Example:
+        Example
+        --------
 
         >>> from pyproj import Transformer
         >>> transformer = Transformer.from_crs(4326, 2100)
@@ -875,6 +877,25 @@ class Transformer:
         Transform boundary densifying the edges to account for nonlinear
         transformations along these edges and extracting the outermost bounds.
 
+        If the destination CRS is geographic and right < left then the bounds
+        crossed the antimeridian. In this scenario there are two polygons,
+        one on each side of the antimeridian. The first polygon should be
+        constructed with (left, bottom, 180, top) and the second with
+        (-180, bottom, top, right).
+
+        To construct the bounding polygons with shapely::
+
+            def bounding_polygon(left, bottom, right, top):
+                if right < left:
+                    return shapely.geometry.MultiPolygon(
+                        [
+                            shapely.geometry.box(left, bottom, 180, top),
+                            shapely.geometry.box(-180, bottom, right, top),
+                        ]
+                    )
+                return shapely.geometry.box(left, bottom, right, top)
+
+
         Parameters
         ----------
         left: float


=====================================
setup.cfg
=====================================
@@ -6,7 +6,7 @@ long_description_content_type = text/markdown
 author = Jeff Whitaker
 author_email = jeffrey.s.whitaker at noaa.gov
 license = MIT
-license-file = LICENSE
+license_file = LICENSE
 platform = any
 keywords = GIS, map, geospatial, coordinate-systems, coordinate-transformation, cartographic-projection, geodesic
 classifiers =


=====================================
setup.py
=====================================
@@ -110,7 +110,11 @@ def get_cythonize_options():
     # Configure optional Cython coverage.
     cythonize_options = {
         "language_level": sys.version_info[0],
-        "compiler_directives": {"embedsignature": True},
+        "compiler_directives": {
+            "c_string_type": "str",
+            "c_string_encoding": "utf-8",
+            "embedsignature": True,
+        },
     }
     if os.environ.get("PYPROJ_FULL_COVERAGE"):
         cythonize_options["compiler_directives"].update(linetrace=True)


=====================================
test/crs/test_crs.py
=====================================
@@ -1442,6 +1442,13 @@ def test_crs_multithread():
             pass
 
 
+def test_crs_multiprocess():
+    # https://github.com/pyproj4/pyproj/issues/933
+    with concurrent.futures.ProcessPoolExecutor(max_workers=2) as executor:
+        for result in executor.map(CRS, [4326 for _ in range(10)]):
+            pass
+
+
 def test_coordinate_operation__to_proj4():
     operation = CoordinateOperation.from_string(
         "+proj=pipeline +step +proj=axisswap +order=2,1 +step "
@@ -1529,3 +1536,9 @@ def test_list_authority():
     assert CRS("+proj=utm +zone=15").list_authority() == [
         AuthorityMatchInfo(auth_name="EPSG", code="32615", confidence=70)
     ]
+
+
+def test_list_authority__multiple():
+    auth_list = CRS("+proj=longlat").list_authority()
+    assert AuthorityMatchInfo(auth_name="OGC", code="CRS84", confidence=70) in auth_list
+    assert AuthorityMatchInfo(auth_name="EPSG", code="4326", confidence=70) in auth_list


=====================================
test/crs/test_crs_cf.py
=====================================
@@ -392,7 +392,7 @@ def test_cf_rotated_latlon():
         "o_proj": "longlat",
         "o_lat_p": 32.5,
         "o_lon_p": 170.0,
-        "lon_0": 0,
+        "lon_0": 180,
         "datum": "WGS84",
         "no_defs": None,
         "type": "crs",
@@ -415,13 +415,68 @@ def test_cf_rotated_latlon__grid():
         "o_proj": "longlat",
         "o_lat_p": 32.5,
         "o_lon_p": 170.0,
-        "lon_0": 1.0,
+        "lon_0": 181.0,
         "datum": "WGS84",
         "no_defs": None,
         "type": "crs",
     }
 
 
+def test_rotated_pole_to_cf():
+    rotated_pole_wkt = (
+        'GEOGCRS["undefined",\n'
+        '    BASEGEOGCRS["Unknown datum based upon the GRS 1980 ellipsoid",\n'
+        '        DATUM["Not specified (based on GRS 1980 ellipsoid)",\n'
+        '            ELLIPSOID["GRS 1980",6378137,298.257222101,\n'
+        '                LENGTHUNIT["metre",1]]],\n'
+        '        PRIMEM["Greenwich",0,\n'
+        '            ANGLEUNIT["degree",0.0174532925199433]]],\n'
+        '    DERIVINGCONVERSION["Pole rotation (netCDF CF convention)",\n'
+        '        METHOD["Pole rotation (netCDF CF convention)"],\n'
+        '        PARAMETER["Grid north pole latitude (netCDF CF '
+        'convention)",2,\n'
+        '            ANGLEUNIT["degree",0.0174532925199433,\n'
+        '                ID["EPSG",9122]]],\n'
+        '        PARAMETER["Grid north pole longitude (netCDF CF '
+        'convention)",3,\n'
+        '            ANGLEUNIT["degree",0.0174532925199433,\n'
+        '                ID["EPSG",9122]]],\n'
+        '        PARAMETER["North pole grid longitude (netCDF CF '
+        'convention)",4,\n'
+        '            ANGLEUNIT["degree",0.0174532925199433,\n'
+        '                ID["EPSG",9122]]]],\n'
+        "    CS[ellipsoidal,2],\n"
+        '        AXIS["geodetic latitude (Lat)",north,\n'
+        "            ORDER[1],\n"
+        '            ANGLEUNIT["degree",0.0174532925199433,\n'
+        '                ID["EPSG",9122]]],\n'
+        '        AXIS["geodetic longitude (Lon)",east,\n'
+        "            ORDER[2],\n"
+        '            ANGLEUNIT["degree",0.0174532925199433,\n'
+        '                ID["EPSG",9122]]]]'
+    )
+    crs = CRS(rotated_pole_wkt)
+    expected_cf = {
+        "semi_major_axis": 6378137.0,
+        "semi_minor_axis": 6356752.314140356,
+        "inverse_flattening": 298.257222101,
+        "reference_ellipsoid_name": "GRS 1980",
+        "longitude_of_prime_meridian": 0.0,
+        "prime_meridian_name": "Greenwich",
+        "geographic_crs_name": "undefined",
+        "grid_mapping_name": "rotated_latitude_longitude",
+        "grid_north_pole_latitude": 2.0,
+        "grid_north_pole_longitude": 3.0,
+        "north_pole_grid_longitude": 4.0,
+        "horizontal_datum_name": "Not specified (based on GRS 1980 ellipsoid)",
+    }
+    cf_dict = crs.to_cf()
+    assert cf_dict.pop("crs_wkt").startswith("GEOGCRS[")
+    assert cf_dict == expected_cf
+    # test roundtrip
+    _test_roundtrip(expected_cf, "GEOGCRS[")
+
+
 def test_cf_lambert_conformal_conic_1sp():
     crs = CRS.from_cf(
         dict(



View it on GitLab: https://salsa.debian.org/debian-gis-team/python-pyproj/-/compare/73d498781c40053de5f9a4657fc258d4b27b2b64...122ca032a695d73703393403c3b47f7feeb98b4e

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/python-pyproj/-/compare/73d498781c40053de5f9a4657fc258d4b27b2b64...122ca032a695d73703393403c3b47f7feeb98b4e
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20210918/b324552f/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list