Skip to content

Commit 5630811

Browse files
committed
correct type annotation
1 parent ebba5fb commit 5630811

File tree

4 files changed

+120
-44
lines changed

4 files changed

+120
-44
lines changed

src/pymap3d/dca.py

Lines changed: 34 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
@@ -39,92 +39,102 @@
3939
"dca2aer",
4040
]
4141

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

44-
45-
def enu2dca(e, n, u, heading, deg=True):
43+
def enu2dca(e, n, u, heading, deg: bool = True):
4644
"""
4745
Converts ENU (East, North, Up) coordinates to DCA (Downrange, Crossrange, Above).
4846
"""
47+
4948
if deg:
5049
heading = radians(heading)
50+
5151
dr = e * sin(heading) + n * cos(heading)
5252
cr = -e * cos(heading) + n * sin(heading)
53-
a = u
54-
return dr, cr, a
5553

54+
return dr, cr, u
5655

57-
def dca2enu(dr, cr, a, heading, deg=True):
56+
57+
def dca2enu(dr, cr, above, heading, deg: bool = True):
5858
"""
5959
Converts DCA (Downrange, Crossrange, Above) coordinates to ENU (East, North, Up).
6060
"""
61+
6162
if deg:
6263
heading = radians(heading)
64+
6365
e = dr * sin(heading) - cr * cos(heading)
6466
n = dr * cos(heading) + cr * sin(heading)
65-
u = a
66-
return e, n, u
67+
68+
return e, n, above
6769

6870

69-
def dca2ned(dr, cr, a, heading, deg=True):
71+
def dca2ned(dr, cr, above, heading, deg: bool = True):
7072
"""
7173
Converts DCA (Downrange, Crossrange, Above) coordinates to NED (North, East, Down).
7274
"""
73-
e, n, u = dca2enu(dr, cr, a, heading, deg=deg)
75+
e, n, u = dca2enu(dr, cr, above, heading, deg=deg)
7476
return n, e, -u
7577

7678

77-
def ned2dca(n, e, d, heading, deg=True):
79+
def ned2dca(n, e, d, heading, deg: bool = True):
7880
"""
7981
Converts NED (North, East, Down) coordinates to DCA (Downrange, Crossrange, Above).
8082
"""
81-
dr, cr, a = enu2dca(e, n, -d, heading, deg=deg)
82-
return dr, cr, a
83+
dr, cr, above = enu2dca(e, n, -d, heading, deg=deg)
84+
return dr, cr, above
8385

8486

85-
def ecef2dca(x, y, z, lat0, lon0, h0, heading, ell=ELL, deg=True):
87+
def ecef2dca(x, y, z, lat0, lon0, h0, heading, ell: Ellipsoid | None = None, deg: bool = True):
8688
"""
8789
Converts ECEF (Earth-Centered, Earth-Fixed) coordinates to DCA (Downrange, Crossrange, Above).
8890
"""
91+
8992
e, n, u = ecef2enu(x, y, z, lat0, lon0, h0, ell, deg=deg)
9093
return enu2dca(e, n, u, heading, deg=deg)
9194

9295

93-
def dca2ecef(dr, cr, a, lat0, lon0, h0, heading, ell=ELL, deg=True):
96+
def dca2ecef(
97+
dr, cr, above, lat0, lon0, h0, heading, ell: Ellipsoid | None = None, deg: bool = True
98+
):
9499
"""
95100
Converts DCA (Downrange, Crossrange, Above) coordinates to ECEF (Earth-Centered, Earth-Fixed) coordinates.
96101
"""
97-
e, n, u = dca2enu(dr, cr, a, heading, deg=deg)
102+
103+
e, n, u = dca2enu(dr, cr, above, heading, deg=deg)
98104
return enu2ecef(e, n, u, lat0, lon0, h0, ell, deg=deg)
99105

100106

101-
def geodetic2dca(lat, lon, h, lat0, lon0, h0, heading, ell=ELL, deg=True):
107+
def geodetic2dca(
108+
lat, lon, h, lat0, lon0, h0, heading, ell: Ellipsoid | None = None, deg: bool = True
109+
):
102110
"""
103111
Converts geodetic coordinates (latitude, longitude, altitude) to DCA (Downrange, Crossrange, Above) coordinates.
104112
"""
105113
e, n, u = geodetic2enu(lat, lon, h, lat0, lon0, h0, ell, deg=deg)
106114
return enu2dca(e, n, u, heading, deg=deg)
107115

108116

109-
def dca2geodetic(dr, cr, a, lat0, lon0, h0, heading, ell=ELL, deg=True):
117+
def dca2geodetic(
118+
dr, cr, above, lat0, lon0, h0, heading, ell: Ellipsoid | None = None, deg: bool = True
119+
):
110120
"""
111121
Converts DCA (Downrange, Crossrange, Above) coordinates to geodetic coordinates (latitude, longitude, altitude).
112122
"""
113-
e, n, u = dca2enu(dr, cr, a, heading, deg=deg)
123+
e, n, u = dca2enu(dr, cr, above, heading, deg=deg)
114124
return enu2geodetic(e, n, u, lat0, lon0, h0, ell, deg=deg)
115125

116126

