Coverage for pygeodesy/constants.py: 97%

221 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2026-03-25 15:01 -0400

1 

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

3 

4u'''Single-instance C{float} and C{int} constants across C{pygeodesy} modules and related 

5functions L{pygeodesy.float_}, L{pygeodesy.float0_}, L{pygeodesy.isclose}, L{pygeodesy.isfinite}, 

6L{pygeodesy.isinf}, L{pygeodesy.isint0}, L{pygeodesy.isnan}, L{pygeodesy.isnear0}, 

7L{pygeodesy.isnear1}, L{pygeodesy.isnear90}, L{pygeodesy.isneg0}, L{pygeodesy.isninf}, 

8L{pygeodesy.isnon0} and L{pygeodesy.remainder}. 

9''' 

10# make sure int/int division yields float quotient, see .basics 

11from __future__ import division as _; del _ # noqa: E702 ; 

12 

13from pygeodesy.basics import _copysign, isbool, iscomplex, isint, signBit 

14from pygeodesy.errors import _ValueError, _xError, _xkwds_get1, _xkwds_item2 

15# from pygeodesy.fsums import _isFsum_2Tuple # _MODS 

16from pygeodesy.internals import _0_0, _100_0, typename 

17from pygeodesy.interns import _INF_, _NAN_, _DMAIN_ # PYCHOK used! 

18from pygeodesy.lazily import _ALL_MODS as _MODS, _ALL_LAZY 

19# from pygeodesy.streprs import Fmt # from .unitsBase 

20from pygeodesy.unitsBase import Float, Int, Radius, Fmt 

21 

22from math import fabs, isinf, isnan, pi as _pi, sqrt 

23try: 

24 from math import inf as _inf, nan as _nan # PYCHOK Python 3+ 

25except ImportError: # Python 2- 

26 _inf, _nan = float(_INF_), float(_NAN_) 

27 

28__all__ = _ALL_LAZY.constants 

29__version__ = '26.02.09' 

30 

31 

32def _copysign_0_0(y): 

33 '''(INTERNAL) copysign(0.0, y), only C{float}. 

34 ''' 

35 return _N_0_0 if signBit(y) else _0_0 

36 

37 

38def _copysign_1_0(y): 

39 '''(INTERNAL) copysign(1.0, y), only C{float}. 

40 ''' 

41 return _N_1_0 if signBit(y) else _1_0 

42 

43 

44def _copysignINF(y): 

45 '''(INTERNAL) copysign(INF, y), only C{float}. 

46 ''' 

47 return NINF if signBit(y) else INF 

48 

49 

50def _flipsign(x, y=-1): 

51 '''(INTERNAL) Negate C{x} for negative C{y}. 

52 ''' 

53 return (-x) if signBit(y) else x 

54 

55 

56def _Float(**name_arg): 

57 '''(INTERNAL) New named, cached C{Float}. 

58 ''' 

59 n, arg = _xkwds_item2(name_arg) 

60 return Float(_float(arg), name=n) 

61 

62 

63def _float(x): # in .datums, .ellipsoids, ... 

64 '''(INTERNAL) Cache initial C{float}s. 

65 ''' 

66 f = float(x) 

67 return _floats.setdefault(f, f) # PYCHOK del _floats 

68 

69 

70def float_(x, sets=False): 

71 '''Get scalar as C{float} or I{intern}'ed C{float}. 

72 

73 @arg x: The scalar (C{scalar}). 

74 @kwarg sets: Use C{True} to C{intern} the B{C{f}}, 

75 otherwise don't (C{bool}). 

76 

77 @return: A C{float}. 

78 

79 @raise ValueError: Invalid B{C{x}}. 

80 ''' 

81 try: 

82 f = float(x) 

83 if f: 

84 _f = _floats.setdefault if sets else _floats.get 

85 f = _f(f, f) 

86 else: 

87 f = _N_0_0 if signBit(f) else _0_0 

88 except Exception as X: 

89 raise _ValueError(x=x, cause=X) 

90 return f 

91 

92 

93def float0_(*xs): 

94 '''Yield C{B{x}s} as a non-NEG0 C{float}. 

95 ''' 

96 for x in xs: 

97 yield float(x) or _0_0 # if x else _0_0 

98 

99 

100def _float0(x): # in .auxilats.auxily, .resections, .vector3dBase, ... 

