pygeodesy 24.5.15__py2.py3-none-any.whl → 24.6.1__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.
Files changed (90) hide show
  1. {PyGeodesy-24.5.15.dist-info → PyGeodesy-24.6.1.dist-info}/METADATA +6 -5
  2. PyGeodesy-24.6.1.dist-info/RECORD +116 -0
  3. pygeodesy/__init__.py +4 -4
  4. pygeodesy/albers.py +41 -41
  5. pygeodesy/auxilats/__init__.py +1 -1
  6. pygeodesy/auxilats/__main__.py +2 -2
  7. pygeodesy/auxilats/auxAngle.py +32 -31
  8. pygeodesy/auxilats/auxLat.py +80 -51
  9. pygeodesy/azimuthal.py +123 -124
  10. pygeodesy/basics.py +46 -10
  11. pygeodesy/booleans.py +13 -14
  12. pygeodesy/cartesianBase.py +25 -23
  13. pygeodesy/clipy.py +3 -3
  14. pygeodesy/constants.py +3 -3
  15. pygeodesy/css.py +50 -42
  16. pygeodesy/datums.py +42 -41
  17. pygeodesy/deprecated/functions.py +9 -3
  18. pygeodesy/dms.py +6 -6
  19. pygeodesy/ecef.py +41 -41
  20. pygeodesy/ellipsoidalBase.py +41 -41
  21. pygeodesy/ellipsoidalBaseDI.py +3 -4
  22. pygeodesy/ellipsoidalGeodSolve.py +2 -2
  23. pygeodesy/ellipsoidalKarney.py +3 -3
  24. pygeodesy/ellipsoidalNvector.py +11 -12
  25. pygeodesy/ellipsoids.py +45 -38
  26. pygeodesy/elliptic.py +3 -4
  27. pygeodesy/epsg.py +4 -3
  28. pygeodesy/errors.py +52 -20
  29. pygeodesy/etm.py +68 -65
  30. pygeodesy/fmath.py +44 -49
  31. pygeodesy/formy.py +129 -115
  32. pygeodesy/frechet.py +118 -103
  33. pygeodesy/fstats.py +21 -14
  34. pygeodesy/fsums.py +124 -80
  35. pygeodesy/gars.py +10 -9
  36. pygeodesy/geodesicw.py +19 -17
  37. pygeodesy/geodesicx/__init__.py +1 -1
  38. pygeodesy/geodesicx/__main__.py +2 -2
  39. pygeodesy/geodesicx/gx.py +39 -33
  40. pygeodesy/geodesicx/gxarea.py +12 -9
  41. pygeodesy/geodesicx/gxbases.py +3 -4
  42. pygeodesy/geodesicx/gxline.py +6 -8
  43. pygeodesy/geodsolve.py +29 -28
  44. pygeodesy/geohash.py +60 -57
  45. pygeodesy/geoids.py +34 -32
  46. pygeodesy/hausdorff.py +114 -101
  47. pygeodesy/heights.py +137 -130
  48. pygeodesy/internals.py +16 -11
  49. pygeodesy/interns.py +3 -6
  50. pygeodesy/iters.py +19 -17
  51. pygeodesy/karney.py +21 -17
  52. pygeodesy/ktm.py +25 -18
  53. pygeodesy/latlonBase.py +12 -11
  54. pygeodesy/lazily.py +6 -6
  55. pygeodesy/lcc.py +24 -25
  56. pygeodesy/ltp.py +143 -113
  57. pygeodesy/ltpTuples.py +207 -150
  58. pygeodesy/mgrs.py +26 -26
  59. pygeodesy/named.py +172 -90
  60. pygeodesy/namedTuples.py +33 -25
  61. pygeodesy/nvectorBase.py +8 -8
  62. pygeodesy/osgr.py +40 -48
  63. pygeodesy/points.py +18 -18
  64. pygeodesy/props.py +29 -16
  65. pygeodesy/rhumb/__init__.py +1 -1
  66. pygeodesy/rhumb/aux_.py +13 -15
  67. pygeodesy/rhumb/bases.py +12 -5
  68. pygeodesy/rhumb/ekx.py +24 -18
  69. pygeodesy/rhumb/solve.py +13 -10
  70. pygeodesy/simplify.py +16 -16
  71. pygeodesy/solveBase.py +18 -18
  72. pygeodesy/sphericalBase.py +17 -21
  73. pygeodesy/sphericalTrigonometry.py +21 -21
  74. pygeodesy/streprs.py +5 -5
  75. pygeodesy/trf.py +13 -11
  76. pygeodesy/triaxials.py +68 -64
  77. pygeodesy/units.py +35 -35
  78. pygeodesy/unitsBase.py +24 -11
  79. pygeodesy/ups.py +66 -70
  80. pygeodesy/utily.py +3 -3
  81. pygeodesy/utm.py +183 -187
  82. pygeodesy/utmups.py +38 -38
  83. pygeodesy/utmupsBase.py +104 -106
  84. pygeodesy/vector2d.py +6 -7
  85. pygeodesy/vector3d.py +16 -17
  86. pygeodesy/vector3dBase.py +4 -5
  87. pygeodesy/webmercator.py +43 -51
  88. PyGeodesy-24.5.15.dist-info/RECORD +0 -116
  89. {PyGeodesy-24.5.15.dist-info → PyGeodesy-24.6.1.dist-info}/WHEEL +0 -0
  90. {PyGeodesy-24.5.15.dist-info → PyGeodesy-24.6.1.dist-info}/top_level.txt +0 -0
