[Git][debian-gis-team/python-pyproj][experimental] 12 commits: Revert "Update branch in gbp.conf & Vcs-Git URL."
Bas Couwenberg (@sebastic)
gitlab at salsa.debian.org
Wed Sep 15 07:02:39 BST 2021
Bas Couwenberg pushed to branch experimental at Debian GIS Project / python-pyproj
Commits:
e10208f9 by Bas Couwenberg at 2021-09-04T07:47:08+02:00
Revert "Update branch in gbp.conf & Vcs-Git URL."
This reverts commit b9791eab94e1cfd27c66e250c07b7e7602ad0632.
- - - - -
07745ebb by Bas Couwenberg at 2021-09-04T07:49:28+02:00
New upstream version 3.2.0
- - - - -
94f9f1c0 by Bas Couwenberg at 2021-09-04T07:49:31+02:00
Update upstream source from tag 'upstream/3.2.0'
Update to upstream version '3.2.0'
with Debian dir 937e04f7c47bd178fbc150dd568ccf94cfcad13f
- - - - -
4a4edfd0 by Bas Couwenberg at 2021-09-04T07:49:57+02:00
New upstream release.
- - - - -
cf2b5719 by Bas Couwenberg at 2021-09-04T07:50:28+02:00
Set distribution to unstable.
- - - - -
413a5890 by Bas Couwenberg at 2021-09-08T17:43:10+02:00
Bump Standards-Version to 4.6.0, no changes.
- - - - -
73d49878 by Bas Couwenberg at 2021-09-14T06:02:08+02:00
Bump debhelper compat to 12.
Changes:
- Drop --list-missing from dh_install
- - - - -
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.
- - - - -
30 changed files:
- debian/changelog
- − debian/compat
- debian/control
- debian/rules
- 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/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,3 +1,19 @@
+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, 15 Sep 2021 07:30:23 +0200
+
+python-pyproj (3.2.0-1) unstable; urgency=medium
+
+ * New upstream release.
+ * Move from experimental to unstable.
+
+ -- Bas Couwenberg <sebastic at debian.org> Sat, 04 Sep 2021 07:50:13 +0200
+
python-pyproj (3.2~rc0-1~exp1) experimental; urgency=medium
* New upstream release.
=====================================
debian/compat deleted
=====================================
@@ -1 +0,0 @@
-10
=====================================
debian/control
=====================================
@@ -4,7 +4,7 @@ Uploaders: David Paleino <dapal at debian.org>,
Bas Couwenberg <sebastic at debian.org>
Section: python
Priority: optional
-Build-Depends: debhelper (>= 10~),
+Build-Depends: debhelper-compat (= 12),
dh-python,
cython3,
libproj-dev (>= 7.1.0),
@@ -20,7 +20,7 @@ Build-Depends: debhelper (>= 10~),
python3-pytest-cov,
python3-shapely,
python3-xarray
-Standards-Version: 4.5.1
+Standards-Version: 4.6.0
Vcs-Browser: https://salsa.debian.org/debian-gis-team/python-pyproj
Vcs-Git: https://salsa.debian.org/debian-gis-team/python-pyproj.git -b experimental
Homepage: https://github.com/pyproj4/pyproj
=====================================
debian/rules
=====================================
@@ -31,9 +31,6 @@ else
endif
endif
-override_dh_install:
- dh_install --list-missing
-
override_dh_python3:
dh_python3 -ppython3-pyproj
dh_numpy3 -ppython3-pyproj
=====================================
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.0rc0"
+__version__ = "3.2.1.rc0"
__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/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/03dad8515e15f3d70f2b145dfda7cfb810b35570...fae38b9432680c74639567d99688cdc50a25c8f4
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/python-pyproj/-/compare/03dad8515e15f3d70f2b145dfda7cfb810b35570...fae38b9432680c74639567d99688cdc50a25c8f4
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/20210915/2e345278/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list