Coverage for pygeodesy/geodesicx/gxbases.py: 93%

1

2# -*- coding: utf-8 -*-

3

4 u'''(INTERNAL) Private L{geodesicx} base class, functions and constants.

5

6 Copyright (C) U{Charles Karney<mailto:Karney@Alum.MIT.edu>} (2008-2024)

7 and licensed under the MIT/X11 License. For more information, see the

8 U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.

9'''

10

11 from pygeodesy.basics import isodd, _MODS

12 from pygeodesy.constants import _EPSmin as _TINY, _0_0, isfinite

13 from pygeodesy.errors import _or, _xkwds_item2

14 from pygeodesy.fmath import hypot as _hypot

15# from pygeodesy.interns import _numpy_ # _MODS

16 from pygeodesy.karney import _CapsBase, GeodesicError, _2cos2x, \

17 _norm2, _sincos2d, _sum3

18# from pygeodesy.lazily import _ALL_MODS as _MODS # from .basics

19

20 from math import fabs, ldexp as _ldexp

21

22 __all__ = ()

23 __version__ = '25.06.01'

24

25# valid C{nC4}s and C{C4order}s, see _xnC4 below

26 _nC4s = {24: 2900, 27: 4032, 30: 5425}

27# assert (_TINY * EPS) > 0 and (_TINY + EPS) == EPS # underflow guard

28

29

30 class _GeodesicBase(_CapsBase): # in .geodsolve

31 '''(INTERNAL) Base class for C{[_]Geodesic*Exact}.

32 '''

33# def toRepr(self, prec=6, sep=_COMMASPACE_, **unused): # PYCHOK signature

34# '''Return this C{GeodesicExact*} items string.

35#

36# @kwarg prec: The C{float} precision, number of decimal digits (0..9).

37# Trailing zero decimals are stripped for B{C{prec}} values

38# of 1 and above, but kept for negative B{C{prec}} values.

39# @kwarg sep: Separator to join (C{str}).

40#

41# @return: C{GeodesicExact*} (C{str}).

42# '''

43# return Fmt.PAREN(self.named, self.toStr(prec=prec, sep=sep))

44 pass

45

46

47 class _Gfloats(dict):

48 '''(INTERNAL) Numpy or "Unique" floats.

49 '''

50 n = 0 # total number of floats

51 nC4 = 0

52

53 def __init__(self, nC4): # PYCHOK signature

54 self.nC4 = nC4

55

56 def __call__(self, fs):

57 '''Return a C{numpy.array} or C{tuple} of C{float}s.

58 '''

59 np = _MODS.imported(_MODS.interns._numpy_)

60 if np: # use numpy, already imported

61 cs = np.array(fs, dtype=float)

62 else:

63 _f = self.setdefault # avoid duplicates

64 cs = tuple(_f(f, f) for f in map(float, fs)) # PYCHOK as attr

65 self.n += len(fs)

66 return cs

67

68

69 def _cosSeries(c4s, sx, cx): # PYCHOK shared .geodesicx.gx and -.gxline

70 '''(INTERNAL) I{Karney}'s cosine series expansion using U{Clenshaw

71 summation<https://WikiPedia.org/wiki/Clenshaw_algorithm>}.

72 '''

73 ar = _2cos2x(cx, sx)

74 y0 = t0 = y1 = t1 = _0_0

75 c4 = list(c4s)

76 _c4 = c4.pop

77 if isodd(len(c4)):

78 y0 = _c4()

79 while c4:

80 # y1 = ar * y0 - y1 + c4.pop()

81 # y0 = ar * y1 - y0 + c4.pop()

82 y1, t1, _ = _sum3(-y1, -t1, ar * y0, ar * t0, _c4())

83 y0, t0, _ = _sum3(-y0, -t0, ar * y1, ar * t1, _c4())

84 # s = (y0 - y1) * cx

85 s, t, _ = _sum3(cx * y0, _0_0, cx * t0, -cx * y1, -cx * t1)

86 return s + t

87

88# Y0, Y1 = Fsum(), Fsum()