pygeodesy/epsg.py CHANGED
@@ -28,7 +28,7 @@ from pygeodesy.utmupsBase import _to3zBhp, _UPS_ZONE, _UTM_ZONE_MIN, \
28
28
  _UTM_ZONE_MAX, _UTMUPS_ZONE_INVALID
29
29
 
30
30
  __all__ = _ALL_LAZY.epsg
31
- __version__ = '22.09.24'
31
+ __version__ = '24.05.18'
32
32
 
33
33
  # _EPSG_INVALID = _UTMUPS_ZONE_INVALID
34
34
  _EPSG_N_01 = 32601 # EPSG code for UTM zone 01 N
@@ -49,11 +49,12 @@ class Epsg(Int):
49
49
  _utmups = None
50
50
  _zone = _UTMUPS_ZONE_INVALID
51
51
 
52
- def __new__(cls, eisu, name=NN):
52
+ def __new__(cls, eisu, **name):
53
53
  '''New L{Epsg} (I{European Petroleum Survey Group}) code from a
54
54
  UTM/USP coordinate or other EPSG code.
55
55
 
56
56
  @arg eisu: Other code (L{Epsg}, C{int}, C{str}, L{Utm} or L{Ups}).
57
+ @kwarg name: Optional C{B{name}=NN} (C{str}).
57
58
 
58
59
  @return: New L{Epsg}.
59
60
 
@@ -88,7 +89,7 @@ class Epsg(Int):
88
89
  self.name = u.name
89
90
 
90
91
  if name:
91
- self.name = name
92
+ self.rename(name)
92
93
  return self
93
94
 
94
95
  def __repr__(self):
pygeodesy/errors.py CHANGED
@@ -14,20 +14,23 @@ C{PYGEODESY_EXCEPTION_CHAINING=std} or to any non-empty string.
14
14
  # from pygeodesy.basics import isint, isodd, issubclassof, itemsorted, _xinstanceof, _zip # _MODS
15
15
  # from pygeodesy.ellipsoidalBase import CartesianEllipsoidalBase, LatLonEllipsoidalBase # _MODS
16
16
  # from pygeodesy import errors # _MODS, _MODS.getattr
17
- # from pygeodesy.internals import _tailof # from .lazily
17
+ from pygeodesy.internals import _plural, _tailof
18
18
  from pygeodesy.interns import MISSING, NN, _a_, _an_, _and_, _clip_, _COLON_, _COLONSPACE_, \
19
19
  _COMMASPACE_, _datum_, _ellipsoidal_, _incompatible_, _invalid_, \
20
- _len_, _not_, _or_, _SPACE_, _specified_, _UNDER_, _vs_, _with_
21
- from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _getenv, _PYTHON_X_DEV, _tailof
20
+ _keyword_, _len_, _not_, _or_, _SPACE_, _specified_, _UNDER_, \
21
+ _vs_, _with_
22
+ from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _getenv, _PYTHON_X_DEV
22
23
  # from pygeodesy.streprs import Fmt, unstr # _MODS
23
24
  # from pygeodesy.vector3dBase import Vector3dBase # _MODS
24
25
 
25
26
  from copy import copy as _copy
26
27
 
27
28
  __all__ = _ALL_LAZY.errors # _ALL_DOCS('_InvalidError', '_IsnotError') _under
28
- __version__ = '24.05.15'
29
+ __version__ = '24.05.29'
29
30
 
31
+ _argument_ = 'argument'
30
32
  _box_ = 'box'
33
+ _expected_ = 'expected'
31
34
  _limiterrors = True # in .formy
32
35
  _name_value_ = repr('name=value')
33
36
  _rangerrors = True # in .dms
@@ -142,6 +145,21 @@ def _TypesError(name, value, *Types, **kwds):
142
145
  return _TypeError(name, value, txt=t, **kwds)
143
146
 
144
147
 
148
+ class _UnexpectedError(TypeError): # note, a TypeError!
149
+ '''(INTERNAL) Format a C{TypeError} I{without exception chaining}.
150
+ '''
151
+ def __init__(self, *args, **kwds):
152
+ n = len(kwds)
153
+ if args:
154
+ a = _plural(_argument_, len(args))
155
+ n = _and(a, _plural(_keyword_, n)) if n else a
156
+ else:
157
+ n = _plural(_SPACE_(_keyword_, _argument_), n)
158
+ u = _MODS.streprs.unstr(_SPACE_(n, NN), *args, **kwds)
159
+ # _error_init(TypeError, self, (u,), txt_not_=_expected_)
160
+ TypeError.__init__(self, _SPACE_(u, _not_, _expected_))
161
+
162
+
145
163
  class _ValueError(ValueError):
146
164
  '''(INTERNAL) Format a C{ValueError} with/-out exception chaining.
