Skip to content

Commit 48ad176

Browse files
committed
correct type annotation
1 parent ebba5fb commit 48ad176

File tree

5 files changed

+134
-48
lines changed

5 files changed

+134
-48
lines changed

src/pymap3d/dca.py

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
"""
22
Transforms involving DCA (Downrange, Crossrange, Above)
33
4-
This module provides functions to convert coordinates between DCA and
4+
This module provides functions to convert coordinates between DCA and
55
other coordinate systems such as ENU (East, North, Up), NED (North, East, Down),
6-
ECEF (Earth-Centered, Earth-Fixed), and geodetic coordinates (latitude, longitude, altitude).
6+
ECEF (Earth-Centered, Earth-Fixed), and geodetic coordinates (latitude, longitude, altitude).
77
88
It also includes transformations to/from AER (Azimuth, Elevation, Range).
99
@@ -21,6 +21,8 @@
2121
- dca2aer: Convert DCA to AER coordinates.
2222
"""
2323

24+
from __future__ import annotations
25+
2426
from .mathfun import sin, cos, radians
2527
from .ecef import ecef2enu, enu2ecef
2628
from .enu import geodetic2enu, enu2geodetic, aer2enu, enu2aer
@@ -39,92 +41,102 @@
3941
"dca2aer",
4042
]
4143

42-
ELL = Ellipsoid.from_name("wgs84") # Default reference ellipsoid (WGS84)
43-
4444

45-
def enu2dca(e, n, u, heading, deg=True):
45+
def enu2dca(e, n, u, heading, deg: bool = True):
4646
"""
4747
Converts ENU (East, North, Up) coordinates to DCA (Downrange, Crossrange, Above).
4848
"""
49+
4950
if deg:
5051
heading = radians(heading)
52+
5153
dr = e * sin(heading) + n * cos(heading)
5254
cr = -e * cos(heading) + n * sin(heading)
53-
a = u
54-
return dr, cr, a
55+
56+
return dr, cr, u
5557

5658

57-
def dca2enu(dr, cr, a, heading, deg=True):
59+
def dca2enu(dr, cr, above, heading, deg: bool = True):
5860
"""
5961
Converts DCA (Downrange, Crossrange, Above) coordinates to ENU (East, North, Up).
6062
"""
63+
6164
if deg:
6265
heading = radians(heading)
66+
6367
e = dr * sin(heading) - cr * cos(heading)
6468
n = dr * cos(heading) + cr * sin(heading)
65-
u = a
66-
return e, n, u
69+
70+
return e, n, above
6771

6872

69-
def dca2ned(dr, cr, a, heading, deg=True):
73+
def dca2ned(dr, cr, above, heading, deg: bool = True):
7074
"""
7175
Converts DCA (Downrange, Crossrange, Above) coordinates to NED (North, East, Down).
7276
"""
73-
e, n, u = dca2enu(dr, cr, a, heading, deg=deg)
77+
e, n, u = dca2enu(dr, cr, above, heading, deg=deg)
7478
return n, e, -u
7579

7680

77-
def ned2dca(n, e, d, heading, deg=True):
81+
def ned2dca(n, e, d, heading, deg: bool = True):
7882
"""
7983
Converts NED (North, East, Down) coordinates to DCA (Downrange, Crossrange, Above).
8084
"""
81-
dr, cr, a = enu2dca(e, n, -d, heading, deg=deg)
82-
return dr, cr, a
85+
dr, cr, above = enu2dca(e, n, -d, heading, deg=deg)
86+
return dr, cr, above
8387

8488

85-
def ecef2dca(x, y, z, lat0, lon0, h0, heading, ell=ELL, deg=True):
89+
def ecef2dca(x, y, z, lat0, lon0, h0, heading, ell: Ellipsoid | None = None, deg: bool = True):
8690
"""
8791
Converts ECEF (Earth-Centered, Earth-Fixed) coordinates to DCA (Downrange, Crossrange, Above).
8892
"""
93+
8994
e, n, u = ecef2enu(x, y, z, lat0, lon0, h0, ell, deg=deg)
9095
return enu2dca(e, n, u, heading, deg=deg)
9196