101 '''(INTERNAL) Return C{float(B{f})} or C{INT0}. 

102 ''' 

103 if x: 

104 f = float(x) 

105 f = _floats.get(f, f) 

106 elif x is INT0: 

107 f = x 

108 else: 

109 f = float(x) or _0_0 # force None, NN error 

110 return f 

111 

112 

113def floats_(*xs, **sets): # sets=False 

114 '''Yield each scalar as C{float} or I{intern}'ed C{float}. 

115 

116 @arg xs: One more values (C{scalar}), all positional. 

117 @kwarg sets: Use C{B{sets}=True} to C{intern} each 

118 B{C{fs}}, otherwise don't C{intern}. 

119 

120 @raise ValueError: Some B{C{xs}} is not C{scalar}. 

121 ''' 

122 if sets: 

123 sets = _xkwds_get1(sets, sets=False) 

124 _f = _floats.setdefault if sets else _floats.get 

125 try: 

126 for i, x in enumerate(xs): 

127 f = float(x) 

128 yield _f(f, f) if f else \ 

129 (_N_0_0 if signBit(f) else _0_0) # preserve NEG0 

130 except Exception as X: 

131 xs_i = Fmt.SQUARE(xs=i) 

132 raise _ValueError(xs_i, x, cause=X) 

133 

134 

135def _floatuple(*fs): 

136 '''(INTERNAL) Cache a tuple of C{float}s. 

137 ''' 

138 return tuple(map(_float, fs)) 

139 

140 

141try: 

142 from math import log2 as _log2 

143except ImportError: # Python 3.3- 

144 from math import log as _log 

145 

146 def _log2(x): # in .rhumb.aux_, .auxilats.auxLat 

147 return _log(x, 2) 

148 

149 

150def _naninf(p, *xs): 

151 '''(INTERNAL) Return C{NAN}, C{NINF} or C{INF}. 

152 ''' 

153 for x in xs: 

154 p *= x # fmath.fprod(xs) 

155 return _copysignINF(p) if isfinite(p) else p 

156 

157 

158def _over(p, q): 

159 '''(INTERNAL) Return C{B{p} / B{q}} without C{ZeroDivisionError} exceptions. 

160 ''' 

161 try: 

162 return (p / q) # if p else _copysign_0_0(q) 

163 except ZeroDivisionError: 

164 return (_copysignINF(p) if isfinite(p) else NAN) if p else p 

165 

166 

167def _over_1(p, q): 

168 '''(INTERNAL) Return C{B{p} / B{q}} with exact C{1.0} and without C{ZeroDivisionError} exceptions. 

169 ''' 

170 if fabs(p) != fabs(q): 

171 r = _over(p, q) 

172 else: 

173 r = _flipsign(p, q) 

174 if p: 

175 r = _copysign_1_0(r) 

176 return r 

177 

178 

179def _1_over(x): 

180 '''(INTERNAL) Return reciprocal C{1 / B{x}} avoiding C{ZeroDivisionError} exceptions. 

181 ''' 

182 try: 

183 return _1_0 / float(x) 

184 except ZeroDivisionError: 

185 return NINF if isneg0(x) else INF 

186 

187 

188def _Radius(**name_arg): 

189 '''(INTERNAL) New named, cached C{Radius}. 

190 ''' 

191 n, arg = _xkwds_item2(name_arg) 

192 return Radius(_float(arg), name=n) 

193 

194 

195_floats = {} # PYCHOK floats cache, in .__main__ 

196# _float = float # PYCHOK expected 

197# del _floats # XXX zap floats cache never 

198 

199_0_0 = _float( _0_0) # PYCHOK expected 

200_0_0_1T = _0_0, # PYCHOK 1-tuple 

201_0_0_9T = _0_0_1T * 9 # PYCHOK 9-tuple 

202_0_0001 = _float( 0.0001) # PYCHOK expected 

203_0_001 = _float( 0.001) # PYCHOK expected 

204_0_01 = _float( 0.01) # PYCHOK expected 

205_0_1 = _float( 0.1) # PYCHOK expected 

206_0_125 = _float( 0.125) # PYCHOK expected 

207_0_25 = _float( 0.25) # PYCHOK expected 

208_0_5 = _float( 0.5) # PYCHOK expected 

209_0_75 = _float( 0.75) # PYCHOK expected 