147
165
  '''
@@ -234,7 +252,7 @@ class LenError(_ValueError): # in .ecef, .fmath, .heights, .iters, .named
234
252
  class LimitError(_ValueError):
235
253
  '''Error raised for lat- or longitudinal values or deltas exceeding
236
254
  the given B{C{limit}} in functions L{pygeodesy.equirectangular},
237
- L{pygeodesy.equirectangular_}, C{nearestOn*} and C{simplify*}
255
+ L{pygeodesy.equirectangular4}, C{nearestOn*} and C{simplify*}
238
256
  or methods with C{limit} or C{options} keyword arguments.
239
257
 
240
258
  @see: Subclass L{UnitError}.
@@ -375,8 +393,8 @@ def crosserrors(raiser=None):
375
393
  return t
376
394
 
377
395
 
378
- def _error_init(Error, inst, args, fmt_name_value='%s (%r)', txt=NN,
379
- cause=None, **kwds): # by .lazily
396
+ def _error_init(Error, inst, args, fmt_name_value='%s (%r)', txt_not_=NN,
397
+ txt__=None, txt=NN, cause=None, **kwds):
380
398
  '''(INTERNAL) Format an error text and initialize an C{Error} instance.
381
399
 
382
400
  @arg Error: The error super-class (C{Exception}).
@@ -388,6 +406,8 @@ def _error_init(Error, inst, args, fmt_name_value='%s (%r)', txt=NN,
388
406
  given as C{name=value} keyword arguments.
389
407
  @kwarg fmt_name_value: Format for (name, value) (C{str}).
390
408
  @kwarg txt: Optional explanation of the error (C{str}).
409
+ @kwarg txt__: Alternate C{B{txt}=B{txt__}.__name__}.
410
+ @kwarg txt_not_: Negative explanation C{B{txt}=_not_(B{txt_not_})}.
391
411
  @kwarg cause: Optional, caught error (L{Exception}), for
392
412
  exception chaining (supported in Python 3+).
393
413
  @kwarg kwds: Additional C{B{name}=value} pairs, if any.
@@ -409,8 +429,10 @@ def _error_init(Error, inst, args, fmt_name_value='%s (%r)', txt=NN,
409
429
  t += _fmtuple(_MODS.basics.itemsorted(kwds))
410
430
  t = _or(*t) if t else _SPACE_(_name_value_, MISSING)
411
431
 
412
- if txt is not None:
413
- x = str(txt) or (str(cause) if cause else _invalid_)
432
+ x = _not_(txt_not_) if txt_not_ else (txt if txt__ is None
433
+ else txt__.__name__)
434
+ if x is not None:
435
+ x = str(x) or (str(cause) if cause else _invalid_)
414
436
  C = _COMMASPACE_ if _COLON_ in t else _COLONSPACE_
415
437
  t = C(t, x)
416
438
  # else: # LenError, _xzip, .dms, .heights, .vector2d
@@ -599,7 +621,7 @@ def _xcallable(**names_callables):
599
621
  '''
600
622
  for n, c in names_callables.items():
601
623
  if not callable(c):
602
- raise _TypeError(n, c, txt=_not_(callable.__name__))
624
+ raise _TypeError(n, c, txt_not_=callable.__name__) # txt__
603
625
 
604
626
 
605
627
  def _xdatum(datum1, datum2, Error=None):
@@ -625,7 +647,7 @@ def _xellipsoidal(**name_value): # see _xellipsoidall elel
625
647
  return v
626
648
  except AttributeError:
627
649
  pass
628
- raise _TypeError(n, v, txt=_not_(_ellipsoidal_))
650
+ raise _TypeError(n, v, txt_not_=_ellipsoidal_)
629
651
  raise _xAssertionError(_xellipsoidal, name_value)
630
652
 
631
653
 
@@ -716,7 +738,7 @@ except AttributeError:
716
738
  # b = getattr(inst, n, None)
717
739
  # if b is None: # invalid bool attr
718
740
  # t = _SPACE_(_EQUAL_(n, repr(v)), 'for', inst.__class__.__name__) # XXX .classname
