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/utm.py CHANGED
@@ -35,17 +35,16 @@ and Henrik Seidel U{'Die Mathematik der Gauß-Krueger-Abbildung'
35
35
 
36
36
  from pygeodesy.basics import len2, map2, neg # splice
37
37
  from pygeodesy.constants import EPS, EPS0, _K0_UTM, _0_0, _0_0001
38
- from pygeodesy.datums import _ellipsoidal_datum, _WGS84
38
+ from pygeodesy.datums import _ellipsoidal_datum, _WGS84, _under
39
39
  from pygeodesy.dms import degDMS, parseDMS2
40
40
  from pygeodesy.errors import MGRSError, RangeError, _ValueError, \
41
- _xkwds_get
41
+ _xkwds_get, _xkwds_pop2
42
42
  from pygeodesy.fmath import fdot3, hypot, hypot1, _operator
43
- # from pygeodesy.internals import _under # from .utmupsBase
43
+ # from pygeodesy.internals import _under # from .datums
44
44
  from pygeodesy.interns import MISSING, NN, _by_, _COMMASPACE_, _N_, \
45
45
  _NS_, _outside_, _range_, _S_, _scale0_, \
46
46
  _SPACE_, _UTM_, _V_, _X_, _zone_
47
47
  from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS
48
- # from pygeodesy.named import _xnamed # from .utmupsBase
49
48
  from pygeodesy.namedTuples import EasNor2Tuple, UtmUps5Tuple, \
50
49
  UtmUps8Tuple, UtmUpsLatLon5Tuple
51
50
  from pygeodesy.props import deprecated_method, property_doc_, \
@@ -55,8 +54,8 @@ from pygeodesy.units import Band, Int, Lat, Lon, Meter, Zone
55
54
  from pygeodesy.utily import atan1, degrees90, degrees180, sincos2
56
55
  from pygeodesy.utmupsBase import _hemi, _LLEB, _parseUTMUPS5, _to4lldn, \
57
56
  _to3zBhp, _to3zll, _UPS_LATS, _UPS_ZONE, \
58
- _UTM_LAT_MAX, _UTM_ZONE_MAX, _under, \
59
- _UTM_LAT_MIN, _UTM_ZONE_MIN, _xnamed, \
57
+ _UTM_LAT_MAX, _UTM_ZONE_MAX, \
58
+ _UTM_LAT_MIN, _UTM_ZONE_MIN, \
60
59
  _UTM_ZONE_OFF_MAX, UtmUpsBase
61
60
 
62
61
  from math import asinh, atanh, atan2, cos, cosh, degrees, fabs, \
@@ -64,7 +63,7 @@ from math import asinh, atanh, atan2, cos, cosh, degrees, fabs, \
64
63
  # import operator as _operator # from .fmath
65
64
 
66
65
  __all__ = _ALL_LAZY.utm
67
- __version__ = '24.02.29'
66
+ __version__ = '24.05.31'
68
67
 
69
68
  _Bands = 'CDEFGHJKLMNPQRSTUVWXX' # UTM latitude bands C..X (no
70
69
  # I|O) 8° each, covering 80°S to 84°N and X repeated for 80-84°N
@@ -134,120 +133,6 @@ class _Kseries(object):
134
133
  return fdot3(self._pq, self._sy, self._shx, start=q0)
135
134
 
136
135
 
137
- def _cmlon(zone):
138
- '''(INTERNAL) Central meridian longitude (C{degrees180}).
139
- '''
140
- return (zone * 6) - 183
141
-
142
-
143
- def _false2(e, n, h):
144
- '''(INTERNAL) False easting and northing.
145
- '''
146
- # Karney, "Test data for the transverse Mercator projection (2009)"
147
- # <https://GeographicLib.SourceForge.io/C++/doc/transversemercator.html>
148
- # and <https://Zenodo.org/record/32470#.W4LEJS2ZON8>
149
- e += _FalseEasting # make e relative to central meridian
150
- if h == _S_:
151
- n += _FalseNorthing # make n relative to equator
152
- return e, n
153
-
154
-
155
- def _toBand(lat, *unused, **strict_Error): # see ups._toBand
156
- '''(INTERNAL) Get the I{latitudinal} Band (row) letter.
157
- '''
158
- if _UTM_LAT_MIN <= lat < _UTM_LAT_MAX: # [-80, 84) like Veness
159
- return _Bands[int(lat - _UTM_LAT_MIN) >> 3]
160
- elif _xkwds_get(strict_Error, strict=True):
161
- r = _range_(_UTM_LAT_MIN, _UTM_LAT_MAX, ropen=True)
162
- t = _SPACE_(_outside_, _UTM_, _range_, r)
163
- E = _xkwds_get(strict_Error, Error=RangeError)
164
- raise E(lat=degDMS(lat), txt=t)
165
- else:
166
- return NN # None
167
-
168
-
169
- def _to3zBlat(zone, band, Error=UTMError): # in .mgrs
170
- '''(INTERNAL) Check and return zone, Band and band latitude.
171
-
172
- @arg zone: Zone number or string.
173
- @arg band: Band letter.
174
- @arg Error: Exception to raise (L{UTMError}).
175
-
176
- @return: 3-Tuple (zone, Band, latitude).
177
- '''
178
- z, B, _ = _to3zBhp(zone, band, Error=Error)
179
- if not (_UTM_ZONE_MIN <= z <= _UTM_ZONE_MAX or
180
- (_UPS_ZONE == z and Error is MGRSError)):
181
- raise Error(zone=zone)
182
-
183
- b = None
184
- if B:
185
- if z == _UPS_ZONE: # polar
186
- try:
187
- b = Lat(_UPS_LATS[B], name=_bandLat_)
188
- except KeyError:
189
- raise Error(band=band or B, zone=z)
190
- else: # UTM
191
- b = _Bands.find(B)
192
- if b < 0:
193
- raise Error(band=band or B, zone=z)
194
- b = Int((b << 3) - 80, name=_bandLat_)
195
- B = Band(B)
196
- elif Error is not UTMError:
197
- raise Error(band=band, txt=MISSING)
198
-
199
- return Zone(z), B, b
200
-
201
-
202
- def _to4zBll(lat, lon, cmoff=True, strict=True, Error=RangeError):
203
- '''(INTERNAL) Return zone, Band and lat- and (central) longitude in degrees.
204
-
205
- @arg lat: Latitude (C{degrees}).
206
- @arg lon: Longitude (C{degrees}).
207
- @kwarg cmoff: Offset B{C{lon}} from zone's central meridian.
208
- @kwarg strict: Restrict B{C{lat}} to UTM ranges (C{bool}).
209
- @kwarg Error: Error for out of UTM range B{C{lat}}s.
210
-
211
- @return: 4-Tuple (zone, Band, lat, lon).
212
- '''
213
- z, lat, lon = _to3zll(lat, lon) # in .utmupsBase
214
-
215
- x = lon - _cmlon(z) # z before Norway/Svalbard
216
- if fabs(x) > _UTM_ZONE_OFF_MAX:
217
- t = _SPACE_(_outside_, _UTM_, _zone_, str(z), _by_, degDMS(x, prec=6))
218
- raise Error(lon=degDMS(lon), txt=t)
219
-
220
- B = _toBand(lat, strict=strict, Error=Error)
221
- if B == _X_: # and 0 <= lon < 42: z = int(lon + 183) // 6 + 1
222
- x = _SvalbardXzone.get(z, None)
223
- if x: # Svalbard/Spitsbergen archipelago
224
- z += 1 if lon >= x else -1
225
- elif B == _V_ and z == 31 and lon >= 3:
226
- z += 1 # SouthWestern Norway
227
-
228
- if cmoff: # lon off central meridian
229
- lon -= _cmlon(z) # z after Norway/Svalbard
230
- return Zone(z), (Band(B) if B else None), Lat(lat), Lon(lon)
231
-
232
-
233
- def _to7zBlldfn(latlon, lon, datum, falsed, name, zone, strict, Error, **cmoff):
234
- '''(INTERNAL) Determine 7-tuple (zone, band, lat, lon, datum,
235
- falsed, name) for methods L{toEtm8} and L{toUtm8}.
236
- '''
237
- f = falsed and _xkwds_get(cmoff, cmoff=True) # DEPRECATED
238
- lat, lon, d, name = _to4lldn(latlon, lon, datum, name)
239
- z, B, lat, lon = _to4zBll(lat, lon, cmoff=f, strict=strict)
240
- if zone: # re-zone for ETM/UTM
241
- r, _, _ = _to3zBhp(zone, B)
242
- if r != z:
243
- if not _UTM_ZONE_MIN <= r <= _UTM_ZONE_MAX:
244
- raise Error(zone=zone)
245
- if f: # re-offset from central meridian
246
- lon += _cmlon(z) - _cmlon(r)
247
- z = r
248
- return z, B, lat, lon, d, f, name
249
-
250
-
251
136
  class Utm(UtmUpsBase):
252
137
  '''Universal Transverse Mercator (UTM) coordinate.
253
138
  '''
@@ -260,7 +145,7 @@ class Utm(UtmUpsBase):
260
145
 
261
146
  def __init__(self, zone=31, hemisphere=_N_, easting=166022, # PYCHOK expected
262
147
  northing=0, band=NN, datum=_WGS84, falsed=True,
263
- gamma=None, scale=None, name=NN, **convergence):
148
+ gamma=None, scale=None, **name_convergence):
264
149
  '''New L{Utm} UTM coordinate.
265
150
 
266
151
  @kwarg zone: Longitudinal UTM zone (C{int}, 1..60) or zone with/-out
@@ -275,10 +160,10 @@ class Utm(UtmUpsBase):
275
160
  @kwarg falsed: If C{True}, both B{C{easting}} and B{C{northing}} are
276
161
  falsed (C{bool}).
277
162
  @kwarg gamma: Optional meridian convergence, bearing off grid North,
278
- clockwise from true North (C{degrees}) or C{None}.
279
- @kwarg scale: Optional grid scale factor (C{scalar}) or C{None}.
280
- @kwarg name: Optional name (C{str}).
281
- @kwarg convergence: DEPRECATED, use keyword argument C{B{gamma}=None}.
163
+ clockwise from true North to save (C{degrees}) or C{None}.
164
+ @kwarg scale: Optional grid scale factor to save (C{scalar}) or C{None}.
165
+ @kwarg name_convergence: Optional C{B{name}=NN} (C{str}) and DEPRECATED
166
+ keyword argument C{B{convergence}=None}, use B{C{gamma}}.
282
167
 
283
168
  @raise TypeError: Invalid or near-spherical B{C{datum}}.
284
169
 
@@ -286,14 +171,17 @@ class Utm(UtmUpsBase):
286
171
  B{C{northing}}, B{C{band}}, B{C{convergence}} or
287
172
  B{C{scale}}.
288
173
  '''