117-
def aer2dca(az, el, srange, heading, deg=True):
127+
def aer2dca(az, el, srange, heading, deg: bool = True):
118128
"""
119129
Converts AER (Azimuth, Elevation, Range) coordinates to DCA (Downrange, Crossrange, Above).
120130
"""
121131
e, n, u = aer2enu(az, el, srange, deg=deg)
122132
return enu2dca(e, n, u, heading, deg=deg)
123133

124134

125-
def dca2aer(dr, cr, a, heading, deg=True):
135+
def dca2aer(dr, cr, above, heading, deg: bool = True):
126136
"""
127137
Converts DCA (Downrange, Crossrange, Above) coordinates to AER (Azimuth, Elevation, Range).
128138
"""
129-
e, n, u = dca2enu(dr, cr, a, heading, deg=deg)
139+
e, n, u = dca2enu(dr, cr, above, heading, deg=deg)
130140
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: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import numpy as np
2-
import pymap3d as pm
1+
from .mathfun import degrees, radians, sin, cos, atan2, asin
2+
from .ecef import geodetic2ecef, ecef2geodetic
3+
from .ellipsoid import Ellipsoid
34

4-
def geodetic2nvector(lat, lon, ell=None, deg=True):
5+
6+
def geodetic2nvector(lat, lon, deg: bool = True) -> tuple:
57
"""
68
Convert geodetic coordinates (latitude, longitude) to an n-vector.
79
@@ -19,21 +21,21 @@ def geodetic2nvector(lat, lon, ell=None, deg=True):
1921
n1, n2, n3 : ndarray
2022
Components of the n-vector in the Earth-Centered Earth-Fixed (ECEF) coordinate system.
2123
"""
22-
lat, lon = np.atleast_1d(lat), np.atleast_1d(lon)
2324

2425
if deg:
25-
lat, lon = np.radians(lat), np.radians(lon)
26+
lat, lon = radians(lat), radians(lon)
2627

27-
sin_lat, cos_lat = np.sin(lat), np.cos(lat)
28-
sin_lon, cos_lon = np.sin(lon), np.cos(lon)
28+
sin_lat, cos_lat = sin(lat), cos(lat)
29+
sin_lon, cos_lon = sin(lon), cos(lon)
2930

3031
n1 = cos_lat * cos_lon
3132
n2 = cos_lat * sin_lon
3233
n3 = sin_lat
3334

3435
return n1, n2, n3
3536

36-
def nvector2geodetic(n1, n2, n3, ell=None, deg=True):
37+
38+
def nvector2geodetic(n1, n2, n3, deg=True) -> tuple:
3739
"""
3840
Convert an n-vector back to geodetic coordinates (latitude, longitude).
3941
@@ -49,18 +51,18 @@ def nvector2geodetic(n1, n2, n3, ell=None, deg=True):
4951
lat, lon : ndarray
5052
Geodetic latitude(s) and longitude(s).
5153
"""
52-
n1, n2, n3 = np.atleast_1d(n1), np.atleast_1d(n2), np.atleast_1d(n3)
5354

5455
# Compute latitude and longitude from n-vector
55-
lat = np.arcsin(n3)
56-
lon = np.arctan2(n2, n1)
56+
lat = asin(n3)
57+
lon = atan2(n2, n1)
5758

5859
if deg:
59-
lat, lon = np.degrees(lat), np.degrees(lon)
60+
lat, lon = degrees(lat), degrees(lon)
6061

6162
return lat, lon
6263

63-
def ecef2nvector(x, y, z, ell=None, deg=True):
64+
65+
def ecef2nvector(x, y, z, ell: Ellipsoid | None = None, deg: bool = True):
6466
"""
6567
Convert ECEF coordinates to an n-vector.
6668
@@ -76,10 +78,12 @@ def ecef2nvector(x, y, z, ell=None, deg=True):
7678
n1, n2, n3 : ndarray
7779
Components of the n-vector in the Earth-Centered Earth-Fixed (ECEF) coordinate system.
7880
"""
79-
lat, lon, _ = pm.ecef2geodetic(x, y, z, ell=ell, deg=deg)
80-
return geodetic2nvector(lat, lon, ell=ell, deg=deg)
8181

82-
def nvector2ecef(n1, n2, n3, alt=0, ell=None, deg=True):
82+
lat, lon, _ = ecef2geodetic(x, y, z, ell=ell, deg=deg)
83+
return geodetic2nvector(lat, lon, deg=deg)
84+
85+
86+
def nvector2ecef(n1, n2, n3, alt=0, ell: Ellipsoid | None = None, deg: bool = True):
8387
"""
8488
Convert an n-vector to ECEF coordinates.
8589
@@ -97,5 +101,5 @@ def nvector2ecef(n1, n2, n3, alt=0, ell=None, deg=True):
97101
x, y, z : ndarray
98102
ECEF coordinates in meters.
99103
"""
100-
lat, lon = nvector2geodetic(n1, n2, n3, ell=ell, deg=deg)
101-
return pm.geodetic2ecef(lat, lon, alt, ell=ell, deg=deg)
104+
lat, lon = nvector2geodetic(n1, n2, n3, deg=deg)
105+
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)
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))

0 commit comments

Comments
 (0)