719
- # raise _AttributeError(t, txt=_not_('applicable'))
741
+ # raise _AttributeError(t, txt_not_='applicable')
720
742
  # if v in (False, True) and v != b:
721
743
  # setattr(inst, NN(_UNDER_, n), v)
722
744
 
@@ -726,8 +748,8 @@ def _xkwds_get(kwds, **name_default):
726
748
  C{default} if not present.
727
749
  '''
728
750
  if isinstance(kwds, dict) and len(name_default) == 1:
729
- for n, d in name_default.items():
730
- return kwds.get(n, d)
751
+ for n, v in name_default.items():
752
+ return kwds.get(n, v)
731
753
  raise _xAssertionError(_xkwds_get, kwds, **name_default)
732
754
 
733
755
 
@@ -737,8 +759,18 @@ def _xkwds_get_(kwds, **names_defaults):
737
759
  '''
738
760
  if not isinstance(kwds, dict):
739
761
  raise _xAssertionError(_xkwds_get_, kwds)
740
- for n, d in _MODS.basics.itemsorted(names_defaults):
741
- yield kwds.get(n, d)
762
+ for n, v in _MODS.basics.itemsorted(names_defaults):
763
+ yield kwds.get(n, v)
764
+
765
+
766
+ def _xkwds_get1(kwds, **name_default):
767
+ '''(INTERNAL) Get one C{kwds} value by C{name} or the
768
+ C{default} if not present.
769
+ '''
770
+ v, kwds = _xkwds_pop2(kwds, **name_default)
771
+ if kwds:
772
+ raise _UnexpectedError(**kwds)
773
+ return v
742
774
 
743
775
 
744
776
  def _xkwds_item2(kwds):
@@ -762,11 +794,11 @@ def _xkwds_pop2(kwds, **name_default):
762
794
  reduced C{kwds} copy, otherwise the C{default} and original C{kwds}.
763
795
  '''
764
796
  if isinstance(kwds, dict) and len(name_default) == 1:
765
- for n, d in name_default.items():
797
+ for n, v in name_default.items():
766
798
  if n in kwds:
767
799
  kwds = _copy(kwds)
768
- d = kwds.pop(n, d)
769
- return d, kwds
800
+ v = kwds.pop(n, v)
801
+ return v, kwds
770
802
  raise _xAssertionError(_xkwds_pop2, kwds, **name_default)
771
803
 
772
804
 
@@ -777,7 +809,7 @@ def _Xorder(_Coeffs, Error, **Xorder): # in .auxLat, .ktm, .rhumb.bases, .rhumb
777
809
  if m in _Coeffs and _MODS.basics.isint(m):
778
810
  return m
779
811
  t = sorted(map(str, _Coeffs.keys()))
780
- raise Error(X, m, txt=_not_(_or(*t)))
812
+ raise Error(X, m, txt_not_=_or(*t))
781
813
 
782
814
  # **) MIT License
783
815
  #
pygeodesy/etm.py CHANGED
@@ -71,10 +71,10 @@ from pygeodesy.datums import _ellipsoidal_datum, _WGS84, _EWGS84
71
71
  # from pygeodesy.ellipsoids import _EWGS84 # from .datums
72
72
  from pygeodesy.elliptic import _ALL_LAZY, Elliptic
73
73
  # from pygeodesy.errors import _incompatible # from .named
74
- from pygeodesy.fmath import cbrt, hypot, hypot1, hypot2
75
- from pygeodesy.fsums import Fsum, fsum1f_
76
- from pygeodesy.interns import NN, _COMMASPACE_, _DASH_, _near_, _SPACE_, \
77
- _spherical_
74
+ # from pygeodesy.fsums import Fsum # from .fmath
75
+ from pygeodesy.fmath import cbrt, hypot, hypot1, hypot2, Fsum
76
+ from pygeodesy.interns import _COMMASPACE_, _DASH_, _near_, _SPACE_, \
77
+ _spherical_
78
78
  from pygeodesy.karney import _copyBit, _diff182, _fix90, _norm2, _norm180, \
79
79
  _tand, _unsigned2
80
80
  # from pygeodesy.lazily import _ALL_LAZY # from .elliptic
@@ -92,7 +92,7 @@ from pygeodesy.utm import _cmlon, _LLEB, _parseUTM5, _toBand, _toXtm8, \
92
92
  from math import asinh, atan2, degrees, radians, sinh, sqrt
93
93
 
94
94
  __all__ = _ALL_LAZY.etm
95
- __version__ = '24.05.13'
95
+ __version__ = '24.05.31'
96
96
 
97
97
  _OVERFLOW = _1_EPS**2 # about 2e+31
98
98
  _TAYTOL = pow(EPS, 0.6)
@@ -101,12 +101,6 @@ _TOL_10 = EPS * _0_1
101
101
  _TRIPS = 21 # C++ 10
102
102
 
103
103
 
104
- def _overflow(x):
105
- '''(INTERNAL) Like C{copysign0(OVERFLOW, B{x})}.
106
- '''
107
- return _copyBit(_OVERFLOW, x)
108
-
109
-
110
104
  class ETMError(UTMError):
111
105
  '''Exact Transverse Mercator (ETM) parse, projection or other