289
- if name:
290
- self.name = name
174
+ if name_convergence:
175
+ gamma, name = _xkwds_pop2(name_convergence, convergence=gamma)
176
+ if name:
177
+ self.name = name
291
178
 
292
179
  self._zone, B, _ = _to3zBlat(zone, band)
293
180
 
294
181
  h = str(hemisphere)[:1].upper()
295
182
  if h not in _NS_:
296
183
  raise self._Error(hemisphere=hemisphere)
184
+ self._hemisphere = h
297
185
 
298
186
  e, n = easting, northing # Easting(easting), ...
299
187
  # if not falsed:
@@ -307,9 +195,8 @@ class Utm(UtmUpsBase):
307
195
  # if 0 > n or n > _FalseNorthing:
308
196
  # raise RangeError(northing=northing)
309
197
 
310
- self._hemisphere = h
311
198
  UtmUpsBase.__init__(self, e, n, band=B, datum=datum, falsed=falsed,
312
- gamma=gamma, scale=scale, **convergence)
199
+ gamma=gamma, scale=scale)
313
200
 
314
201
  def __eq__(self, other):
315
202
  return isinstance(other, Utm) and other.zone == self.zone \
@@ -325,7 +212,7 @@ class Utm(UtmUpsBase):
325
212
  def __str__(self):
