diff -rupN --no-dereference pyproj-3.6.0/pyproj/_datadir.pxd pyproj-3.6.0-new/pyproj/_datadir.pxd
--- pyproj-3.6.0/pyproj/_datadir.pxd 2023-06-12 15:27:32.000000000 +0200
+++ pyproj-3.6.0-new/pyproj/_datadir.pxd 2023-09-13 12:13:21.780093772 +0200
@@ -1,7 +1,7 @@
include "proj.pxi"
-cpdef str _get_proj_error()
-cpdef void _clear_proj_error()
+cpdef str _get_proj_error() noexcept
+cpdef void _clear_proj_error() noexcept
cdef PJ_CONTEXT* PYPROJ_GLOBAL_CONTEXT
cdef PJ_CONTEXT* pyproj_context_create() except *
cdef void pyproj_context_destroy(PJ_CONTEXT* context) except *
diff -rupN --no-dereference pyproj-3.6.0/pyproj/_datadir.pyx pyproj-3.6.0-new/pyproj/_datadir.pyx
--- pyproj-3.6.0/pyproj/_datadir.pyx 2023-06-12 15:27:32.000000000 +0200
+++ pyproj-3.6.0-new/pyproj/_datadir.pyx 2023-09-13 12:13:21.781093753 +0200
@@ -6,7 +6,6 @@ from libc.stdlib cimport free, malloc
from pyproj._compat cimport cstrencode
-from pyproj.exceptions import DataDirError
from pyproj.utils import strtobool
# for logging the internal PROJ messages
@@ -79,14 +78,14 @@ def get_user_data_dir(create=False):
)
-cpdef str _get_proj_error():
+cpdef str _get_proj_error() noexcept:
"""
Get the internal PROJ error message. Returns None if no error was set.
"""
return _INTERNAL_PROJ_ERROR
-cpdef void _clear_proj_error():
+cpdef void _clear_proj_error() noexcept:
"""
Clear the internal PROJ error message.
"""
@@ -94,7 +93,7 @@ cpdef void _clear_proj_error():
_INTERNAL_PROJ_ERROR = None
-cdef void pyproj_log_function(void *user_data, int level, const char *error_msg) nogil:
+cdef void pyproj_log_function(void *user_data, int level, const char *error_msg) nogil noexcept:
"""
Log function for catching PROJ errors.
"""
diff -rupN --no-dereference pyproj-3.6.0/pyproject.toml pyproj-3.6.0-new/pyproject.toml
--- pyproj-3.6.0/pyproject.toml 2023-06-12 15:27:32.000000000 +0200
+++ pyproj-3.6.0-new/pyproject.toml 2023-09-13 12:13:21.781093753 +0200
@@ -1,5 +1,5 @@
[build-system]
-requires = ["setuptools>=61.0.0", "wheel", "cython>=0.28.4"]
+requires = ["setuptools>=61.0.0", "wheel", "cython>=3"]
build-backend = "setuptools.build_meta"
[project]
diff -rupN --no-dereference pyproj-3.6.0/setup.py pyproj-3.6.0-new/setup.py
--- pyproj-3.6.0/setup.py 2023-06-12 15:27:32.000000000 +0200
+++ pyproj-3.6.0-new/setup.py 2023-09-13 12:13:21.780093772 +0200
@@ -7,20 +7,39 @@ import sys
from pathlib import Path
from typing import Optional
-from pkg_resources import parse_version
from setuptools import Extension, setup
-PROJ_MIN_VERSION = parse_version("9.0.0")
+PROJ_MIN_VERSION = (9, 0, 0)
CURRENT_FILE_PATH = Path(__file__).absolute().parent
BASE_INTERNAL_PROJ_DIR = Path("proj_dir")
INTERNAL_PROJ_DIR = CURRENT_FILE_PATH / "pyproj" / BASE_INTERNAL_PROJ_DIR
PROJ_VERSION_SEARCH = re.compile(r".*Rel\.\s+(?P<version>\d+\.\d+\.\d+).*")
+VERSION_SEARCH = re.compile(r".*(?P<version>\d+\.\d+\.\d+).*")
-def get_proj_version(proj_dir: Path) -> str:
+def _parse_version(version: str) -> tuple[int, int, int]:
+ """Convert a version string to a tuple of integers."""
+ match = VERSION_SEARCH.search(version)
+ if not match:
+ raise SystemExit(
+ f"PROJ version unable to be determined from {version}. "
+ "Please set the PROJ_VERSION environment variable."
+ )
+ return tuple(
+ int(ver) for ver in match.groupdict()["version"].split(".", maxsplit=2)
+ )
+
+
+def get_proj_version(proj_dir: Path) -> tuple[int, int, int]:
+ """
+ Determine PROJ version.
+
+ Prefer PROJ_VERSION environment variable.
+ If PROJ_VERSION is not set, try to determine the version from the PROJ executable.
+ """
proj_version = os.environ.get("PROJ_VERSION")
if proj_version:
- return proj_version
+ return _parse_version(proj_version)
proj = proj_dir / "bin" / "proj"
proj_ver = subprocess.check_output(str(proj), stderr=subprocess.STDOUT).decode(
"ascii"
@@ -31,15 +50,17 @@ def get_proj_version(proj_dir: Path) ->
"PROJ version unable to be determined. "
"Please set the PROJ_VERSION environment variable."
)
- return match.groupdict()["version"]
+ return _parse_version(match.groupdict()["version"])
-def check_proj_version(proj_version: str) -> None:
+def check_proj_version(proj_version: tuple[int, int, int]) -> None:
"""checks that the PROJ library meets the minimum version"""
- if parse_version(proj_version) < PROJ_MIN_VERSION:
+ if proj_version < PROJ_MIN_VERSION:
+ proj_version_str = ".".join(str(ver) for ver in proj_version)
+ min_proj_version_str = ".".join(str(ver) for ver in PROJ_MIN_VERSION)
raise SystemExit(
- f"ERROR: Minimum supported PROJ version is {PROJ_MIN_VERSION}, installed "
- f"version is {proj_version}. For more information see: "
+ f"ERROR: Minimum supported PROJ version is {min_proj_version_str}, "
+ f"installed version is {proj_version_str}. For more information see: "
"https://pyproj4.github.io/pyproj/stable/installation.html"
)
@@ -154,11 +175,11 @@ def get_extension_modules():
# make sure cython is available
try:
from Cython.Build import cythonize
- except ImportError:
+ except ImportError as error:
raise SystemExit(
"ERROR: Cython.Build.cythonize not found. "
"Cython is required to build pyproj."
- )
+ ) from error
# By default we'll try to get options PROJ_DIR or the local version of proj
proj_dir = get_proj_dir()
@@ -167,9 +188,7 @@ def get_extension_modules():
proj_version = get_proj_version(proj_dir)
check_proj_version(proj_version)
- proj_version_major, proj_version_minor, proj_version_patch = parse_version(
- proj_version
- ).base_version.split(".")
+ proj_version_major, proj_version_minor, proj_version_patch = proj_version
# setup extension options
ext_options = {
@@ -197,9 +216,9 @@ def get_extension_modules():
],
quiet=True,
compile_time_env={
- "CTE_PROJ_VERSION_MAJOR": int(proj_version_major),
- "CTE_PROJ_VERSION_MINOR": int(proj_version_minor),
- "CTE_PROJ_VERSION_PATCH": int(proj_version_patch),
+ "CTE_PROJ_VERSION_MAJOR": proj_version_major,
+ "CTE_PROJ_VERSION_MINOR": proj_version_minor,
+ "CTE_PROJ_VERSION_PATCH": proj_version_patch,
"CTE_PYTHON_IMPLEMENTATION": platform.python_implementation(),
},
**get_cythonize_options(),
diff -rupN --no-dereference pyproj-3.6.0/test/conftest.py pyproj-3.6.0-new/test/conftest.py
--- pyproj-3.6.0/test/conftest.py 2023-06-12 15:27:32.000000000 +0200
+++ pyproj-3.6.0-new/test/conftest.py 2023-09-13 12:13:21.781093753 +0200
@@ -18,6 +18,7 @@ PROJ_GTE_91 = PROJ_LOOSE_VERSION >= vers
PROJ_GTE_911 = PROJ_LOOSE_VERSION >= version.parse("9.1.1")
PROJ_GTE_92 = PROJ_LOOSE_VERSION >= version.parse("9.2.0")
PROJ_GTE_921 = PROJ_LOOSE_VERSION >= version.parse("9.2.1")
+PROJ_GTE_93 = PROJ_LOOSE_VERSION >= version.parse("9.3.0")
def unset_data_dir():
diff -rupN --no-dereference pyproj-3.6.0/test/test_proj.py pyproj-3.6.0-new/test/test_proj.py
--- pyproj-3.6.0/test/test_proj.py 2023-06-12 15:27:32.000000000 +0200
+++ pyproj-3.6.0-new/test/test_proj.py 2023-09-13 12:13:21.782093733 +0200
@@ -5,7 +5,7 @@ import sys
import unittest
from unittest.mock import patch
-import numpy as np
+import numpy
import pytest
from numpy.testing import assert_almost_equal
@@ -441,11 +441,11 @@ def test_reset_errno():
@pytest.mark.parametrize("radians", [False, True])
def test_get_factors__2d_input(radians):
transformer = Proj(3857)
- longitude = np.array([[0, 1], [2, 3]])
- latitude = np.array([[1, 2], [3, 4]])
+ longitude = numpy.array([[0, 1], [2, 3]])
+ latitude = numpy.array([[1, 2], [3, 4]])
if radians:
- longitude = np.radians(longitude)
- latitude = np.radians(latitude)
+ longitude = numpy.radians(longitude)
+ latitude = numpy.radians(latitude)
factors = transformer.get_factors(
longitude=longitude, latitude=latitude, radians=radians
)
@@ -497,22 +497,36 @@ def test_get_factors():
def test_get_factors__nan_inf():
transformer = Proj(3857)
factors = transformer.get_factors(
- longitude=[0, np.nan, np.inf, 0], latitude=[np.nan, 2, 2, np.inf]
+ longitude=[0, numpy.nan, numpy.inf, 0], latitude=[numpy.nan, 2, 2, numpy.inf]
)
- assert_almost_equal(factors.meridional_scale, [np.inf, np.inf, np.inf, np.inf])
- assert_almost_equal(factors.parallel_scale, [np.inf, np.inf, np.inf, np.inf])
- assert_almost_equal(factors.areal_scale, [np.inf, np.inf, np.inf, np.inf])
- assert_almost_equal(factors.angular_distortion, [np.inf, np.inf, np.inf, np.inf])
- assert_almost_equal(
- factors.meridian_parallel_angle, [np.inf, np.inf, np.inf, np.inf]
- )
- assert_almost_equal(factors.meridian_convergence, [np.inf, np.inf, np.inf, np.inf])
- assert_almost_equal(factors.tissot_semimajor, [np.inf, np.inf, np.inf, np.inf])
- assert_almost_equal(factors.tissot_semiminor, [np.inf, np.inf, np.inf, np.inf])
- assert_almost_equal(factors.dx_dlam, [np.inf, np.inf, np.inf, np.inf])
- assert_almost_equal(factors.dx_dphi, [np.inf, np.inf, np.inf, np.inf])
- assert_almost_equal(factors.dy_dlam, [np.inf, np.inf, np.inf, np.inf])
- assert_almost_equal(factors.dy_dphi, [np.inf, np.inf, np.inf, np.inf])
+ assert_almost_equal(
+ factors.meridional_scale, [numpy.inf, numpy.inf, numpy.inf, numpy.inf]
+ )
+ assert_almost_equal(
+ factors.parallel_scale, [numpy.inf, numpy.inf, numpy.inf, numpy.inf]
+ )
+ assert_almost_equal(
+ factors.areal_scale, [numpy.inf, numpy.inf, numpy.inf, numpy.inf]
+ )
+ assert_almost_equal(
+ factors.angular_distortion, [numpy.inf, numpy.inf, numpy.inf, numpy.inf]
+ )
+ assert_almost_equal(
+ factors.meridian_parallel_angle, [numpy.inf, numpy.inf, numpy.inf, numpy.inf]
+ )
+ assert_almost_equal(
+ factors.meridian_convergence, [numpy.inf, numpy.inf, numpy.inf, numpy.inf]
+ )
+ assert_almost_equal(
+ factors.tissot_semimajor, [numpy.inf, numpy.inf, numpy.inf, numpy.inf]
+ )
+ assert_almost_equal(
+ factors.tissot_semiminor, [numpy.inf, numpy.inf, numpy.inf, numpy.inf]
+ )
+ assert_almost_equal(factors.dx_dlam, [numpy.inf, numpy.inf, numpy.inf, numpy.inf])
+ assert_almost_equal(factors.dx_dphi, [numpy.inf, numpy.inf, numpy.inf, numpy.inf])
+ assert_almost_equal(factors.dy_dlam, [numpy.inf, numpy.inf, numpy.inf, numpy.inf])
+ assert_almost_equal(factors.dy_dphi, [numpy.inf, numpy.inf, numpy.inf, numpy.inf])
def test_get_factors__errcheck():
@@ -523,7 +537,7 @@ def test_get_factors__errcheck():
def test_numpy_bool_kwarg_false():
# Issue 564
- south = np.array(50) < 0
+ south = numpy.array(50) < 0
proj = Proj(
proj="utm", zone=32, ellipsis="WGS84", datum="WGS84", units="m", south=south
)
@@ -532,7 +546,7 @@ def test_numpy_bool_kwarg_false():
def test_numpy_bool_kwarg_true():
# Issue 564
- south = np.array(50) > 0
+ south = numpy.array(50) > 0
proj = Proj(
proj="utm", zone=32, ellipsis="WGS84", datum="WGS84", units="m", south=south
)
diff -rupN --no-dereference pyproj-3.6.0/test/test_transformer.py pyproj-3.6.0-new/test/test_transformer.py
--- pyproj-3.6.0/test/test_transformer.py 2023-06-12 15:27:32.000000000 +0200
+++ pyproj-3.6.0-new/test/test_transformer.py 2023-09-13 12:13:21.783093713 +0200
@@ -9,7 +9,7 @@ from itertools import permutations
from pathlib import Path
from unittest.mock import call, patch
-import numpy as np
+import numpy
import pytest
from numpy.testing import assert_almost_equal, assert_array_equal
@@ -22,6 +22,7 @@ from pyproj.transformer import AreaOfInt
from test.conftest import (
PROJ_GTE_91,
PROJ_GTE_92,
+ PROJ_GTE_93,
grids_available,
proj_env,
proj_network_env,
@@ -64,8 +65,8 @@ def test_illegal_transformation():
xx, yy = pyproj.transform(
p1, p2, (-180, -180, 180, 180, -180), (-90, 90, 90, -90, -90)
)
- assert np.all(np.isinf(xx))
- assert np.all(np.isinf(yy))
+ assert numpy.all(numpy.isinf(xx))
+ assert numpy.all(numpy.isinf(yy))
with pytest.warns(FutureWarning), pytest.raises(ProjError):
pyproj.transform(
p1, p2, (-180, -180, 180, 180, -180), (-90, 90, 90, -90, -90), errcheck=True
@@ -412,7 +413,7 @@ def test_always_xy__itransform():
)
-@pytest.mark.parametrize("empty_array", [(), [], np.array([])])
+@pytest.mark.parametrize("empty_array", [(), [], numpy.array([])])
def test_transform_empty_array_xy(empty_array):
transformer = Transformer.from_crs(2193, 4326)
assert_array_equal(
@@ -420,7 +421,7 @@ def test_transform_empty_array_xy(empty_
)
-@pytest.mark.parametrize("empty_array", [(), [], np.array([])])
+@pytest.mark.parametrize("empty_array", [(), [], numpy.array([])])
def test_transform_empty_array_xyzt(empty_array):
transformer = Transformer.from_pipeline("+init=ITRF2008:ITRF2000")
assert_array_equal(
@@ -540,7 +541,7 @@ def test_repr__conditional():
"Description: unavailable until proj_trans is called\n"
"Area of Use:\n- undefined"
)
- elif PROJ_GTE_92:
+ elif PROJ_GTE_92 and not PROJ_GTE_93:
assert trans_repr == (
"<Unknown Transformer: noop>\n"
"Description: Transformation from EGM2008 height to WGS 84 "
@@ -555,7 +556,7 @@ def test_repr__conditional():
"(ballpark vertical transformation, without ellipsoid height "
"to vertical height correction)\n"
"Area of Use:\n"
- "- name: World\n"
+ f"- name: World{'.' if PROJ_GTE_93 else ''}\n"
"- bounds: (-180.0, -90.0, 180.0, 90.0)"
)
@@ -1205,7 +1206,7 @@ def test_transform_bounds_densify(densit
"+proj=laea +lat_0=45 +lon_0=-100 +x_0=0 +y_0=0 "
"+a=6370997 +b=6370997 +units=m +no_defs",
)
- assert np.allclose(
+ assert numpy.allclose(
transformer.transform_bounds(40, -120, 64, -80, densify_pts=density),
expected,
)
@@ -1222,7 +1223,15 @@ def test_transform_bounds_densify(densit
"input_bounds, radians",
[
((-120, 40, -80, 64), False),
- ((np.radians(-120), np.radians(40), np.radians(-80), np.radians(64)), True),
+ (
+ (
+ numpy.radians(-120),
+ numpy.radians(40),
+ numpy.radians(-80),
+ numpy.radians(64),
+ ),
+ True,
+ ),
],
)
def test_transform_bounds_densify__xy(density, expected, input_bounds, radians):
@@ -1232,7 +1241,7 @@ def test_transform_bounds_densify__xy(de
"+a=6370997 +b=6370997 +units=m +no_defs",
always_xy=True,
)
- assert np.allclose(
+ assert numpy.allclose(
transformer.transform_bounds(
*input_bounds, densify_pts=density, radians=radians
),
@@ -1459,8 +1468,8 @@ def test_transform_bounds__south_pole__x
@pytest.mark.parametrize("inplace", [True, False])
def test_transform__fortran_order(inplace):
- lons, lats = np.arange(-180, 180, 20), np.arange(-90, 90, 10)
- lats, lons = np.meshgrid(lats, lons)
+ lons, lats = numpy.arange(-180, 180, 20), numpy.arange(-90, 90, 10)
+ lats, lons = numpy.meshgrid(lats, lons)
f_lons, f_lats = lons.copy(order="F"), lats.copy(order="F")
transformer = Transformer.from_crs(
"EPSG:4326",
@@ -1519,10 +1528,10 @@ def test_4d_transform__inplace__array__i
def test_4d_transform__inplace__numpy():
transformer = Transformer.from_crs(7789, 8401)
- xarr = np.array([3496737.2679], dtype=np.float64)
- yarr = np.array([743254.4507], dtype=np.float64)
- zarr = np.array([5264462.9620], dtype=np.float64)
- tarr = np.array([2019.0], dtype=np.float64)
+ xarr = numpy.array([3496737.2679], dtype=numpy.float64)
+ yarr = numpy.array([743254.4507], dtype=numpy.float64)
+ zarr = numpy.array([5264462.9620], dtype=numpy.float64)
+ tarr = numpy.array([2019.0], dtype=numpy.float64)
t_xarr, t_yarr, t_zarr, t_tarr = transformer.transform(
xx=xarr, yy=yarr, zz=zarr, tt=tarr, inplace=True
)
@@ -1538,10 +1547,10 @@ def test_4d_transform__inplace__numpy():
def test_4d_transform__inplace__numpy__int():
transformer = Transformer.from_crs(7789, 8401)
- xarr = np.array([3496737], dtype=np.int32)
- yarr = np.array([743254], dtype=np.int32)
- zarr = np.array([5264462], dtype=np.int32)
- tarr = np.array([2019], dtype=np.int32)
+ xarr = numpy.array([3496737], dtype=numpy.int32)
+ yarr = numpy.array([743254], dtype=numpy.int32)
+ zarr = numpy.array([5264462], dtype=numpy.int32)
+ tarr = numpy.array([2019], dtype=numpy.int32)
t_xarr, t_yarr, t_zarr, t_tarr = transformer.transform(
xx=xarr, yy=yarr, zz=zarr, tt=tarr, inplace=True
)
diff -rupN --no-dereference pyproj-3.6.0/test/test_utils.py pyproj-3.6.0-new/test/test_utils.py
--- pyproj-3.6.0/test/test_utils.py 2023-06-12 15:27:32.000000000 +0200
+++ pyproj-3.6.0-new/test/test_utils.py 2023-09-13 12:13:21.783093713 +0200
@@ -2,8 +2,6 @@ from array import array
import numpy
import pytest
-from pandas import Series
-from xarray import DataArray
from pyproj.utils import DataType, _copytobuffer, _copytobuffer_return_scalar
@@ -22,7 +20,6 @@ def test__copytobuffer_return_scalar__in
"in_data, data_type",
[
(numpy.array(1), DataType.FLOAT),
- (DataArray(numpy.array(1)), DataType.FLOAT),
(1, DataType.FLOAT),
([1], DataType.LIST),
((1,), DataType.TUPLE),
@@ -32,10 +29,23 @@ def test__copytobuffer(in_data, data_typ
assert _copytobuffer(in_data) == (array("d", [1]), data_type)
-@pytest.mark.parametrize(
- "in_arr", [numpy.array([1]), DataArray(numpy.array([1])), Series(numpy.array([1]))]
-)
-def test__copytobuffer__numpy_array(in_arr):
+def test__copytobuffer__xarray_scalar():
+ xarray = pytest.importorskip("xarray")
+ assert _copytobuffer(xarray.DataArray(numpy.array(1))) == (
+ array("d", [1]),
+ DataType.FLOAT,
+ )
+
+
+@pytest.mark.parametrize("arr_type", ["numpy", "xarray", "pandas"])
+def test__copytobuffer__array(arr_type):
+ in_arr = numpy.array([1])
+ if arr_type == "xarray":
+ xarray = pytest.importorskip("xarray")
+ in_arr = xarray.DataArray(in_arr)
+ elif arr_type == "pandas":
+ pandas = pytest.importorskip("pandas")
+ in_arr = pandas.Series(in_arr)
assert _copytobuffer(in_arr) == (
in_arr.astype("d").__array__(),
DataType.ARRAY,