Coverage for pygeodesy/constants.py: 97%

1

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

3

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

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

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

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

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

9'''

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

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

12

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

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

15# from pygeodesy.fsums import _isFsum_2Tuple # _MODS

16 from pygeodesy.internals import _0_0, _100_0, typename

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

18 from pygeodesy.lazily import _ALL_MODS as _MODS, _ALL_LAZY

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

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

21

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

23 try:

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

25 except ImportError: # Python 2-

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

27

28 __all__ = _ALL_LAZY.constants

29 __version__ = '26.02.09'

30

31

32 def _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

38 def _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

44 def _copysignINF(y):

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

46 '''

47 return NINF if signBit(y) else INF

48

49

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

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

52 '''

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

54

55

56 def _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

63 def _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

70 def 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

93 def 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

100 def _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

113 def 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

135 def _floatuple(*fs):

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

137 '''

138 return tuple(map(_float, fs))

139

140

141 try:

142 from math import log2 as _log2

143 except 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

150 def _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

158 def _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

167 def _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

179 def _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

188 def _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

247 try:

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

259 except 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

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

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

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

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

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

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

275 EPS8 = _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

292 OVERFLOW = _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

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

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

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

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

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

309

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

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

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

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

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

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

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

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

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

319

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

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

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

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

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

325 R_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)

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

330 R_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>

332 R_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

335 R_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

344 def _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

350 try:

351 from math import isclose as _isclose

352 except 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

364 def 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

371 try:

372 from cmath import isfinite as _iscfinite

373 except 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

380 try:

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

382 except 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

390 def 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

411 def 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

425 def 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

439 def 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

452 def 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

465 def 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

476 def 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

488 def 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

499 def 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

513 def _off90(lat):

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

515 '''

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

517

518

519 try:

520 from math import remainder

521 except 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

540 def _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

546 def _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

552 if __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.