326
213
  return self.toStr()
327
214
 
328
- def _xcopy2(self, Xtm, name=NN):
215
+ def _xcopy2(self, Xtm, **name):
329
216
  '''(INTERNAL) Make copy as an B{C{Xtm}} instance.
330
217
 
331
218
  @arg Xtm: Class to return the copy (C{Xtm=Etm}, C{Xtm=Utm} or
@@ -333,7 +220,7 @@ class Utm(UtmUpsBase):
333
220
  '''
334
221
  return Xtm(self.zone, self.hemisphere, self.easting, self.northing,
335
222
  band=self.band, datum=self.datum, falsed=self.falsed,
336
- gamma=self.gamma, scale=self.scale, name=name or self.name)
223
+ gamma=self.gamma, scale=self.scale, name=self._name__(name))
337
224
 
338
225
  @property_doc_(''' the I{latitudinal} band.''')
339
226
  def band(self):
@@ -371,13 +258,11 @@ class Utm(UtmUpsBase):
371
258
  n = _FalseNorthing
372
259
  return EasNor2Tuple(e, n)
373
260
 
374
- def parse(self, strUTM, name=NN):
261
+ def parse(self, strUTM, **name):
375
262
  '''Parse a string to a similar L{Utm} instance.
376
263
 
377
- @arg strUTM: The UTM coordinate (C{str}),
378
- see function L{parseUTM5}.
379
- @kwarg name: Optional instance name (C{str}),
380
- overriding this name.
264
+ @arg strUTM: The UTM coordinate (C{str}), see function L{parseUTM5}.
265
+ @kwarg name: Optional instance name (C{str}), overriding this name.
381
266
 
382
267
  @return: The similar instance (L{Utm}).
383
268
 
@@ -386,7 +271,7 @@ class Utm(UtmUpsBase):
386
271
  @see: Functions L{pygeodesy.parseUPS5} and L{pygeodesy.parseUTMUPS5}.
387
272
  '''