9297

93-
def dca2ecef(dr, cr, a, lat0, lon0, h0, heading, ell=ELL, deg=True):
98+
def dca2ecef(
99+
dr, cr, above, lat0, lon0, h0, heading, ell: Ellipsoid | None = None, deg: bool = True
100+
):
94101
"""
95102
Converts DCA (Downrange, Crossrange, Above) coordinates to ECEF (Earth-Centered, Earth-Fixed) coordinates.
96103
"""
97-
e, n, u = dca2enu(dr, cr, a, heading, deg=deg)
104+
105+
e, n, u = dca2enu(dr, cr, above, heading, deg=deg)
98106
return enu2ecef(e, n, u, lat0, lon0, h0, ell, deg=deg)
99107

100108

101-
def geodetic2dca(lat, lon, h, lat0, lon0, h0, heading, ell=ELL, deg=True):
109+
def geodetic2dca(
110+
lat, lon, h, lat0, lon0, h0, heading, ell: Ellipsoid | None = None, deg: bool = True
111+
):
102112
"""
103113
Converts geodetic coordinates (latitude, longitude, altitude) to DCA (Downrange, Crossrange, Above) coordinates.
104114
"""
105115
e, n, u = geodetic2enu(lat, lon, h, lat0, lon0, h0, ell, deg=deg)
106116
return enu2dca(e, n, u, heading, deg=deg)
107117

108118

109-
def dca2geodetic(dr, cr, a, lat0, lon0, h0, heading, ell=ELL, deg=True):
119+
def dca2geodetic(
120+
dr, cr, above, lat0, lon0, h0, heading, ell: Ellipsoid | None = None, deg: bool = True
121+
):
110122
"""
111123
Converts DCA (Downrange, Crossrange, Above) coordinates to geodetic coordinates (latitude, longitude, altitude).
112124
"""
113-
e, n, u = dca2enu(dr, cr, a, heading, deg=deg)
125+
e, n, u = dca2enu(dr, cr, above, heading, deg=deg)
114126
return enu2geodetic(e, n, u, lat0, lon0, h0, ell, deg=deg)
115127

116128

117-
def aer2dca(az, el, srange, heading, deg=True):
129+
def aer2dca(az, el, srange, heading, deg: bool = True):
118130
"""
119131
Converts AER (Azimuth, Elevation, Range) coordinates to DCA (Downrange, Crossrange, Above).
120132
"""
121133
e, n, u = aer2enu(az, el, srange, deg=deg)
122134
return enu2dca(e, n, u, heading, deg=deg)
123135

124136

125-
def dca2aer(dr, cr, a, heading, deg=True):
137+
def dca2aer(dr, cr, above, heading, deg: bool = True):
126138
"""
127139
Converts DCA (Downrange, Crossrange, Above) coordinates to AER (Azimuth, Elevation, Range).
128140
"""
129-
e, n, u = dca2enu(dr, cr, a, heading, deg=deg)
141+
e, n, u = dca2enu(dr, cr, above, heading, deg=deg)
130142
return enu2aer(e, n, u, deg=deg)