210_1_0 = _float( 1) # PYCHOK expected 

211_1_0_1T = _1_0, # PYCHOK 1-tuple 

212_1_5 = _float( 1.5) # PYCHOK expected 

213_1_75 = _float( 1.75) # PYCHOK expected 

214_2_0 = _float( 2) # PYCHOK expected 

215_3_0 = _float( 3) # PYCHOK expected 

216_4_0 = _float( 4) # PYCHOK expected 

217_5_0 = _float( 5) # PYCHOK expected 

218_6_0 = _float( 6) # PYCHOK expected 

219_8_0 = _float( 8) # PYCHOK expected 

220_9_0 = _float( 9) # PYCHOK expected 

221_10_0 = _float( 10) # PYCHOK expected 

222_16_0 = _float( 16) # PYCHOK expected 

223_32_0 = _float( 32) # PYCHOK expected 

224_45_0 = _float( 45) # PYCHOK expected 

225_60_0 = _float( 60) # PYCHOK expected 

226_64_0 = _float( 64) # PYCHOK expected 

227_90_0 = _float( 90) # PYCHOK expected 

228_100_0 = _float(_100_0) # PYCHOK expected 

229_180_0 = _float( 180) # PYCHOK expected 

230_270_0 = _float( 270) # PYCHOK expected 

231_360_0 = _float( 360) # PYCHOK expected 

232_720_0 = _float( 720) # PYCHOK expected 

233_1000_0 = _float(1000) # PYCHOK expected 

234_3600_0 = _float(3600) # PYCHOK expected 

235 

236_N_0_0 = float( '-0.0') # PYCHOK NOT _float! 

237_N_0_5 = _float( -_0_5) # PYCHOK expected 

238_N_1_0 = _float( -_1_0) # PYCHOK expected 

239_N_2_0 = _float( -_2_0) # PYCHOK expected 

240_N_90_0 = _float( -_90_0) # PYCHOK expected 

241_N_180_0 = _float(-_180_0) # PYCHOK expected 

242 

243_M_KM = _1000_0 # meter per Kilo meter, see .utily 

244_M_NM = _float(1852.0) # meter per Nautical Mile, exactly 

245_M_SM = _float(1609.344) # meter per Statute Mile 

246 

247try: 

248 from sys import float_info as _f_i 

249 # @see: <https://NumPy.org/doc/stable/reference/generated/numpy.finfo.html> 

250 DIG = Int( DIG =_f_i.dig) # PYCHOK system's float decimal digits 

251 EPS = _Float(EPS =_f_i.epsilon) # PYCHOK system's EPSilon 

252 MANT_DIG = Int( MANT_DIG=_f_i.mant_dig) # PYCHOK system's float mantissa bits 

253 MAX = _Float(MAX =_f_i.max) # PYCHOK system's MAX float 1.7976931348623157e+308 

254 MAX_EXP = Int( MAX_EXP =_f_i.max_exp) # PYTHON system's max base 2 exponent 

255 MIN = _Float(MIN =_f_i.min) # PYCHOK system's MIN float 2.2250738585072014e-308 

256 MIN_EXP = Int( MIN_EXP =_f_i.min_exp) # PYTHON system's min base 2 exponent 

257# RADIX = Int( RADIX =_f_i.radix) # PYTHON system's float base 

258 del _f_i 

259except ImportError: # PYCHOK no cover 

260 DIG = Int( DIG =15) # PYCHOK system's 64-bit float decimal digits 

261 EPS = _Float(EPS =2.220446049250313e-16) # PYCHOK EPSilon 2**-52, M{EPS +/- 1 != 1} 

262 MANT_DIG = Int( MANT_DIG=53) # PYCHOK float mantissa bits ≈ 53 (C{int}) 

263 MAX = _Float(MAX =pow(_2_0, 1023) * (_2_0 - EPS)) # PYCHOK ≈ 10**308 

264 MAX_EXP = Int( MAX_ESP =_log2(MAX)) # 308 base 10 

265 MIN = _Float(MIN =pow(_2_0, -1021)) # PYCHOK ≈ 10**-308 

266 MIN_EXP = Int(MIN_EXP =_log2(MIN)) # -307 base 10 

267# RADIX = Int(Radix =2) # base 

268 

269EPS0 = _Float( EPS0 = EPS**2) # PYCHOK near-/non-zero comparison 4.930381e-32, or EPS or EPS_2 