388
273
  return parseUTM5(strUTM, datum=self.datum, Utm=self.classof,
389
- name=name or self.name)
274
+ name=self._name__(name))
390
275
 
391
276
  @deprecated_method
392
277
  def parseUTM(self, strUTM): # PYCHOK no cover
@@ -479,11 +364,12 @@ class Utm(UtmUpsBase):
479
364
  # gamma and scale: Karney 2011 Eq 26, 27 and 28
480
365
  p = neg(K.ps(-1))
481
366
  q = K.qs(0)
482
- s = hypot(p, q) * E.a / A0
483
- ll._gamma = degrees(atan1(tan(y) * tanh(x)) + atan2(q, p))
484
- ll._scale = (E.e2s(sin(a)) * hypot1(T) * H / s) if s else s # INF?
367
+ g = degrees(atan1(tan(y) * tanh(x)) + atan2(q, p))
368
+ k = hypot(p, q) * E.a / A0
369
+ if k:
370
+ k = E.e2s(sin(a)) * hypot1(T) * H / k # INF?
485
371
  ll._iteration = i
486
- self._latlon5args(ll, _toBand, unfalse, eps)
372
+ self._latlon5args(ll, g, k, _toBand, unfalse, eps)
487
373
 
488
374
  def toRepr(self, prec=0, fmt=Fmt.SQUARE, sep=_COMMASPACE_, B=False, cs=False, **unused): # PYCHOK expected
489
375
  '''Return a string representation of this UTM coordinate.
@@ -536,12 +422,13 @@ class Utm(UtmUpsBase):
536
422
  (C{str}, 'N[orth]'|'S[outh]').
537
423
  @kwarg eps: Optional convergence limit, L{EPS} or above
538
424
  (C{float}), see method L{Utm.toLatLon}.
539
- @kwarg falsed: False both easting and northing (C{bool}).
425
+ @kwarg falsed: If C{True}, false both easting and northing
426
+ (C{bool}).
540
427
 
541
428
  @return: The UPS coordinate (L{Ups}).
542
429
  '''
543
430
  u = self._ups
544
- if u is None or u.pole != (pole or u.pole) or falsed != bool(u.falsed):
431
+ if u is None or u.pole != (pole or u.pole) or bool(falsed) != u.falsed:
545
432
  ll = self.toLatLon(LatLon=_LLEB, eps=eps, unfalse=True)
546
433
  ups = _MODS.ups