112
106
  L{Etm} issue or L{ExactTransverseMercator} conversion failure.
@@ -157,23 +151,21 @@ class Etm(Utm):
157
151
  self._exactTM = exactTM
158
152
  self._scale0 = exactTM.k0
159
153
 
160
- def parse(self, strETM, name=NN):
154
+ def parse(self, strETM, **name):
161
155
  '''Parse a string to a similar L{Etm} instance.
162
156
 
163
- @arg strETM: The ETM coordinate (C{str}),
164
- see function L{parseETM5}.
165
- @kwarg name: Optional instance name (C{str}),
166
- overriding this name.
157
+ @arg strETM: The ETM coordinate (C{str}), see function L{parseETM5}.
158
+ @kwarg name: Optional C{B{name}=NN} (C{str}), overriding this name.
167
159
 
168
160
  @return: The instance (L{Etm}).
169
161
 
170
162
  @raise ETMError: Invalid B{C{strETM}}.
171
163
 
172
- @see: Function L{pygeodesy.parseUPS5}, L{pygeodesy.parseUTM5}
173
- and L{pygeodesy.parseUTMUPS5}.
164
+ @see: Function L{pygeodesy.parseUPS5}, L{pygeodesy.parseUTM5} and
165
+ L{pygeodesy.parseUTMUPS5}.
174
166
  '''
175
167
  return parseETM5(strETM, datum=self.datum, Etm=self.classof,
176
- name=name or self.name)
168
+ name=self._name__(name))
177
169
 
178
170
  @deprecated_method
179
171
  def parseETM(self, strETM): # PYCHOK no cover
@@ -217,9 +209,7 @@ class Etm(Utm):
217
209
  lat, lon, g, k = xTM.reverse(e, n, lon0=lon0)
218
210
 
219
211
  ll = _LLEB(lat, lon, datum=d, name=self.name) # utm._LLEB
220
- ll._gamma = g
221
- ll._scale = k
222
- self._latlon5args(ll, _toBand, unfalse, xTM)
212
+ self._latlon5args(ll, g, k, _toBand, unfalse, xTM)
223
213
 
224
214
  def toUtm(self): # PYCHOK signature
225
215
  '''Copy this ETM to a UTM coordinate.
@@ -247,7 +237,7 @@ class ExactTransverseMercator(_NamedBase):
247
237
  _sigmaC = None # most recent _sigmaInv04 case C{int}
248
238
  _zetaC = None # most recent _zetaInv04 case C{int}
249
239
 
250
- def __init__(self, datum=_WGS84, lon0=0, k0=_K0_UTM, extendp=False, name=NN, raiser=False):
240
+ def __init__(self, datum=_WGS84, lon0=0, k0=_K0_UTM, extendp=False, raiser=False, **name):
251
241
  '''New L{ExactTransverseMercator} projection.
252
242
 