270EPS02 = _Float( EPS02 = EPS**4) # PYCHOK near-zero-squared comparison 2.430865e-63 

271EPS_2 = _Float( EPS_2 = EPS / _2_0) # PYCHOK ≈ 1.110223024625e-16 

272EPS1 = _Float( EPS1 =_1_0 - EPS) # PYCHOK ≈ 0.9999999999999998 

273EPS2 = _Float( EPS2 = EPS * _2_0) # PYCHOK ≈ 4.440892098501e-16 

274EPS4 = _Float( EPS4 = EPS * _4_0) # PYCHOK ≈ 8.881784197001e-16 

275EPS8 = _Float( EPS8 = EPS * _8_0) # PYCHOK ≈ 1.776356839400e-15 

276# _1EPS = _Float(_1EPS =_1_0 + EPS) # PYCHOK ≈ 1.0000000000000002 

277_1_EPS = _Float(_1_EPS =_1_0 / EPS) # PYCHOK = 4503599627370496.0 

278# _2_EPS = _Float(_2_EPS =_2_0 / EPS) # PYCHOK = 9007199254740992.0 

279_EPS2e4 = _Float(_EPS2e4 = EPS2 * 1.e4) # PYCHOK ≈ 4.440892098501e-12 

280_EPS4e8 = _Float(_EPS4e8 = EPS4 * 1.e8) # PYCHOK ≈ 8.881784197001e-08 

281_EPSjam = _Float(_EPSjam = pow(EPS, _0_75)) # PYCHOK = 1.818989403546e-12 

282_EPSmin = _Float(_EPSmin = sqrt(MIN)) # PYCHOK = 1.49166814624e-154 

283_EPSqrt = _Float(_EPSqrt = sqrt(EPS)) # PYCHOK = 1.490116119385e-08 

284_EPStol = _Float(_EPStol =_EPSqrt * _0_1) # PYCHOK = 1.490116119385e-09 == sqrt(EPS * _0_01) 

285 

286_89_999 = _Float(_89_999 =_90_0 * EPS1) # just below 90.0 

287# <https://Numbers.Computation.Free.FR/Constants/Miscellaneous/digits.html> 

288# _1__90 = _Float(_1__90 =_1_0 / _90_0) # PYCHOK = 0.011_111_111_111_111_111_111_111_111_111_111_111_111_111_111_11111 

289_2__PI = _Float(_2__PI =_2_0 / _pi) # PYCHOK = 0.636_619_772_367_581_343_075_535_053_490_057_448_137_838_582_96182 

290_K0_UTM = _Float(_K0_UTM = 0.9996) # PYCHOK in .etm, .ktm, .utm, UTM scale at central meridian 

291_K0_UPS = _Float(_K0_UPS = 0.994) # PYCHOK in .ups, scale factor at central meridian 

292OVERFLOW = _Float(OVERFLOW=_1_0 / EPS0) # PYCHOK = 2.028240960365e+31 

293_SQRT2 = _Float(_SQRT2 =sqrt(_2_0)) # PYCHOK <https://WikiPedia.org/wiki/Square_root_of_2> 

294# 1.414213562373095_048_801_688_724_209_698_078_569_671_875_376_948_073_176_679_737_99 

295# _1SQRT2= _Float(_1SQRT2 =sqrt(_2_0) + 1) 

296# 0.707106781186547_524_400_844_362_104_849_039_284_835_937_688_474_036_588_339_868_99 

297_SQRT2_2 = _Float(_SQRT2_2=sqrt(_0_5)) # PYCHOK = 0.707106781186547_5 == sqrt(2) / 2 

298# sqrt(3) <https://WikiPedia.org/wiki/Square_root_of_3> 

299# 1.732050807568877_293_527_446_341_505_872_366_942_805_253_810_380_628_055_806 

300_SQRT3 = _Float(_SQRT3 =sqrt(_3_0)) # PYCHOK = 1.732050807568877_2 == sqrt(3) 

301# 0.866025403784438_646_763_723_170_752_936_183_471_402_626_905_190_314_027_903 

302_SQRT3_2 = _Float(_SQRT3_2=sqrt(_0_75)) # PYCHOK = 0.866025403784438_6 == sqrt(3) / 2 

303 

304INF = Float(INF =_inf) # PYCHOK INFinity, see function L{isinf}, L{isfinite}, NOT _Float! 