src/pymap3d/ecef.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,6 @@ def enu2ecef(
525525
"""
526526

527527
x0, y0, z0 = geodetic2ecef(lat0, lon0, h0, ell, deg=deg)
528-
dx, dy, dz = enu2uvw(e1, n1, u1, lat0, lon0, deg=deg)
528+
u, v, w = enu2uvw(e1, n1, u1, lat0, lon0, deg=deg)
529529

530-
return x0 + dx, y0 + dy, z0 + dz
530+
return x0 + u, y0 + v, z0 + w

src/pymap3d/nvector.py

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
import numpy as np
2-
import pymap3d as pm
1+
from __future__ import annotations
32

4-
def geodetic2nvector(lat, lon, ell=None, deg=True):
3+
from .mathfun import degrees, radians, sin, cos, atan2, asin
4+
from .ecef import geodetic2ecef, ecef2geodetic
5+
from .ellipsoid import Ellipsoid
6+
7+
8+
def geodetic2nvector(lat, lon, deg: bool = True) -> tuple:
59
"""
610
Convert geodetic coordinates (latitude, longitude) to an n-vector.
711
@@ -19,21 +23,21 @@ def geodetic2nvector(lat, lon, ell=None, deg=True):
1923
n1, n2, n3 : ndarray
2024
Components of the n-vector in the Earth-Centered Earth-Fixed (ECEF) coordinate system.
2125
"""
22-
lat, lon = np.atleast_1d(lat), np.atleast_1d(lon)
2326

2427
if deg:
25-
lat, lon = np.radians(lat), np.radians(lon)
28+
lat, lon = radians(lat), radians(lon)
2629

27-
sin_lat, cos_lat = np.sin(lat), np.cos(lat)
28-
sin_lon, cos_lon = np.sin(lon), np.cos(lon)
30+
sin_lat, cos_lat = sin(lat), cos(lat)
31+
sin_lon, cos_lon = sin(lon), cos(lon)
2932

3033
n1 = cos_lat * cos_lon
3134
n2 = cos_lat * sin_lon
3235
n3 = sin_lat
3336

3437
return n1, n2, n3
3538

36-
def nvector2geodetic(n1, n2, n3, ell=None, deg=True):
39+
40+
def nvector2geodetic(n1, n2, n3, deg=True) -> tuple:
3741
"""
3842
Convert an n-vector back to geodetic coordinates (latitude, longitude).
3943
@@ -49,18 +53,18 @@ def nvector2geodetic(n1, n2, n3, ell=None, deg=True):
4953
lat, lon : ndarray
5054
Geodetic latitude(s) and longitude(s).
5155
"""
52-
n1, n2, n3 = np.atleast_1d(n1), np.atleast_1d(n2), np.atleast_1d(n3)
5356

5457
# Compute latitude and longitude from n-vector
55-
lat = np.arcsin(n3)
56-
lon = np.arctan2(n2, n1)
58+
lat = asin(n3)
59+
lon = atan2(n2, n1)
5760

5861
if deg:
59-
lat, lon = np.degrees(lat), np.degrees(lon)
62+
lat, lon = degrees(lat), degrees(lon)
6063

6164
return lat, lon
6265

63-
def ecef2nvector(x, y, z, ell=None, deg=True):
66+
67+
def ecef2nvector(x, y, z, ell: Ellipsoid | None = None, deg: bool = True):
6468
"""
6569
Convert ECEF coordinates to an n-vector.
6670
@@ -76,10 +80,12 @@ def ecef2nvector(x, y, z, ell=None, deg=True):
7680
n1, n2, n3 : ndarray
7781
Components of the n-vector in the Earth-Centered Earth-Fixed (ECEF) coordinate system.
7882
"""
79-
lat, lon, _ = pm.ecef2geodetic(x, y, z, ell=ell, deg=deg)
80-
return geodetic2nvector(lat, lon, ell=ell, deg=deg)
8183

82-
def nvector2ecef(n1, n2, n3, alt=0, ell=None, deg=True):
84+
lat, lon, _ = ecef2geodetic(x, y, z, ell=ell, deg=deg)
85+
return geodetic2nvector(lat, lon, deg=deg)
86+
87+
88+
def nvector2ecef(n1, n2, n3, alt=0, ell: Ellipsoid | None = None, deg: bool = True):
8389
"""
8490
Convert an n-vector to ECEF coordinates.
8591
@@ -97,5 +103,5 @@ def nvector2ecef(n1, n2, n3, alt=0, ell=None, deg=True):
97103
x, y, z : ndarray
98104
ECEF coordinates in meters.
99105
"""
100-
lat, lon = nvector2geodetic(n1, n2, n3, ell=ell, deg=deg)
101-
return pm.geodetic2ecef(lat, lon, alt, ell=ell, deg=deg)
106+
lat, lon = nvector2geodetic(n1, n2, n3, deg=deg)
107+
return geodetic2ecef(lat, lon, alt, ell=ell, deg=deg)

src/pymap3d/tests/test_dca.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import pytest
2+
3+
import pymap3d as pm
4+
5+
6+
def get_ellipsoid_params():
7+
ell = pm.Ellipsoid.from_name("wgs84")
8+
return ell.semimajor_axis, ell.semiminor_axis
9+
10+
11+
A, B = get_ellipsoid_params()
12+
13+
14+
@pytest.mark.parametrize(
15+
"enu,dca,heading",
16+
[
17+
((0, 0, 0), (0, 0, 0), 15),
18+
((-7.0710678118654755, 12.24744871391589, 1000), (10, 10, 1000), 15),
19+
((-2.455756079379457, 13.927284806400378, 1000), (10, 10, 1000), 35),
20+
],
21+
)
22+
def test_enu_dca(enu, dca, heading):
23+
24+
assert pm.dca2enu(*dca, heading) == pytest.approx(enu)
25+
assert pm.enu2dca(*enu, heading) == pytest.approx(dca)
26+
27+
ned = enu[1], enu[0], -enu[2]
28+
assert pm.dca2ned(*dca, heading) == pytest.approx(ned)
29+
assert pm.ned2dca(*ned, heading) == pytest.approx(dca)
30+
31+
32+
@pytest.mark.parametrize(
33+
"ecef,dca,heading",
34+
[
35+
((6027079.293014112, 1614951.0292814733, 1317408.7685803245), (0, 0, 0), 15),
36+
((6028023.481548891, 1615196.7033287943, 1317628.660083717), (10, 10, 1000), 15),
37+
],
38+
)
39+
def test_ecef_dca(ecef, dca, heading):
40+
lat0, lon0, h0 = 12.0, 15.0, 30.0
41+
42+
assert pm.dca2ecef(*dca, lat0, lon0, h0, heading) == pytest.approx(ecef)
43+
assert pm.ecef2dca(*ecef, lat0, lon0, h0, heading) == pytest.approx(dca, rel=1e-9)
44+
45+
46+
def test_geodetic_dca():
47+
lat0, lon0, h0 = 12.0, 15.0, 30.0
48+
heading = 15.0
49+
50+
lat, lon, h = 12.1, 15.1, 30.1
51+
dca = pm.geodetic2dca(lat, lon, h, lat0, lon0, h0, heading)
52+
53+
assert pm.dca2geodetic(*dca, lat0, lon0, h0, heading) == pytest.approx((lat, lon, h))
54+
55+
56+
def test_aer_dca():
57+
heading = 15.0
58+
59+
az, el, r = 10.0, 20.0, 1000.0
60+
dca = pm.aer2dca(az, el, r, heading)
61+
62+
assert pm.dca2aer(*dca, heading) == pytest.approx((az, el, r))

src/pymap3d/tests/test_geodetic.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,12 @@ def test_scalar_geodetic2ecef(lla):
7070
lla1 = pm.ecef2geodetic(*xyz)
7171

7272
try:
73-
np.testing.assert_allclose(lla1, lla, rtol=1e-4)
73+
lla1 = np.array(lla1)
74+
lla = np.array(lla)
7475
except NameError:
75-
assert lla1 == approx(lla, rel=1e-4)
76+
pass
77+
78+
assert lla1 == approx(lla, rel=1e-4)
7679

7780
if scalar:
7881
assert all(isinstance(n, float) for n in xyz)
@@ -107,9 +110,12 @@ def test_scalar_ecef2geodetic(xyz):
107110
xyz1 = pm.geodetic2ecef(*lla)
108111

109112
try:
110-
np.testing.assert_allclose(xyz1, xyz, rtol=1e-4)
113+
xyz1 = np.array(xyz1)
114+
xyz = np.array(xyz)
111115
except NameError:
112-
assert xyz1 == approx(xyz, rel=1e-4)
116+
pass
117+
118+
assert xyz1 == approx(xyz, rel=1e-4)
113119

114120
if scalar:
115121
assert all(isinstance(n, float) for n in xyz1)

0 commit comments

Comments
 (0)