253
243
  @kwarg datum: The I{non-spherical} datum or ellipsoid (L{Datum},
@@ -255,8 +245,8 @@ class ExactTransverseMercator(_NamedBase):
255
245
  @kwarg lon0: Central meridian, default (C{degrees180}).
256
246
  @kwarg k0: Central scale factor (C{float}).
257
247
  @kwarg extendp: Use the I{extended} domain (C{bool}), I{standard} otherwise.
258
- @kwarg name: Optional name for the projection (C{str}).
259
248
  @kwarg raiser: If C{True}, throw an L{ETMError} for convergence failures (C{bool}).
249
+ @kwarg name: Optional C{B{name}=NN} for the projection (C{str}).
260
250
 
261
251
  @raise ETMError: Near-spherical B{C{datum}} or C{ellipsoid} or invalid B{C{lon0}}
262
252
  or B{C{k0}}.
@@ -444,14 +434,14 @@ class ExactTransverseMercator(_NamedBase):
444
434
 
445
435
  f = flattening
446
436
 
447
- def forward(self, lat, lon, lon0=None, name=NN): # MCCABE 13
437
+ def forward(self, lat, lon, lon0=None, **name): # MCCABE 13
448
438
  '''Forward projection, from geographic to transverse Mercator.
449
439
 
450
440
  @arg lat: Latitude of point (C{degrees}).
451
441
  @arg lon: Longitude of point (C{degrees}).
452
442
  @kwarg lon0: Central meridian (C{degrees180}), overriding
453
443
  the default if not C{None}.
454
- @kwarg name: Optional name (C{str}).
444
+ @kwarg name: Optional C{B{name}=NN} (C{str}).
455
445
 
456
446
  @return: L{Forward4Tuple}C{(easting, northing, gamma, scale)}.
457
447
 
@@ -500,7 +490,7 @@ class ExactTransverseMercator(_NamedBase):
500
490
  if _lon:
501
491
  x, g = neg_(x, g)
502
492
  return Forward4Tuple(x, y, g, k, iteration=self._iteration,
503
- name=name or self.name)
493
+ name=self._name__(name))
504
494
 
505
495
  def _Inv03(self, psi, dlam, _3_mv_e): # (xi, deta, _3_mv)
506
496
  '''(INTERNAL) Partial C{_zetaInv04} or C{_sigmaInv04}, Case 2
@@ -609,6 +599,7 @@ class ExactTransverseMercator(_NamedBase):
609
599
  _U_2 = Fsum(u).fsum2f_
610
600
  _V_2 = Fsum(v).fsum2f_
611
601
  # min iterations 2, max 6 or 7, mean 3.9 or 4.0
602
+ _hy2 = hypot2
612
603
  for i in range(1, _TRIPS): # GEOGRAPHICLIB_PANIC
613
604
  sncndn6 = self._sncndn6(u, v)
614
605
  du, dv = _zetaDwd2(*sncndn6)
@@ -620,7 +611,7 @@ class ExactTransverseMercator(_NamedBase):
620
611
  if d2 < tol2:
621
612
  r = False
622
613
  break
623
- d2 = hypot2(dU, dV)
614
+ d2 = _hy2(dU, dV)
624
615
 
625
616
  self._iteration = i
626
617
  if r: # PYCHOK no cover
@@ -680,14 +671,14 @@ class ExactTransverseMercator(_NamedBase):
680
671
  self._mu = mu
681
672
  self._mv = mv
682
673
 
683
- def reverse(self, x, y, lon0=None, name=NN):
674
+ def reverse(self, x, y, lon0=None, **name):
684
675
  '''Reverse projection, from Transverse Mercator to geographic.
685
676
 
686
677
  @arg x: Easting of point (C{meters}).
687
678
  @arg y: Northing of point (C{meters}).
688
- @kwarg lon0: Central meridian (C{degrees180}), overriding
689
- the default if not C{None}.
690
- @kwarg name: Optional name (C{str}).
679
+ @kwarg lon0: Optional central meridian (C{degrees180}),
680
+ overriding the default (C{iff not None}).
681
+ @kwarg name: Optional C{B{name}=NN} (C{str}).
691
682
 
692
683
  @return: L{Reverse4Tuple}C{(lat, lon, gamma, scale)}.
693
684
 
@@ -730,9 +721,9 @@ class ExactTransverseMercator(_NamedBase):
730
721
  lon, g = neg_(lon, g)
731
722
  lat += self._lat0
732
723
  lon += self._lon0 if lon0 is None else _norm180(lon0)
733
- return Reverse4Tuple(lat, _norm180(lon), g, k, # _norm180(lat)
724
+ return Reverse4Tuple(lat, _norm180(lon), g, k, # _fix90(lat)
734
725
  iteration=self._iteration,
735
- name=name or self.name)
726
+ name=self._name__(name))
736
727
 
737
728
  def _scaled2(self, tau, d2, snu, cnu, dnu, snv, cnv, dnv):
738
729
  '''(INTERNAL) C{scaled}.
@@ -768,7 +759,7 @@ class ExactTransverseMercator(_NamedBase):
768
759
  # = sqrt(mv + mv * tau**2 + mu) * sqrt(q2)
769
760
  k, q2 = _0_0, (mv * snv**2 + cnudnv**2)
770
761
  if q2 > 0:
771
- k2 = fsum1f_(mu, mv, mv * tau**2)
762
+ k2 = (tau**2 + _1_0) * mv + mu
772
763
  if k2 > 0:
773
764
  k = sqrt(k2) * sqrt(q2 / d2) * self.k0
774
765
  else:
@@ -811,13 +802,14 @@ class ExactTransverseMercator(_NamedBase):
811
802
  real /*v*/, real snv, real cnv, real dnv,
812
803
  real &du, real &dv)}.
