Coverage for pygeodesy/namedTuples.py: 95%
240 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-04-25 13:15 -0400
« prev ^ index » next coverage.py v7.6.1, created at 2025-04-25 13:15 -0400
2# -*- coding: utf-8 -*-
4u'''Named tuples.
6Tuples returned by C{pygeodesy} functions and class methods
7are all instances of some C{Named...Tuple} class, all sub-classes
8of C{_NamedTuple} defined in C{pygeodesy.named}.
9'''
11from pygeodesy.basics import isinstanceof, issubclassof, map1, _xinstanceof
12# from pygeodesy.cartesianBase import CartesianBase # _MODS
13# from pygeodesy.constants import INT0 # from .units
14# from pygeodesy.dms import toDMS # _MODS
15from pygeodesy.errors import _TypeError, _xattr, _xkwds, _xkwds_not
16from pygeodesy.interns import NN, _1_, _2_, _a_, _A_, _area_, _angle_, _b_, _B_, \
17 _band_, _c_, _C_, _D_, _datum_, _distance_, _E_, \
18 _easting_, _end_, _fi_, _gamma_, _h_, _height_, \
19 _hemipole_, _initial_, _j_, _lam_, _lat_, _lon_, \
20 _n_, _northing_, _number_, _outside_, _phi_, \
21 _point_, _precision_, _points_, _radius_, _scale_, \
22 _start_, _x_, _y_, _z_, _zone_
23# from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS # from .named
24from pygeodesy.named import _NamedTuple, _Pass, _ALL_LAZY, _MODS
25from pygeodesy.props import deprecated_property_RO, property_RO
26from pygeodesy.units import Band, Bearing, Degrees, Degrees2, Easting, FIx, \
27 Height, Int, Lam, Lat, Lon, Meter, Meter2, \
28 Northing, Number_, Phi, Precision_, Radians, \
29 Radius, Scalar, Str, INT0
31__all__ = _ALL_LAZY.namedTuples
32__version__ = '24.11.22'
34# __DUNDER gets mangled in class
35_closest_ = 'closest'
36_destination_ = 'destination'
37_elel_ = 'll'
38_final_ = 'final'
39_fraction_ = 'fraction'
42class Bearing2Tuple(_NamedTuple):
43 '''2-Tuple C{(initial, final)} bearings, both in compass C{degrees360}.
44 '''
45 _Names_ = (_initial_, _final_)
46 _Units_ = ( Bearing, Bearing)
49class Bounds2Tuple(_NamedTuple): # .geohash.py, .latlonBase.py, .points.py
50 '''2-Tuple C{(latlonSW, latlonNE)} with the bounds' lower-left and
51 upper-right corner as C{LatLon} instance.
52 '''
53 _Names_ = ('latlonSW', 'latlonNE')
54 _Units_ = (_Pass, _Pass)
57class Bounds4Tuple(_NamedTuple): # .geohash.py, .points.py
58 '''4-Tuple C{(latS, lonW, latN, lonE)} with the bounds' lower-left
59 C{(LatS, LowW)} and upper-right C{(latN, lonE)} corner lat- and
60 longitudes.
61 '''
62 _Names_ = ('latS', 'lonW', 'latN', 'lonE')
63 _Units_ = ( Lat, Lon, Lat, Lon)
65 def enclosures(self, S_other, *W_N_E):
66 '''Get the enclosures of this around an other L{Bounds4Tuple}.
68 @arg S_other: Bottom C{latS} (C{scalar}) or an other
69 L{Bounds4Tuple} instance.
70 @arg W_N_E: Left C{lonW}, top C{latN} and right C{lonE},
71 each a (C{scalar}) for C{scalar B{S_other}}.
73 @return: A L{Bounds4Tuple} with the I{margin} at each of
74 the 4 sides, positive if this side I{encloses}
75 (is on the I{outside} of) the other, negative
76 if not or zero if abutting.
77 '''
78 s, w, n, e = self
79 S, W, N, E = map1(float, S_other, *W_N_E) if W_N_E else S_other
80 return Bounds4Tuple(map1(float, S - s, W - w, n - N, e - E)) # *map1
82 def overlap(self, S_other, *W_N_E):
83 '''Intersect this with an other L{Bounds4Tuple}.
85 @arg S_other: Bottom C{latS} (C{scalar}) or an other
86 L{Bounds4Tuple} instance.
87 @arg W_N_E: Left C{lonW}, top C{latN} and right C{lonE},
88 each a (C{scalar}) for C{scalar B{S_other}}.
90 @return: C{None} if the bounds do not overlap, otherwise
91 the intersection of both as a L{Bounds4Tuple}.
92 '''
93 s, w, n, e = self
94 S, W, N, E = map1(float, S_other, *W_N_E) if W_N_E else S_other
95 return None if s > N or n < S or w > E or e < W else \
96 Bounds4Tuple(max(s, S), max(w, W), min(n, N), min(e, E))
99class Destination2Tuple(_NamedTuple): # .ellipsoidalKarney.py, -Vincenty.py
100 '''2-Tuple C{(destination, final)}, C{destination} in C{LatLon}
101 and C{final} bearing in compass C{degrees360}.
102 '''
103 _Names_ = (_destination_, _final_)
104 _Units_ = (_Pass, Bearing)
107class Destination3Tuple(_NamedTuple): # .karney.py
108 '''3-Tuple C{(lat, lon, final)}, destination C{lat}, C{lon} in
109 C{degrees90} respectively C{degrees180} and C{final} bearing
110 in compass C{degrees360}.
111 '''
112 _Names_ = (_lat_, _lon_, _final_)
113 _Units_ = ( Lat, Lon, Bearing)
116class Distance2Tuple(_NamedTuple): # .datum.py, .ellipsoidalBase.py
117 '''2-Tuple C{(distance, initial)}, C{distance} in C{meter} and
118 C{initial} bearing in compass C{degrees360}.
119 '''
120 _Names_ = (_distance_, _initial_)
121 _Units_ = ( Meter, Bearing)
124class Distance3Tuple(_NamedTuple): # .ellipsoidalKarney.py, -Vincenty.py
125 '''3-Tuple C{(distance, initial, final)}, C{distance} in C{meter}
126 and C{initial} and C{final} bearing, both in compass C{degrees360}.
127 '''
128 _Names_ = (_distance_, _initial_, _final_)
129 _Units_ = ( Meter, Bearing, Bearing)
132class Distance4Tuple(_NamedTuple): # .formy.py, .points.py
133 '''4-Tuple C{(distance2, delta_lat, delta_lon, unroll_lon2)} with
134 the distance in C{degrees squared}, the latitudinal C{delta_lat
135 = B{lat2} - B{lat1}}, the wrapped, unrolled and adjusted
136 longitudinal C{delta_lon = B{lon2} - B{lon1}} and C{unroll_lon2},
137 the unrolled or original B{C{lon2}}.
139 @note: Use Function L{pygeodesy.degrees2m} to convert C{degrees
140 squared} to C{meter} as M{degrees2m(sqrt(distance2), ...)}
141 or M{degrees2m(hypot(delta_lat, delta_lon), ...)}.
142 '''
143 _Names_ = ('distance2', 'delta_lat', 'delta_lon', 'unroll_lon2')
144 _Units_ = ( Degrees2, Degrees, Degrees, Degrees)
147class EasNor2Tuple(_NamedTuple): # .css, .osgr, .ups, .utm, .utmupsBase
148 '''2-Tuple C{(easting, northing)}, both in C{meter}, conventionally.
149 '''
150 _Names_ = (_easting_, _northing_)
151 _Units_ = ( Easting, Northing)
154class EasNor3Tuple(_NamedTuple): # .css.py, .lcc.py
155 '''3-Tuple C{(easting, northing, height)}, all in C{meter}, conventionally.
156 '''
157 _Names_ = (_easting_, _northing_, _height_)
158 _Units_ = ( Easting, Northing, Height)
161class _Convergence(object):
162 '''(INTERNAL) DEPRECATED Property C{convergence}, use property C{gamma}.'''
163 @deprecated_property_RO
164 def convergence(self):
165 '''DEPRECATED, use property C{gamma}.
166 '''
167 return self.gamma # PYCHOK self[.]
170class Forward4Tuple(_NamedTuple, _Convergence):
171 '''4-Tuple C{(easting, northing, gamma, scale)} in
172 C{meter}, C{meter}, meridian convergence C{gamma} at
173 point in C{degrees} and the C{scale} of projection
174 at point C{scalar}.
175 '''
176 _Names_ = (_easting_, _northing_, _gamma_, _scale_)
177 _Units_ = ( Easting, Northing, Degrees, Scalar)
180class Intersection3Tuple(_NamedTuple): # .css.py, .lcc.py
181 '''3-Tuple C{(point, outside1, outside2)} of an intersection
182 C{point} and C{outside1}, the position of the C{point},
183 C{-1} if before the start, C{+1} if after the end and C{0}
184 if on or between the start and end point of the first line.
185 Similarly, C{outside2} is C{-2}, C{+2} or C{0} to indicate
186 the position of C{point} on the second line or path. If a
187 path was specified with an initial bearing instead of an
188 end point, C{outside1} and/or C{outside2} will be C{0} if
189 the intersection C{point} is on the start point or C{+1}
190 respectively C{+2} if the intersection C{point} is after
191 the start point, in the direction of the bearing.
192 '''
193 _Names_ = (_point_, _outside_ + _1_, _outside_ + _2_)
194 _Units_ = (_Pass, Int, Int)
197class LatLon2Tuple(_NamedTuple):
198 '''2-Tuple C{(lat, lon)} in C{degrees90} and C{degrees180}.
199 '''
200 _Names_ = (_lat_, _lon_)
201 _Units_ = ( Lat, Lon)
203 def to3Tuple(self, height, **name):
204 '''Extend this L{LatLon2Tuple} to a L{LatLon3Tuple}.
206 @arg height: The height to add (C{scalar}).
207 @kwarg name: Optional C{B{name}=NN} (C{str}), overriding
208 this name.
210 @return: A L{LatLon3Tuple}C{(lat, lon, height)}.
212 @raise ValueError: Invalid B{C{height}}.
213 '''
214 return self._xtend(LatLon3Tuple, height, **name)
216 def to4Tuple(self, height, datum, **name):
217 '''Extend this L{LatLon2Tuple} to a L{LatLon4Tuple}.
219 @arg height: The height to add (C{scalar}).
220 @arg datum: The datum to add (C{Datum}).
221 @kwarg name: Optional C{B{name}=NN} (C{str}), overriding
222 this name.
224 @return: A L{LatLon4Tuple}C{(lat, lon, height, datum)}.
226 @raise TypeError: If B{C{datum}} not a C{Datum}.
228 @raise ValueError: Invalid B{C{height}}.
229 '''
230 return self.to3Tuple(height).to4Tuple(datum, **name)
233class LatLon3Tuple(_NamedTuple):
234 '''3-Tuple C{(lat, lon, height)} in C{degrees90}, C{degrees180}
235 and C{meter}, conventionally.
236 '''
237 _Names_ = (_lat_, _lon_, _height_)
238 _Units_ = ( Lat, Lon, Height)
240 def to4Tuple(self, datum, **name):
241 '''Extend this L{LatLon3Tuple} to a L{LatLon4Tuple}.
243 @arg datum: The datum to add (C{Datum}).
244 @kwarg name: Optional C{B{name}=NN} (C{str}), overriding
245 this name.
247 @return: A L{LatLon4Tuple}C{(lat, lon, height, datum)}.
249 @raise TypeError: If B{C{datum}} not a C{Datum}.
250 '''
251 _xinstanceof(_MODS.datums.Datum, datum=datum)
252 return self._xtend(LatLon4Tuple, datum, **name)
255class LatLon4Tuple(LatLon3Tuple): # .cartesianBase, .css, .ecef, .lcc
256 '''4-Tuple C{(lat, lon, height, datum)} in C{degrees90},
257 C{degrees180}, C{meter} and L{Datum}.
258 '''
259 _Names_ = (_lat_, _lon_, _height_, _datum_)
260 _Units_ = ( Lat, Lon, Height, _Pass)
263def _LL4Tuple(lat, lon, height, datum, LatLon, LatLon_kwds, inst=None,
264 iteration=None, **name):
265 '''(INTERNAL) Return a L{LatLon4Tuple} or a B{C{LatLon}} instance.
266 '''
267 if LatLon is None: # ignore LatLon_kwds
268 r = LatLon4Tuple(lat, lon, height, datum, **name)
269 else:
270 kwds = {} if inst is None else _xkwds_not(None,
271# datum=_xattr(inst, datum=None),
272 epoch=_xattr(inst, epoch=None),
273 reframe=_xattr(inst, reframe=None)) # PYCHOK indent
274 kwds.update(datum=datum, height=height, **name)
275 if LatLon_kwds:
276 kwds.update(LatLon_kwds)
277 r = LatLon(lat, lon, **kwds)
278 if iteration is not None: # like .named._namedTuple.__new__
279 r._iteration = iteration
280 return r
283class LatLonDatum3Tuple(_NamedTuple): # .lcc.py, .osgr.py
284 '''3-Tuple C{(lat, lon, datum)} in C{degrees90}, C{degrees180}
285 and L{Datum}.
286 '''
287 _Names_ = (_lat_, _lon_, _datum_)
288 _Units_ = ( Lat, Lon, _Pass)
291class LatLonDatum5Tuple(LatLonDatum3Tuple, _Convergence): # .ups.py, .utm.py, .utmupsBase.py
292 '''5-Tuple C{(lat, lon, datum, gamma, scale)} in C{degrees90},
293 C{degrees180}, L{Datum}, C{degrees} and C{float}.
294 '''
295 _Names_ = LatLonDatum3Tuple._Names_ + (_gamma_, _scale_)
296 _Units_ = LatLonDatum3Tuple._Units_ + ( Degrees, Scalar)
299class LatLonPrec3Tuple(_NamedTuple): # .gars.py, .wgrs.py
300 '''3-Tuple C{(lat, lon, precision)} in C{degrees}, C{degrees}
301 and C{int}.
302 '''
303 _Names_ = (_lat_, _lon_, _precision_)
304 _Units_ = ( Lat, Lon, Precision_)
306 def to5Tuple(self, height, radius, **name):
307 '''Extend this L{LatLonPrec3Tuple} to a L{LatLonPrec5Tuple}.
309 @arg height: The height to add (C{float} or C{None}).
310 @arg radius: The radius to add (C{float} or C{None}).
311 @kwarg name: Optional C{B{name}=NN} (C{str}), overriding
312 this name.
314 @return: A L{LatLonPrec5Tuple}C{(lat, lon, precision,
315 height, radius)}.
316 '''
317 return self._xtend(LatLonPrec5Tuple, height, radius, **name)
320class LatLonPrec5Tuple(LatLonPrec3Tuple): # .wgrs.py
321 '''5-Tuple C{(lat, lon, precision, height, radius)} in C{degrees},
322 C{degrees}, C{int} and C{height} or C{radius} in C{meter} (or
323 C{None} if missing).
324 '''
325 _Names_ = LatLonPrec3Tuple._Names_ + (_height_, _radius_)
326 _Units_ = LatLonPrec3Tuple._Units_ + ( Height, Radius)
329class _NamedTupleTo(_NamedTuple): # in .testNamedTuples
330 '''(INTERNAL) Base for C{-.toDegrees}, C{-.toRadians}.
331 '''
332 def _Degrees3(self, *xs, **toDMS_kwds):
333 '''(INTERNAL) Convert C{xs} from C{Radians} to C{Degrees} or C{toDMS}.
334 '''
335 if toDMS_kwds:
336 toDMS_kwds = _xkwds(toDMS_kwds, ddd=1, pos=NN)
337 toDMS, s = _MODS.dms.toDMS, None
338 else:
339 toDMS, s = None, self
340 for x in xs:
341 if not isinstanceof(x, Degrees):
342 x, s = x.toDegrees(), None
343 yield toDMS(x, **toDMS_kwds) if toDMS else x
344 yield s
346 def _Radians3(self, *xs, **unused):
347 '''(INTERNAL) Convert C{xs} from C{Degrees} to C{Radians}.
348 '''
349 s = self
350 for x in xs:
351 if not isinstanceof(x, Radians):
352 x, s = x.toRadians(), None
353 yield x
354 yield s
357class NearestOn2Tuple(_NamedTuple): # .ellipsoidalBaseDI
358 '''2-Tuple C{(closest, fraction)} of the C{closest} point
359 on and C{fraction} along a line (segment) between two
360 points. The C{fraction} is C{0} if the closest point
361 is the first or C{1} the second of the two points.
362 Negative C{fraction}s indicate the closest point is
363 C{before} the first point. For C{fraction > 1.0}
364 the closest point is after the second point.
365 '''
366 _Names_ = (_closest_, _fraction_)
367 _Units_ = (_Pass, _Pass)
370class NearestOn3Tuple(_NamedTuple): # .points.py, .sphericalTrigonometry
371 '''3-Tuple C{(closest, distance, angle)} of the C{closest}
372 point on the polygon, either a C{LatLon} instance or a
373 L{LatLon3Tuple}C{(lat, lon, height)} and the C{distance}
374 and C{angle} to the C{closest} point are in C{meter}
375 respectively compass C{degrees360}.
376 '''
377 _Names_ = (_closest_, _distance_, _angle_)
378 _Units_ = (_Pass, Meter, Degrees)
381# NearestOn4Tuple DEPRECATED, see .deprecated.classes.NearestOn4Tuple
384class NearestOn5Tuple(_NamedTuple):
385 '''5-Tuple C{(lat, lon, distance, angle, height)} all in C{degrees},
386 except C{height}. The C{distance} is the L{pygeodesy.equirectangular}
387 distance between the closest and the reference B{C{point}} in C{degrees}.
388 The C{angle} from the reference B{C{point}} to the closest point is in
389 compass C{degrees360}, see function L{pygeodesy.compassAngle}. The
390 C{height} is the (interpolated) height at the closest point in C{meter}
391 or C{0}.
392 '''
393 _Names_ = (_lat_, _lon_, _distance_, _angle_, _height_)
394 _Units_ = ( Lat, Lon, Degrees, Degrees, Meter)
397class NearestOn6Tuple(_NamedTuple): # .latlonBase.py, .vector3d.py
398 '''6-Tuple C{(closest, distance, fi, j, start, end)} with the C{closest}
399 point, the C{distance} in C{meter}, conventionally and the C{start}
400 and C{end} point of the path or polygon edge. Fractional index C{fi}
401 (an L{FIx} instance) and index C{j} indicate the path or polygon edge
402 and the fraction along that edge with the C{closest} point. The
403 C{start} and C{end} points may differ from the given path or polygon
404 points at indices C{fi} respectively C{j}, when unrolled (C{wrap} is
405 C{True}). Also, the C{start} and/or C{end} point may be the same
406 instance as the C{closest} point, for example when the very first
407 path or polygon point is the nearest.
408 '''
409 _Names_ = (_closest_, _distance_, _fi_, _j_, _start_, _end_)
410 _Units_ = (_Pass, Meter, FIx, Number_, _Pass , _Pass)
413class NearestOn8Tuple(_NamedTuple): # .ellipsoidalBaseDI
414 '''8-Tuple C{(closest, distance, fi, j, start, end, initial, final)},
415 like L{NearestOn6Tuple} but extended with the C{initial} and the
416 C{final} bearing at the reference respectively the C{closest}
417 point, both in compass C{degrees}.
418 '''
419 _Names_ = NearestOn6Tuple._Names_ + Distance3Tuple._Names_[-2:]
420 _Units_ = NearestOn6Tuple._Units_ + Distance3Tuple._Units_[-2:]
423class PhiLam2Tuple(_NamedTuple): # .frechet, .hausdorff, .latlonBase, .points, .vector3d
424 '''2-Tuple C{(phi, lam)} with latitude C{phi} in C{radians[PI_2]}
425 and longitude C{lam} in C{radians[PI]}.
427 @note: Using C{phi/lambda} for lat-/longitude in C{radians}
428 follows Chris Veness' U{convention
429 <https://www.Movable-Type.co.UK/scripts/latlong.html>}.
430 '''
431 _Names_ = (_phi_, _lam_)
432 _Units_ = ( Phi, Lam)
434 def to3Tuple(self, height, **name):
435 '''Extend this L{PhiLam2Tuple} to a L{PhiLam3Tuple}.
437 @arg height: The height to add (C{scalar}).
438 @kwarg name: Optional C{B{name}=NN} (C{str}),
439 overriding this name.
441 @return: A L{PhiLam3Tuple}C{(phi, lam, height)}.
443 @raise ValueError: Invalid B{C{height}}.
444 '''
445 return self._xtend(PhiLam3Tuple, height, **name)
447 def to4Tuple(self, height, datum):
448 '''Extend this L{PhiLam2Tuple} to a L{PhiLam4Tuple}.
450 @arg height: The height to add (C{scalar}).
451 @arg datum: The datum to add (C{Datum}).
453 @return: A L{PhiLam4Tuple}C{(phi, lam, height, datum)}.
455 @raise TypeError: If B{C{datum}} not a C{Datum}.
457 @raise ValueError: Invalid B{C{height}}.
458 '''
459 return self.to3Tuple(height).to4Tuple(datum)
462class PhiLam3Tuple(_NamedTuple): # .nvector.py, extends -2Tuple
463 '''3-Tuple C{(phi, lam, height)} with latitude C{phi} in
464 C{radians[PI_2]}, longitude C{lam} in C{radians[PI]} and
465 C{height} in C{meter}.
467 @note: Using C{phi/lambda} for lat-/longitude in C{radians}
468 follows Chris Veness' U{convention
469 <https://www.Movable-Type.co.UK/scripts/latlong.html>}.
470 '''
471 _Names_ = (_phi_, _lam_, _height_)
472 _Units_ = ( Phi, Lam, Height)
474 def to4Tuple(self, datum, **name):
475 '''Extend this L{PhiLam3Tuple} to a L{PhiLam4Tuple}.
477 @arg datum: The datum to add (C{Datum}).
478 @kwarg name: Optional C{B{name}=NN} (C{str}),
479 overriding this name.
481 @return: A L{PhiLam4Tuple}C{(phi, lam, height, datum)}.
483 @raise TypeError: If B{C{datum}} not a C{Datum}.
484 '''
485 _xinstanceof(_MODS.datums.Datum, datum=datum)
486 return self._xtend(PhiLam4Tuple, datum, **name)
489class PhiLam4Tuple(_NamedTuple): # extends -3Tuple
490 '''4-Tuple C{(phi, lam, height, datum)} with latitude C{phi} in
491 C{radians[PI_2]}, longitude C{lam} in C{radians[PI]}, C{height}
492 in C{meter} and L{Datum}.
494 @note: Using C{phi/lambda} for lat-/longitude in C{radians}
495 follows Chris Veness' U{convention
496 <https://www.Movable-Type.co.UK/scripts/latlong.html>}.
497 '''
498 _Names_ = (_phi_, _lam_, _height_, _datum_)
499 _Units_ = ( Phi, Lam, Height, _Pass)
502class Point3Tuple(_NamedTuple):
503 '''3-Tuple C{(x, y, ll)} in C{meter}, C{meter} and C{LatLon}.
504 '''
505 _Names_ = (_x_, _y_, _elel_)
506 _Units_ = ( Meter, Meter, _Pass)
509class Points2Tuple(_NamedTuple): # .formy, .latlonBase
510 '''2-Tuple C{(number, points)} with the C{number} of points
511 and -possible reduced- C{list} or C{tuple} of C{points}.
512 '''
513 _Names_ = (_number_, _points_)
514 _Units_ = ( Number_, _Pass)
517class Reverse4Tuple(_NamedTuple, _Convergence):
518 '''4-Tuple C{(lat, lon, gamma, scale)} with C{lat}- and
519 C{lon}gitude in C{degrees}, meridian convergence C{gamma}
520 at point in C{degrees} and the C{scale} of projection at
521 point C{scalar}.
522 '''
523 _Names_ = (_lat_, _lon_, _gamma_, _scale_)
524 _Units_ = ( Lat, Lon, Degrees, Scalar)
527class Triangle7Tuple(_NamedTuple):
528 '''7-Tuple C{(A, a, B, b, C, c, area)} with interior angles C{A},
529 C{B} and C{C} in C{degrees}, spherical sides C{a}, C{b} and C{c}
530 in C{meter} conventionally and the C{area} of a (spherical)
531 triangle in I{square} C{meter} conventionally.
532 '''
533 _Names_ = (_A_, _a_, _B_, _b_, _C_, _c_, _area_)
534 _Units_ = ( Degrees, Meter, Degrees, Meter, Degrees, Meter, Meter2)
537class Triangle8Tuple(_NamedTuple):
538 '''8-Tuple C{(A, a, B, b, C, c, D, E)} with interior angles C{A},
539 C{B} and C{C}, spherical sides C{a}, C{b} and C{c}, the I{spherical
540 deficit} C{D} and the I{spherical excess} C{E} of a (spherical)
541 triangle, all in C{radians}.
542 '''
543 _Names_ = (_A_, _a_, _B_, _b_, _C_, _c_, _D_, _E_)
544 _Units_ = ( Radians, Radians, Radians, Radians, Radians, Radians, Radians, Radians)
547class Trilaterate5Tuple(_NamedTuple): # .latlonBase, .nvector
548 '''5-Tuple C{(min, minPoint, max, maxPoint, n)} with C{min} and C{max}
549 in C{meter}, the corresponding trilaterated C{minPoint} and C{maxPoint}
550 as C{LatLon} and the number C{n}. For area overlap, C{min} and C{max}
551 are the smallest respectively largest overlap found. For perimeter
552 intersection, C{min} and C{max} represent the closest respectively
553 farthest intersection margin. Count C{n} is the total number of
554 trilaterated overlaps or intersections found, C{0, 1, 2...6} with
555 C{0} meaning concentric.
557 @see: The C{ellipsoidalKarney-}, C{ellipsoidalVincenty-} and
558 C{sphericalTrigonometry.LatLon.trilaterate5} method for further
559 details on corner cases, like concentric or single trilaterated
560 results.
561 '''
562 _Names_ = (min.__name__, 'minPoint', max.__name__, 'maxPoint', _n_)
563 _Units_ = (Meter, _Pass, Meter, _Pass, Number_)
566class UtmUps2Tuple(_NamedTuple): # .epsg.py
567 '''2-Tuple C{(zone, hemipole)} as C{int} and C{str}, where
568 C{zone} is C{1..60} for UTM or C{0} for UPS and C{hemipole}
569 C{'N'|'S'} is the UTM hemisphere or the UPS pole.
570 '''
571 _Names_ = (_zone_, _hemipole_)
572 _Units_ = ( Number_, Str)
575class UtmUps5Tuple(_NamedTuple): # .mgrs.py, .ups.py, .utm.py, .utmups.py
576 '''5-Tuple C{(zone, hemipole, easting, northing, band)} as C{int},
577 C{str}, C{meter}, C{meter} and C{band} letter, where C{zone} is
578 C{1..60} for UTM or C{0} for UPS, C{hemipole} C{'N'|'S'} is the UTM
579 hemisphere or the UPS pole and C{band} is C{""} or the I{longitudinal}
580 UTM band C{'C'|'D'|..|'W'|'X'} or I{polar} UPS band C{'A'|'B'|'Y'|'Z'}.
581 '''
582 _Names_ = (_zone_, _hemipole_, _easting_, _northing_, _band_)
583 _Units_ = ( Number_, Str, Easting, Northing, Band)
585 def __new__(cls, z, h, e, n, B, Error=None, **name):
586 if Error is not None:
587 e = Easting( e, Error=Error)
588 n = Northing(n, Error=Error)
589 return _NamedTuple.__new__(cls, z, h, e, n, B, **name)
592class UtmUps8Tuple(_NamedTuple, _Convergence): # .ups, .utm, .utmups
593 '''8-Tuple C{(zone, hemipole, easting, northing, band, datum,
594 gamma, scale)} as C{int}, C{str}, C{meter}, C{meter}, C{band}
595 letter, C{Datum}, C{degrees} and C{scalar}, where C{zone} is
596 C{1..60} for UTM or C{0} for UPS, C{hemipole} C{'N'|'S'} is
597 the UTM hemisphere or the UPS pole and C{band} is C{""} or
598 the I{longitudinal} UTM band C{'C'|'D'|..|'W'|'X'} or
599 I{polar} UPS band C{'A'|'B'|'Y'|'Z'}.
600 '''
601 _Names_ = (_zone_, _hemipole_, _easting_, _northing_,
602 _band_, _datum_, _gamma_, _scale_)
603 _Units_ = ( Number_, Str, Easting, Northing,
604 Band, _Pass, Degrees, Scalar)
606 def __new__(cls, z, h, e, n, B, d, g, s, Error=None, **name): # PYCHOK 11 args
607 if Error is not None:
608 e = Easting( e, Error=Error)
609 n = Northing(n, Error=Error)
610 g = Degrees(gamma=g, Error=Error)
611 s = Scalar(scale=s, Error=Error)
612 return _NamedTuple.__new__(cls, z, h, e, n, B, d, g, s, **name)
615class UtmUpsLatLon5Tuple(_NamedTuple): # .ups.py, .utm.py, .utmups.py
616 '''5-Tuple C{(zone, band, hemipole, lat, lon)} as C{int},
617 C{str}, C{str}, C{degrees90} and C{degrees180}, where
618 C{zone} is C{1..60} for UTM or C{0} for UPS, C{band} is
619 C{""} or the I{longitudinal} UTM band C{'C'|'D'|..|'W'|'X'}
620 or I{polar} UPS band C{'A'|'B'|'Y'|'Z'} and C{hemipole}
621 C{'N'|'S'} is the UTM hemisphere or the UPS pole.
622 '''
623 _Names_ = (_zone_, _band_, _hemipole_, _lat_, _lon_)
624 _Units_ = ( Number_, Band, Str, Lat, Lon)
626 def __new__(cls, z, B, h, lat, lon, Error=None, **name):
627 if Error is not None:
628 lat = Lat(lat, Error=Error)
629 lon = Lon(lon, Error=Error)
630 return _NamedTuple.__new__(cls, z, B, h, lat, lon, **name)
633class Vector2Tuple(_NamedTuple):
634 '''2-Tuple C{(x, y)} of (geocentric) components, each in
635 C{meter} or the same C{units}.
636 '''
637 _Names_ = (_x_, _y_)
638 _Units_ = ( Scalar, Scalar)
640 def toCartesian(self, Cartesian, **Cartesian_kwds):
641 '''Return this C{Vector2Tuple} as a C{Cartesian}.
643 @arg Cartesian: The C{Cartesian} class to use.
644 @kwarg Cartesian_kwds: Optional, additional C{Cartesian}
645 keyword arguments.
647 @return: The C{B{Cartesian}} instance with C{z=0}.
648 '''
649 return _v2Cls(self.xyz, Cartesian, Cartesian_kwds)
651 def to3Tuple(self, z=INT0, **name):
652 '''Extend this L{Vector2Tuple} to a L{Vector3Tuple}.
654 @kwarg z: The Z component add (C{scalar}).
655 @kwarg name: Optional C{B{name}=NN} (C{str}),
656 overriding this name.
658 @return: A L{Vector3Tuple}C{(x, y, z)}.
660 @raise ValueError: Invalid B{C{z}}.
661 '''
662 return self._xtend(Vector3Tuple, z, **name)
664 @property_RO
665 def xyz(self):
666 '''Get X, Y and Z=0 components (C{Vector3Tuple}).
667 '''
668 return Vector3Tuple(*self.xyz3)
670 @property_RO
671 def xyz3(self):
672 '''Get X, Y and Z=0 components as C{3-tuple}.
673 '''
674 return self.x, self.y, INT0
677class Vector3Tuple(_NamedTuple):
678 '''3-Tuple C{(x, y, z)} of (geocentric) components, all in
679 C{meter} or the same C{units}.
680 '''
681 _Names_ = (_x_, _y_, _z_)
682 _Units_ = ( Scalar, Scalar, Scalar)
684 def toCartesian(self, Cartesian, **Cartesian_kwds):
685 '''Return this C{Vector3Tuple} as a C{Cartesian}.
687 @arg Cartesian: The C{Cartesian} class to use.
688 @kwarg Cartesian_kwds: Optional, additional C{Cartesian}
689 keyword arguments.
691 @return: The C{B{Cartesian}} instance.
692 '''
693 return _v2Cls(self, Cartesian, Cartesian_kwds)
695 def to4Tuple(self, h=INT0, **name):
696 '''Extend this L{Vector3Tuple} to a L{Vector4Tuple}.
698 @arg h: The height to add (C{scalar}).
699 @kwarg name: Optional C{B{name}=NN} (C{str}),
700 overriding this name.
702 @return: A L{Vector4Tuple}C{(x, y, z, h)}.
704 @raise ValueError: Invalid B{C{h}}.
705 '''
706 return self._xtend(Vector4Tuple, h, **name)
708 @property_RO
709 def xyz(self):
710 '''Get X, Y and Z components (C{Vector3Tuple}).
711 '''
712 return self
714 @property_RO
715 def xyz3(self):
716 '''Get X, Y and Z components as C{3-tuple}.
717 '''
718 return tuple(self)
721class Vector4Tuple(_NamedTuple): # .nvector.py
722 '''4-Tuple C{(x, y, z, h)} of (geocentric) components, all
723 in C{meter} or the same C{units}.
724 '''
725 _Names_ = (_x_, _y_, _z_, _h_)
726 _Units_ = ( Scalar, Scalar, Scalar, Height)
728 def toCartesian(self, Cartesian, **Cartesian_kwds):
729 '''Return this C{Vector4Tuple} as a C{Cartesian}.
731 @arg Cartesian: The C{Cartesian} class to use.
732 @kwarg Cartesian_kwds: Optional, additional C{Cartesian}
733 keyword arguments.
735 @return: The C{B{Cartesian}} instance.
736 '''
737 return _v2Cls(self, Cartesian, Cartesian_kwds)
739 def to3Tuple(self):
740 '''Reduce this L{Vector4Tuple} to a L{Vector3Tuple}.
742 @return: A L{Vector3Tuple}C{(x, y, z)}.
743 '''
744 return self.xyz
746 @property_RO
747 def xyz(self):
748 '''Get X, Y and Z components (L{Vector3Tuple}).
749 '''
750 return Vector3Tuple(*self.xyz)
752 @property_RO
753 def xyz3(self):
754 '''Get X, Y and Z components as C{3-tuple}.
755 '''
756 return tuple(self[:3])
759def _v2Cls(v, Cls, Cartesian_kwds): # in .vector3d
760 if issubclassof(Cls, _MODS.cartesianBase.CartesianBase): # _MODS.vector3d.Vector3d)
761 return Cls(v, **Cartesian_kwds)
762 raise _TypeError(Cartesian=Cls, **Cartesian_kwds)
764# **) MIT License
765#
766# Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
767#
768# Permission is hereby granted, free of charge, to any person obtaining a
769# copy of this software and associated documentation files (the "Software"),
770# to deal in the Software without restriction, including without limitation
771# the rights to use, copy, modify, merge, publish, distribute, sublicense,
772# and/or sell copies of the Software, and to permit persons to whom the
773# Software is furnished to do so, subject to the following conditions:
774#
775# The above copyright notice and this permission notice shall be included
776# in all copies or substantial portions of the Software.
777#
778# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
779# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
780# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
781# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
782# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
783# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
784# OTHER DEALINGS IN THE SOFTWARE.