547
434
  self._ups = u = ups.toUps8(ll, Ups=ups.Ups, falsed=falsed,
@@ -554,7 +441,8 @@ class Utm(UtmUpsBase):
554
441
  @arg zone: New UTM zone (C{int}).
555
442
  @kwarg eps: Optional convergence limit, L{EPS} or above
556
443
  (C{float}), see method L{Utm.toLatLon}.
557
- @kwarg falsed: False both easting and northing (C{bool}).
444
+ @kwarg falsed: If C{True}, fFalse both easting and northing
445
+ (C{bool}).
558
446
 
559
447
  @return: The UTM coordinate (L{Utm}).
560
448
  '''
@@ -562,7 +450,7 @@ class Utm(UtmUpsBase):
562
450
  return self.copy()
563
451
  elif zone:
564
452
  u = self._utm
565
- if u is None or u.zone != zone or falsed != u.falsed:
453
+ if u is None or u.zone != zone or bool(falsed) != u.falsed:
566
454
  ll = self.toLatLon(LatLon=_LLEB, eps=eps, unfalse=True)
567
455
  self._utm = u = toUtm8(ll, Utm=self.classof, falsed=falsed,
568
456
  name=self.name, zone=zone)
@@ -576,7 +464,25 @@ class Utm(UtmUpsBase):
576
464
  return self._zone
577
465
 
578
466
 
579
- def _parseUTM5(strUTM, datum, Xtm, falsed, Error=UTMError, name=NN): # imported by .etm
467
+ def _cmlon(zone):
468
+ '''(INTERNAL) Central meridian longitude (C{degrees180}).
469
+ '''
470
+ return (zone * 6) - 183
471
+
472
+
473
+ def _false2(e, n, h):
474
+ '''(INTERNAL) False easting and northing.
475
+ '''
476
+ # Karney, "Test data for the transverse Mercator projection (2009)"
477
+ # <https://GeographicLib.SourceForge.io/C++/doc/transversemercator.html>
478
+ # and <https://Zenodo.org/record/32470#.W4LEJS2ZON8>
479
+ e += _FalseEasting # make e relative to central meridian
480
+ if h == _S_:
481
+ n += _FalseNorthing # make n relative to equator
482
+ return e, n
483
+
484
+
485
+ def _parseUTM5(strUTM, datum, Xtm, falsed, Error=UTMError, **name): # imported by .etm
580
486
  '''(INTERNAL) Parse a string representing a UTM coordinate,
581
487
  consisting of C{"zone[band] hemisphere easting northing"},
582
488
  see L{pygeodesy.parseETM5} and L{parseUTM5}.
@@ -585,16 +491,11 @@ def _parseUTM5(strUTM, datum, Xtm, falsed, Error=UTMError, name=NN): # imported
585
491
  if _UTM_ZONE_MIN > z or z > _UTM_ZONE_MAX or (B and B not in _Bands):
586
492
  raise Error(strUTM=strUTM, zone=z, band=B)
587
493
 
588
- if Xtm is None:
589
- r = UtmUps5Tuple(z, h, e, n, B, Error=Error, name=name)
590
- else:
591
- r = Xtm(z, h, e, n, band=B, datum=datum, falsed=falsed)
592
- if name:
593
- r = _xnamed(r, name, force=True)
594
- return r
494
+ return UtmUps5Tuple(z, h, e, n, B, Error=Error, **name) if Xtm is None else \
495
+ Xtm(z, h, e, n, band=B, datum=datum, falsed=bool(falsed), **name)
595
496
 
596
497
 
597
- def parseUTM5(strUTM, datum=_WGS84, Utm=Utm, falsed=True, name=NN):
498
+ def parseUTM5(strUTM, datum=_WGS84, Utm=Utm, falsed=True, **name):
598
499
  '''Parse a string representing a UTM coordinate, consisting
599
500
  of C{"zone[band] hemisphere easting northing"}.
600
501
 
@@ -603,8 +504,9 @@ def parseUTM5(strUTM, datum=_WGS84, Utm=Utm, falsed=True, name=NN):
603
504
  L{Ellipsoid2} or L{a_f2Tuple}).
604
505
  @kwarg Utm: Optional class to return the UTM coordinate
605
506
  (L{Utm}) or C{None}.
606
- @kwarg falsed: Both easting and northing are falsed (C{bool}).
607
- @kwarg name: Optional B{C{Utm}} name (C{str}).
507
+ @kwarg falsed: Use C{B{falsed}=True} if both easting and
508
+ northing are falsed (C{bool}).
509
+ @kwarg name: Optional B{C{Utm}} C{B{name}=NN} (C{str}).
608
510
 
609
511
  @return: The UTM coordinate (B{C{Utm}}) or if B{C{Utm}}
610
512
  is C{None}, a L{UtmUps5Tuple}C{(zone, hemipole,
@@ -615,28 +517,29 @@ def parseUTM5(strUTM, datum=_WGS84, Utm=Utm, falsed=True, name=NN):
615
517
 
616
518
  @raise TypeError: Invalid B{C{datum}}.
617
519
  '''
618
- return _parseUTM5(strUTM, datum, Utm, falsed, name=name)
520
+ return _parseUTM5(strUTM, datum, Utm, falsed, **name)
619
521
 
620
522
 
621
523
  def toUtm8(latlon, lon=None, datum=None, Utm=Utm, falsed=True,
622
- name=NN, strict=True,
623
- zone=None, **cmoff):
524
+ strict=True, zone=None, **name_cmoff):
624
525
  '''Convert a lat-/longitude point to a UTM coordinate.
625
526
 
626
527
  @arg latlon: Latitude (C{degrees}) or an (ellipsoidal)
627
528
  geodetic C{LatLon} point.
628
- @kwarg lon: Optional longitude (C{degrees}) or C{None}.
529
+ @kwarg lon: Optional longitude (C{degrees}), required
530
+ if B{C{latlon}} is in C{degrees}.
629
531
  @kwarg datum: Optional datum for this UTM coordinate,
630
532
  overriding B{C{latlon}}'s datum (L{Datum},
631
533
  L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}).
632
534
  @kwarg Utm: Optional class to return the UTM coordinate
633
535
  (L{Utm}) or C{None}.
634
536
  @kwarg falsed: False both easting and northing (C{bool}).
635
- @kwarg name: Optional B{C{Utm}} name (C{str}).
636
537
  @kwarg strict: Restrict B{C{lat}} to UTM ranges (C{bool}).
637
538
  @kwarg zone: Optional UTM zone to enforce (C{int} or C{str}).
638
- @kwarg cmoff: DEPRECATED, use B{C{falsed}}. Offset longitude
639
- from the zone's central meridian (C{bool}).
539
+ @kwarg name_cmoff: Optional B{C{Utm}} C{B{name}=NN} (C{str}) and
540
+ DEPRECATED C{B{cmoff}=True} to offset the longitude
541
+ from the zone's central meridian (C{bool}), use
542
+ C{B{falsed}} instead.
640
543
 
641
544
  @return: The UTM coordinate (B{C{Utm}}) or if B{C{Utm}} is
642
545
  C{None} or not B{C{falsed}}, a L{UtmUps8Tuple}C{(zone,
@@ -651,17 +554,17 @@ def toUtm8(latlon, lon=None, datum=None, Utm=Utm, falsed=True,
651
554
 
652
555
  @raise UTMError: Invalid B{C{zone}}.
653
556
 
654
- @raise ValueError: If B{C{lon}} value is missing or if
655
- B{C{latlon}} is invalid.
557
+ @raise ValueError: If B{C{lon}} value is missing or B{C{latlon}}
558
+ is invalid.
656
559
 
657
560
  @note: Implements Karney’s method, using 8-th order Krüger series,
658
561
  giving results accurate to 5 nm (or better) for distances
659
562
  up to 3,900 Km from the central meridian.
660
563
  '''