813
804
  '''
805
+ mu = self._mu
814
806
  snuv = snu * snv
815
807
  # Reciprocal of 55.9: dw / ds = dn(w)^2/_mv,
816
808
  # expanding complex dn(w) using A+S 16.21.4
817
- d = self._mv * (cnv**2 + self._mu * snuv**2)**2
818
- r = cnv * dnu * dnv
819
- i = cnu * snuv * self._mu
820
- du = (r**2 - i**2) / d
809
+ d = (cnv**2 + snuv**2 * mu)**2 * self._mv
810
+ r = cnv * dnu * dnv
811
+ i = cnu * snuv * mu
812
+ du = (r**2 - i**2) / d # (r + i) * (r - i) / d
821
813
  dv = neg(r * i * _2_0 / d)
822
814
  return du, dv
823
815
 
@@ -1030,7 +1022,13 @@ class ExactTransverseMercator(_NamedBase):
1030
1022
  return g_k # or (g, k, lat, lon)
1031
1023
 
1032
1024
 
1033
- def parseETM5(strUTM, datum=_WGS84, Etm=Etm, falsed=True, name=NN):
1025
+ def _overflow(x):
1026
+ '''(INTERNAL) Like C{copysign0(OVERFLOW, B{x})}.
1027
+ '''
1028
+ return _copyBit(_OVERFLOW, x)
1029
+
1030
+
1031
+ def parseETM5(strUTM, datum=_WGS84, Etm=Etm, falsed=True, **name):
1034
1032
  '''Parse a string representing a UTM coordinate, consisting
1035
1033
  of C{"zone[band] hemisphere easting northing"}.
1036
1034
 
@@ -1040,7 +1038,7 @@ def parseETM5(strUTM, datum=_WGS84, Etm=Etm, falsed=True, name=NN):
1040
1038
  @kwarg Etm: Optional class to return the UTM coordinate
1041
1039
  (L{Etm}) or C{None}.
1042
1040
  @kwarg falsed: Both easting and northing are C{falsed} (C{bool}).
1043
- @kwarg name: Optional B{C{Etm}} name (C{str}).
1041
+ @kwarg name: Optional B{C{Etm}} C{B{name}=NN} (C{str}).
1044
1042
 
1045
1043
  @return: The UTM coordinate (B{C{Etm}}) or if B{C{Etm}} is
1046
1044
  C{None}, a L{UtmUps5Tuple}C{(zone, hemipole, easting,
@@ -1051,29 +1049,30 @@ def parseETM5(strUTM, datum=_WGS84, Etm=Etm, falsed=True, name=NN):
1051
1049
 
1052
1050
  @raise TypeError: Invalid or near-spherical B{C{datum}}.
1053
1051
  '''
1054
- r = _parseUTM5(strUTM, datum, Etm, falsed, Error=ETMError, name=name)
1052
+ r = _parseUTM5(strUTM, datum, Etm, falsed, Error=ETMError, **name)
1055
1053
  return r
1056
1054
 
1057
1055
 
1058
1056
  def toEtm8(latlon, lon=None, datum=None, Etm=Etm, falsed=True,
1059
- name=NN, strict=True,
1060
- zone=None, **cmoff):
1057
+ strict=True, zone=None, **name_cmoff):
1061
1058
  '''Convert a geodetic lat-/longitude to an ETM coordinate.
1062
1059
 
1063
1060
  @arg latlon: Latitude (C{degrees}) or an (ellipsoidal)
1064
1061
  geodetic C{LatLon} instance.
1065
- @kwarg lon: Optional longitude (C{degrees}) or C{None}.
1062
+ @kwarg lon: Optional longitude (C{degrees}), required
1063
+ if B{C{latlon}} is in C{degrees}.
1066
1064
  @kwarg datum: Optional datum for the ETM coordinate,
1067
1065
  overriding B{C{latlon}}'s datum (L{Datum},
1068
1066
  L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}).
1069
1067
  @kwarg Etm: Optional class to return the ETM coordinate
1070
1068
  (L{Etm}) or C{None}.
1071
1069
  @kwarg falsed: False both easting and northing (C{bool}).
1072
- @kwarg name: Optional B{C{Utm}} name (C{str}).
1073
1070
  @kwarg strict: Restrict B{C{lat}} to UTM ranges (C{bool}).
1074
1071
  @kwarg zone: Optional UTM zone to enforce (C{int} or C{str}).
1075
- @kwarg cmoff: DEPRECATED, use B{C{falsed}}. Offset longitude
1076
- from the zone's central meridian (C{bool}).
1072
+ @kwarg name_cmoff: Optional B{C{Etm}} C{B{name}=NN} (C{str})
1073
+ and DEPRECATED keyword argument C{B{cmoff}=True}
1074
+ to offset the longitude from the zone's central
1075
+ meridian (C{bool}), use B{C{falsed}} instead.
1077
1076
 
1078
1077
  @return: The ETM coordinate as an B{C{Etm}} instance or a
