pygeodesy 25.8.25__py2.py3-none-any.whl → 25.9.9__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pygeodesy/__init__.py +7 -6
- pygeodesy/auxilats/__init__.py +1 -1
- pygeodesy/auxilats/auxAngle.py +4 -3
- pygeodesy/auxilats/auxily.py +1 -1
- pygeodesy/basics.py +4 -4
- pygeodesy/booleans.py +25 -25
- pygeodesy/constants.py +37 -7
- pygeodesy/deprecated/functions.py +1 -0
- pygeodesy/dms.py +2 -2
- pygeodesy/ecef.py +3 -3
- pygeodesy/ellipsoidalExact.py +4 -4
- pygeodesy/ellipsoidalGeodSolve.py +3 -3
- pygeodesy/ellipsoids.py +52 -41
- pygeodesy/elliptic.py +8 -11
- pygeodesy/errors.py +18 -5
- pygeodesy/etm.py +8 -8
- pygeodesy/fmath.py +1 -1
- pygeodesy/geodesicx/__init__.py +1 -1
- pygeodesy/geodesicx/__main__.py +1 -0
- pygeodesy/geodesicx/gx.py +30 -37
- pygeodesy/geodesicx/gxbases.py +1 -5
- pygeodesy/geodesicx/gxline.py +43 -34
- pygeodesy/geodsolve.py +10 -17
- pygeodesy/internals.py +37 -13
- pygeodesy/karney.py +19 -18
- pygeodesy/ktm.py +1 -1
- pygeodesy/lazily.py +12 -11
- pygeodesy/lcc.py +5 -5
- pygeodesy/named.py +10 -13
- pygeodesy/rhumb/__init__.py +1 -1
- pygeodesy/rhumb/aux_.py +1 -1
- pygeodesy/rhumb/bases.py +7 -8
- pygeodesy/rhumb/ekx.py +9 -9
- pygeodesy/solveBase.py +14 -3
- pygeodesy/sphericalTrigonometry.py +4 -4
- pygeodesy/utily.py +200 -159
- pygeodesy/vector3dBase.py +6 -6
- {pygeodesy-25.8.25.dist-info → pygeodesy-25.9.9.dist-info}/METADATA +8 -7
- {pygeodesy-25.8.25.dist-info → pygeodesy-25.9.9.dist-info}/RECORD +41 -41
- {pygeodesy-25.8.25.dist-info → pygeodesy-25.9.9.dist-info}/WHEEL +0 -0
- {pygeodesy-25.8.25.dist-info → pygeodesy-25.9.9.dist-info}/top_level.txt +0 -0
pygeodesy/elliptic.py
CHANGED
|
@@ -75,7 +75,7 @@ U{22<https://DLMF.NIST.gov/22>}.
|
|
|
75
75
|
# make sure int/int division yields float quotient, see .basics
|
|
76
76
|
from __future__ import division as _; del _ # noqa: E702 ;
|
|
77
77
|
|
|
78
|
-
from pygeodesy.basics import copysign0, map2, neg, neg_
|
|
78
|
+
from pygeodesy.basics import copysign0, map2, neg, neg_
|
|
79
79
|
from pygeodesy.constants import EPS, INF, NAN, PI, PI_2, PI_4, \
|
|
80
80
|
_EPStol as _TolJAC, _0_0, _0_25, \
|
|
81
81
|
_0_5, _1_0, _2_0, _N_2_0, _3_0, \
|
|
@@ -85,7 +85,7 @@ from pygeodesy.constants import _EPSjam as _TolJAM # PYCHOK used!
|
|
|
85
85
|
# from pygeodesy.errors import _ValueError # from .fsums
|
|
86
86
|
from pygeodesy.fmath import favg, Fdot_, fma, hypot1, zqrt
|
|
87
87
|
from pygeodesy.fsums import Fsum, _fsum, _ValueError
|
|
88
|
-
|
|
88
|
+
from pygeodesy.internals import _Enum, typename
|
|
89
89
|
from pygeodesy.interns import NN, _delta_, _DOT_, _f_, _invalid_, \
|
|
90
90
|
_invokation_, _negative_, _SPACE_
|
|
91
91
|
from pygeodesy.karney import _K_2_0, _norm180, _signBit, _sincos2
|
|
@@ -100,7 +100,7 @@ from math import asin, asinh, atan, ceil, cosh, fabs, floor, radians, \
|
|
|
100
100
|
sin, sinh, sqrt, tan, tanh # tan as _tan
|
|
101
101
|
|
|
102
102
|
__all__ = _ALL_LAZY.elliptic
|
|
103
|
-
__version__ = '25.
|
|
103
|
+
__version__ = '25.09.04'
|
|
104
104
|
|
|
105
105
|
_TolRD = zqrt(EPS * 0.002)
|
|
106
106
|
_TolRF = zqrt(EPS * 0.030)
|
|
@@ -108,13 +108,10 @@ _TolRG0 = _TolJAC * 2.7
|
|
|
108
108
|
_TRIPS = 28 # Max depth, 6-18 might be sufficient
|
|
109
109
|
|
|
110
110
|
|
|
111
|
-
class _Cs(
|
|
112
|
-
'''(INTERAL) Complete
|
|
111
|
+
class _Cs(_Enum):
|
|
112
|
+
'''(INTERAL) Complete Integrals cache.
|
|
113
113
|
'''
|
|
114
|
-
|
|
115
|
-
# for n,v in kwds.items():
|
|
116
|
-
# setattr(self, n, v)
|
|
117
|
-
self.__dict__ = kwds
|
|
114
|
+
pass
|
|
118
115
|
|
|
119
116
|
|
|
120
117
|
class Elliptic(_Named):
|
|
@@ -773,8 +770,8 @@ class Elliptic(_Named):
|
|
|
773
770
|
def _S(**kwds):
|
|
774
771
|
return Scalar_(Error=EllipticError, **kwds)
|
|
775
772
|
|
|
776
|
-
self._k2
|
|
777
|
-
self._kp2
|
|
773
|
+
self._k2 = _S(k2 = k2, low=None, high=_1_0)
|
|
774
|
+
self._kp2 = _S(kp2=_1p2(kp2, k2)) # low=_0_0
|
|
778
775
|
|
|
779
776
|
self._alpha2 = _S(alpha2 = alpha2, low=None, high=_1_0)
|
|
780
777
|
self._alphap2 = _S(alphap2=_1p2(alphap2, alpha2)) # low=_0_0
|
pygeodesy/errors.py
CHANGED
|
@@ -16,9 +16,10 @@ C{PYGEODESY_EXCEPTION_CHAINING=std} or to any non-empty string.
|
|
|
16
16
|
# from pygeodesy import errors # _MODS, _MODS.getattr
|
|
17
17
|
from pygeodesy.internals import _envPYGEODESY, _plural, _tailof, typename
|
|
18
18
|
from pygeodesy.interns import MISSING, NN, _a_, _an_, _and_, _clip_, _COLON_, _COLONSPACE_, \
|
|
19
|
-
_COMMASPACE_, _datum_, _ELLIPSIS_, _ellipsoidal_,
|
|
20
|
-
|
|
21
|
-
_specified_, _UNDER_,
|
|
19
|
+
_COMMASPACE_, _datum_, _DOT_, _ELLIPSIS_, _ellipsoidal_, \
|
|
20
|
+
_EQUALSPACED_, _immutable_, _incompatible_, _invalid_, _keyword_, \
|
|
21
|
+
_LatLon_, _len_, _not_, _or_, _SPACE_, _specified_, _UNDER_, \
|
|
22
|
+
_vs_, _with_
|
|
22
23
|
from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _PYTHON_X_DEV
|
|
23
24
|
# from pygeodesy import streprs as _streprs # _MODS.into
|
|
24
25
|
# from pygeodesy.unitsBase import Str # _MODS
|
|
@@ -27,11 +28,12 @@ from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _PYTHON_X_DEV
|
|
|
27
28
|
from copy import copy as _copy
|
|
28
29
|
|
|
29
30
|
__all__ = _ALL_LAZY.errors # _ALL_DOCS('_InvalidError', '_IsnotError') _under
|
|
30
|
-
__version__ = '25.
|
|
31
|
+
__version__ = '25.09.04'
|
|
31
32
|
|
|
32
33
|
_argument_ = 'argument'
|
|
33
34
|
_basics = _MODS.into(basics=__name__)
|
|
34
35
|
_box_ = 'box'
|
|
36
|
+
_del_ = 'del'
|
|
35
37
|
_expected_ = 'expected'
|
|
36
38
|
_limiterrors = True # in .formy
|
|
37
39
|
_name_value_ = repr('name=value')
|
|
@@ -224,7 +226,8 @@ class IntersectionError(_ValueError): # in .ellipsoidalBaseDI, .formy, ...
|
|
|
224
226
|
'''New L{IntersectionError}.
|
|
225
227
|
'''
|
|
226
228
|
if args:
|
|
227
|
-
|
|
229
|
+
t = _COMMASPACE_(*map(repr, args))
|
|
230
|
+
_ValueError.__init__(self, t, **kwds)
|
|
228
231
|
else:
|
|
229
232
|
_ValueError.__init__(self, **kwds)
|
|
230
233
|
|
|
@@ -474,6 +477,16 @@ def exception_chaining(exc=None):
|
|
|
474
477
|
getattr(exc, '__cause__', None) # _DCAUSE_
|
|
475
478
|
|
|
476
479
|
|
|
480
|
+
def _ImmutableError(inst, attr, value=_del_, Error=_TypeError): # PYCHOK self
|
|
481
|
+
'''(INTERNAL) Format an C{immutable _TypeError}.
|
|
482
|
+
'''
|
|
483
|
+
n = typename(inst)
|
|
484
|
+
n = _DOT_(_xattr(inst, name=n), attr)
|
|
485
|
+
t = _SPACE_(_del_, n) if value is _del_ else \
|
|
486
|
+
_EQUALSPACED_(n, repr(value))
|
|
487
|
+
return Error(_immutable_, txt=t)
|
|
488
|
+
|
|
489
|
+
|
|
477
490
|
def _incompatible(this):
|
|
478
491
|
'''(INTERNAL) Format an C{"incompatible with ..."} text.
|
|
479
492
|
'''
|
pygeodesy/etm.py
CHANGED
|
@@ -75,8 +75,8 @@ from pygeodesy.datums import _ellipsoidal_datum, _WGS84, _EWGS84
|
|
|
75
75
|
# from pygeodesy.fsums import Fsum # from .fmath
|
|
76
76
|
from pygeodesy.fmath import cbrt, hypot, hypot1, hypot2, Fsum
|
|
77
77
|
from pygeodesy.interns import _COMMASPACE_, _DMAIN_, _near_, _SPACE_, _spherical_
|
|
78
|
-
from pygeodesy.karney import _K_2_4,
|
|
79
|
-
|
|
78
|
+
from pygeodesy.karney import _K_2_4, _diff182, _fix90, _norm2, _norm180, \
|
|
79
|
+
_signBit, _tand, _unsigned2
|
|
80
80
|
# from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS # from .named
|
|
81
81
|
from pygeodesy.named import callername, _incompatible, _NamedBase, \
|
|
82
82
|
_ALL_LAZY, _MODS
|
|
@@ -93,7 +93,7 @@ from pygeodesy.utm import _cmlon, _LLEB, _parseUTM5, _toBand, _toXtm8, \
|
|
|
93
93
|
from math import asinh, degrees, radians, sinh, sqrt
|
|
94
94
|
|
|
95
95
|
__all__ = _ALL_LAZY.etm
|
|
96
|
-
__version__ = '25.
|
|
96
|
+
__version__ = '25.08.31'
|
|
97
97
|
|
|
98
98
|
_OVERFLOW = _1_EPS**2 # ~2e+31
|
|
99
99
|
_TAYTOL = pow(EPS, 0.6)
|
|
@@ -790,7 +790,7 @@ class ExactTransverseMercator(_NamedBase):
|
|
|
790
790
|
mu = mu / d2
|
|
791
791
|
mv = mv / d2
|
|
792
792
|
else:
|
|
793
|
-
mu, mv = map1(
|
|
793
|
+
mu, mv = map1(_copysignOVERFLOW, mu, mv)
|
|
794
794
|
xi = self._Eu.fE(snu, cnu, dnu) - mu
|
|
795
795
|
v -= self._Ev.fE(snv, cnv, dnv) - mv
|
|
796
796
|
return xi, v, d2
|
|
@@ -910,12 +910,12 @@ class ExactTransverseMercator(_NamedBase):
|
|
|
910
910
|
if d1 > EPS02: # _EPSmin
|
|
911
911
|
t1 = snu * dnv / sqrt(d1)
|
|
912
912
|
else: # like atan(overflow) = pi/2
|
|
913
|
-
t1, d1 =
|
|
913
|
+
t1, d1 = _copysignOVERFLOW(snu), 0
|
|
914
914
|
d2 = cnu2 * self._mu + cnv**2 * mv
|
|
915
915
|
if d2 > EPS02: # _EPSmin
|
|
916
916
|
t2 = sinh(e * asinh(e * snu / sqrt(d2)))
|
|
917
917
|
else:
|
|
918
|
-
t2, d2 =
|
|
918
|
+
t2, d2 = _copysignOVERFLOW(snu), 0
|
|
919
919
|
# psi = asinh(t1) - asinh(t2)
|
|
920
920
|
# taup = sinh(psi)
|
|
921
921
|
taup = t1 * hypot1(t2) - t2 * hypot1(t1)
|
|
@@ -1026,10 +1026,10 @@ _allPropertiesOf_n(22, ExactTransverseMercator, Property_RO) # PYCHOK assert _R
|
|
|
1026
1026
|
del _0_1, _allPropertiesOf_n, EPS, _1_EPS, _EWGS84
|
|
1027
1027
|
|
|
1028
1028
|
|
|
1029
|
-
def
|
|
1029
|
+
def _copysignOVERFLOW(x):
|
|
1030
1030
|
'''(INTERNAL) Like C{copysign0(OVERFLOW, B{x})}.
|
|
1031
1031
|
'''
|
|
1032
|
-
return
|
|
1032
|
+
return (-_OVERFLOW) if _signBit(x) else _OVERFLOW
|
|
1033
1033
|
|
|
1034
1034
|
|
|
1035
1035
|
def parseETM5(strUTM, datum=_WGS84, Etm=Etm, falsed=True, **name):
|
pygeodesy/fmath.py
CHANGED
|
@@ -25,7 +25,7 @@ from math import fabs, sqrt # pow
|
|
|
25
25
|
import operator as _operator # in .datums, .trf, .utm
|
|
26
26
|
|
|
27
27
|
__all__ = _ALL_LAZY.fmath
|
|
28
|
-
__version__ = '25.
|
|
28
|
+
__version__ = '25.08.31'
|
|
29
29
|
|
|
30
30
|
# sqrt(2) - 1 <https://WikiPedia.org/wiki/Square_root_of_2>
|
|
31
31
|
_0_4142 = 0.41421356237309504880 # ~ 3_730_904_090_310_553 / 9_007_199_254_740_992
|
pygeodesy/geodesicx/__init__.py
CHANGED
pygeodesy/geodesicx/__main__.py
CHANGED
|
@@ -46,6 +46,7 @@ def _main(**C4order): # PYCHOK no cover
|
|
|
46
46
|
from sys import argv # .internals._isPyChOK
|
|
47
47
|
_main(C4order=int(argv[1])) if len(argv) == 2 and argv[1].isdigit() else _main()
|
|
48
48
|
|
|
49
|
+
|
|
49
50
|
# % python3.13 -m pygeodesy.geodesicx
|
|
50
51
|
# pygeodesy.geodesicx 25.06.01: C4order=30, C4n=5425, C4u=5107, C4u_n=94.1%, C4x=465, C4t=tuple, C4z=166008, geographiclib 2.0 (pygeodesy 25.5.28 Python 3.13.3 64bit arm64 macOS 15.5)
|
|
51
52
|
|
pygeodesy/geodesicx/gx.py
CHANGED
|
@@ -65,7 +65,7 @@ from pygeodesy.utily import atan2, atan2d as _atan2d_reverse, _unrollon, \
|
|
|
65
65
|
from math import copysign, cos, degrees, fabs, radians, sqrt
|
|
66
66
|
|
|
67
67
|
__all__ = ()
|
|
68
|
-
__version__ = '25.
|
|
68
|
+
__version__ = '25.09.02'
|
|
69
69
|
|
|
70
70
|
_MAXIT1 = 20
|
|
71
71
|
_MAXIT2 = 10 + _MAXIT1 + MANT_DIG # MANT_DIG == C++ digits
|
|
@@ -134,7 +134,7 @@ class GeodesicExact(_GeodesicBase):
|
|
|
134
134
|
_datum = _WGS84
|
|
135
135
|
_nC4 = 30 # default C4order
|
|
136
136
|
|
|
137
|
-
def __init__(self, a_ellipsoid=_EWGS84, f=None, C4order=None, **name_C4Order): # for backward compatibility
|
|
137
|
+
def __init__(self, a_ellipsoid=_EWGS84, f=None, caps=None, C4order=None, **name_C4Order): # for backward compatibility
|
|
138
138
|
'''New L{GeodesicExact} instance.
|
|
139
139
|
|
|
140
140
|
@arg a_ellipsoid: An ellipsoid (L{Ellipsoid}) or datum (L{Datum}) or
|
|
@@ -142,6 +142,8 @@ class GeodesicExact(_GeodesicBase):
|
|
|
142
142
|
conventionally in C{meter}), see B{C{f}}.
|
|
143
143
|
@arg f: The flattening of the ellipsoid (C{scalar}) if B{C{a_ellipsoid}}
|
|
144
144
|
is specified as C{scalar}.
|
|
145
|
+
@kwarg caps: Optional default capabilities for L{GeodesicLineExact} instances,
|
|
146
|
+
use C{B{caps}=Caps.NONFINITONAN} to handle nonfinites silently.
|
|
145
147
|
@kwarg C4order: Optional series expansion order (C{int}), see property
|
|
146
148
|
L{C4order}, default C{30}.
|
|
147
149
|
@kwarg name_C4Order: Optional C{B{name}=NN} (C{str}) and the DEPRECATED
|
|
@@ -154,9 +156,11 @@ class GeodesicExact(_GeodesicBase):
|
|
|
154
156
|
if name:
|
|
155
157
|
self.name = name
|
|
156
158
|
else:
|
|
157
|
-
name =
|
|
159
|
+
name = name_C4Order # no name
|
|
158
160
|
|
|
159
161
|
_earth_datum(self, a_ellipsoid, f=f, **name)
|
|
162
|
+
if caps:
|
|
163
|
+
self._caps |= caps & Caps._OUT_MASK
|
|
160
164
|
if C4order: # XXX private copy, always?
|
|
161
165
|
self.C4order = C4order
|
|
162
166
|
|
|
@@ -193,10 +197,7 @@ class GeodesicExact(_GeodesicBase):
|
|
|
193
197
|
@arg lon1: Longitude of the first point (C{degrees}).
|
|
194
198
|
@arg azi1: Azimuth at the first point (compass C{degrees}).
|
|
195
199
|
@arg a12: Arc length between the points (C{degrees}), can be negative.
|
|
196
|
-
@kwarg caps:
|
|
197
|
-
specifying the capabilities the L{GeodesicLineExact} instance
|
|
198
|
-
should possess, i.e., which quantities can be returned by methods
|
|
199
|
-
L{GeodesicLineExact.Position} and L{GeodesicLineExact.ArcPosition}.
|
|
200
|
+
@kwarg caps: Desired capabilities for the L{GeodesicLineExact} instance.
|
|
200
201
|
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
201
202
|
|
|
202
203
|
@return: A L{GeodesicLineExact} instance.
|
|
@@ -370,10 +371,7 @@ class GeodesicExact(_GeodesicBase):
|
|
|
370
371
|
@arg lon1: Longitude of the first point (C{degrees}).
|
|
371
372
|
@arg azi1: Azimuth at the first point (compass C{degrees}).
|
|
372
373
|
@arg s12: Distance between the points (C{meter}), can be negative.
|
|
373
|
-
@kwarg caps:
|
|
374
|
-
specifying the capabilities the L{GeodesicLineExact} instance
|
|
375
|
-
should possess, i.e., which quantities can be returned by methods
|
|
376
|
-
L{GeodesicLineExact.Position}.
|
|
374
|
+
@kwarg caps: Desired capabilities for the L{GeodesicLineExact} instance.
|
|
377
375
|
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
378
376
|
|
|
379
377
|
@return: A L{GeodesicLineExact} instance.
|
|
@@ -487,7 +485,7 @@ class GeodesicExact(_GeodesicBase):
|
|
|
487
485
|
@return: A L{GDict} ...
|
|
488
486
|
'''
|
|
489
487
|
C = outmask if arcmode else (outmask | Caps.DISTANCE_IN)
|
|
490
|
-
glX = self.Line(lat1, lon1, azi1, C | Caps.LINE_OFF)
|
|
488
|
+
glX = self.Line(lat1, lon1, azi1, caps=C | Caps.LINE_OFF)
|
|
491
489
|
return glX._GDictPosition(arcmode, s12_a12, outmask)
|
|
492
490
|
|
|
493
491
|
def _GDictInverse(self, lat1, lon1, lat2, lon2, outmask=Caps.STANDARD): # MCCABE 33, 41 vars
|
|
@@ -495,22 +493,23 @@ class GeodesicExact(_GeodesicBase):
|
|
|
495
493
|
|
|
496
494
|
@return: A L{GDict} ...
|
|
497
495
|
'''
|
|
498
|
-
Cs = Caps
|
|
496
|
+
r, Cs = GDict(), Caps
|
|
499
497
|
if self._debug: # PYCHOK no cover
|
|
500
498
|
outmask |= Cs._DEBUG_INVERSE & self._debug
|
|
499
|
+
outmask |= self.caps
|
|
501
500
|
outmask &= Cs._OUT_MASK # incl. _SALP_CALPs_ and _DEBUG_
|
|
502
|
-
|
|
501
|
+
if _toNAN(outmask, lat1, lon1, lat2, lon2):
|
|
502
|
+
return r._toNAN(outmask, lat1=lat1, lon1=lon1, lat2=lat2, lon2=lon2)
|
|
503
|
+
|
|
503
504
|
# compute longitude difference carefully (with _diff182):
|
|
504
505
|
# result is in [-180, +180] but -180 is only for west-going
|
|
505
506
|
# geodesics, +180 is for east-going and meridional geodesics
|
|
506
507
|
lon12, lon12s = _diff182(lon1, lon2)
|
|
507
508
|
# see C{result} from geographiclib.geodesic.Inverse
|
|
508
509
|
if (outmask & Cs.LONG_UNROLL): # == (lon1 + lon12) + lon12s
|
|
509
|
-
r
|
|
510
|
+
r.set_(lon1=lon1, lon2=fsumf_(lon1, lon12, lon12s))
|
|
510
511
|
elif (outmask & Cs.LONGITUDE):
|
|
511
|
-
r
|
|
512
|
-
else:
|
|
513
|
-
r = GDict()
|
|
512
|
+
r.set_(lon1=_norm180(lon1), lon2=_norm180(lon2))
|
|
514
513
|
if _K_2_0: # GeographicLib 2.0
|
|
515
514
|
# make longitude difference positive
|
|
516
515
|
lon12, lon_ = _unsigned2(lon12)
|
|
@@ -543,7 +542,7 @@ class GeodesicExact(_GeodesicBase):
|
|
|
543
542
|
r.set_(lat1=lat1, lat2=lat2)
|
|
544
543
|
# Swap points so that point with higher (abs) latitude is
|
|
545
544
|
# point 1. If one latitude is a NAN, then it becomes lat1.
|
|
546
|
-
swap_ =
|
|
545
|
+
swap_ = isnan(lat2) or fabs(lat1) < fabs(lat2)
|
|
547
546
|
if swap_:
|
|
548
547
|
lat1, lat2 = lat2, lat1
|
|
549
548
|
lon_ = not lon_
|
|
@@ -580,7 +579,7 @@ class GeodesicExact(_GeodesicBase):
|
|
|
580
579
|
p = _PDict(sbet1=sbet1, cbet1=cbet1, dn1=self._dn(sbet1, cbet1),
|
|
581
580
|
sbet2=sbet2, cbet2=cbet2, dn2=self._dn(sbet2, cbet2))
|
|
582
581
|
|
|
583
|
-
_meridian = _b = True # i.e.
|
|
582
|
+
_meridian = _b = True # i.e. meridian = b = False
|
|
584
583
|
if lat1 == -90 or slam12 == 0:
|
|
585
584
|
# Endpoints are on a single full meridian,
|
|
586
585
|
# so the geodesic might lie on a meridian.
|
|
@@ -597,17 +596,18 @@ class GeodesicExact(_GeodesicBase):
|
|
|
597
596
|
# echo 20.001 0 20.001 0 | GeodSolve -i
|
|
598
597
|
# In fact, we will have sig12 > PI/2 for meridional
|
|
599
598
|
# geodesic which is not a shortest path.
|
|
600
|
-
if
|
|
599
|
+
if sig12 < _TOL2 or m12x >= 0: # GeographicLib 2.5.1
|
|
601
600
|
# Need at least 2 to handle 90 0 90 180
|
|
602
601
|
# Prevent negative s12 or m12 from geographiclib 1.52
|
|
603
602
|
if sig12 < _TINY3 or (sig12 < _TOL0 and (s12x < 0 or m12x < 0)):
|
|
604
603
|
sig12 = m12x = s12x = _0_0
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
604
|
+
# else:
|
|
605
|
+
# m12x *= self.b
|
|
606
|
+
# s12x *= self.b
|
|
607
|
+
_meridian = _b = False # i.e. meridian = b = True
|
|
608
608
|
C = 1
|
|
609
|
-
|
|
610
|
-
|
|
609
|
+
# else: # m12 < 0, prolate and too close to anti-podal
|
|
610
|
+
# _meridian = True # i.e. meridian = False
|
|
611
611
|
a12 = _0_0 # if _b else degrees(sig12)
|
|
612
612
|
|
|
613
613
|
if _meridian:
|
|
@@ -651,7 +651,7 @@ class GeodesicExact(_GeodesicBase):
|
|
|
651
651
|
if (outmask & Cs.AREA):
|
|
652
652
|
somg12, comg12 = _sincos2(lam12 / (self.f1 * dnm))
|
|
653
653
|
|
|
654
|
-
else: # _meridian is False
|
|
654
|
+
else: # _meridian is False, i.e. meridian is True
|
|
655
655
|
somg12 = comg12 = NAN
|
|
656
656
|
|
|
657
657
|
r.set_(a12=a12 if _b else degrees(sig12)) # in [0, 180]
|
|
@@ -702,8 +702,7 @@ class GeodesicExact(_GeodesicBase):
|
|
|
702
702
|
p.update(r) # r overrides p
|
|
703
703
|
r = p.toGDict()
|
|
704
704
|
|
|
705
|
-
|
|
706
|
-
return r._toNAN(outmask) if toNAN else r
|
|
705
|
+
return self._iter2tion(r, **p)
|
|
707
706
|
|
|
708
707
|
def _GenDirect(self, lat1, lon1, azi1, arcmode, s12_a12, outmask=Caps.STANDARD):
|
|
709
708
|
'''(INTERNAL) The general I{Inverse} geodesic calculation.
|
|
@@ -791,10 +790,7 @@ class GeodesicExact(_GeodesicBase):
|
|
|
791
790
|
@arg lon1: Longitude of the first point (C{degrees}).
|
|
792
791
|
@arg lat2: Latitude of the second point (C{degrees}).
|
|
793
792
|
@arg lon2: Longitude of the second point (C{degrees}).
|
|
794
|
-
@kwarg caps:
|
|
795
|
-
specifying the capabilities the L{GeodesicLineExact} instance
|
|
796
|
-
should possess, i.e., which quantities can be returned by methods
|
|
797
|
-
L{GeodesicLineExact.Position} and L{GeodesicLineExact.ArcPosition}.
|
|
793
|
+
@kwarg caps: Desired capabilities for the L{GeodesicLineExact} instance.
|
|
798
794
|
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
799
795
|
|
|
800
796
|
@return: A L{GeodesicLineExact} instance.
|
|
@@ -1106,10 +1102,7 @@ class GeodesicExact(_GeodesicBase):
|
|
|
1106
1102
|
@arg lat1: Latitude of the first point (C{degrees}).
|
|
1107
1103
|
@arg lon1: Longitude of the first point (C{degrees}).
|
|
1108
1104
|
@arg azi1: Azimuth at the first point (compass C{degrees}).
|
|
1109
|
-
@kwarg caps:
|
|
1110
|
-
specifying the capabilities the L{GeodesicLineExact} instance
|
|
1111
|
-
should possess, i.e., which quantities can be returned by methods
|
|
1112
|
-
L{GeodesicLineExact.Position} and L{GeodesicLineExact.ArcPosition}.
|
|
1105
|
+
@kwarg caps: Desired capabilities for the L{GeodesicLineExact} instance.
|
|
1113
1106
|
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
1114
1107
|
|
|
1115
1108
|
@return: A L{GeodesicLineExact} instance.
|
pygeodesy/geodesicx/gxbases.py
CHANGED
|
@@ -150,11 +150,7 @@ def _sinf1cos2d(lat, f1):
|
|
|
150
150
|
def _toNAN(outmask, *args):
|
|
151
151
|
'''(INTERNAL) Is any C{arg} not finite?
|
|
152
152
|
'''
|
|
153
|
-
|
|
154
|
-
for arg in args:
|
|
155
|
-
if not isfinite(arg):
|
|
156
|
-
return True
|
|
157
|
-
return False
|
|
153
|
+
return bool(outmask & _CapsBase.NONFINITONAN) and not all(map(isfinite, args))
|
|
158
154
|
|
|
159
155
|
|
|
160
156
|
def _xnC4(**name_nC4):
|
pygeodesy/geodesicx/gxline.py
CHANGED
|
@@ -38,9 +38,11 @@ from __future__ import division as _; del _ # noqa: E702 ;
|
|
|
38
38
|
|
|
39
39
|
# from pygeodesy.basics import _xinstanceof # _MODS
|
|
40
40
|
from pygeodesy.constants import NAN, _EPSqrt as _TOL, \
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
_copysign_1_0, isfinite, \
|
|
42
|
+
_0_0, _1_0, _180_0, _360_0, \
|
|
43
|
+
_2__PI # PYCHOK used!
|
|
43
44
|
from pygeodesy.errors import _xError, _xkwds_pop2
|
|
45
|
+
# from pygeodesy.fmath import fremainder # from .karney
|
|
44
46
|
from pygeodesy.fsums import fsumf_, fsum1f_
|
|
45
47
|
from pygeodesy.geodesicx.gxbases import _cosSeries, _GeodesicBase, \
|
|
46
48
|
_sincos12, _sin1cos2, \
|
|
@@ -49,14 +51,14 @@ from pygeodesy.geodesicx.gxbases import _cosSeries, _GeodesicBase, \
|
|
|
49
51
|
from pygeodesy.lazily import _ALL_DOCS, _ALL_MODS as _MODS
|
|
50
52
|
from pygeodesy.karney import _around, _atan2d, Caps, GDict, _fix90, \
|
|
51
53
|
_K_2_0, _llz2gl, _norm2, _norm180, \
|
|
52
|
-
_sincos2, _sincos2d
|
|
54
|
+
_sincos2, _sincos2d, fremainder
|
|
53
55
|
from pygeodesy.props import Property_RO, property_ROver, _update_all
|
|
54
56
|
from pygeodesy.utily import atan2, atan2d as _atan2d_reverse, sincos2
|
|
55
57
|
|
|
56
|
-
from math import
|
|
58
|
+
from math import degrees, fabs, radians
|
|
57
59
|
|
|
58
60
|
__all__ = ()
|
|
59
|
-
__version__ = '25.
|
|
61
|
+
__version__ = '25.09.09'
|
|
60
62
|
|
|
61
63
|
_glXs = [] # instances of C{[_]GeodesicLineExact} to be updated
|
|
62
64
|
|
|
@@ -83,6 +85,7 @@ class _GeodesicLineExact(_GeodesicBase):
|
|
|
83
85
|
'''
|
|
84
86
|
_a13 = _s13 = NAN
|
|
85
87
|
# _azi1 = _0_0
|
|
88
|
+
_caps = Caps._AZIMUTH_LATITUDE_LONG_UNROLL
|
|
86
89
|
# _cchi1 = NAN
|
|
87
90
|
# _dn1 = NAN
|
|
88
91
|
_gX = None # Exact only
|
|
@@ -106,7 +109,6 @@ class _GeodesicLineExact(_GeodesicBase):
|
|
|
106
109
|
salp1, calp1 = _sincos2d(_around(azi1))
|
|
107
110
|
if name_:
|
|
108
111
|
self.name = name_
|
|
109
|
-
self._toNAN = _toNAN(caps, lat1, lon1, azi1, salp1, calp1)
|
|
110
112
|
|
|
111
113
|
self._gX = gX # GeodesicExact only
|
|
112
114
|
self._lat1 = lat1 = _fix90(lat1)
|
|
@@ -115,7 +117,9 @@ class _GeodesicLineExact(_GeodesicBase):
|
|
|
115
117
|
self._salp1 = salp1
|
|
116
118
|
self._calp1 = calp1
|
|
117
119
|
# allow lat, azimuth and unrolling of lon
|
|
118
|
-
self._caps
|
|
120
|
+
self._caps |= caps | gX.caps # | Caps._AZIMUTH_LATITUDE_LONG_UNROLL
|
|
121
|
+
|
|
122
|
+
self._toNAN = _toNAN(self._caps, lat1, lon1, azi1, salp1, calp1)
|
|
119
123
|
|
|
120
124
|
sbet1, cbet1 = _sinf1cos2d(_around(lat1), gX.f1)
|
|
121
125
|
self._dn1 = gX._dn(sbet1, cbet1)
|
|
@@ -282,8 +286,11 @@ class _GeodesicLineExact(_GeodesicBase):
|
|
|
282
286
|
def _eF(self):
|
|
283
287
|
'''(INTERNAL) Cached/memoized C{Elliptic} function.
|
|
284
288
|
'''
|
|
285
|
-
|
|
286
|
-
|
|
289
|
+
e = _MODS.elliptic
|
|
290
|
+
try: # see .gx.GeodesicExact._ef_reset_k2
|
|
291
|
+
return e.Elliptic(k2=-self._k2, alpha2=-self.geodesic.ep2)
|
|
292
|
+
except e.EllipticError: # nonfinite
|
|
293
|
+
return None
|
|
287
294
|
|
|
288
295
|
def _GDictPosition(self, arcmode, s12_a12, outmask=Caps.STANDARD): # MCCABE 17
|
|
289
296
|
'''(INTERNAL) Generate a new position along the geodesic.
|
|
@@ -293,26 +300,27 @@ class _GeodesicLineExact(_GeodesicBase):
|
|
|
293
300
|
C{lon1}, C{azi1} and arc length C{a12} always included,
|
|
294
301
|
except when C{a12=NAN}.
|
|
295
302
|
'''
|
|
296
|
-
Cs = Caps
|
|
303
|
+
r, Cs = GDict(), Caps
|
|
297
304
|
if outmask:
|
|
298
305
|
outmask &= self._caps & Cs._OUT_MASK
|
|
299
306
|
eF = self._eF
|
|
307
|
+
if eF is None or self._toNAN or not isfinite(s12_a12): # _toNAN(outmask, s12_a12)?
|
|
308
|
+
# E2 = sig12 = ssig12 = csig12 = NAN
|
|
309
|
+
d = dict(a12=s12_a12) if arcmode else dict(s12=s12_a12)
|
|
310
|
+
return r._toNAN(outmask | Cs.NONFINITONAN, # for backward compatibility
|
|
311
|
+
lat1=self.lat1, lon1=self.lon1, azi1=self.azi1, **d)
|
|
300
312
|
gX = self.geodesic # ._gX
|
|
301
|
-
r = GDict(a12=NAN, s12=NAN) # both a12 and s12, always
|
|
302
313
|
|
|
303
|
-
if
|
|
304
|
-
|
|
305
|
-
return r._toNAN(outmask | Cs.NONFINITONAN) # for backward compatibility
|
|
306
|
-
elif arcmode: # s12_a12 is (spherical) arc length
|
|
307
|
-
r.set_(a12=s12_a12)
|
|
314
|
+
if arcmode: # s12_a12 is (spherical) arc length
|
|
315
|
+
r.set_(a12=s12_a12, s12=NAN)
|
|
308
316
|
sig12 = radians(s12_a12)
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
317
|
+
ssig12, csig12 = sincos2(sig12) # utily, no NEG0
|
|
318
|
+
if not _K_2_0: # PYCHOK no cover
|
|
319
|
+
d = fremainder(fabs(s12_a12), _180_0)
|
|
320
|
+
if d == 90:
|
|
321
|
+
csig12 = _0_0
|
|
322
|
+
elif d == 0:
|
|
323
|
+
ssig12 = _0_0
|
|
316
324
|
E2 = _0_0
|
|
317
325
|
elif self._caps_DISTANCE_IN: # s12_a12 is distance
|
|
318
326
|
t = s12_a12 / self._E0b
|
|
@@ -321,9 +329,9 @@ class _GeodesicLineExact(_GeodesicBase):
|
|
|
321
329
|
E2 = -eF.deltaEinv(*_sincos12(-s, c, *self._stau1_ctau1))
|
|
322
330
|
sig12 = fsum1f_(self._E1, -E2, t) # == t - (E2 - E1)
|
|
323
331
|
ssig12, csig12 = _sincos2(sig12)
|
|
324
|
-
r.set_(a12=degrees(sig12))
|
|
332
|
+
r.set_(a12=degrees(sig12), s12=s12_a12)
|
|
325
333
|
else: # uninitialized or impossible distance requested
|
|
326
|
-
return r
|
|
334
|
+
return r.set_(a12=NAN, s12=NAN)
|
|
327
335
|
|
|
328
336
|
# sig2 = sig1 + sig12
|
|
329
337
|
ssig1, csig1 = self._ssig1, self._csig1
|
|
@@ -331,7 +339,6 @@ class _GeodesicLineExact(_GeodesicBase):
|
|
|
331
339
|
dn2 = eF.fDelta(*t)
|
|
332
340
|
|
|
333
341
|
if (outmask & Cs.DISTANCE):
|
|
334
|
-
outmask ^= Cs.DISTANCE
|
|
335
342
|
if arcmode: # or f_0_01
|
|
336
343
|
E2 = eF.deltaE(ssig2, csig2, dn2)
|
|
337
344
|
# AB1 = _E0 * (E2 - _E1)
|
|
@@ -343,7 +350,7 @@ class _GeodesicLineExact(_GeodesicBase):
|
|
|
343
350
|
s12 = s12_a12
|
|
344
351
|
r.set_(s12=s12)
|
|
345
352
|
|
|
346
|
-
if not outmask: # all done, see ._GenSet
|
|
353
|
+
if not (outmask ^ Cs.DISTANCE): # all done, see ._GenSet
|
|
347
354
|
return r
|
|
348
355
|
|
|
349
356
|
if self._debug: # PYCHOK no cover
|
|
@@ -374,17 +381,19 @@ class _GeodesicLineExact(_GeodesicBase):
|
|
|
374
381
|
if (outmask & Cs.LONGITUDE):
|
|
375
382
|
schi1 = self._somg1
|
|
376
383
|
cchi1 = self._cchi1
|
|
377
|
-
schi2 = ssig2 * salp0
|
|
378
|
-
cchi2 = gX.f1 * dn2 * csig2
|
|
384
|
+
schi2 = ssig2 * salp0 # schi2 = somg2 without normalization
|
|
385
|
+
cchi2 = gX.f1 * dn2 * csig2
|
|
379
386
|
lam12 = salp0 * self._H0e2_f1 * fsum1f_(eF.deltaH(ssig2, csig2, dn2),
|
|
380
387
|
-self._H1, sig12)
|
|
381
388
|
if (outmask & Cs.LONG_UNROLL):
|
|
382
|
-
|
|
383
|
-
tchi1 =
|
|
384
|
-
tchi2 =
|
|
385
|
-
chi12 =
|
|
389
|
+
e = _copysign_1_0(salp0) # east-going?
|
|
390
|
+
tchi1 = e * schi1
|
|
391
|
+
tchi2 = e * schi2
|
|
392
|
+
chi12 = e * fsum1f_(atan2(ssig1, csig1), -atan2(ssig2, csig2),
|
|
386
393
|
atan2(tchi2, cchi2), -atan2(tchi1, cchi1), sig12)
|
|
387
394
|
lon2 = self.lon1 + degrees(chi12 - lam12)
|
|
395
|
+
if fabs(lon2) > _360_0: # XXX kludge
|
|
396
|
+
lon2 = _norm180(lon2)
|
|
388
397
|
else:
|
|
389
398
|
chi12 = atan2(*_sincos12(schi1, cchi1, schi2, cchi2))
|
|
390
399
|
lon2 = _norm180(self._lon1_norm180 + _norm180(degrees(chi12 - lam12)))
|
|
@@ -398,7 +407,7 @@ class _GeodesicLineExact(_GeodesicBase):
|
|
|
398
407
|
J12 = self._D0k2 * fsumf_(eF.deltaD(ssig2, csig2, dn2), -self._D1, sig12)
|
|
399
408
|
if (outmask & Cs._DEBUG_DIRECT_LINE): # PYCHOK no cover
|
|
400
409
|
r.set_(ssig1=ssig1, dn1=dn1, D0k2=self._D0k2,
|
|
401
|
-
csig1=csig1,
|
|
410
|
+
csig1=csig1, dn2=dn2, D1=self._D1, J12=J12)
|
|
402
411
|
if (outmask & Cs.REDUCEDLENGTH):
|
|
403
412
|
# Add parens around (csig1 * ssig2) and (ssig1 * csig2) to
|
|
404
413
|
# ensure accurate cancellation in the case of coincident points.
|
pygeodesy/geodsolve.py
CHANGED
|
@@ -16,14 +16,13 @@ from pygeodesy.interns import _DMAIN_, NN, _UNDER_
|
|
|
16
16
|
from pygeodesy.karney import Caps, GeodesicError, GeodSolve12Tuple, \
|
|
17
17
|
_sincos2d, _Xables, _0_0, NAN
|
|
18
18
|
from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS
|
|
19
|
-
from pygeodesy.named import _name1__
|
|
20
19
|
from pygeodesy.namedTuples import Destination3Tuple, Distance3Tuple
|
|
21
20
|
from pygeodesy.props import Property, Property_RO, property_RO
|
|
22
21
|
from pygeodesy.solveBase import _SolveGDictBase, _SolveGDictLineBase
|
|
23
22
|
from pygeodesy.utily import _unrollon, _Wrap, wrap360
|
|
24
23
|
|
|
25
24
|
__all__ = _ALL_LAZY.geodsolve
|
|
26
|
-
__version__ = '25.
|
|
25
|
+
__version__ = '25.09.03'
|
|
27
26
|
|
|
28
27
|
|
|
29
28
|
class _GeodesicSolveBase(_SolveGDictBase):
|
|
@@ -131,18 +130,15 @@ class GeodesicSolve(_GeodesicSolveBase):
|
|
|
131
130
|
'''
|
|
132
131
|
return self.DirectLine(ll1.lat, ll1.lon, azi12, **caps_name)
|
|
133
132
|
|
|
134
|
-
def DirectLine(self, lat1, lon1, azi1, **
|
|
133
|
+
def DirectLine(self, lat1, lon1, azi1, caps=Caps.ALL, **name):
|
|
135
134
|
'''Set up a L{GeodesicLineSolve} to compute several points
|
|
136
135
|
on a single geodesic.
|
|
137
136
|
|
|
138
137
|
@arg lat1: Latitude of the first point (C{degrees}).
|
|
139
138
|
@arg lon1: Longitude of the first point (C{degrees}).
|
|
140
139
|
@arg azi1: Azimuth at the first point (compass C{degrees}).
|
|
141
|
-
@kwarg
|
|
142
|
-
|
|
143
|
-
of L{Caps<pygeodesy.karney.Caps>} values specifying
|
|
144
|
-
the capabilities the L{GeodesicLineSolve} instance
|
|
145
|
-
should possess.
|
|
140
|
+
@kwarg caps: Desired capabilities for the L{GeodesicLineSolve} instance.
|
|
141
|
+
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
146
142
|
|
|
147
143
|
@return: A L{GeodesicLineSolve} instance.
|
|
148
144
|
|
|
@@ -154,9 +150,9 @@ class GeodesicSolve(_GeodesicSolveBase):
|
|
|
154
150
|
<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1GeodesicExact.html>}
|
|
155
151
|
and Python U{Geodesic.Line<https://GeographicLib.SourceForge.io/Python/doc/code.html>}.
|
|
156
152
|
'''
|
|
157
|
-
return GeodesicLineSolve(self, lat1, lon1, azi1,
|
|
153
|
+
return GeodesicLineSolve(self, lat1, lon1, azi1, caps=caps, **name)
|
|
158
154
|
|
|
159
|
-
Line = DirectLine
|
|
155
|
+
Line = ArcDirectLine = DirectLine
|
|
160
156
|
|
|
161
157
|
def _Inverse(self, ll1, ll2, wrap, **outmask): # PYCHOK no cover
|
|
162
158
|
'''(INTERNAL) Short-cut version, see .ellipsoidalBaseDI.intersecant2.
|
|
@@ -182,7 +178,7 @@ class GeodesicSolve(_GeodesicSolveBase):
|
|
|
182
178
|
ll2 = _unrollon(ll1, _Wrap.point(ll2))
|
|
183
179
|
return self.InverseLine(ll1.lat, ll1.lon, ll2.lat, ll2.lon, **caps_name)
|
|
184
180
|
|
|
185
|
-
def InverseLine(self, lat1, lon1, lat2, lon2, **
|
|
181
|
+
def InverseLine(self, lat1, lon1, lat2, lon2, caps=Caps.ALL, **name): # PYCHOK no cover
|
|
186
182
|
'''Set up a L{GeodesicLineSolve} to compute several points
|
|
187
183
|
on a single geodesic.
|
|
188
184
|
|
|
@@ -190,11 +186,8 @@ class GeodesicSolve(_GeodesicSolveBase):
|
|
|
190
186
|
@arg lon1: Longitude of the first point (C{degrees}).
|
|
191
187
|
@arg lat2: Latitude of the second point (C{degrees}).
|
|
192
188
|
@arg lon2: Longitude of the second point (C{degrees}).
|
|
193
|
-
@kwarg
|
|
194
|
-
|
|
195
|
-
of L{Caps<pygeodesy.karney.Caps>} values specifying
|
|
196
|
-
the capabilities the L{GeodesicLineSolve} instance
|
|
197
|
-
should possess.
|
|
189
|
+
@kwarg caps: Desired capabilities for the L{GeodesicLineSolve} instance.
|
|
190
|
+
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
198
191
|
|
|
199
192
|
@return: A L{GeodesicLineSolve} instance.
|
|
200
193
|
|
|
@@ -205,7 +198,7 @@ class GeodesicSolve(_GeodesicSolveBase):
|
|
|
205
198
|
Python U{Geodesic.InverseLine<https://GeographicLib.SourceForge.io/Python/doc/code.html>}.
|
|
206
199
|
'''
|
|
207
200
|
r = self.Inverse(lat1, lon1, lat2, lon2)
|
|
208
|
-
gl = GeodesicLineSolve(self, lat1, lon1, r.azi1,
|
|
201
|
+
gl = GeodesicLineSolve(self, lat1, lon1, r.azi1, caps=caps, **name)
|
|
209
202
|
gl._a13 = r.a12 # gl.SetArc(r.a12)
|
|
210
203
|
gl._s13 = r.s12 # gl.SetDistance(r.s12)
|
|
211
204
|
return gl
|