661
- z, B, lat, lon, d, f, name = _to7zBlldfn(latlon, lon, datum,
662
- falsed, name, zone,
663
- strict, UTMError, **cmoff)
664
- d = _ellipsoidal_datum(d, name=name)
564
+ z, B, lat, lon, d, f, n = _to7zBlldfn(latlon, lon, datum,
565
+ falsed, zone, strict,
566
+ UTMError, **name_cmoff)
567
+ d = _ellipsoidal_datum(d, name=n)
665
568
  E = d.ellipsoid
666
569
 
667
570
  a, b = radians(lat), radians(lon)
@@ -681,8 +584,8 @@ def toUtm8(latlon, lon=None, datum=None, Utm=Utm, falsed=True,
681
584
  A0 = E.A * getattr(Utm, _under(_scale0_), _K0_UTM) # Utm is class or None
682
585
 
683
586
  K = _Kseries(E.AlphaKs, x, y) # Krüger series
684
- y = K.ys(y) * A0 # ξ
685
- x = K.xs(x) * A0 # η
587
+ y = K.ys(y) * A0 # ξ
588
+ x = K.xs(x) * A0 # η
686
589
 
687
590
  # convergence: Karney 2011 Eq 23, 24
688
591
  p_ = K.ps(1)
@@ -692,43 +595,136 @@ def toUtm8(latlon, lon=None, datum=None, Utm=Utm, falsed=True,
692
595
  k = E.e2s(sin(a)) * T12 / H * (A0 / E.a * hypot(p_, q_))
693
596
 
694
597
  return _toXtm8(Utm, z, lat, x, y,
695
- B, d, g, k, f, name, latlon, EPS)
598
+ B, d, g, k, f, n, latlon, EPS)
599
+
600
+
601
+ def _toBand(lat, *unused, **strict_Error): # see ups._toBand
602
+ '''(INTERNAL) Get the I{latitudinal} Band (row) letter.
603
+ '''
604
+ if _UTM_LAT_MIN <= lat < _UTM_LAT_MAX: # [-80, 84) like Veness
605
+ return _Bands[int(lat - _UTM_LAT_MIN) >> 3]
606
+ elif _xkwds_get(strict_Error, strict=True):
607
+ r = _range_(_UTM_LAT_MIN, _UTM_LAT_MAX, ropen=True)
608
+ t = _SPACE_(_outside_, _UTM_, _range_, r)
609
+ E = _xkwds_get(strict_Error, Error=RangeError)
610
+ raise E(lat=degDMS(lat), txt=t)
611
+ else:
612
+ return NN # None
696
613
 
697
614
 
698
615
  def _toXtm8(Xtm, z, lat, x, y, B, d, g, k, f, # PYCHOK 13+ args
699
- name, latlon, eps, Error=UTMError):
616
+ n, latlon, eps_other, Error=UTMError):
700
617
  '''(INTERNAL) Helper for methods L{toEtm8} and L{toUtm8}.
701
618
  '''
