pygeodesy 25.5.25__py2.py3-none-any.whl → 25.5.28__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 +1 -1
- pygeodesy/__main__.py +2 -1
- pygeodesy/booleans.py +10 -10
- pygeodesy/constants.py +6 -6
- pygeodesy/css.py +1 -1
- pygeodesy/ellipsoidalExact.py +22 -27
- pygeodesy/ellipsoidalGeodSolve.py +19 -19
- pygeodesy/ellipsoidalKarney.py +22 -27
- pygeodesy/ellipsoidalVincenty.py +12 -11
- pygeodesy/elliptic.py +15 -10
- pygeodesy/frechet.py +2 -2
- pygeodesy/geodesicw.py +27 -8
- pygeodesy/geodesicx/gx.py +51 -47
- pygeodesy/geodesicx/gxarea.py +53 -64
- pygeodesy/geodesicx/gxbases.py +12 -2
- pygeodesy/geodesicx/gxline.py +9 -6
- pygeodesy/geoids.py +1 -1
- pygeodesy/hausdorff.py +2 -2
- pygeodesy/heights.py +4 -4
- pygeodesy/internals.py +2 -2
- pygeodesy/interns.py +5 -5
- pygeodesy/karney.py +27 -11
- pygeodesy/lcc.py +2 -2
- pygeodesy/props.py +3 -3
- pygeodesy/sphericalBase.py +3 -3
- pygeodesy/sphericalNvector.py +18 -15
- pygeodesy/sphericalTrigonometry.py +19 -20
- pygeodesy/vector2d.py +2 -2
- {pygeodesy-25.5.25.dist-info → pygeodesy-25.5.28.dist-info}/METADATA +2 -2
- {pygeodesy-25.5.25.dist-info → pygeodesy-25.5.28.dist-info}/RECORD +32 -32
- {pygeodesy-25.5.25.dist-info → pygeodesy-25.5.28.dist-info}/WHEEL +0 -0
- {pygeodesy-25.5.25.dist-info → pygeodesy-25.5.28.dist-info}/top_level.txt +0 -0
pygeodesy/geodesicw.py
CHANGED
|
@@ -39,7 +39,7 @@ from contextlib import contextmanager
|
|
|
39
39
|
# from math import fabs # from .utily
|
|
40
40
|
|
|
41
41
|
__all__ = _ALL_LAZY.geodesicw
|
|
42
|
-
__version__ = '25.
|
|
42
|
+
__version__ = '25.05.28'
|
|
43
43
|
|
|
44
44
|
_plumb_ = 'plumb'
|
|
45
45
|
_TRIPS = 65
|
|
@@ -96,6 +96,28 @@ class _gWrapped(_kWrapped):
|
|
|
96
96
|
if name:
|
|
97
97
|
self._name, _ = _name2__(name, _or_nameof=E)
|
|
98
98
|
|
|
99
|
+
def Area(self, polyline=False, **name): # like GeodesicExact.Area
|
|
100
|
+
'''Return a C{PolygonArea} instance with method C{Compute} extended.
|
|
101
|
+
'''
|
|
102
|
+
_AreaBase = _wrapped._PolygonArea # in .karney._kwrapped
|
|
103
|
+
|
|
104
|
+
class _PolygonArea(_AreaBase):
|
|
105
|
+
# def __init__(self, *earth_polyline):
|
|
106
|
+
# _PolygonArea.__init__(self, *earth_polyline)
|
|
107
|
+
|
|
108
|
+
def Compute(self, reverse=False, sign=True, polar=False):
|
|
109
|
+
'''Use C{B{polar}=True} to adjust the area, see function
|
|
110
|
+
L{areaOf<pygeodesy.geodesicx.gxarea.areaOf>}.
|
|
111
|
+
'''
|
|
112
|
+
n, p, a = _AreaBase.Compute(self, reverse=reverse, sign=sign)
|
|
113
|
+
if polar: # see .geodesicx.gxarea.GeodesicAreaExact._reduced
|
|
114
|
+
a += _copysign(self.area0 * _0_5 * n, a)
|
|
115
|
+
return n, p, a
|
|
116
|
+
|
|
117
|
+
A = _PolygonArea(self, polyline)
|
|
118
|
+
A.name = _name2__(name, _or_nameof=self)
|
|
119
|
+
return A
|
|
120
|
+
|
|
99
121
|
def ArcDirect(self, lat1, lon1, azi1, a12, outmask=Caps.STANDARD): # PYCHOK no cover
|
|
100
122
|
'''Return the C{_Geodesic.ArcDirect} result as L{GDict}.
|
|
101
123
|
'''
|
|
@@ -108,8 +130,6 @@ class _gWrapped(_kWrapped):
|
|
|
108
130
|
'''
|
|
109
131
|
return self._GenDirectLine(lat1, lon1, azi1, True, a12, caps, **name)
|
|
110
132
|
|
|
111
|
-
Area = _Geodesic.Polygon # like GeodesicExact.Area
|
|
112
|
-
|
|
113
133
|
@property_RO
|
|
114
134
|
def datum(self):
|
|
115
135
|
'''Get this geodesic's datum (C{Datum}).
|
|
@@ -257,9 +277,8 @@ class _gWrapped(_kWrapped):
|
|
|
257
277
|
'''
|
|
258
278
|
return self._name
|
|
259
279
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
WGS84 = None # _EWGS84.geodesicw recusion
|
|
280
|
+
Polygon = Area
|
|
281
|
+
WGS84 = None # _EWGS84.geodesicw recusion
|
|
263
282
|
|
|
264
283
|
# Geodesic.ArcDirect.__doc__ = _Geodesic.ArcDirect.__doc__
|
|
265
284
|
# Geodesic.Direct.__doc__ = _Geodesic.Direct.__doc__
|
|
@@ -461,7 +480,7 @@ class Geodesic(_gWrapped): # overwritten by 1st instance
|
|
|
461
480
|
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
462
481
|
'''
|
|
463
482
|
g = _wrapped.Geodesic(a_ellipsoid, f=f, **name)
|
|
464
|
-
_MODS.geodesicw.Geodesic = g
|
|
483
|
+
_MODS.geodesicw.Geodesic = type(g) # overwrite class
|
|
465
484
|
return g
|
|
466
485
|
|
|
467
486
|
|
|
@@ -485,7 +504,7 @@ class GeodesicLine(_gWrapped): # overwritten by 1st instance
|
|
|
485
504
|
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
486
505
|
'''
|
|
487
506
|
gl = _wrapped.GeodesicLine(geodesic, lat1, lon1, azi1, caps=caps, **name)
|
|
488
|
-
_MODS.geodesicw.GeodesicLine = gl
|
|
507
|
+
_MODS.geodesicw.GeodesicLine = type(gl) # overwrite class
|
|
489
508
|
return gl
|
|
490
509
|
|
|
491
510
|
|
pygeodesy/geodesicx/gx.py
CHANGED
|
@@ -44,11 +44,11 @@ from pygeodesy.constants import EPS, EPS0, EPS02, MANT_DIG, NAN, PI, _EPSqrt, \
|
|
|
44
44
|
from pygeodesy.datums import _earth_datum, _WGS84, _EWGS84
|
|
45
45
|
# from pygeodesy.ellipsoids import _EWGS84 # from .datums
|
|
46
46
|
from pygeodesy.errors import GeodesicError, _xkwds_pop2
|
|
47
|
-
from pygeodesy.fmath import hypot
|
|
47
|
+
from pygeodesy.fmath import fdot_, hypot, hypot2, Fmt
|
|
48
48
|
from pygeodesy.fsums import fsumf_, fsum1f_
|
|
49
49
|
from pygeodesy.geodesicx.gxbases import _cosSeries, _GeodesicBase, \
|
|
50
50
|
_sincos12, _sin1cos2, _sinf1cos2d, \
|
|
51
|
-
_TINY, _xnC4
|
|
51
|
+
_TINY, _toNAN, _xnC4
|
|
52
52
|
from pygeodesy.geodesicx.gxline import _GeodesicLineExact, _update_glXs
|
|
53
53
|
# from pygeodesy.internals import typename # from .basics
|
|
54
54
|
from pygeodesy.interns import NN, _DOT_, _UNDER_
|
|
@@ -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.05.
|
|
68
|
+
__version__ = '25.05.28'
|
|
69
69
|
|
|
70
70
|
_MAXIT1 = 20
|
|
71
71
|
_MAXIT2 = 10 + _MAXIT1 + MANT_DIG # MANT_DIG == C++ digits
|
|
@@ -96,15 +96,12 @@ def _eTOL2(f):
|
|
|
96
96
|
# (bet1 + bet2) / 2, the relative error in the azimuth
|
|
97
97
|
# consistency check is sig12^2 * abs(f) * min(1, 1-f/2) / 2.
|
|
98
98
|
# (Error measured for 1/100 < b/a < 100 and abs(f) >= 1/1000.
|
|
99
|
-
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
# Here 0.1 is a safety factor (error decreased by 100) and
|
|
106
|
-
# max(0.001, abs(f)) stops etol2 getting too large in the
|
|
107
|
-
# nearly spherical case.
|
|
99
|
+
# For a given f and sig12, the max error occurs for lines
|
|
100
|
+
# near the pole. If the old rule for computing dnm = (dn1 +
|
|
101
|
+
# dn2)/2 is used, then the error increases by a factor of 2.)
|
|
102
|
+
# Setting this equal to epsilon gives sig12 = eTOL2. Here,
|
|
103
|
+
# safety factor 0.1 (error decreased by 100) and max(0.001, abs(f))
|
|
104
|
+
# stops eTOL2 getting too large in the nearly spherical case.
|
|
108
105
|
t = min(_1_0, _1_0 - f * _0_5) * max(_0_001, fabs(f)) * _0_5
|
|
109
106
|
return _TOL3 / (sqrt(t) if t > EPS02 else EPS0)
|
|
110
107
|
|
|
@@ -216,11 +213,10 @@ class GeodesicExact(_GeodesicBase):
|
|
|
216
213
|
return GeodesicLineExact(self, lat1, lon1, azi1, caps=caps, **name)._GenSet(self._debug, a12=a12)
|
|
217
214
|
|
|
218
215
|
def Area(self, polyline=False, **name):
|
|
219
|
-
'''Set up a L{GeodesicAreaExact} to compute area and
|
|
220
|
-
perimeter of a polygon.
|
|
216
|
+
'''Set up a L{GeodesicAreaExact} to compute area and perimeter of a polygon.
|
|
221
217
|
|
|
222
|
-
@kwarg polyline: If C{True}, compute the perimeter only, otherwise
|
|
223
|
-
|
|
218
|
+
@kwarg polyline: If C{True}, compute the perimeter only, otherwise the
|
|
219
|
+
perimeter and area (C{bool}).
|
|
224
220
|
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
225
221
|
|
|
226
222
|
@return: A L{GeodesicAreaExact} instance.
|
|
@@ -429,17 +425,21 @@ class GeodesicExact(_GeodesicBase):
|
|
|
429
425
|
def _eF_reset_cHe2_f1(self, x, y):
|
|
430
426
|
'''(INTERNAL) Reset elliptic function and return M{cH * e2 / f1 * ...}.
|
|
431
427
|
'''
|
|
432
|
-
|
|
433
|
-
|
|
428
|
+
if isnan(x):
|
|
429
|
+
y = NAN
|
|
430
|
+
else:
|
|
431
|
+
self._eF_reset_k2(x)
|
|
432
|
+
y *= self._eF.cH * self._e2_f1
|
|
433
|
+
return y
|
|
434
434
|
|
|
435
435
|
def _eF_reset_k2(self, x):
|
|
436
436
|
'''(INTERNAL) Reset elliptic function and return C{k2}.
|
|
437
437
|
'''
|
|
438
438
|
ep2 = self.ep2
|
|
439
|
-
|
|
440
|
-
self._eF.reset(k2=-
|
|
439
|
+
x *= x * ep2 # see .gxline._GeodesicLineExact._eF
|
|
440
|
+
self._eF.reset(k2=-x, alpha2=-ep2) # kp2, alphap2 defaults
|
|
441
441
|
_update_glXs(self) # zap cached/memoized _GeodesicLineExact attrs
|
|
442
|
-
return
|
|
442
|
+
return x
|
|
443
443
|
|
|
444
444
|
@Property_RO
|
|
445
445
|
def ellipsoid(self):
|
|
@@ -499,6 +499,7 @@ class GeodesicExact(_GeodesicBase):
|
|
|
499
499
|
if self._debug: # PYCHOK no cover
|
|
500
500
|
outmask |= Cs._DEBUG_INVERSE & self._debug
|
|
501
501
|
outmask &= Cs._OUT_MASK # incl. _SALP_CALPs_ and _DEBUG_
|
|
502
|
+
toNAN = _toNAN(outmask, lat1, lon1, lat2, lon2)
|
|
502
503
|
# compute longitude difference carefully (with _diff182):
|
|
503
504
|
# result is in [-180, +180] but -180 is only for west-going
|
|
504
505
|
# geodesics, +180 is for east-going and meridional geodesics
|
|
@@ -700,7 +701,9 @@ class GeodesicExact(_GeodesicBase):
|
|
|
700
701
|
eFk2=eF.k2, eFa2=eF.alpha2)
|
|
701
702
|
p.update(r) # r overrides p
|
|
702
703
|
r = p.toGDict()
|
|
703
|
-
|
|
704
|
+
|
|
705
|
+
r = self._iter2tion(r, **p)
|
|
706
|
+
return r._toNAN(outmask) if toNAN else r
|
|
704
707
|
|
|
705
708
|
def _GenDirect(self, lat1, lon1, azi1, arcmode, s12_a12, outmask=Caps.STANDARD):
|
|
706
709
|
'''(INTERNAL) The general I{Inverse} geodesic calculation.
|
|
@@ -805,8 +808,7 @@ class GeodesicExact(_GeodesicBase):
|
|
|
805
808
|
<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1GeodesicExact.html>} and
|
|
806
809
|
Python U{Geodesic.InverseLine<https://GeographicLib.SourceForge.io/Python/doc/code.html>}.
|
|
807
810
|
'''
|
|
808
|
-
|
|
809
|
-
r = self._GDictInverse(lat1, lon1, lat2, lon2, caps | Cs._SALP_CALPs_)
|
|
811
|
+
r = self._GDictInverse(lat1, lon1, lat2, lon2, caps | Caps._SALP_CALPs_)
|
|
810
812
|
return GeodesicLineExact(self, lat1, lon1, None, caps=caps, _s_calp1=(r.salp1, r.calp1),
|
|
811
813
|
**name)._GenSet(self._debug, **r)
|
|
812
814
|
|
|
@@ -839,12 +841,12 @@ class GeodesicExact(_GeodesicBase):
|
|
|
839
841
|
# (tan(bet1/2) + tan(bet2/2)) /
|
|
840
842
|
# (tan(bet1/2) * tan(bet2/2) + 1))
|
|
841
843
|
# with tan(x/2) = sin(x) / (1 + cos(x))
|
|
842
|
-
dbet1 =
|
|
843
|
-
dbet2 =
|
|
844
|
-
domg12 =
|
|
845
|
-
salp12 = (p.sbet1
|
|
846
|
-
calp12 = (p.sbet1
|
|
847
|
-
alp12 =
|
|
844
|
+
dbet1 = p.cbet1 + _1_0
|
|
845
|
+
dbet2 = p.cbet2 + _1_0
|
|
846
|
+
domg12 = comg12 + _1_0
|
|
847
|
+
salp12 = fdot_(p.sbet1, dbet2, dbet1, p.sbet2) * somg12
|
|
848
|
+
calp12 = fdot_(p.sbet1, p.sbet2, dbet1, dbet2) * domg12
|
|
849
|
+
alp12 = atan2(salp12, calp12) * _2_0
|
|
848
850
|
else:
|
|
849
851
|
# alp12 = alp2 - alp1, used in atan2, no need to normalize
|
|
850
852
|
salp12, calp12 = _sincos12(salp1, calp1, salp2, calp2)
|
|
@@ -870,7 +872,7 @@ class GeodesicExact(_GeodesicBase):
|
|
|
870
872
|
and C{p.set_sigs} updated for Newton, C{sig12=None}.
|
|
871
873
|
'''
|
|
872
874
|
sig12 = None # use Newton
|
|
873
|
-
salp1 = calp1 = salp2 = calp2 =
|
|
875
|
+
salp1 = calp1 = salp2 = calp2 = NAN
|
|
874
876
|
|
|
875
877
|
# bet12 = bet2 - bet1 in [0, PI)
|
|
876
878
|
sbet12, cbet12 = _sincos12(p.sbet1, p.cbet1, p.sbet2, p.cbet2)
|
|
@@ -878,12 +880,15 @@ class GeodesicExact(_GeodesicBase):
|
|
|
878
880
|
if shortline:
|
|
879
881
|
# sin((bet1 + bet2)/2)^2 = (sbet1 + sbet2)^2 / (
|
|
880
882
|
# (cbet1 + cbet2)^2 + (sbet1 + sbet2)^2)
|
|
881
|
-
t =
|
|
882
|
-
|
|
883
|
-
|
|
883
|
+
t = p.sbet1 + p.sbet2
|
|
884
|
+
if t:
|
|
885
|
+
t *= t / hypot2(t, p.cbet1 + p.cbet2)
|
|
886
|
+
dnm = sqrt(self.ep2 * t + _1_0)
|
|
887
|
+
else:
|
|
888
|
+
dnm = _1_0
|
|
884
889
|
somg12, comg12 = _sincos2(lam12 / (self.f1 * dnm))
|
|
885
890
|
else:
|
|
886
|
-
somg12, comg12 = p.slam12, p.clam12
|
|
891
|
+
somg12, comg12, dnm = p.slam12, p.clam12, NAN
|
|
887
892
|
|
|
888
893
|
# bet12a = bet2 + bet1 in (-PI, 0], note -sbet1
|
|
889
894
|
sbet12a, cbet12a = _sincos12(-p.sbet1, p.cbet1, p.sbet2, p.cbet2)
|
|
@@ -894,8 +899,8 @@ class GeodesicExact(_GeodesicBase):
|
|
|
894
899
|
salp1 = p.cbet2 * somg12
|
|
895
900
|
calp1 = (sbet12a - t) if comg12 < 0 else (sbet12 + t)
|
|
896
901
|
|
|
897
|
-
ssig12 =
|
|
898
|
-
csig12 =
|
|
902
|
+
ssig12 = hypot(salp1, calp1)
|
|
903
|
+
csig12 = fdot_(p.sbet1, p.sbet2, p.cbet1, p.cbet2 * comg12)
|
|
899
904
|
|
|
900
905
|
if shortline and ssig12 < self._eTOL2: # really short lines
|
|
901
906
|
t = c if comg12 < 0 else s
|
|
@@ -970,8 +975,8 @@ class GeodesicExact(_GeodesicBase):
|
|
|
970
975
|
#
|
|
971
976
|
# omg12 is near PI, estimate work with omg12a = PI - omg12
|
|
972
977
|
k = _Astroid(x, y)
|
|
973
|
-
|
|
974
|
-
|
|
978
|
+
k1 = _1_0 + k
|
|
979
|
+
sca *= (y * k1 / k) if f < 0 else (x * k / k1)
|
|
975
980
|
s, c = _sincos2(-sca) # omg12a
|
|
976
981
|
# update spherical estimate of alp1 using omg12 instead of lam12
|
|
977
982
|
salp1 = p.cbet2 * s
|
|
@@ -1082,16 +1087,15 @@ class GeodesicExact(_GeodesicBase):
|
|
|
1082
1087
|
# Missing a factor of self.b. Add parens around
|
|
1083
1088
|
# (csig1 * ssig2) and (ssig1 * csig2) to ensure
|
|
1084
1089
|
# accurate cancellation for coincident points.
|
|
1085
|
-
m12b =
|
|
1086
|
-
|
|
1087
|
-
|
|
1090
|
+
m12b = fdot_(p.dn2, (p.csig1 * p.ssig2),
|
|
1091
|
+
-p.dn1, (p.ssig1 * p.csig2),
|
|
1092
|
+
J12, (p.csig1 * p.csig2))
|
|
1088
1093
|
if (outmask & Cs.GEODESICSCALE):
|
|
1089
|
-
M12 = M21 = p.ssig1
|
|
1090
|
-
p.csig1 * p.csig2
|
|
1094
|
+
M12 = M21 = fdot_(p.ssig1, p.ssig2, p.csig1, p.csig2)
|
|
1091
1095
|
t = (p.cbet1 - p.cbet2) * self.ep2 * \
|
|
1092
1096
|
(p.cbet1 + p.cbet2) / (p.dn1 + p.dn2)
|
|
1093
|
-
M12 += (p.ssig2
|
|
1094
|
-
M21 -= (p.ssig1
|
|
1097
|
+
M12 += fdot_(p.ssig2, t, p.csig2, J12) * p.ssig1 / p.dn1
|
|
1098
|
+
M21 -= fdot_(p.ssig1, t, p.csig1, J12) * p.ssig2 / p.dn2
|
|
1095
1099
|
|
|
1096
1100
|
return s12b, m12b, m0, M12, M21
|
|
1097
1101
|
|
|
@@ -1310,7 +1314,7 @@ def _Astroid(x, y):
|
|
|
1310
1314
|
u = _cbrt(S * _2_0) # == T3 + _copysign(abs(S), T3)
|
|
1311
1315
|
else:
|
|
1312
1316
|
u = _0_0
|
|
1313
|
-
v =
|
|
1317
|
+
v = hypot(u, y) # sqrt(u**2 + q)
|
|
1314
1318
|
# avoid loss of accuracy when u < 0
|
|
1315
1319
|
u = (q / (v - u)) if u < 0 else (v + u)
|
|
1316
1320
|
w = (u - q) / (v + v) # positive?
|
pygeodesy/geodesicx/gxarea.py
CHANGED
|
@@ -17,7 +17,7 @@ U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
|
|
|
17
17
|
# make sure int/int division yields float quotient
|
|
18
18
|
from __future__ import division as _; del _ # noqa: E702 ;
|
|
19
19
|
|
|
20
|
-
from pygeodesy.basics import isodd, unsigned0
|
|
20
|
+
from pygeodesy.basics import _copysign, isodd, unsigned0
|
|
21
21
|
from pygeodesy.constants import NAN, _0_0, _0_5, _720_0
|
|
22
22
|
from pygeodesy.internals import printf, typename
|
|
23
23
|
# from pygeodesy.interns import _COMMASPACE_ # from .lazily
|
|
@@ -31,19 +31,16 @@ from pygeodesy.props import Property, Property_RO, property_RO
|
|
|
31
31
|
from math import fmod as _fmod
|
|
32
32
|
|
|
33
33
|
__all__ = ()
|
|
34
|
-
__version__ = '25.05.
|
|
34
|
+
__version__ = '25.05.28'
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class GeodesicAreaExact(_NamedBase):
|
|
38
|
-
'''Area and perimeter of a geodesic polygon, an enhanced
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@note: The name of this class C{*Exact} is a misnomer, see
|
|
45
|
-
I{Karney}'s comments at C++ attribute U{GeodesicExact._c2
|
|
46
|
-
<https://GeographicLib.SourceForge.io/C++/doc/
|
|
38
|
+
'''Area and perimeter of a geodesic polygon, an enhanced version of I{Karney}'s
|
|
39
|
+
Python class U{PolygonArea<https://GeographicLib.SourceForge.io/html/python/
|
|
40
|
+
code.html#module-geographiclib.polygonarea>} using the more accurate surface area.
|
|
41
|
+
|
|
42
|
+
@note: The name of this class C{*Exact} is a misnomer, see I{Karney}'s comments at
|
|
43
|
+
C++ attribute U{GeodesicExact._c2<https://GeographicLib.SourceForge.io/C++/doc/
|
|
47
44
|
GeodesicExact_8cpp_source.html>}.
|
|
48
45
|
'''
|
|
49
46
|
_Area = None
|
|
@@ -140,36 +137,35 @@ class GeodesicAreaExact(_NamedBase):
|
|
|
140
137
|
|
|
141
138
|
@Property_RO
|
|
142
139
|
def area0x(self):
|
|
143
|
-
'''Get the ellipsoid's surface area (C{meter} I{squared}),
|
|
144
|
-
|
|
140
|
+
'''Get the ellipsoid's surface area (C{meter} I{squared}), more accurate
|
|
141
|
+
for very I{oblate} ellipsoids.
|
|
145
142
|
'''
|
|
146
143
|
return self.ellipsoid.areax # not .area!
|
|
147
144
|
|
|
148
145
|
area0 = area0x # for C{geographiclib} compatibility
|
|
149
146
|
|
|
150
|
-
def Compute(self, reverse=False, sign=True):
|
|
147
|
+
def Compute(self, reverse=False, sign=True, polar=False):
|
|
151
148
|
'''Compute the accumulated perimeter and area.
|
|
152
149
|
|
|
153
|
-
@kwarg reverse: If C{True}, clockwise traversal counts as a
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
the
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
the
|
|
166
|
-
with C{polyline=False}. For perimeter
|
|
167
|
-
C{polyline=True}, area is C{NAN}.
|
|
168
|
-
|
|
169
|
-
@note: Arbitrarily complex polygons are allowed. In the case
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
I{figure-8} polygon will partially cancel.
|
|
150
|
+
@kwarg reverse: If C{True}, clockwise traversal counts as a positive area instead
|
|
151
|
+
of counter-clockwise (C{bool}).
|
|
152
|
+
@kwarg sign: If C{True}, return a signed result for the area if the polygon is
|
|
153
|
+
traversed in the "wrong" direction instead of returning the area for
|
|
154
|
+
the rest of the earth.
|
|
155
|
+
@kwarg polar: Use C{B{polar}=True} if the polygon encloses a pole (C{bool}), see
|
|
156
|
+
function L{ispolar<pygeodesy.points.ispolar>} and U{area of a polygon
|
|
157
|
+
enclosing a pole<https://GeographicLib.SourceForge.io/C++/doc/
|
|
158
|
+
classGeographicLib_1_1GeodesicExact.html#a3d7a9155e838a09a48dc14d0c3fac525>}.
|
|
159
|
+
|
|
160
|
+
@return: L{Area3Tuple}C{(number, perimeter, area)} with the number of points, the
|
|
161
|
+
perimeter in C{meter} and the (signed) area in C{meter**2}. The perimeter
|
|
162
|
+
includes the length of a final edge, connecting the current to the initial
|
|
163
|
+
point, if this polygon was initialized with C{polyline=False}. For perimeter
|
|
164
|
+
only, i.e. C{polyline=True}, area is C{NAN}.
|
|
165
|
+
|
|
166
|
+
@note: Arbitrarily complex polygons are allowed. In the case of self-intersecting
|
|
167
|
+
polygons, the area is accumulated "algebraically". E.g., the areas of both
|
|
168
|
+
loops in a I{figure-8} polygon will partially cancel.
|
|
173
169
|
|
|
174
170
|
@note: More points and edges can be added after this call.
|
|
175
171
|
'''
|
|
@@ -179,7 +175,7 @@ class GeodesicAreaExact(_NamedBase):
|
|
|
179
175
|
a = NAN if self.polyline else p
|
|
180
176
|
elif self._Area:
|
|
181
177
|
r = self._Inverse(self.lat1, self.lon1, self.lat0, self.lon0)
|
|
182
|
-
a = self._reduced(r.S12, reverse, sign,
|
|
178
|
+
a = self._reduced(r.S12, r.xing, n, reverse=reverse, sign=sign, polar=polar)
|
|
183
179
|
p = self._Peri.Sum(r.s12)
|
|
184
180
|
else:
|
|
185
181
|
p = self._Peri.Sum()
|
|
@@ -279,7 +275,7 @@ class GeodesicAreaExact(_NamedBase):
|
|
|
279
275
|
t = _COMMASPACE_.join(pairs(d, prec=10))
|
|
280
276
|
printf('%s %s: %s (%s)', self.named2, n, t, callername(up=2))
|
|
281
277
|
|
|
282
|
-
def _reduced(self, S12, reverse, sign,
|
|
278
|
+
def _reduced(self, S12, xing, n, reverse=False, sign=True, polar=False):
|
|
283
279
|
'''(INTERNAL) Accumulate and reduce area to allowed range.
|
|
284
280
|
'''
|
|
285
281
|
a0 = self.area0x
|
|
@@ -296,6 +292,8 @@ class GeodesicAreaExact(_NamedBase):
|
|
|
296
292
|
a = A.Add(-a0)
|
|
297
293
|
elif a <= -a0_:
|
|
298
294
|
a = A.Add( a0)
|
|
295
|
+
if polar: # see .geodesicw._gwrapped.Geodesic.Area
|
|
296
|
+
a = A.Add(_copysign(a0 * _0_5 * n, a)) # - if reverse or sign?
|
|
299
297
|
return unsigned0(a)
|
|
300
298
|
|
|
301
299
|
def Reset(self):
|
|
@@ -313,63 +311,54 @@ class GeodesicAreaExact(_NamedBase):
|
|
|
313
311
|
|
|
314
312
|
Clear = Reset
|
|
315
313
|
|
|
316
|
-
def TestEdge(self, azi, s,
|
|
314
|
+
def TestEdge(self, azi, s, **reverse_sign_polar):
|
|
317
315
|
'''Compute the properties for a tentative, additional edge
|
|
318
316
|
|
|
319
317
|
@arg azi: Azimuth at the current the point (compass C{degrees}).
|
|
320
318
|
@arg s: Length of the edge (C{meter}).
|
|
321
|
-
@kwarg
|
|
322
|
-
|
|
323
|
-
(C{bool}).
|
|
324
|
-
@kwarg sign: If C{True}, return a signed result for the area if
|
|
325
|
-
the polygon is traversed in the "wrong" direction
|
|
326
|
-
instead of returning the area for the rest of the
|
|
327
|
-
earth.
|
|
319
|
+
@kwarg reverse_sign_polar: Optional C{B{reverse}=False}, C{B{sign}=True} and
|
|
320
|
+
C{B{polar}=False} keyword arguments, see method L{Compute}.
|
|
328
321
|
|
|
329
322
|
@return: L{Area3Tuple}C{(number, perimeter, area)}.
|
|
330
323
|
|
|
331
324
|
@raise GeodesicError: No points.
|
|
332
325
|
'''
|
|
333
|
-
n = self.num + 1
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
a, r = NAN, None
|
|
337
|
-
elif n < 2:
|
|
338
|
-
raise GeodesicError(num=self.num)
|
|
326
|
+
r, n = None, self.num + 1
|
|
327
|
+
if n < 2: # raise GeodesicError(num=self.num)
|
|
328
|
+
a = p = NAN # like .test_Planimeter19
|
|
339
329
|
else:
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
330
|
+
p = self._Peri.Sum(s)
|
|
331
|
+
if self.polyline:
|
|
332
|
+
a = NAN
|
|
333
|
+
else:
|
|
334
|
+
d = self._Direct(azi, s)
|
|
335
|
+
r = self._Inverse(d.lat2, d.lon2, self.lat0, self.lon0)
|
|
336
|
+
a = self._reduced(d.S12 + r.S12, d.xing + r.xing, n, **reverse_sign_polar)
|
|
337
|
+
p += r.s12
|
|
344
338
|
if self.verbose: # PYCHOK no cover
|
|
345
339
|
self._print(n, p, a, r, azi=azi, s=s)
|
|
346
340
|
return Area3Tuple(n, p, a)
|
|
347
341
|
|
|
348
|
-
def TestPoint(self, lat, lon,
|
|
342
|
+
def TestPoint(self, lat, lon, **reverse_sign_polar):
|
|
349
343
|
'''Compute the properties for a tentative, additional vertex
|
|
350
344
|
|
|
351
345
|
@arg lat: Latitude of the point (C{degrees}).
|
|
352
346
|
@arg lon: Longitude of the point (C{degrees}).
|
|
353
|
-
@kwarg
|
|
354
|
-
|
|
355
|
-
(C{bool}).
|
|
356
|
-
@kwarg sign: If C{True}, return a signed result for the area if
|
|
357
|
-
the polygon is traversed in the "wrong" direction
|
|
358
|
-
instead of returning the area for the rest of the
|
|
359
|
-
earth.
|
|
347
|
+
@kwarg reverse_sign_polar: Optional C{B{reverse}=False}, C{B{sign}=True} and
|
|
348
|
+
C{B{polar}=False} keyword arguments, see method L{Compute}.
|
|
360
349
|
|
|
361
350
|
@return: L{Area3Tuple}C{(number, perimeter, area)}.
|
|
362
351
|
'''
|
|
363
352
|
r, n = None, self.num + 1
|
|
364
353
|
if n < 2:
|
|
365
354
|
p = _0_0
|
|
366
|
-
a =
|
|
355
|
+
a = NAN if self.polyline else p
|
|
367
356
|
else:
|
|
368
357
|
i = self._Inverse(self.lat1, self.lon1, lat, lon)
|
|
369
358
|
p = self._Peri.Sum(i.s12)
|
|
370
359
|
if self._Area:
|
|
371
360
|
r = self._Inverse(lat, lon, self.lat0, self.lon0)
|
|
372
|
-
a = self._reduced(i.S12 + r.S12,
|
|
361
|
+
a = self._reduced(i.S12 + r.S12, i.xing + r.xing, n, **reverse_sign_polar)
|
|
373
362
|
p += r.s12
|
|
374
363
|
else:
|
|
375
364
|
a = NAN
|
pygeodesy/geodesicx/gxbases.py
CHANGED
|
@@ -9,7 +9,7 @@ U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
|
|
|
9
9
|
'''
|
|
10
10
|
|
|
11
11
|
from pygeodesy.basics import isodd, _MODS
|
|
12
|
-
from pygeodesy.constants import _EPSmin as _TINY, _0_0
|
|
12
|
+
from pygeodesy.constants import _EPSmin as _TINY, _0_0, isfinite
|
|
13
13
|
from pygeodesy.errors import _or, _xkwds_item2
|
|
14
14
|
from pygeodesy.fmath import hypot as _hypot
|
|
15
15
|
# from pygeodesy.interns import _numpy_ # _MODS
|
|
@@ -20,7 +20,7 @@ from pygeodesy.karney import _CapsBase, GeodesicError, _2cos2x, \
|
|
|
20
20
|
from math import fabs, ldexp as _ldexp
|
|
21
21
|
|
|
22
22
|
__all__ = ()
|
|
23
|
-
__version__ = '
|
|
23
|
+
__version__ = '25.05.28'
|
|
24
24
|
|
|
25
25
|
# valid C{nC4}s and C{C4order}s, see _xnC4 below
|
|
26
26
|
_nC4s = {24: 2900, 27: 4032, 30: 5425}
|
|
@@ -147,6 +147,16 @@ def _sinf1cos2d(lat, f1):
|
|
|
147
147
|
return sbet, (cbet if fabs(cbet) > _TINY else _TINY)
|
|
148
148
|
|
|
149
149
|
|
|
150
|
+
def _toNAN(outmask, *args):
|
|
151
|
+
'''(INTERNAL) Is any C{arg} not finite?
|
|
152
|
+
'''
|
|
153
|
+
if (outmask & _CapsBase.NONFINITONAN): # Caps.NONFINITONAN
|
|
154
|
+
for arg in args:
|
|
155
|
+
if not isfinite(arg):
|
|
156
|
+
return True
|
|
157
|
+
return False
|
|
158
|
+
|
|
159
|
+
|
|
150
160
|
def _xnC4(**name_nC4):
|
|
151
161
|
'''(INTERNAL) Validate C{C4order}.
|
|
152
162
|
'''
|
pygeodesy/geodesicx/gxline.py
CHANGED
|
@@ -37,13 +37,14 @@ from __future__ import division as _; del _ # noqa: E702 ;
|
|
|
37
37
|
# - s and c prefixes mean sin and cos
|
|
38
38
|
|
|
39
39
|
# from pygeodesy.basics import _xinstanceof # _MODS
|
|
40
|
-
from pygeodesy.constants import NAN, _EPSqrt as _TOL,
|
|
41
|
-
_180_0, _2__PI,
|
|
40
|
+
from pygeodesy.constants import NAN, _EPSqrt as _TOL, \
|
|
41
|
+
_0_0, _1_0, _180_0, _2__PI, \
|
|
42
|
+
_copysign_1_0, isfinite
|
|
42
43
|
from pygeodesy.errors import _xError, _xkwds_pop2
|
|
43
44
|
from pygeodesy.fsums import fsumf_, fsum1f_
|
|
44
45
|
from pygeodesy.geodesicx.gxbases import _cosSeries, _GeodesicBase, \
|
|
45
46
|
_sincos12, _sin1cos2, \
|
|
46
|
-
_sinf1cos2d, _TINY
|
|
47
|
+
_sinf1cos2d, _TINY, _toNAN
|
|
47
48
|
# from pygeodesy.geodesicw import _Intersecant2 # _MODS
|
|
48
49
|
from pygeodesy.lazily import _ALL_DOCS, _ALL_MODS as _MODS
|
|
49
50
|
from pygeodesy.karney import _around, _atan2d, Caps, GDict, _fix90, \
|
|
@@ -55,7 +56,7 @@ from pygeodesy.utily import atan2, atan2d as _atan2d_reverse, sincos2
|
|
|
55
56
|
from math import cos, degrees, fabs, floor, radians, sin
|
|
56
57
|
|
|
57
58
|
__all__ = ()
|
|
58
|
-
__version__ = '25.05.
|
|
59
|
+
__version__ = '25.05.28'
|
|
59
60
|
|
|
60
61
|
_glXs = [] # instances of C{[_]GeodesicLineExact} to be updated
|
|
61
62
|
|
|
@@ -91,6 +92,7 @@ class _GeodesicLineExact(_GeodesicBase):
|
|
|
91
92
|
# _salp1 = _calp1 = NAN
|
|
92
93
|
# _somg1 = _comg1 = NAN
|
|
93
94
|
# _ssig1 = _csig1 = NAN
|
|
95
|
+
# _toNAN = False
|
|
94
96
|
|
|
95
97
|
def __init__(self, gX, lat1, lon1, azi1, caps, **name_):
|
|
96
98
|
'''(INTERNAL) New C{[_]GeodesicLineExact} instance.
|
|
@@ -104,6 +106,7 @@ class _GeodesicLineExact(_GeodesicBase):
|
|
|
104
106
|
salp1, calp1 = _sincos2d(_around(azi1))
|
|
105
107
|
if name_:
|
|
106
108
|
self.name = name_
|
|
109
|
+
self._toNAN = _toNAN(caps, lat1, lon1, azi1, salp1, calp1)
|
|
107
110
|
|
|
108
111
|
self._gX = gX # GeodesicExact only
|
|
109
112
|
self._lat1 = lat1 = _fix90(lat1)
|
|
@@ -297,9 +300,9 @@ class _GeodesicLineExact(_GeodesicBase):
|
|
|
297
300
|
gX = self.geodesic # ._gX
|
|
298
301
|
r = GDict(a12=NAN, s12=NAN) # both a12 and s12, always
|
|
299
302
|
|
|
300
|
-
if not isfinite(s12_a12):
|
|
303
|
+
if self._toNAN or not isfinite(s12_a12): # _toNAN(outmask, s12_a12)?
|
|
301
304
|
# E2 = sig12 = ssig12 = csig12 = NAN
|
|
302
|
-
return r._toNAN(outmask)
|
|
305
|
+
return r._toNAN(outmask | Cs.NONFINITONAN) # for backward compatibility
|
|
303
306
|
elif arcmode: # s12_a12 is (spherical) arc length
|
|
304
307
|
r.set_(a12=s12_a12)
|
|
305
308
|
sig12 = radians(s12_a12)
|
pygeodesy/geoids.py
CHANGED
|
@@ -355,7 +355,7 @@ class _GeoidBase(_HeightBase):
|
|
|
355
355
|
# build grid axis, hi = lo + (n - 1) * d
|
|
356
356
|
m, a = len2(frange(lo, n, d))
|
|
357
357
|
if m != n:
|
|
358
|
-
raise LenError(self
|
|
358
|
+
raise LenError(type(self), grid=m, **{name: n})
|
|
359
359
|
if d < 0:
|
|
360
360
|
d, a = -d, list(reversed(a))
|
|
361
361
|
a = self.numpy.array(a)
|
pygeodesy/hausdorff.py
CHANGED
|
@@ -85,7 +85,7 @@ from pygeodesy import unitsBase as _unitsBase # _Str_..., _xUnit, _xUnits
|
|
|
85
85
|
from random import Random
|
|
86
86
|
|
|
87
87
|
__all__ = _ALL_LAZY.hausdorff
|
|
88
|
-
__version__ = '25.05.
|
|
88
|
+
__version__ = '25.05.26'
|
|
89
89
|
|
|
90
90
|
_formy = _MODS.into(formy=__name__)
|
|
91
91
|
|
|
@@ -124,7 +124,7 @@ class Hausdorff(_Named):
|
|
|
124
124
|
@raise HausdorffError: Insufficient number of B{C{point1s}} or an invalid
|
|
125
125
|
B{C{point1}}, B{C{seed}} or B{C{units}}.
|
|
126
126
|
'''
|
|
127
|
-
name, kwds = _name2__(**name__kwds) # name__=self
|
|
127
|
+
name, kwds = _name2__(**name__kwds) # name__=type(self)
|
|
128
128
|
if name:
|
|
129
129
|
self.name = name
|
|
130
130
|
|
pygeodesy/heights.py
CHANGED
|
@@ -92,7 +92,7 @@ from pygeodesy.units import _isDegrees, Float_, Int_
|
|
|
92
92
|
# from math import radians # from .points
|
|
93
93
|
|
|
94
94
|
__all__ = _ALL_LAZY.heights
|
|
95
|
-
__version__ = '25.05.
|
|
95
|
+
__version__ = '25.05.26'
|
|
96
96
|
|
|
97
97
|
_error_ = 'error'
|
|
98
98
|
_formy = _MODS.into(formy=__name__)
|
|
@@ -212,7 +212,7 @@ class _HeightNamed(_Named): # in .geoids
|
|
|
212
212
|
n, lats = len2(lats)
|
|
213
213
|
m, lons = len2(lons)
|
|
214
214
|
if n != m: # format a LenError, but raise self._Error
|
|
215
|
-
e = LenError(self
|
|
215
|
+
e = LenError(type(self), lats=n, lons=m, txt=None)
|
|
216
216
|
raise self._Error(str(e))
|
|
217
217
|
llis = [LLiC(*t, datum=d) for t in zip(lats, lons)]
|
|
218
218
|
return llis
|
|
@@ -363,13 +363,13 @@ class _HeightBase(_HeightNamed): # in .geoids
|
|
|
363
363
|
def numpy(self):
|
|
364
364
|
'''Get the C{numpy} module or C{None}.
|
|
365
365
|
'''
|
|
366
|
-
return _xnumpy(self
|
|
366
|
+
return _xnumpy(type(self), 1, 9) # overwrite property_ROver
|
|
367
367
|
|
|
368
368
|
@property_ROver
|
|
369
369
|
def scipy(self):
|
|
370
370
|
'''Get the C{scipy} module or C{None}.
|
|
371
371
|
'''
|
|
372
|
-
return _xscipy(self
|
|
372
|
+
return _xscipy(type(self), 1, 2) # overwrite property_ROver
|
|
373
373
|
|
|
374
374
|
@property_ROver
|
|
375
375
|
def scipy_interpolate(self):
|