1079
1078
  L{UtmUps8Tuple}C{(zone, hemipole, easting, northing,
@@ -1097,24 +1096,25 @@ def toEtm8(latlon, lon=None, datum=None, Etm=Etm, falsed=True,
1097
1096
  @raise ValueError: The B{C{lon}} value is missing or B{C{latlon}}
1098
1097
  is invalid.
1099
1098
  '''
1100
- z, B, lat, lon, d, f, name = _to7zBlldfn(latlon, lon, datum,
1101
- falsed, name, zone,
1102
- strict, ETMError, **cmoff)
1099
+ z, B, lat, lon, d, f, n = _to7zBlldfn(latlon, lon, datum,
1100
+ falsed, zone, strict,
1101
+ ETMError, **name_cmoff)
1103
1102
  lon0 = _cmlon(z) if f else None
1104
1103
  x, y, g, k = d.exactTM.forward(lat, lon, lon0=lon0)
1105
1104
 
1106
1105
  return _toXtm8(Etm, z, lat, x, y, B, d, g, k, f,
1107
- name, latlon, d.exactTM, Error=ETMError)
1106
+ n, latlon, d.exactTM, Error=ETMError)
1108
1107
 
1109
1108
 
1110
1109
  if __name__ == '__main__': # MCCABE 13
1111
1110
 
1112
- from pygeodesy.internals import _usage
1113
1111
  from pygeodesy import fstr, KTransverseMercator, printf
1112
+ from pygeodesy.internals import _usage
1114
1113
  from sys import argv, exit as _exit
1115
1114
 
1116
1115
  # mimick some of I{Karney}'s utility C{TransverseMercatorProj}
1117
1116
  _f = _r = _s = _t = False
1117
+ _p = -6
1118
1118
  _as = argv[1:]
1119
1119
  while _as and _as[0].startswith(_DASH_):
1120
1120
  _a = _as.pop(0)
@@ -1124,16 +1124,19 @@ if __name__ == '__main__': # MCCABE 13
1124
1124
  _f, _r = True, False
1125
1125
  elif '-reverse'.startswith(_a):
1126
1126
  _f, _r = False, True
1127
+ elif '-precision'.startswith(_a):
1128
+ _p = int(_as.pop(0))
1127
1129
  elif '-series'.startswith(_a):
1128
1130
  _s, _t = True, False
1129
1131
  elif _a == '-t':
1130
1132
  _s, _t = False, True
1131
1133
  elif '-help'.startswith(_a):
1132
- _exit(_usage(argv[0], '[-s | -t]',
1134
+ _exit(_usage(argv[0], '[-s | -t ]',
1135
+ '[-p[recision] <ndigits>',
1133
1136
  '[-f[orward] <lat> <lon>',
1134
- '| -r[everse] <easting> <northing>',
1135
- '| <lat> <lon>]',
1136
- '| -h[elp]'))
1137
+ '|-r[everse] <easting> <northing>',
1138
+ '|<lat> <lon>]',
1139
+ '|-h[elp]'))
1137
1140
  else:
1138
1141
  _exit('%s: option %r not supported' % (_usage(*argv), _a))
1139
1142
  if len(_as) > 1:
@@ -1152,18 +1155,18 @@ if __name__ == '__main__': # MCCABE 13
1152
1155
  t = tm.reverse(*f2)
1153
1156
  else:
1154
1157
  t = tm.forward(*f2)
1155
- printf('%s: %s', tm.classname, fstr(t, sep=_SPACE_))
1158
+ printf('%s: %s', tm.classname, fstr(t, prec=_p, sep=_SPACE_))
1156
1159
  t = tm.reverse(t.easting, t.northing)
1157
- printf('%s: %s', tm.classname, fstr(t, sep=_SPACE_))
1160
+ printf('%s: %s', tm.classname, fstr(t, prec=_p, sep=_SPACE_))
1158
1161
 
1159
1162
 
1160
- # % python3 -m pygeodesy.etm 33.33 44.44
1161
- # ExactTransverseMercator: 4276926.114804 4727193.767015 28.375537 1.233325
1162
- # ExactTransverseMercator: 33.33 44.44 28.375537 1.233325
1163
+ # % python3 -m pygeodesy.etm -p 12 33.33 44.44
1164
+ # ExactTransverseMercator: 4276926.11480390653 4727193.767015309073 28.375536563148 1.233325101778
1165
+ # ExactTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
1163
1166
 
1164
- # % python3 -m pygeodesy.etm -s 33.33 44.44
1165
- # KTransverseMercator: 4276926.114804 4727193.767015 28.375537 1.233325
1166
- # KTransverseMercator: 33.33 44.44 28.375537 1.233325
1167
+ # % python3 -m pygeodesy.etm -s -p 12 33.33 44.44
1168
+ # KTransverseMercator: 4276926.114803904667 4727193.767015310004 28.375536563148 1.233325101778
1169
+ # KTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
1167
1170
 
1168
1171
  # % echo 33.33 44.44 | .../bin/TransverseMercatorProj
1169
1172
  # 4276926.114804 4727193.767015 28.375536563148 1.233325101778