702
619
  h = _hemi(lat)
703
620
  if f:
704
621
  x, y = _false2(x, y, h)
705
622
  if Xtm is None: # DEPRECATED
706
- r = UtmUps8Tuple(z, h, x, y, B, d, g, k, Error=Error, name=name)
623
+ r = UtmUps8Tuple(z, h, x, y, B, d, g, k, Error=Error, name=n)
707
624
  else:
708
- r = _xnamed(Xtm(z, h, x, y, band=B, datum=d, falsed=f,
709
- gamma=g, scale=k), name)
625
+ r = Xtm(z, h, x, y, band=B, datum=d, falsed=f,
626
+ gamma=g, scale=k, name=n)
710
627
  if isinstance(latlon, _LLEB) and d is latlon.datum: # see ups.toUtm8
711
- r._latlon5args(latlon, _toBand, f, eps) # XXX weakref(latlon)?
712
- latlon._gamma = g
713
- latlon._scale = k
628
+ r._latlon5args(latlon, g, k, _toBand, f, eps_other) # XXX weakref(latlon)?
714
629
  elif not r._band:
715
630
  r._band = _toBand(lat)
716
631
  return r
717
632
 
718
633
 
719
- def utmZoneBand5(lat, lon, cmoff=False, name=NN):
634
+ def _to3zBlat(zone, band, Error=UTMError): # in .mgrs
635
+ '''(INTERNAL) Check and return zone, Band and band latitude.
636
+
637
+ @arg zone: Zone number or string.
638
+ @arg band: Band letter.
639
+ @arg Error: Exception to raise (L{UTMError}).
640
+
641
+ @return: 3-Tuple (zone, Band, latitude).
642
+ '''
643
+ z, B, _ = _to3zBhp(zone, band, Error=Error)
644
+ if not (_UTM_ZONE_MIN <= z <= _UTM_ZONE_MAX or
645
+ (_UPS_ZONE == z and Error is MGRSError)):
646
+ raise Error(zone=zone)
647
+
648
+ b = None
649
+ if B:
650
+ if z == _UPS_ZONE: # polar
651
+ try:
652
+ b = Lat(_UPS_LATS[B], name=_bandLat_)
653
+ except KeyError:
654
+ raise Error(band=band or B, zone=z)
655
+ else: # UTM
656
+ b = _Bands.find(B)
657
+ if b < 0:
658
+ raise Error(band=band or B, zone=z)
659
+ b = Int((b << 3) - 80, name=_bandLat_)
660
+ B = Band(B)
661
+ elif Error is not UTMError:
662
+ raise Error(band=band, txt=MISSING)
663
+
664
+ return Zone(z), B, b
665
+
666
+
667
+ def _to4zBll(lat, lon, cmoff=True, strict=True, Error=RangeError):
668
+ '''(INTERNAL) Return zone, Band and lat- and (central) longitude in degrees.
669
+
670
+ @arg lat: Latitude (C{degrees}).
671
+ @arg lon: Longitude (C{degrees}).
672
+ @kwarg cmoff: If C{True}, offset B{C{lon}} from the zone's central meridian.
673
+ @kwarg strict: Restrict B{C{lat}} to the UTM ranges (C{bool}).
674
+ @kwarg Error: Error for out of UTM range B{C{lat}}s.
675
+
676
+ @return: 4-Tuple (zone, Band, lat, lon).
677
+ '''
678
+ z, lat, lon = _to3zll(lat, lon) # in .utmupsBase
679
+
680
+ x = lon - _cmlon(z) # z before Norway/Svalbard
681
+ if fabs(x) > _UTM_ZONE_OFF_MAX:
682
+ t = _SPACE_(_outside_, _UTM_, _zone_, str(z), _by_, degDMS(x, prec=6))
683
+ raise Error(lon=degDMS(lon), txt=t)
684
+
685
+ B = _toBand(lat, strict=strict, Error=Error)
686
+ if B == _X_: # and 0 <= lon < 42: z = int(lon + 183) // 6 + 1
687
+ x = _SvalbardXzone.get(z, None)
688
+ if x: # Svalbard/Spitsbergen archipelago
689
+ z += 1 if lon >= x else -1
690
+ elif B == _V_ and z == 31 and lon >= 3:
691
+ z += 1 # SouthWestern Norway
692
+
693
+ if cmoff: # lon off central meridian
694
+ lon -= _cmlon(z) # z I{after} Norway/Svalbard
695
+ return Zone(z), (Band(B) if B else None), Lat(lat), Lon(lon)
696
+
697
+
698
+ def _to7zBlldfn(latlon, lon, datum, falsed, zone, strict, Error, **name_cmoff):
699
+ '''(INTERNAL) Determine 7-tuple (zone, band, lat, lon, datum,
700
+ falsed, name) for methods L{toEtm8} and L{toUtm8}.
701
+ '''
702
+ f, name = _xkwds_pop2(name_cmoff, cmoff=falsed) # DEPRECATED
703
+ lat, lon, d, n = _to4lldn(latlon, lon, datum, name)
704
+ z, B, lat, lon = _to4zBll(lat, lon, cmoff=f, strict=strict)
705
+ if zone: # re-zone for ETM/UTM
706
+ r, _, _ = _to3zBhp(zone, B)
707
+ if r != z:
708
+ if not _UTM_ZONE_MIN <= r <= _UTM_ZONE_MAX:
709
+ raise Error(zone=zone)
710
+ if f: # re-offset from central meridian
711
+ lon += _cmlon(z) - _cmlon(r)
712
+ z = r
713
+ return z, B, lat, lon, d, f, n
714
+
715
+
716
+ def utmZoneBand5(lat, lon, cmoff=False, **name):
720
717
  '''Return the UTM zone number, Band letter, hemisphere and
721
718
  (clipped) lat- and longitude for a given location.
722
719
 
723
720
  @arg lat: Latitude in degrees (C{scalar} or C{str}).
724
721
  @arg lon: Longitude in degrees (C{scalar} or C{str}).
725
- @kwarg cmoff: Offset longitude from the zone's central
722
+ @kwarg cmoff: If C{True}, offset longitude from the zone's central
726
723
  meridian (C{bool}).
727
- @kwarg name: Optional name (C{str}).
724
+ @kwarg name: Optional C{B{name}=NN} (C{str}).
728
725
 
729
- @return: A L{UtmUpsLatLon5Tuple}C{(zone, band, hemipole,
730
- lat, lon)} where C{hemipole} is the C{'N'|'S'}
731
- UTM hemisphere.
726
+ @return: A L{UtmUpsLatLon5Tuple}C{(zone, band, hemipole, lat, lon)}
727
+ where C{hemipole} is the C{'N'|'S'} UTM hemisphere.
732
728
 
733
729
  @raise RangeError: If B{C{lat}} outside the valid UTM bands or if
734
730
  B{C{lat}} or B{C{lon}} outside the valid range
@@ -738,7 +734,7 @@ def utmZoneBand5(lat, lon, cmoff=False, name=NN):
738
734
  '''
739
735
  lat, lon = parseDMS2(lat, lon)
740
736
  z, B, lat, lon = _to4zBll(lat, lon, cmoff=cmoff)
741
- return UtmUpsLatLon5Tuple(z, B, _hemi(lat), lat, lon, name=name)
737
+ return UtmUpsLatLon5Tuple(z, B, _hemi(lat), lat, lon, **name)
742
738
 
743
739
  # **) MIT License
744
740
  #