305INT0 = Int( INT0= 0) # PYCHOK unique int(0) instance, see .fsums, useZ=False 

306NAN = Float(NAN =_nan) # PYCHOK Not-A-Number, see function L{isnan}, NOT _Float! 

307NEG0 = Float(NEG0=_N_0_0) # PYCHOK NEGative 0.0, see function L{isneg0}, NOT _Float! 

308NINF = Float(NINF=-INF) # PYCHOK Negative INFinity, NOT _Float! 

309 

310PI = _Float(PI =_pi) # 3.1415_9265_3589_7932_3846_2643_3832_795 

311PI2 = _Float(PI2 =_pi * _2_0) # PYCHOK Two PI, M{PI * 2} aka I{Tau} 

312PI_2 = _Float(PI_2 =_pi / _2_0) # PYCHOK Half PI, M{PI / 2} 

313PI3 = _Float(PI3 =_pi * _3_0) # PYCHOK Three PI, M{PI * 3} 

314PI3_2 = _Float(PI3_2=_pi * _1_5) # PYCHOK PI and a half, M{PI * 3 / 2} 

315PI_3 = _Float(PI_3 =_pi / _3_0) # PYCHOK One third PI, M{PI / 3} 

316PI4 = _Float(PI4 =_pi * _4_0) # PYCHOK Four PI, M{PI * 4} 

317PI_4 = _Float(PI_4 =_pi / _4_0) # PYCHOK Quarter PI, M{PI / 4} 

318PI_6 = _Float(PI_6 =_pi / _6_0) # PYCHOK One sixth PI, M{PI / 6} 

319 

320R_MA = _Radius(R_MA=6378137.0) # PYCHOK equatorial earth radius (C{meter}), WGS84, EPSG:3785 

321R_MB = _Radius(R_MB=6356752.3) # PYCHOK polar earth radius (C{meter}), WGS84, EPSG:3785 

322R_M = _Radius(R_M =6371008.771415) # PYCHOK mean, spherical earth radius (C{meter}) 

323R_KM = _Radius(R_KM=R_M / _M_KM) # PYCHOK mean, spherical earth radius (C{KM}, Kilo meter) 

324R_NM = _Radius(R_NM=R_M / _M_NM) # PYCHOK mean, spherical earth radius (C{NM}, nautical miles) 

325R_SM = _Radius(R_SM=R_M / _M_SM) # PYCHOK mean, spherical earth radius (C{SM}, statute miles) 

326# See <https://www.EdWilliams.org/avform.htm>, <https://www.DTIC.mil/dtic/tr/fulltext/u2/a216843.pdf> 

327# and <https://GitHub.com/NASA/MultiDop/blob/master/src/share/man/man3/geog_lib.3> based on the 

328# International Standard Nautical Mile of 1,852 meter (1' latitude) 

329R_FM = _Radius(R_FM=6371000.0) # PYCHOK former FAI Sphere earth radius (C{meter}) 

330R_GM = _Radius(R_GM=6371230.0) # PYCHOK avg. radius, distance to geoid surface (C{meter}) 

331# <http://Wiki.GIS.com/wiki/index.php/Ellipsoidal_quadratic_mean_radius> 

332R_QM = _Radius(R_QM=6372797.560856) # PYCHOK earth' quadratic mean radius (C{meter}) 

333# Rtri= _Radius(Rtri=6372797.5559594) # PYCHOK Rtriaxial quadratic mean radius (C{meter}), WGS84 

334# Rbi = _Radius(Rbi =6367453.6345163) # PYCHOK Rbiaxial quadratic mean radius (C{meter}), WGS84 

335R_VM = _Radius(R_VM=6366707.0194937) # PYCHOK aViation/naVigation earth radius (C{meter}) 

336# R_AU= Meter( R_AU=149597870700.0) # PYCHOK <https://WikiPedia.org/wiki/Astronomical_unit> 

337 

338_INF_NAN_NINF = {INF, NAN, NINF, _inf, _nan} 

339_pos_self = _1_0.__pos__() is _1_0 # PYCHOK in .fsums, .vector3dBase 

340_1_3rd = 1 / 3 # in .fmath 

341_2_3rd = 2 / 3 # in .fmath, .formy 

342 

343 

344def _0_0s(n): 

