Skip to content

Commit 358415d

Browse files
committed
loxodrome_direct: further correct east-west asymptote
fixes #43
1 parent 20cd729 commit 358415d

File tree

4 files changed

+27
-22
lines changed

4 files changed

+27
-22
lines changed

Examples/lox_stability.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from __future__ import annotations
33
import logging
44
from pathlib import Path
5-
from math import isclose, nan
5+
from math import isclose
66

77
from pymap3d.lox import loxodrome_direct
88

@@ -24,16 +24,12 @@ def matlab_func(lat1: float, lon1: float, rng: float, az: float) -> tuple[float,
2424
return eng.reckon("rh", lat1, lon1, rng, az, eng.wgs84Ellipsoid(), nargout=2) # type: ignore
2525

2626

27-
lat_last, lon_last = nan, nan
28-
clat, clon, rng = 40.0, -80.0, 10.0 # arbitrary
27+
clat, clon, rng = 35.0, 140.0, 50000.0 # arbitrary
2928

3029
for i in range(20):
3130
for azi in (90 + 10.0 ** (-i), -90 + 10.0 ** (-i), 270 + 10.0 ** (-i), -270 + 10.0 ** (-i)):
3231
lat, lon = loxodrome_direct(clat, clon, rng, azi)
3332

34-
assert lat != lat_last
35-
assert lon != lon_last
36-
3733
lat_matlab, lon_matlab = matlab_func(clat, clon, rng, azi)
3834
rstr = f"azimuth: {azi} lat,lon: Python: {lat}, {lon} Matlab: {lat_matlab}, {lon_matlab}"
3935
if not (

src/pymap3d/lox.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
import typing
55

66
try:
7-
from numpy import radians, degrees, cos, arctan2 as atan2, tan, pi, array, broadcast_arrays
7+
from numpy import radians, degrees, cos, arctan2 as atan2, tan, array, broadcast_arrays
88
except ImportError:
9-
from math import radians, degrees, cos, atan2, tan, pi # type: ignore
9+
from math import radians, degrees, cos, atan2, tan # type: ignore
10+
11+
from math import pi, tau
1012

1113
from .ellipsoid import Ellipsoid
1214
from .utils import sign
@@ -34,6 +36,9 @@
3436
]
3537

3638

39+
COS_EPS = 1e-9
40+
41+
3742
def meridian_dist(lat: ndarray, ell: Ellipsoid = None, deg: bool = True) -> float:
3843
"""
3944
Computes the ground distance on an ellipsoid from the equator to the input latitude.
@@ -159,7 +164,7 @@ def loxodrome_inverse(
159164
dist = meridian_arc(lat2, lat1, deg=False, ell=ell) / aux
160165

161166
# straight east or west
162-
i = aux < 1e-9
167+
i = aux < COS_EPS
163168
try:
164169
dist[i] = departure(lon2[i], lon1[i], lat1[i], ell, deg=False)
165170
except (AttributeError, TypeError):
@@ -215,8 +220,10 @@ def loxodrome_direct(
215220
if deg:
216221
lat1, lon1, a12 = radians(lat1), radians(lon1), radians(a12)
217222

223+
a12 = a12 % tau
224+
218225
try:
219-
lat1, rng = broadcast_arrays(lat1, rng)
226+
lat1, rng, a12 = broadcast_arrays(lat1, rng, a12)
220227
if (abs(lat1) > pi / 2).any(): # type: ignore
221228
raise ValueError("-90 <= latitude <= 90")
222229
if (rng < 0).any(): # type: ignore
@@ -239,14 +246,14 @@ def loxodrome_direct(
239246
iso = geodetic2isometric(lat1, ell, deg=False)
240247

241248
# stability near singularities
242-
i = abs(cos(a12)) < 1e-9
249+
i = abs(cos(a12)) < COS_EPS
243250
dlon = tan(a12) * (newiso - iso)
244251

245252
try:
246-
dlon[i] = sign(dlon[i]) * rng[i] / rcurve.transverse(lat1[i], ell=ell, deg=False) # type: ignore
253+
dlon[i] = sign(pi - a12[i]) * rng[i] / rcurve.parallel(lat1[i], ell=ell, deg=False) # type: ignore
247254
except (AttributeError, TypeError):
248255
if i: # straight east or west
249-
dlon = sign(dlon) * rng / rcurve.transverse(lat1, ell=ell, deg=False)
256+
dlon = sign(pi - a12) * rng / rcurve.parallel(lat1, ell=ell, deg=False)
250257

251258
lon2 = lon1 + dlon
252259

src/pymap3d/rcurve.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
import typing
55

66
try:
7-
from numpy import radians, sin, cos, sqrt
7+
from numpy import sin, cos, sqrt
88
except ImportError:
9-
from math import radians, sin, cos, sqrt # type: ignore
9+
from math import sin, cos, sqrt # type: ignore
1010

1111
from .ellipsoid import Ellipsoid
1212
from .utils import sanitize
@@ -37,10 +37,12 @@ def geocentric_radius(geodetic_lat: ndarray, ell: Ellipsoid = None, deg: bool =
3737
)
3838

3939

40-
def parallel(lat: ndarray, ell: Ellipsoid = None, deg: bool = True) -> float:
40+
def parallel(lat: float | ndarray, ell: Ellipsoid = None, deg: bool = True) -> float:
4141
"""
4242
computes the radius of the small circle encompassing the globe at the specified latitude
4343
44+
like Matlab rcurve('parallel', ...)
45+
4446
Parameters
4547
----------
4648
lat : float
@@ -56,8 +58,7 @@ def parallel(lat: ndarray, ell: Ellipsoid = None, deg: bool = True) -> float:
5658
radius of ellipsoid (meters)
5759
"""
5860

59-
if deg:
60-
lat = radians(lat)
61+
lat, ell = sanitize(lat, ell, deg)
6162

6263
return cos(lat) * transverse(lat, ell, deg=False)
6364

@@ -82,10 +83,7 @@ def meridian(lat: ndarray, ell: Ellipsoid = None, deg: bool = True) -> ndarray:
8283
radius of ellipsoid
8384
"""
8485

85-
if ell is None:
86-
ell = Ellipsoid()
87-
if deg:
88-
lat = radians(lat)
86+
lat, ell = sanitize(lat, ell, deg)
8987

9088
f1 = ell.semimajor_axis * (1 - ell.eccentricity ** 2)
9189
f2 = 1 - (ell.eccentricity * sin(lat)) ** 2

src/pymap3d/tests/test_rhumb.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ def test_numpy_2d_loxodrome_inverse():
9595
"lat0,lon0,rng,az,lat1,lon1",
9696
[
9797
(40, -80, 10000, 30, 40.0000779959676, -79.9999414477481),
98+
(35, 140, 50000, 90, 35, 140.548934481815),
99+
(35, 140, 50000, -270, 35, 140.548934481815),
100+
(35, 140, 50000, 270, 35, 139.451065518185),
101+
(35, 140, 50000, -90, 35, 139.451065518185),
98102
(0, 0, 0, 0, 0, 0),
99103
(0, 0, 10018754.17, 90, 0, 90),
100104
(0, 0, 10018754.17, -90, 0, -90),

0 commit comments

Comments
 (0)