[Git][debian-gis-team/pymap3d][upstream] New upstream version 2.7.3
Antonio Valentino (@antonio.valentino)
gitlab at salsa.debian.org
Sat Nov 27 12:23:26 GMT 2021
Antonio Valentino pushed to branch upstream at Debian GIS Project / pymap3d
Commits:
51b4fedb by Antonio Valentino at 2021-11-27T12:13:52+00:00
New upstream version 2.7.3
- - - - -
9 changed files:
- .github/workflows/ci.yml
- README.md
- pyproject.toml
- setup.cfg
- src/pymap3d/aer.py
- src/pymap3d/ecef.py
- src/pymap3d/rsphere.py
- src/pymap3d/tests/test_geodetic.py
- src/pymap3d/utils.py
Changes:
=====================================
.github/workflows/ci.yml
=====================================
@@ -7,7 +7,7 @@ on:
- .github/workflows/ci.yml
pull-request:
- "**.py"
- - .github/workflows/ci_stdlib_only.yml
+ - .github/workflows/ci.yml
jobs:
=====================================
README.md
=====================================
@@ -76,6 +76,9 @@ where tuple `lla` is comprised of scalar or N-D arrays `(lat,lon,alt)`.
Example scripts are in the [examples](./Examples) directory.
+Native Python float is typically [64 bit](https://docs.python.org/3/library/stdtypes.html#typesnumeric).
+Numpy can select real precision bits: 32, 64, 128, etc.
+
### Functions
Popular mapping toolbox functions ported to Python include the
=====================================
pyproject.toml
=====================================
@@ -1,6 +1,6 @@
[project]
name = "pymap3d"
-version = "2.6.1"
+version = "2.7.2"
description = "pure Python (no prereqs) coordinate conversions, following convention of several popular Matlab routines."
readme = "README.md"
requires-python = ">=3.7"
=====================================
setup.cfg
=====================================
@@ -1,6 +1,6 @@
[metadata]
name = pymap3d
-version = 2.7.2
+version = 2.7.3
author = Michael Hirsch, Ph.D.
author_email = scivision at users.noreply.github.com
description = pure Python (no prereqs) coordinate conversions, following convention of several popular Matlab routines.
=====================================
src/pymap3d/aer.py
=====================================
@@ -11,7 +11,7 @@ from .ellipsoid import Ellipsoid
try:
from .eci import eci2ecef, ecef2eci
except ImportError:
- eci2ecef = ecef2eci = None # type: ignore
+ pass
if typing.TYPE_CHECKING:
from numpy import ndarray
@@ -212,10 +212,11 @@ def eci2aer(
srange : float
slant range [meters]
"""
- if eci2ecef is None:
- raise ImportError("pip install numpy")
- xecef, yecef, zecef = eci2ecef(x, y, z, t, use_astropy=use_astropy)
+ try:
+ xecef, yecef, zecef = eci2ecef(x, y, z, t, use_astropy=use_astropy)
+ except NameError:
+ raise ImportError("pip install numpy")
return ecef2aer(xecef, yecef, zecef, lat0, lon0, h0, deg=deg)
@@ -271,12 +272,13 @@ def aer2eci(
z : float
ECEF z coordinate (meters)
"""
- if ecef2eci is None:
- raise ImportError("pip install numpy")
x, y, z = aer2ecef(az, el, srange, lat0, lon0, h0, ell, deg=deg)
- return ecef2eci(x, y, z, t, use_astropy=use_astropy)
+ try:
+ return ecef2eci(x, y, z, t, use_astropy=use_astropy)
+ except NameError:
+ raise ImportError("pip install numpy")
def aer2ecef(
=====================================
src/pymap3d/ecef.py
=====================================
@@ -3,7 +3,20 @@ from __future__ import annotations
import typing
try:
- from numpy import radians, sin, cos, tan, arctan as atan, hypot, degrees, arctan2 as atan2, sqrt
+ from numpy import (
+ radians,
+ sin,
+ cos,
+ tan,
+ arctan as atan,
+ hypot,
+ degrees,
+ arctan2 as atan2,
+ sqrt,
+ finfo,
+ where,
+ )
+ from .eci import eci2ecef, ecef2eci
except ImportError:
from math import radians, sin, cos, tan, atan, hypot, degrees, atan2, sqrt # type: ignore
@@ -13,11 +26,6 @@ from datetime import datetime
from .ellipsoid import Ellipsoid
from .utils import sanitize
-try:
- from .eci import eci2ecef, ecef2eci
-except ImportError:
- eci2ecef = ecef2eci = None # type: ignore
-
if typing.TYPE_CHECKING:
from numpy import ndarray
@@ -80,8 +88,7 @@ def geodetic2ecef(
N = ell.semimajor_axis ** 2 / sqrt(
ell.semimajor_axis ** 2 * cos(lat) ** 2 + ell.semiminor_axis ** 2 * sin(lat) ** 2
)
- # Compute cartesian (geocentric) coordinates given (curvilinear) geodetic
- # coordinates.
+ # Compute cartesian (geocentric) coordinates given (curvilinear) geodetic coordinates.
x = (N + alt) * cos(lat) * cos(lon)
y = (N + alt) * cos(lat) * sin(lon)
z = (N * (ell.semiminor_axis / ell.semimajor_axis) ** 2 + alt) * sin(lat)
@@ -150,18 +157,36 @@ def ecef2geodetic(
Beta = -pi / 2
# eqn. 13
- eps = ((ell.semiminor_axis * u - ell.semimajor_axis * huE + E ** 2) * sin(Beta)) / (
+ dBeta = ((ell.semiminor_axis * u - ell.semimajor_axis * huE + E ** 2) * sin(Beta)) / (
ell.semimajor_axis * huE * 1 / cos(Beta) - E ** 2 * cos(Beta)
)
- Beta += eps
+ Beta += dBeta
+
+ # eqn. 4c
# %% final output
lat = atan(ell.semimajor_axis / ell.semiminor_axis * tan(Beta))
+ try:
+ # patch latitude for float32 precision loss
+ lim_pi2 = pi / 2 - finfo(dBeta.dtype).eps
+ lat = where(Beta >= lim_pi2, pi / 2, lat)
+ lat = where(Beta <= -lim_pi2, -pi / 2, lat)
+ except NameError:
+ pass
+
lon = atan2(y, x)
# eqn. 7
- alt = hypot(z - ell.semiminor_axis * sin(Beta), Q - ell.semimajor_axis * cos(Beta))
+ cosBeta = cos(Beta)
+ try:
+ # patch altitude for float32 precision loss
+ cosBeta = where(Beta >= lim_pi2, 0, cosBeta)
+ cosBeta = where(Beta <= -lim_pi2, 0, cosBeta)
+ except NameError:
+ pass
+
+ alt = hypot(z - ell.semiminor_axis * sin(Beta), Q - ell.semimajor_axis * cosBeta)
# inside ellipsoid?
inside = (
@@ -395,10 +420,11 @@ def eci2geodetic(
eci2geodetic() a.k.a. eci2lla()
"""
- if eci2ecef is None:
- raise ImportError("pip install numpy")
- xecef, yecef, zecef = eci2ecef(x, y, z, t, use_astropy=use_astropy)
+ try:
+ xecef, yecef, zecef = eci2ecef(x, y, z, t, use_astropy=use_astropy)
+ except NameError:
+ raise ImportError("pip install numpy")
return ecef2geodetic(xecef, yecef, zecef, ell, deg)
@@ -446,12 +472,13 @@ def geodetic2eci(
geodetic2eci() a.k.a lla2eci()
"""
- if ecef2eci is None:
- raise ImportError("pip install numpy")
x, y, z = geodetic2ecef(lat, lon, alt, ell, deg)
- return ecef2eci(x, y, z, t, use_astropy=use_astropy)
+ try:
+ return ecef2eci(x, y, z, t, use_astropy=use_astropy)
+ except NameError:
+ raise ImportError("pip install numpy")
def enu2ecef(
=====================================
src/pymap3d/rsphere.py
=====================================
@@ -8,8 +8,6 @@ try:
except ImportError:
from math import radians, sin, cos, log, sqrt, degrees # type: ignore
- asarray = None # type: ignore
-
from .ellipsoid import Ellipsoid
from . import rcurve
from .vincenty import vdist
@@ -123,8 +121,11 @@ def euler(
"""
if not deg:
lat1, lon1, lat2, lon2 = degrees(lat1), degrees(lon1), degrees(lat2), degrees(lon2)
- if asarray is not None:
+
+ try:
lat1, lat2 = asarray(lat1), asarray(lat2)
+ except NameError:
+ pass
latmid = lat1 + (lat2 - lat1) / 2 # compute the midpoint
=====================================
src/pymap3d/tests/test_geodetic.py
=====================================
@@ -13,6 +13,28 @@ ELL = pm.Ellipsoid()
A = ELL.semimajor_axis
B = ELL.semiminor_axis
+xyzlla = [
+ ((A - 1, 0, 0), (0, 0, -1)),
+ ((0, A - 1, 0), (0, 90, -1)),
+ ((0, 0, B - 1), (90, 0, -1)),
+ ((0, 0, B - 1), (89.999999, 0, -1)),
+ ((0, 0, B - 1), (89.99999, 0, -1)),
+ ((0, 0, -B + 1), (-90, 0, -1)),
+ ((0, 0, -B + 1), (-89.999999, 0, -1)),
+ ((0, 0, -B + 1), (-89.99999, 0, -1)),
+ ((-A + 1, 0, 0), (0, 180, -1)),
+]
+
+llaxyz = [
+ ((0, 0, -1), (A - 1, 0, 0)),
+ ((0, 90, -1), (0, A - 1, 0)),
+ ((0, -90, -1), (0, -A + 1, 0)),
+ ((90, 0, -1), (0, 0, B - 1)),
+ ((90, 15, -1), (0, 0, B - 1)),
+ ((-90, 0, -1), (0, 0, -B + 1)),
+]
+
+
atol_dist = 1e-6 # 1 micrometer
@@ -94,7 +116,7 @@ def test_xarray():
xyz = pm.geodetic2ecef(*xr_lla)
assert xyz == approx(xyz0)
- # %%
+
xr_xyz = xarray.DataArray(list(xyz0))
lla = pm.ecef2geodetic(*xr_xyz)
@@ -136,31 +158,12 @@ def test_ecef():
assert pm.ecef2geodetic((A - 1) / sqrt(2), (A - 1) / sqrt(2), 0) == approx([0, 45, -1])
- at pytest.mark.parametrize(
- "lla, xyz",
- [
- ((0, 0, -1), (A - 1, 0, 0)),
- ((0, 90, -1), (0, A - 1, 0)),
- ((0, -90, -1), (0, -A + 1, 0)),
- ((90, 0, -1), (0, 0, B - 1)),
- ((90, 15, -1), (0, 0, B - 1)),
- ((-90, 0, -1), (0, 0, -B + 1)),
- ],
-)
+ at pytest.mark.parametrize("lla, xyz", llaxyz)
def test_geodetic2ecef(lla, xyz):
assert pm.geodetic2ecef(*lla) == approx(xyz, abs=atol_dist)
- at pytest.mark.parametrize(
- "xyz, lla",
- [
- ((A - 1, 0, 0), (0, 0, -1)),
- ((0, A - 1, 0), (0, 90, -1)),
- ((0, 0, B - 1), (90, 0, -1)),
- ((0, 0, -B + 1), (-90, 0, -1)),
- ((-A + 1, 0, 0), (0, 180, -1)),
- ],
-)
+ at pytest.mark.parametrize("xyz, lla", xyzlla)
def test_ecef2geodetic(xyz, lla):
lat, lon, alt = pm.ecef2geodetic(*xyz)
assert lat == approx(lla[0])
@@ -221,3 +224,37 @@ def test_somenan():
lat, lon, alt = pm.ecef2geodetic(xyz[:, 0], xyz[:, 1], xyz[:, 2])
assert (lat[0], lon[0], alt[0]) == approx(lla0)
+
+
+ at pytest.mark.parametrize("xyz, lla", xyzlla)
+def test_numpy_ecef2geodetic(xyz, lla):
+ np = pytest.importorskip("numpy")
+ lat, lon, alt = pm.ecef2geodetic(
+ *np.array(
+ [
+ [xyz],
+ ],
+ dtype=np.float32,
+ ).T
+ )
+ assert lat[0] == approx(lla[0])
+ assert lon[0] == approx(lla[1])
+ assert alt[0] == approx(lla[2])
+
+
+ at pytest.mark.parametrize("lla, xyz", llaxyz)
+def test_numpy_geodetic2ecef(lla, xyz):
+ np = pytest.importorskip("numpy")
+ x, y, z = pm.geodetic2ecef(
+ *np.array(
+ [
+ [lla],
+ ],
+ dtype=np.float32,
+ ).T
+ )
+
+ atol_dist = 1 # meters
+ assert x[0] == approx(xyz[0], abs=atol_dist)
+ assert y[0] == approx(xyz[1], abs=atol_dist)
+ assert z[0] == approx(xyz[2], abs=atol_dist)
=====================================
src/pymap3d/utils.py
=====================================
@@ -4,15 +4,14 @@ all assume radians"""
from __future__ import annotations
import typing
+from math import pi
from .ellipsoid import Ellipsoid
try:
- from numpy import hypot, cos, sin, arctan2 as atan2, radians, pi, asarray, sign
+ from numpy import hypot, cos, sin, arctan2 as atan2, radians, asarray, sign
except ImportError:
- from math import atan2, hypot, cos, sin, radians, pi # type: ignore
-
- asarray = None # type: ignore
+ from math import atan2, hypot, cos, sin, radians # type: ignore
def sign(x: float) -> float: # type: ignore
"""signum function"""
@@ -63,17 +62,22 @@ def sph2cart(az: ndarray, el: ndarray, r: ndarray) -> tuple[ndarray, ndarray, nd
def sanitize(
lat: float | ndarray, ell: typing.Optional[Ellipsoid], deg: bool
) -> tuple[float | ndarray, Ellipsoid]:
+
if ell is None:
ell = Ellipsoid()
- if asarray is not None:
+
+ try:
lat = asarray(lat)
+ except NameError:
+ pass
+
if deg:
lat = radians(lat)
- if asarray is not None:
+ try:
if (abs(lat) > pi / 2).any(): # type: ignore
raise ValueError("-pi/2 <= latitude <= pi/2")
- else:
+ except AttributeError:
if abs(lat) > pi / 2: # type: ignore
raise ValueError("-pi/2 <= latitude <= pi/2")
View it on GitLab: https://salsa.debian.org/debian-gis-team/pymap3d/-/commit/51b4fedba9e1668bbbe5041fa8506d38472dfc1e
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/pymap3d/-/commit/51b4fedba9e1668bbbe5041fa8506d38472dfc1e
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/20211127/91e07981/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list