345 '''(INTERNAL) Return an C{B{n}-tuple} of C{_0_0} zeros. 

346 ''' 

347 return _0_0_9T[:n] if 0 <= n <= len(_0_0_9T) else (_0_0_1T * n) 

348 

349 

350try: 

351 from math import isclose as _isclose 

352except ImportError: # Python 3.4- 

353 

354 def _isclose(a, b, rel_tol=1e-9, abs_tol=0): 

355 '''Mimick Python 3.5+ C{math.isclose}. 

356 ''' 

357 t, d = abs_tol, fabs(a - b) 

358 if d > t: 

359 r = max(fabs(a), fabs(b)) * rel_tol 

360 t = max(r, t) 

361 return d <= t 

362 

363 

364def isclose(a, b, rel_tol=1e-12, abs_tol=EPS0): 

365 '''Like C{math.isclose}, but with defaults such 

366 that C{isclose(0, EPS0)} is C{True} by default. 

367 ''' 

368 return _isclose(a, b, rel_tol=rel_tol, abs_tol=abs_tol) 

369 

370 

371try: 

372 from cmath import isfinite as _iscfinite 

373except ImportError: # Python 3.1- 

374 

375 def _iscfinite(x): # PYCHOK not self? 

376 '''Mimick Python 3.2+ C{cmath.isfinite}. 

377 ''' 

378 return _isfinite(x.real) and _isfinite(x.imag) 

379 

380try: 

381 from math import isfinite as _isfinite # in .ellipsoids, ... 

382except ImportError: # Python 3.1- 

383 

384 def _isfinite(x): # PYCHOK not self? 

385 '''Mimick Python 3.2+ C{math.isfinite}. 

386 ''' 

387 return not (isinf(x) or isnan(x)) 

388 

389 

390def isfinite(obj): 

391 '''Check a finite C{scalar}, C{complex}, ... value. 

392 

393 @arg obj: Value (C{scalar}, C{complex}, an L{Fsum} or 

394 L{Fsum2Tuple}). 

395 

396 @return: C{False} if B{C{obj}} is C{INF}, C{NINF} or 

397 C{NAN}, C{True} otherwise. 

398 

399 @raise TypeError: Non-scalar and non-complex B{C{obj}}. 

400 ''' 

401 try: 

402 return (obj not in _INF_NAN_NINF) and _isfinite(obj) 

403 except Exception as x: 

404 if iscomplex(obj): # _isfinite(complex) thows TypeError 

405 return _iscfinite(obj) 

406 if _MODS.fsums._isFsum_2Tuple(obj): # OverflowError 

407 return obj.is_finite() 

408 raise _xError(x, Fmt.PAREN(typename(isfinite), obj)) 

409 

410 

411def isint0(obj, both=False): 

412 '''Check for L{INT0} or C{int(0)} value. 

413 

414 @arg obj: The object (any C{type}). 

415 @kwarg both: If C{true}, also check C{float(0)} (C{bool}). 

416 

417 @return: C{True} if B{C{obj}} is L{INT0}, C{int(0)} or 

418 C{float(0)}, C{False} otherwise. 

419 ''' 

420 return (obj is INT0 or obj is int(0) or 

421 bool(both and (not obj) and isint(obj, both=True))) and \ 

422 not isbool(obj) 

423 

424 

425def isnear0(x, eps0=EPS0): 

426 '''Is B{C{x}} near I{zero} within a tolerance? 

427 

428 @arg x: Value (C{scalar}). 

429 @kwarg eps0: Near-I{zero} tolerance (C{EPS0}). 

430 

431 @return: C{True} if C{abs(B{x}) < B{eps0}}, 

432 C{False} otherwise. 

433 

434 @see: Function L{isnon0}. 

435 ''' 

436 return bool(eps0 > x > -eps0) 

437 

438 

439def isnear1(x, eps1=EPS0): 

440 '''Is B{C{x}} near I{one} within a tolerance? 

441 

442 @arg x: Value (C{scalar}). 

443 @kwarg eps1: Near-I{one} tolerance (C{EPS0}). 

444 

445 @return: C{isnear0(B{x} - 1, eps0=B{eps1})}. 

446 

447 @see: Function L{isnear0}. 

448 ''' 

449 return bool(eps1 > (x - _1_0) > -eps1) 

450 

451 

452def isnear90(x, eps90=EPS0): 