89# ar = _2cos2x(cx, sx)

90# c4 = list(c4s)

91# _c4 = c4.pop

92# if isodd(len(c4)):

93# Y0 += _c4()

94# while c4:

95# # y1 = ar * y0 - y1 + c4.pop()

96# # y0 = ar * y1 - y0 + c4.pop()

97# Y1 = Y0 * ar - Y1 + _c4()

98# Y0 = Y1 * ar - Y0 + _c4()

99# # s = (y0 - y1) * cx

100# return float((Y0 - Y1) * cx)

101

102

103 _f = float # in _f2 and .geodesicx._C4_24, _27 and _30

104

105

106 def _f2(hi, lo): # in .geodesicx._C4_24, _27 and _30

107 '''(INTERNAL) For C{_coeffs}.

108 '''

109 return _ldexp(_f(hi), 52) + _f(lo)

110

111

112 def _sincos12(sin1, cos1, sin2, cos2, sineg0=False):

113 '''(INTERNAL) Compute the sine and cosine of angle

114 M{ang12 = atan2(sin2, cos2) - atan2(sin1, cos1)}.

115

116 Negate C{sin1} to get C{sin12} and C{cos12} of the sum

117 M{ang12 = atan2(sin2, cos2) + atan2(sin1, cos1)}.

118

119 @kwarg sineg0: If C{True}, make negative C{sin12} zero (C{bool}).

120

121 @return: 2-Tuple C{(sin12, cos12)}.

122 '''

123 s = sin2 * cos1 - sin1 * cos2

124 c = cos2 * cos1 + sin1 * sin2

125 if sineg0 and s < 0:

126 s = _0_0 # max(s, _0_0) or NEG0?

127 return s, c

128

129

130 def _sin1cos2(sin1, cos1, sin2, cos2):

131 '''(INTERNAL) Compute the C{sin1 * cos2} sine and its cosine.

132

133 @return: 2-Tuple C{(sin1 * cos2, hypot(sin1 * sin2, cos1)}.

134 '''

135 s = sin1 * cos2

136 c = _hypot(sin1 * sin2, cos1)

137 return s, c # _norm2(s, c)

138

139

140 def _sinf1cos2d(lat, f1):

141 '''(INTERNAL) See C{GeodesicExact} and C{_GeodesicLineExact}.

142 '''

143 sbet, cbet = _sincos2d(lat)

144 # ensure cbet1 = +epsilon at poles; doing the fix on beta means

145 # that sig12 will be <= 2*tiny for two points at the same pole

146 sbet, cbet = _norm2(sbet * f1, cbet)

147 return sbet, (cbet if fabs(cbet) > _TINY else _TINY)

148

149

150 def _toNAN(outmask, *args):

151 '''(INTERNAL) Is any C{arg} not finite?

152 '''

153 return bool(outmask & _CapsBase.NONFINITONAN) and not all(map(isfinite, args))

154

155

156 def _xnC4(**name_nC4):

157 '''(INTERNAL) Validate C{C4order}.

158 '''

159 n, nC4 = _xkwds_item2(name_nC4)

160 if nC4 not in _nC4s or not isinstance(nC4, int):

161 t = map(str, _nC4s)

162 raise GeodesicError(n, nC4, txt_not_=_or(*t))

163 return _nC4s[nC4]

164

165

166# **) MIT License

167#

168# Copyright (C) 2016-2026 -- mrJean1 at Gmail -- All Rights Reserved.

169#

170# Permission is hereby granted, free of charge, to any person obtaining a

171# copy of this software and associated documentation files (the "Software"),

172# to deal in the Software without restriction, including without limitation

173# the rights to use, copy, modify, merge, publish, distribute, sublicense,

174# and/or sell copies of the Software, and to permit persons to whom the

175# Software is furnished to do so, subject to the following conditions:

176#

177# The above copyright notice and this permission notice shall be included

178# in all copies or substantial portions of the Software.

179#

180# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS

181# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

182# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL

183# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR

184# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,

185# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR

186# OTHER DEALINGS IN THE SOFTWARE.