453 '''Is B{C{x}} near I{90} within a tolerance? 

454 

455 @arg x: Value (C{scalar}). 

456 @kwarg eps90: Near-I{90} tolerance (C{EPS0}). 

457 

458 @return: C{isnear0(B{x} - 90, eps0=eps90)}. 

459 

460 @see: Function L{isnear0}. 

461 ''' 

462 return bool(eps90 > (x - _90_0) > -eps90) 

463 

464 

465def isneg(x): 

466 '''Check for negative C{x}, including L{NEG0}. 

467 

468 @arg x: Value (C{scalar}). 

469 

470 @return: C{True} if C{B{x} < 0 or NEG0}, 

471 C{False} otherwise. 

472 ''' 

473 return signBit(x) 

474 

475 

476def isneg0(x): 

477 '''Check for L{NEG0}, negative C{0.0}. 

478 

479 @arg x: Value (C{scalar}). 

480 

481 @return: C{True} if B{C{x}} is C{NEG0} or C{-0.0}, 

482 C{False} otherwise. 

483 ''' 

484 return (not x) and _copysign(1, x) < 0 

485# and str(x).startswith(_MINUS_) 

486 

487 

488def isninf(x): 

489 '''Check for L{NINF}, negative C{INF}. 

490 

491 @arg x: Value (C{scalar}). 

492 

493 @return: C{True} if B{C{x}} is C{NINF} or C{-inf}, 

494 C{False} otherwise. 

495 ''' 

496 return x is NINF or (x < 0 and not isfinite(x)) 

497 

498 

499def isnon0(x, eps0=EPS0): 

500 '''Is B{C{x}} non-zero with a tolerance? 

501 

502 @arg x: Value (C{scalar}). 

503 @kwarg eps0: Non-zero tolerance (C{EPS0}). 

504 

505 @return: C{True} if C{abs(B{x}) > B{eps0}}, 

506 C{False} otherwise. 

507 

508 @see: Function L{isnear0}. 

509 ''' 

510 return not bool(eps0 > x > -eps0) # not isnear0 

511 

512 

513def _off90(lat): 

514 '''(INTERNAL) Off 90.0 for .gars and .wgrs. 

515 ''' 

516 return max(min(lat, _89_999), -_89_999) 

517 

518 

519try: 

520 from math import remainder 

521except ImportError: # Python 3.6- 

522 from math import fmod as _fmod 

523 

524 def remainder(x, y): 

525 '''Mimick Python 3.7+ C{math.remainder}. 

526 ''' 

527 if isnan(y): 

528 x = NAN 

529 elif x and not isnan(x): 

530 y = fabs(y) 

531 x = _fmod(x, y) 

532 h = _0_5 * y 

533 if x >= h: 

534 x -= y 

535 elif x < -h: 

536 x += y 

537 return x # keep signed 0.0 

538 

539 

540def _umod_360(deg): 

541 '''(INTERNAL) Non-negative C{deg} modulo 360, basic C{.utily.wrap360}. 

542 ''' 

543 return (deg % _360_0) or _0_0 

544 

545 

546def _umod_PI2(rad): 

547 '''(INTERNAL) Non-negative C{rad} modulo PI2, basic C{.utily.wrapPI2}. 

548 ''' 

549 return (rad % PI2) or _0_0 

550 

551 

552if __name__ == _DMAIN_: 

553 

554 def _main(globalocals): 

555 from pygeodesy import itemsorted, printf 

556 from pygeodesy.interns import _DALL_, _UNDER_ 

557 

558 t = n = v = [] 

559 for n, v in itemsorted(globalocals): 

560 if isinstance(v, (Float, Int, Radius)): 

561 printf('%9s: %r', n, v.toRepr(std=False)) 

562 if v.name != n: 

563 raise AssertionError('%r != %r' % (n, v)) 

564 if v.name is not n: 

565 raise AssertionError('%r is not %r' % (n, v)) 

566 if not n.startswith(_UNDER_): 

567 t.append(n) 

568 t.append(typename(float_)) 

569 printf('%s = %r', _DALL_, tuple(t)) 

570 

571 _main(globals()) # or locals() 

572 

573# **) MIT License 

574# 

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

576# 

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

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

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

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

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

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

583# 

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

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

586# 

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

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

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

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

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

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

593# OTHER DEALINGS IN THE SOFTWARE.