Coverage for pygeodesy / namedTuples.py: 91%
366 statements
« prev ^ index » next coverage.py v7.14.0, created at 2026-09-08 14:37 -0400
« prev ^ index » next coverage.py v7.14.0, created at 2026-09-08 14:37 -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
13from pygeodesy.constants import INT0, NAN, _0_5, _isNAN, fabs # PYCHOK used! _0_5
14# from pygeodesy.dms import toDMS # _MODS
15from pygeodesy.errors import _TypeError, _xattr, _xkwds, _xkwds_not, _xkwds_pop2
16# from pygeodesy.internals import typename # from .named
17from pygeodesy.interns import NN, _1_, _2_, _a_, _A_, _area_, _angle_, _b_, _B_, \
18 _band_, _beta_, _c_, _C_, _D_, _datum_, _distance_, _E_, \
19 _easting_, _end_, _fi_, _gamma_, _H_, _h_, _height_, \
20 _hemipole_, _initial_, _j_, _lam_, _lat_, _lon_, _N_, \
21 _n_, _northing_, _number_, _outside_, _phi_, _point_, \
22 _precision_, _points_, _radius_, _scale_, _start_, \
23 _x_, _y_, _z_, _zone_
24# from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS # from .named
25from pygeodesy.named import _NamedTuple, _Pass, _ALL_LAZY, _MODS, typename
26from pygeodesy.props import deprecated_property_RO, Property_RO, property_RO
27from pygeodesy.units import Band, Bearing, Degrees, Degrees2, Easting, FIx, \
28 Height, Int, Lam, Lamd, Lat, Lon, Meter, Meter2, \
29 Northing, Number_, Phi, Phid, Precision_, Radians, \
30 Radius, Scalar, Str
31# from math import fabs # from .constants
33__all__ = _ALL_LAZY.namedTuples
34__version__ = '26.08.14'
36# __DUNDER gets mangled in class
37_closest_ = 'closest'
38_destination_ = 'destination'
39_elel_ = 'll'
40_final_ = 'final'
41_fraction_ = 'fraction'
44class Bearing2Tuple(_NamedTuple):
45 '''2-Tuple C{(initial, final)} bearings, both in compass C{degrees360}.
46 '''
47 _Names_ = (_initial_, _final_)
48 _Units_ = ( Bearing, Bearing)
51class Bounds2Tuple(_NamedTuple): # .geohash.py, .latlonBase.py, .points.py
52 '''2-Tuple C{(latlonSW, latlonNE)} with the bounds' lower-left and
53 upper-right corner as C{LatLon} instance.
54 '''
55 _Names_ = ('latlonSW', 'latlonNE')
56 _Units_ = (_Pass, _Pass)
59class Bounds4Tuple(_NamedTuple): # .geohash.py, .points.py
60 '''4-Tuple C{(latS, lonW, latN, lonE)} with the bounds' lower-left
61 C{(LatS, LonW)} and upper-right C{(latN, lonE)} corner lat- and
62 longitudes.
63 '''
64 _Names_ = ('latS', 'lonW', 'latN', 'lonE')
65 _Units_ = ( Lat, Lon, Lat, Lon)
67 def _dupof(self, which, *args, **name):
68 kwds = name or dict(name=self.name or typename(which))
69 return self.classof(*args, **kwds)
71 def enclosures(self, S_other, *W_N_E, **name):
72 '''Get the enclosures of this around an other L{Bounds4Tuple}.
74 @arg S_other: Bottom C{latS} (C{scalar}) or an other
75 L{Bounds4Tuple} instance.
76 @arg W_N_E: Left C{lonW}, top C{latN} and right C{lonE},
77 each a (C{scalar}) for C{scalar B{S_other}}.
79 @return: A L{Bounds4Tuple} with the I{margin} at each of
80 the 4 sides, positive if this side I{encloses}
81 (is on the I{outside} of) the other, negative
82 if not or zero if abutting.
83 '''
84 s, w, n, e, \
85 S, W, N, E = self._8_tuple(S_other, W_N_E)
86 return self._dupof(Bounds4Tuple.enclosures,
87 map1(float, S - s, W - w,
88 n - N, e - E), **name)
90 def isinside(self, lat, lon, eps=0):
91 '''Are B{C{lat}} and B{C{lon}} both inside these bounds?
93 @arg lat: Latitude (C{degrees}, scalar or C{str}).
94 @arg lon: Longitude (C{degrees}, scalar or C{str}).
95 @kwarg eps: Over-/undersize the C{RD} bounds (C{degrees}).
97 @return: C{False} if B{C{lat}} or B{C{lon}} is C{NAN} or
98 outside these bounds, C{True} otherwise.
99 '''
100 return _isinside(Lat(lat, clip=0), Lon(lon, clip=0),
101 Degrees(eps=eps) if eps else 0, self)
103 @Property_RO
104 def latC(self):
105 '''Get the center latitude (C{degrees}).
106 '''
107 return Lat(latC=(self.latS + self.latN) * _0_5)
109 @Property_RO
110 def latZ(self):
111 '''Get the latitudinal size (C{degrees}).
112 '''
113 return Lat(latZ=self.latN - self.latS)
115 @Property_RO
116 def lonC(self):
117 '''Get the center longitude (C{degrees}).
118 '''
119 return Lon(lonC=(self.lonW + self.lonE) * _0_5)
121 @Property_RO
122 def lonZ(self):
123 '''Get the longitudinal size (C{degrees}).
124 '''
125 return Lon(lonZ=self.lonE - self.lonW)
127 def overlap(self, S_other, *W_N_E, **name):
128 '''Intersect this with an other L{Bounds4Tuple}.
130 @arg S_other: Bottom C{latS} (C{scalar}) or an other
131 L{Bounds4Tuple} instance.
132 @arg W_N_E: Left C{lonW}, top C{latN} and right C{lonE},
133 each a (C{scalar}) for C{scalar B{S_other}}.
135 @return: C{None} if the bounds do not overlap, otherwise
136 the intersection of both as a L{Bounds4Tuple}.
137 '''
138 s, w, n, e, \
139 S, W, N, E = self._8_tuple(S_other, W_N_E)
140 return None if s > N or n < S or w > E or e < W else \
141 self._dupof(Bounds4Tuple.overlap,
142 max(s, S), max(w, W),
143 min(n, N), min(e, E), **name)
145 def resize(self, eps):
146 '''Get these bounds, over- or undersize by C{B{eps}}.
148 @arg eps: In- or decrease (C{degrees}).
150 @return: A L{Bounds4Tuple}C{(latS, lonW, latN, lonE)}
151 with all 4 bounds resized.
152 '''
153 return _resize4(self, Degrees(eps=eps))
155 def _8_tuple(self, S_other, W_N_E):
156 # return 8-tuple this (s, w, n, e) + other (S, W, N, E)
157 if W_N_E:
158 S_other = Bounds4Tuple(S_other, *W_N_E)
159 else:
160 _xinstanceof(Bounds4Tuple, S_other=S_other)
161 return tuple(self) + S_other.toUnits()
163 def union(self, S_other, *W_N_E, **name):
164 '''Union of this and an other L{Bounds4Tuple}.
166 @arg S_other: Bottom C{latS} (C{scalar}) or an other
167 L{Bounds4Tuple} instance.
168 @arg W_N_E: Left C{lonW}, top C{latN} and right C{lonE},
169 each a (C{scalar}) for C{scalar B{S_other}}.
171 @return: The C{union} a L{Bounds4Tuple}.
172 '''
173 s, w, n, e, \
174 S, W, N, E = self._8_tuple(S_other, W_N_E)
175 return self._dupof(Bounds4Tuple.union,
176 min(s, S), min(w, W),
177 max(n, N), max(e, E), **name)
180class Circle4Tuple(_NamedTuple):
181 '''4-Tuple C{(radius, height, lat, beta)} with the C{radius} and C{height}
182 of a parallel I{circle of latitude} at (geodetic) latitude C{lat} and
183 I{parametric (or reduced) auxiliary latitude} C{beta} on a I{biaxial
184 ellipsoid}.
186 The C{height} is the (signed) distance along the z-axis between the
187 parallel and the equator. At near-polar C{lat}s, the C{radius} is C{0},
188 the C{height} is the ellipsoid's polar radius (signed) and C{beta}
189 equals C{lat}. The latter are in C{degrees90}, always.
191 @see: Class L{Ellipse5Tuple}.
192 '''
193 _Names_ = (_radius_, _height_, _lat_, _beta_)
194 _Units_ = ( Radius, Height, Lat, Lat)
196 @property_RO
197 def abc3(self):
198 '''Get the non-negative semi-axes as 3-tuple C{(a, b, c)}.
199 '''
200 return map1(fabs, self.radius, self.radius, self.height) # PYCHOK named
203class Destination2Tuple(_NamedTuple): # .ellipsoidalKarney.py, -Vincenty.py
204 '''2-Tuple C{(destination, final)}, C{destination} in C{LatLon}
205 and C{final} bearing in compass C{degrees360}.
206 '''
207 _Names_ = (_destination_, _final_)
208 _Units_ = (_Pass, Bearing)
211class Destination3Tuple(_NamedTuple): # .karney.py
212 '''3-Tuple C{(lat, lon, final)}, destination C{lat}, C{lon} in
213 C{degrees90} respectively C{degrees180} and C{final} bearing
214 in compass C{degrees360}.
215 '''
216 _Names_ = (_lat_, _lon_, _final_)
217 _Units_ = ( Lat, Lon, Bearing)
220class Distance2Tuple(_NamedTuple): # .datum.py, .ellipsoidalBase.py
221 '''2-Tuple C{(distance, initial)}, C{distance} in C{meter} and
222 C{initial} bearing in compass C{degrees360}.
223 '''
224 _Names_ = (_distance_, _initial_)
225 _Units_ = ( Meter, Bearing)
228class Distance3Tuple(_NamedTuple): # .ellipsoidalKarney.py, -Vincenty.py
229 '''3-Tuple C{(distance, initial, final)}, C{distance} in C{meter}
230 and C{initial} and C{final} bearing, both in compass C{degrees360}.
231 '''
232 _Names_ = (_distance_, _initial_, _final_)
233 _Units_ = ( Meter, Bearing, Bearing)
236class Distance4Tuple(_NamedTuple): # .formy.py, .points.py
237 '''4-Tuple C{(distance2, delta_lat, delta_lon, unroll_lon2)} with
238 the distance in C{degrees squared}, the latitudinal C{delta_lat
239 = B{lat2} - B{lat1}}, the wrapped, unrolled and adjusted
240 longitudinal C{delta_lon = B{lon2} - B{lon1}} and C{unroll_lon2},
241 the unrolled or original B{C{lon2}}.
243 @note: Use Function L{pygeodesy.degrees2m} to convert C{degrees
244 squared} to C{meter} as M{degrees2m(sqrt(distance2), ...)}
245 or M{degrees2m(hypot(delta_lat, delta_lon), ...)}.
246 '''
247 _Names_ = ('distance2', 'delta_lat', 'delta_lon', 'unroll_lon2')
248 _Units_ = ( Degrees2, Degrees, Degrees, Degrees)
251class EasNor2Tuple(_NamedTuple): # .css, .osgr, .ups, .utm, .utmupsBase
252 '''2-Tuple C{(easting, northing)}, both in C{meter}, conventionally.
253 '''
254 _Names_ = (_easting_, _northing_)
255 _Units_ = ( Easting, Northing)
258class EasNor3Tuple(_NamedTuple): # .css.py, .lcc.py
259 '''3-Tuple C{(easting, northing, height)}, all in C{meter}, conventionally.
260 '''
261 _Names_ = (_easting_, _northing_, _height_)
262 _Units_ = ( Easting, Northing, Height)
265class _Convergence(object):
266 '''(INTERNAL) DEPRECATED Property C{convergence}, use property C{gamma}.'''
267 @deprecated_property_RO
268 def convergence(self):
269 '''DEPRECATED, use property C{gamma}.
270 '''
271 return self.gamma # PYCHOK self[.]
274class Ellipse5Tuple(_NamedTuple): # in .triaxials.bases._UnOrderedTriaxialBase.ellipse5
275 '''5-Tuple C{(a, b, height, lat, beta)} with semi-axes C{a} and C{b} of a parallel
276 I{ellipse of latitude} at (geodetic) latitude C{lat} and I{parametric (or reduced)
277 auxiliary latitude} C{beta} of a I{triaxial ellipsoid}.
279 The C{height} is the (signed) distance between the parallel and the triaxial's
280 equatorial plane. At near-polar C{lat}s, C{a} and C{b} are C{0}, the C{height}
281 is the triaxial semi-axis C{c} (signed) and C{beta} equals C{lat}. The latter
282 are in C{degrees90}, always.
284 @see: Class L{Circle4Tuple}.
285 '''
286 _Names_ = (_a_, _b_, _height_, _lat_, _beta_)
287 _Units_ = ( Radius, Radius, Height, Lat, Lat)
289 @property_RO
290 def abc3(self):
291 '''Get the semi-axes as 3-tuple C{(a, b, c)}, non-negative.
292 '''
293 return map1(fabs, self.a, self.b, self.height) # PYCHOK namedTuple
295 @property_RO
296 def abc3ordered(self):
297 '''Get the semi-axes as 3-tuple C{(a, b, c)}, non-negative, ordered.
298 '''
299 return tuple(reversed(sorted(self.abc3)))
301 def toTriaxial(self, **Triaxial_and_kwds): # like .Ellipse.toTriaxial_
302 '''Return a L{Triaxial_<pygeodesy.Triaxial>} from this tuple's semi-axes C{abc3ordered}.
304 @kwarg Triaxial_and_kwds: Optional C{B{Triaxial}=Triaxial} class and additional
305 C{Triaxial} keyword arguments.
306 '''
307 T, kwds = _xkwds_pop2(Triaxial_and_kwds, Triaxial=_MODS.triaxials.Triaxial)
308 return T(*self.abc3ordered, **_xkwds(kwds, name=self.name)) # 'NN'
310 def toTriaxial_(self, **Triaxial_and_kwds): # like .Ellipse.toTriaxial_
311 '''Return a L{Triaxial_<pygeodesy.Triaxial_>} from this tuple's semi-axes C{abc3}.
313 @kwarg Triaxial_and_kwds: Optional C{B{Triaxial}=Triaxial_} class and additional
314 C{Triaxial_} keyword arguments.
315 '''
316 T, kwds = _xkwds_pop2(Triaxial_and_kwds, Triaxial=_MODS.triaxials.Triaxial_)
317 return T(*self.abc3, **_xkwds(kwds, name=self.name)) # 'NN'
320class Forward4Tuple(_NamedTuple, _Convergence):
321 '''4-Tuple C{(easting, northing, gamma, scale)} in C{meter}, C{meter}, meridian
322 convergence C{gamma} at point in C{degrees} and the C{scale} of projection at
323 point C{scalar}.
324 '''
325 _Names_ = (_easting_, _northing_, _gamma_, _scale_)
326 _Units_ = ( Easting, Northing, Degrees, Scalar)
329class Intersection3Tuple(_NamedTuple): # .css.py, .lcc.py
330 '''3-Tuple C{(point, outside1, outside2)} of an intersection C{point} and C{outside1},
331 the position of the C{point}, C{-1} if before the start, C{+1} if after the end and
332 C{0} if on or between the start and end point of the first line.
334 Similarly, C{outside2} is C{-2}, C{+2} or C{0} to indicate the position of the
335 intersection C{point} on the second line or path.
337 If a path was specified with an initial bearing instead of an end point, C{outside1}
338 and/or C{outside2} will be C{0} if the intersection C{point} is on the start point
339 or C{+1} respectively C{+2} if the intersection C{point} is after the start point,
340 in the direction of the bearing.
341 '''
342 _Names_ = (_point_, _outside_ + _1_, _outside_ + _2_)
343 _Units_ = (_Pass, Int, Int)
346class LatLon2Tuple(_NamedTuple):
347 '''2-Tuple C{(lat, lon)} in C{degrees90} and C{degrees180}.
348 '''
349 _Names_ = (_lat_, _lon_)
350 _Units_ = ( Lat, Lon)
352 def to3Tuple(self, height, **name):
353 '''Extend this L{LatLon2Tuple} to a L{LatLon3Tuple}.
355 @arg height: The height to add (C{scalar}).
356 @kwarg name: Optional C{B{name}=NN} (C{str}), overriding
357 this name.
359 @return: A L{LatLon3Tuple}C{(lat, lon, height)}.
361 @raise ValueError: Invalid B{C{height}}.
362 '''
363 return self._xtend(LatLon3Tuple, height, **name)
365 def to4Tuple(self, height, datum, **name):
366 '''Extend this L{LatLon2Tuple} to a L{LatLon4Tuple}.
368 @arg height: The height to add (C{scalar}).
369 @arg datum: The datum to add (C{Datum}).
370 @kwarg name: Optional C{B{name}=NN} (C{str}), overriding
371 this name.
373 @return: A L{LatLon4Tuple}C{(lat, lon, height, datum)}.
375 @raise TypeError: If B{C{datum}} not a C{Datum}.
377 @raise ValueError: Invalid B{C{height}}.
378 '''
379 return self.to3Tuple(height).to4Tuple(datum, **name)
382class LatLon3Tuple(_NamedTuple):
383 '''3-Tuple C{(lat, lon, height)} in C{degrees90}, C{degrees180}
384 and C{meter}, conventionally.
385 '''
386 _Names_ = LatLon2Tuple._Names_ + (_height_,)
387 _Units_ = LatLon2Tuple._Units_ + ( Height,)
389 def to4Tuple(self, datum, **name):
390 '''Extend this L{LatLon3Tuple} to a L{LatLon4Tuple}.
392 @arg datum: The datum to add (C{Datum}).
393 @kwarg name: Optional C{B{name}=NN} (C{str}), overriding
394 this name.
396 @return: A L{LatLon4Tuple}C{(lat, lon, height, datum)}.
398 @raise TypeError: If B{C{datum}} not a C{Datum}.
399 '''
400 _xinstanceof(_MODS.datums.Datum, datum=datum)
401 return self._xtend(LatLon4Tuple, datum, **name)
404class LatLon4Tuple(LatLon3Tuple): # .cartesianBase, .css, .ecef, .lcc
405 '''4-Tuple C{(lat, lon, height, datum)} in C{degrees90},
406 C{degrees180}, C{meter} and L{Datum}.
407 '''
408 _Names_ = LatLon3Tuple._Names_ + (_datum_,)
409 _Units_ = LatLon3Tuple._Units_ + (_Pass,)
412def _LL4Tuple(lat, lon, height, datum, LatLon, LatLon_kwds, inst=None,
413 iteration=None, **name):
414 '''(INTERNAL) Return a L{LatLon4Tuple} or a B{C{LatLon}} instance.
415 '''
416 if LatLon is None: # ignore LatLon_kwds
417 r = LatLon4Tuple(lat, lon, height, datum, **name)
418 else:
419 kwds = {} if inst is None else _xkwds_not(None,
420# datum=_xattr(inst, datum=None),
421 epoch=_xattr(inst, epoch=None),
422 reframe=_xattr(inst, reframe=None)) # PYCHOK indent
423 kwds.update(datum=datum, height=height, **name)
424 if LatLon_kwds:
425 kwds.update(LatLon_kwds)
426 r = LatLon(lat, lon, **kwds)
427 if iteration is not None: # like .named._namedTuple.__new__
428 r._iteration = iteration
429 return r
432class LatLonDatum3Tuple(_NamedTuple): # .lcc.py, .osgr.py
433 '''3-Tuple C{(lat, lon, datum)} in C{degrees90}, C{degrees180}
434 and L{Datum}.
435 '''
436 _Names_ = LatLon2Tuple._Names_ + (_datum_,)
437 _Units_ = LatLon2Tuple._Units_ + (_Pass,)
440class LatLonDatum5Tuple(LatLonDatum3Tuple, _Convergence): # .ups.py, .utm.py, .utmupsBase.py
441 '''5-Tuple C{(lat, lon, datum, gamma, scale)} in C{degrees90},
442 C{degrees180}, L{Datum}, C{degrees} and C{float}.
443 '''
444 _Names_ = LatLonDatum3Tuple._Names_ + (_gamma_, _scale_)
445 _Units_ = LatLonDatum3Tuple._Units_ + ( Degrees, Scalar)
448class LatLonHeight3Tuple(_NamedTuple): # pyaxqg, pybelbg
449 '''3-Tuple C{(lat, lon, H)} with orthometric height C{H} in C{meter}, conventionally.
450 '''
451 _Names_ = LatLon2Tuple._Names_ + (_H_,)
452 _Units_ = LatLon3Tuple._Units_
455class LatLonNgeoid3Tuple(_NamedTuple): # pyaxqg, pybelbg
456 '''3-Tuple C{(lat, lon, N)} with geoid height C{N} in C{meter}, conventionally.
457 '''
458 _Names_ = LatLon2Tuple._Names_ + (_N_,)
459 _Units_ = LatLon3Tuple._Units_
462class _H_lat_lon_height4Tuple(_NamedTuple): # pyaxqg, pybelbg, pyrdnap
463 '''4-Tuple C{(H, lat, lon, height)} (INTERNAL) Base and "middle" tuple for
464 C{pyaxqg.AxQG8Tuple}, C{pybelbg.BeLBG7Tuple} and C{pyrdnap.RDNAP7Tuple}.
465 '''
466 _Names_ = (_H_, _lat_, _lon_, _height_) # middle names, extended
467 _Units_ = ( Height, Lat, Lon, Height) # middle units, extended
469 @Property_RO
470 def h(self):
471 '''Get the ellipsoidal height C{h} (C{meter}, conventionally) or C{NAN}.
472 '''
473 return self.height # PYCHOK height
475 @Property_RO
476 def lam(self):
477 '''Get the longitude (B{C{radians}}).
478 '''
479 return Lamd(self.lon) # PYCHOK lon
481 @Property_RO
482 def latlon(self):
483 '''Get the lat-, longitude in C{degrees} (L{LatLon2Tuple}C{(lat, lon)}).
484 '''
485 return LatLon2Tuple(self.lat, self.lon, name=self.name) # PYCHOK lat, lon, name
487 @Property_RO
488 def latlonheight(self):
489 '''Get the lat-, longitude in C{degrees} and ellipsoidal height (L{LatLon3Tuple}C{(lat, lon, height)}).
490 '''
491 return self.latlon.to3Tuple(self.height) # PYCHOK height
493 @Property_RO
494 def latlonHeight(self):
495 '''Get the lat-, longitude in C{degrees} and orthometric height (L{LatLonHeight3Tuple}C{(lat, lon, H)}).
496 '''
497 return LatLonHeight3Tuple(self.lat, self.lon, self.H) # PYCHOK lat, lon, H
499 @Property_RO
500 def latlonNgeoid(self):
501 '''Get the lat-, longitude in C{degrees} and geoid height (L{LatLonNgeoid3Tuple}C{(lat, lon, N)}).
502 '''
503 return LatLonNgeoid3Tuple(self.lat, self.lon, self.N, name=self.name) # PYCHOK lat, lon, name
505 @Property_RO
506 def N(self):
507 '''Get the geoid height C{N} (C{meter}, conventionally) or C{NAN}.
508 '''
509 N = self.height - self.H # PYCHOK height, H
510 return NAN if _isNAN(N) else Height(N=N)
512 @Property_RO
513 def phi(self):
514 '''Get the latitude (B{C{radians}}).
515 '''
516 return Phid(self.lat) # PYCHOK lat
518 @Property_RO
519 def philam(self):
520 '''Get the lat- and longitude in C{radians} (L{PhiLam2Tuple}C{(phi, lam)}).
521 '''
522 return PhiLam2Tuple(self.phi, self.lam, name=self.name) # PYCHOK lam, phi
524 @Property_RO
525 def philamheight(self):
526 '''Get the lat-, longitude in C{radians} and ellipsoidal height (L{PhiLam3Tuple}C{(phi, lam, height)}).
527 '''
528 return self.philam.to3Tuple(self.height) # PYCHOK height
530 @Property_RO
531 def philamHeight(self):
532 '''Get the lat-, longitude in C{radians} and orthometric height (L{PhiLamHeight3Tuple}C{(phi, lam, H)}).
533 '''
534 return PhiLamHeight3Tuple(self.phi, self.lam, self.H) # PYCHOK phi, lam, H
536 @Property_RO
537 def philamNgeoid(self):
538 '''Get the lat-, longitude in C{radians} and geoid height (L{PhiLamNgeoid3Tuple}C{(phi, lam, N)}).
539 '''
540 return PhiLamNgeoid3Tuple(self.phi, self.lam, self.N, name=self.name) # PYCHOK phi, lam, name
543class LatLonPrec3Tuple(_NamedTuple): # .gars.py, .wgrs.py
544 '''3-Tuple C{(lat, lon, precision)} in C{degrees}, C{degrees}
545 and C{int}.
546 '''
547 _Names_ = (_lat_, _lon_, _precision_)
548 _Units_ = ( Lat, Lon, Precision_)
550 def to5Tuple(self, height, radius, **name):
551 '''Extend this L{LatLonPrec3Tuple} to a L{LatLonPrec5Tuple}.
553 @arg height: The height to add (C{float} or C{None}).
554 @arg radius: The radius to add (C{float} or C{None}).
555 @kwarg name: Optional C{B{name}=NN} (C{str}), overriding
556 this name.
558 @return: A L{LatLonPrec5Tuple}C{(lat, lon, precision,
559 height, radius)}.
560 '''
561 return self._xtend(LatLonPrec5Tuple, height, radius, **name)
564class LatLonPrec5Tuple(LatLonPrec3Tuple): # .wgrs.py
565 '''5-Tuple C{(lat, lon, precision, height, radius)} in C{degrees},
566 C{degrees}, C{int} and C{height} or C{radius} in C{meter} (or
567 C{None} if missing).
568 '''
569 _Names_ = LatLonPrec3Tuple._Names_ + (_height_, _radius_)
570 _Units_ = LatLonPrec3Tuple._Units_ + ( Height, Radius)
573class _NamedTupleTo(_NamedTuple): # in .testNamedTuples
574 '''(INTERNAL) Base for C{-.toDegrees}, C{-.toRadians}.
575 '''
576 def _Degrees3(self, *xs, **toDMS_kwds):
577 '''(INTERNAL) Convert C{xs} from C{Radians} to C{Degrees} or C{toDMS}.
578 '''
579 if toDMS_kwds:
580 toDMS_kwds = _xkwds(toDMS_kwds, ddd=1, pos=NN)
581 toDMS, s = _MODS.dms.toDMS, None
582 else:
583 toDMS, s = None, self
584 for x in xs:
585 if not isinstanceof(x, Degrees):
586 x, s = x.toDegrees(), None
587 yield toDMS(x, **toDMS_kwds) if toDMS else x
588 yield s
590 def _Radians3(self, *xs, **unused):
591 '''(INTERNAL) Convert C{xs} from C{Degrees} to C{Radians}.
592 '''
593 s = self
594 for x in xs:
595 if not isinstanceof(x, Radians):
596 x, s = x.toRadians(), None
597 yield x
598 yield s
601class NearestOn2Tuple(_NamedTuple): # .ellipsoidalBaseDI
602 '''2-Tuple C{(closest, fraction)} of the C{closest} point
603 on and C{fraction} along a line (segment) between two
604 points. The C{fraction} is C{0} if the closest point
605 is the first or C{1} the second of the two points.
606 Negative C{fraction}s indicate the closest point is
607 C{before} the first point. For C{fraction > 1.0}
608 the closest point is after the second point.
609 '''
610 _Names_ = (_closest_, _fraction_)
611 _Units_ = (_Pass, _Pass)
614class NearestOn3Tuple(_NamedTuple): # .points.py, .sphericalTrigonometry
615 '''3-Tuple C{(closest, distance, angle)} of the C{closest}
616 point on the polygon, either a C{LatLon} instance or a
617 L{LatLon3Tuple}C{(lat, lon, height)} and the C{distance}
618 and C{angle} to the C{closest} point are in C{meter}
619 respectively compass C{degrees360}.
620 '''
621 _Names_ = (_closest_, _distance_, _angle_)
622 _Units_ = (_Pass, Meter, Degrees)
625# NearestOn4Tuple DEPRECATED, see .deprecated.classes.NearestOn4Tuple
628class NearestOn5Tuple(_NamedTuple):
629 '''5-Tuple C{(lat, lon, distance, angle, height)} all in C{degrees},
630 except C{height}. The C{distance} is the L{pygeodesy.equirectangular}
631 distance between the closest and the reference B{C{point}} in C{degrees}.
632 The C{angle} from the reference B{C{point}} to the closest point is in
633 compass C{degrees360}, see function L{pygeodesy.compassAngle}. The
634 C{height} is the (interpolated) height at the closest point in C{meter}
635 or C{0}.
636 '''
637 _Names_ = (_lat_, _lon_, _distance_, _angle_, _height_)
638 _Units_ = ( Lat, Lon, Degrees, Degrees, Meter)
641class NearestOn6Tuple(_NamedTuple): # .latlonBase.py, .vector3d.py
642 '''6-Tuple C{(closest, distance, fi, j, start, end)} with the C{closest}
643 point, the C{distance} in C{meter}, conventionally and the C{start}
644 and C{end} point of the path or polygon edge. Fractional index C{fi}
645 (an L{FIx} instance) and index C{j} indicate the path or polygon edge
646 and the fraction along that edge with the C{closest} point. The
647 C{start} and C{end} points may differ from the given path or polygon
648 points at indices C{fi} respectively C{j}, when unrolled (C{wrap} is
649 C{True}). Also, the C{start} and/or C{end} point may be the same
650 instance as the C{closest} point, for example when the very first
651 path or polygon point is the nearest.
652 '''
653 _Names_ = (_closest_, _distance_, _fi_, _j_, _start_, _end_)
654 _Units_ = (_Pass, Meter, FIx, Number_, _Pass , _Pass)
657class NearestOn8Tuple(_NamedTuple): # .ellipsoidalBaseDI
658 '''8-Tuple C{(closest, distance, fi, j, start, end, initial, final)},
659 like L{NearestOn6Tuple} but extended with the C{initial} and the
660 C{final} bearing at the reference respectively the C{closest}
661 point, both in compass C{degrees}.
662 '''
663 _Names_ = NearestOn6Tuple._Names_ + Distance3Tuple._Names_[-2:]
664 _Units_ = NearestOn6Tuple._Units_ + Distance3Tuple._Units_[-2:]
667class PhiLam2Tuple(_NamedTuple): # .frechet, .hausdorff, .latlonBase, .points, .vector3d
668 '''2-Tuple C{(phi, lam)} with latitude C{phi} and
669 longitude C{lam}, both in C{radians}.
670 '''
671 _Names_ = (_phi_, _lam_)
672 _Units_ = ( Phi, Lam)
674 def to3Tuple(self, height, **name):
675 '''Extend this L{PhiLam2Tuple} to a L{PhiLam3Tuple}.
677 @arg height: The height to add (C{scalar}).
678 @kwarg name: Optional C{B{name}=NN} (C{str}),
679 overriding this name.
681 @return: A L{PhiLam3Tuple}C{(phi, lam, height)}.
683 @raise ValueError: Invalid B{C{height}}.
684 '''
685 return self._xtend(PhiLam3Tuple, height, **name)
687 def to4Tuple(self, height, datum):
688 '''Extend this L{PhiLam2Tuple} to a L{PhiLam4Tuple}.
690 @arg height: The height to add (C{scalar}).
691 @arg datum: The datum to add (C{Datum}).
693 @return: A L{PhiLam4Tuple}C{(phi, lam, height, datum)}.
695 @raise TypeError: If B{C{datum}} not a C{Datum}.
697 @raise ValueError: Invalid B{C{height}}.
698 '''
699 return self.to3Tuple(height).to4Tuple(datum)
702class PhiLam3Tuple(_NamedTuple): # .nvector.py, extends -2Tuple
703 '''3-Tuple C{(phi, lam, height)} with latitude C{phi} and
704 longitude C{lam}, both in C{radians} and C{height} in C{meter}.
705 '''
706 _Names_ = PhiLam2Tuple._Names_ + (_height_,)
707 _Units_ = PhiLam2Tuple._Units_ + (Height,)
709 def to4Tuple(self, datum, **name):
710 '''Extend this L{PhiLam3Tuple} to a L{PhiLam4Tuple}.
712 @arg datum: The datum to add (C{Datum}).
713 @kwarg name: Optional C{B{name}=NN} (C{str}),
714 overriding this name.
716 @return: A L{PhiLam4Tuple}C{(phi, lam, height, datum)}.
718 @raise TypeError: If B{C{datum}} not a C{Datum}.
719 '''
720 _xinstanceof(_MODS.datums.Datum, datum=datum)
721 return self._xtend(PhiLam4Tuple, datum, **name)
724class PhiLam4Tuple(_NamedTuple): # extends -3Tuple
725 '''4-Tuple C{(phi, lam, height, datum)} with latitude C{phi}
726 and longitude C{lam}, both in C{radians}, C{height} in
727 C{meter} and C{datum} (L{Datum}).
728 '''
729 _Names_ = PhiLam3Tuple._Names_ + (_datum_,)
730 _Units_ = PhiLam3Tuple._Units_ + (_Pass,)
733class PhiLamHeight3Tuple(_NamedTuple): # like LatLonHeight3Tuple
734 '''3-Tuple C{(phi, lam, H)} with latitude C{phi} and longitude C{lam},
735 both in C{radians} and orthometric height C{H} in C{meter}.
736 '''
737 _Names_ = PhiLam2Tuple._Names_ + (_H_,)
738 _Units_ = PhiLam3Tuple._Units_
741class PhiLamNgeoid3Tuple(_NamedTuple): # like LatLonNgeoid3Tuple
742 '''3-Tuple C{(phi, lam, N)} with latitude C{phi} and longitude
743 C{lam}, both in C{radians} and geoid height C{N} in C{meter}.
744 '''
745 _Names_ = PhiLam2Tuple._Names_ + (_N_,)
746 _Units_ = PhiLam3Tuple._Units_
749class Point3Tuple(_NamedTuple):
750 '''3-Tuple C{(x, y, ll)} in C{meter}, C{meter} and C{LatLon}.
751 '''
752 _Names_ = (_x_, _y_, _elel_)
753 _Units_ = ( Meter, Meter, _Pass)
756class Points2Tuple(_NamedTuple): # .formy, .latlonBase
757 '''2-Tuple C{(number, points)} with the C{number} of points
758 and -possible reduced- C{list} or C{tuple} of C{points}.
759 '''
760 _Names_ = (_number_, _points_)
761 _Units_ = ( Number_, _Pass)
764class RD4Tuple(_NamedTuple): # .ltp, pyrdnap
765 '''4-Tuple C{(minRDx, minRDy, maxRDx, maxRDy)} with the region bounds'
766 lower-left C{(minRDx, minRDy)} and upper-right C{(maxRDx, maxRDy)}
767 corner in C{meter}, conventionaly.
768 '''
769 _Names_ = ('minRDx', 'minRDy', 'maxRDx', 'maxRDy')
770 _Units_ = ( Meter, Meter, Meter, Meter)
772 def isinside(self, RDx, RDy, eps=0):
773 '''Are B{C{RDx}} and B{C{RDy}} both inside these C{RD} bounds?
775 @arg RDx: X coordinate (C{meter}).
776 @arg RDy: Y coordinate (C{meter}).
777 @kwarg eps: Over-/undersize the C{RD} bounds (C{meter}).
779 @return: C{False} if B{C{RDx}} or B{C{RDy}} is C{NAN} or
780 outsize these C{RD} bounds, C{True} otherwise.
781 '''
782 z = Meter(eps=eps) if eps else 0
783 return _isinside(Meter(RDx=RDx), Meter(RDy=RDy), z, self)
785 def resize(self, eps):
786 '''Get these bounds, over- or undersize by C{B{eps}}.
788 @arg eps: In- or decrease (C{degrees}).
790 @return: A L{RD4Tuple}C{(minRDx, minRDy, maxRDx, maxRDy)}
791 with all 4 bounds resized.
792 '''
793 return _resize4(self, Meter(eps=eps))
796class Reverse4Tuple(_NamedTuple, _Convergence):
797 '''4-Tuple C{(lat, lon, gamma, scale)} with C{lat}- and
798 C{lon}gitude in C{degrees}, meridian convergence C{gamma}
799 at point in C{degrees} and the C{scale} of projection at
800 point C{scalar}.
801 '''
802 _Names_ = (_lat_, _lon_, _gamma_, _scale_)
803 _Units_ = ( Lat, Lon, Degrees, Scalar)
806class Triangle7Tuple(_NamedTuple):
807 '''7-Tuple C{(A, a, B, b, C, c, area)} with interior angles C{A},
808 C{B} and C{C} in C{degrees}, spherical sides C{a}, C{b} and C{c}
809 in C{meter} conventionally and the C{area} of a (spherical)
810 triangle in I{square} C{meter} conventionally.
811 '''
812 _Names_ = (_A_, _a_, _B_, _b_, _C_, _c_, _area_)
813 _Units_ = ( Degrees, Meter, Degrees, Meter, Degrees, Meter, Meter2)
816class Triangle8Tuple(_NamedTuple):
817 '''8-Tuple C{(A, a, B, b, C, c, D, E)} with interior angles C{A},
818 C{B} and C{C}, spherical sides C{a}, C{b} and C{c}, the I{spherical
819 deficit} C{D} and the I{spherical excess} C{E} of a (spherical)
820 triangle, all in C{radians}.
821 '''
822 _Names_ = (_A_, _a_, _B_, _b_, _C_, _c_, _D_, _E_)
823 _Units_ = ( Radians, Radians, Radians, Radians, Radians, Radians, Radians, Radians)
826class Trilaterate5Tuple(_NamedTuple): # .latlonBase, .nvector
827 '''5-Tuple C{(min, minPoint, max, maxPoint, n)} with C{min} and C{max}
828 in C{meter}, the corresponding trilaterated C{minPoint} and C{maxPoint}
829 as C{LatLon} and the number C{n}. For area overlap, C{min} and C{max}
830 are the smallest respectively largest overlap found. For perimeter
831 intersection, C{min} and C{max} represent the closest respectively
832 farthest intersection margin. Count C{n} is the total number of
833 trilaterated overlaps or intersections found, C{0, 1, 2...6} with
834 C{0} meaning concentric.
836 @see: The C{ellipsoidalKarney-}, C{ellipsoidalVincenty-} and
837 C{sphericalTrigonometry.LatLon.trilaterate5} method for further
838 details on corner cases, like concentric or single trilaterated
839 results.
840 '''
841 _Names_ = (min.__name__, 'minPoint', max.__name__, 'maxPoint', _n_)
842 _Units_ = (Meter, _Pass, Meter, _Pass, Number_)
845class UtmUps2Tuple(_NamedTuple): # .epsg.py
846 '''2-Tuple C{(zone, hemipole)} as C{int} and C{str}, where
847 C{zone} is C{1..60} for UTM or C{0} for UPS and C{hemipole}
848 C{'N'|'S'} is the UTM hemisphere or the UPS pole.
849 '''
850 _Names_ = (_zone_, _hemipole_)
851 _Units_ = ( Number_, Str)
854class UtmUps5Tuple(_NamedTuple): # .mgrs.py, .ups.py, .utm.py, .utmups.py
855 '''5-Tuple C{(zone, hemipole, easting, northing, band)} as C{int},
856 C{str}, C{meter}, C{meter} and C{band} letter, where C{zone} is
857 C{1..60} for UTM or C{0} for UPS, C{hemipole} C{'N'|'S'} is the UTM
858 hemisphere or the UPS pole and C{band} is C{""} or the I{longitudinal}
859 UTM band C{'C'|'D'|..|'W'|'X'} or I{polar} UPS band C{'A'|'B'|'Y'|'Z'}.
860 '''
861 _Names_ = (_zone_, _hemipole_, _easting_, _northing_, _band_)
862 _Units_ = ( Number_, Str, Easting, Northing, Band)
864 def __new__(cls, z, h, e, n, B, Error=None, **name):
865 if Error is not None:
866 e = Easting( e, Error=Error)
867 n = Northing(n, Error=Error)
868 return _NamedTuple.__new__(cls, z, h, e, n, B, **name)
871class UtmUps8Tuple(_NamedTuple, _Convergence): # .ups, .utm, .utmups
872 '''8-Tuple C{(zone, hemipole, easting, northing, band, datum,
873 gamma, scale)} as C{int}, C{str}, C{meter}, C{meter}, C{band}
874 letter, C{Datum}, C{degrees} and C{scalar}, where C{zone} is
875 C{1..60} for UTM or C{0} for UPS, C{hemipole} C{'N'|'S'} is
876 the UTM hemisphere or the UPS pole and C{band} is C{""} or
877 the I{longitudinal} UTM band C{'C'|'D'|..|'W'|'X'} or
878 I{polar} UPS band C{'A'|'B'|'Y'|'Z'}.
879 '''
880 _Names_ = (_zone_, _hemipole_, _easting_, _northing_,
881 _band_, _datum_, _gamma_, _scale_)
882 _Units_ = ( Number_, Str, Easting, Northing,
883 Band, _Pass, Degrees, Scalar)
885 def __new__(cls, z, h, e, n, B, d, g, s, Error=None, **name): # PYCHOK 11 args
886 if Error is not None:
887 e = Easting( e, Error=Error)
888 n = Northing(n, Error=Error)
889 g = Degrees(gamma=g, Error=Error)
890 s = Scalar(scale=s, Error=Error)
891 return _NamedTuple.__new__(cls, z, h, e, n, B, d, g, s, **name)
894class UtmUpsLatLon5Tuple(_NamedTuple): # .ups.py, .utm.py, .utmups.py
895 '''5-Tuple C{(zone, band, hemipole, lat, lon)} as C{int},
896 C{str}, C{str}, C{degrees90} and C{degrees180}, where
897 C{zone} is C{1..60} for UTM or C{0} for UPS, C{band} is
898 C{""} or the I{longitudinal} UTM band C{'C'|'D'|..|'W'|'X'}
899 or I{polar} UPS band C{'A'|'B'|'Y'|'Z'} and C{hemipole}
900 C{'N'|'S'} is the UTM hemisphere or the UPS pole.
901 '''
902 _Names_ = (_zone_, _band_, _hemipole_, _lat_, _lon_)
903 _Units_ = ( Number_, Band, Str, Lat, Lon)
905 def __new__(cls, z, B, h, lat, lon, Error=None, **name):
906 if Error is not None:
907 lat = Lat(lat, Error=Error)
908 lon = Lon(lon, Error=Error)
909 return _NamedTuple.__new__(cls, z, B, h, lat, lon, **name)
912class Vector2Tuple(_NamedTuple):
913 '''2-Tuple C{(x, y)} of (geocentric) components, each in
914 C{meter} or the same C{units}.
915 '''
916 _Names_ = (_x_, _y_)
917 _Units_ = ( Scalar, Scalar)
919 def toCartesian(self, Cartesian, **Cartesian_kwds):
920 '''Return this C{Vector2Tuple} as a C{Cartesian}.
922 @arg Cartesian: The C{Cartesian} class to use.
923 @kwarg Cartesian_kwds: Optional, additional C{Cartesian}
924 keyword arguments.
926 @return: The C{B{Cartesian}} instance with C{z=0}.
927 '''
928 return _v2Cls(self.xyz, Cartesian, Cartesian_kwds)
930 def to3Tuple(self, z=INT0, **name):
931 '''Extend this L{Vector2Tuple} to a L{Vector3Tuple}.
933 @kwarg z: The Z component add (C{scalar}).
934 @kwarg name: Optional C{B{name}=NN} (C{str}),
935 overriding this name.
937 @return: A L{Vector3Tuple}C{(x, y, z)}.
939 @raise ValueError: Invalid B{C{z}}.
940 '''
941 return self._xtend(Vector3Tuple, z, **name)
943 @property_RO
944 def xyz(self):
945 '''Get X, Y and Z=0 components (C{Vector3Tuple}).
946 '''
947 return Vector3Tuple(*self.xyz3)
949 @property_RO
950 def xyz3(self):
951 '''Get X, Y and Z=0 components as C{3-tuple}.
952 '''
953 return self.x, self.y, INT0
956class Vector3Tuple(_NamedTuple):
957 '''3-Tuple C{(x, y, z)} of (geocentric) components, all in
958 C{meter} or the same C{units}.
959 '''
960 _Names_ = (_x_, _y_, _z_)
961 _Units_ = ( Scalar, Scalar, Scalar)
963 def toCartesian(self, Cartesian, **Cartesian_kwds):
964 '''Return this C{Vector3Tuple} as a C{Cartesian}.
966 @arg Cartesian: The C{Cartesian} class to use.
967 @kwarg Cartesian_kwds: Optional, additional C{Cartesian}
968 keyword arguments.
970 @return: The C{B{Cartesian}} instance.
971 '''
972 return _v2Cls(self, Cartesian, Cartesian_kwds)
974 def to4Tuple(self, h=INT0, **name):
975 '''Extend this L{Vector3Tuple} to a L{Vector4Tuple}.
977 @arg h: The height to add (C{scalar}).
978 @kwarg name: Optional C{B{name}=NN} (C{str}),
979 overriding this name.
981 @return: A L{Vector4Tuple}C{(x, y, z, h)}.
983 @raise ValueError: Invalid B{C{h}}.
984 '''
985 return self._xtend(Vector4Tuple, h, **name)
987 @property_RO
988 def xyz(self):
989 '''Get X, Y and Z components (C{Vector3Tuple}).
990 '''
991 return self
993 @property_RO
994 def xyz3(self):
995 '''Get X, Y and Z components as C{3-tuple}.
996 '''
997 return tuple(self)
1000class _xyzh_Tuple(_NamedTuple): # .nvector.py
1001 '''(INTERNAL) Base 4/5-Tuple for C{Vector4Tuple},
1002 and C{triaxials.triaxials3.Cartesian5Tuple}.
1003 '''
1004 _Names_ = (_x_, _y_, _z_, _h_)
1005 _Units_ = ( Scalar, Scalar, Scalar, Height)
1007 def toCartesian(self, Cartesian, **Cartesian_kwds):
1008 '''Return this tuple as a C{Cartesian}.
1010 @arg Cartesian: The C{Cartesian} class to use.
1011 @kwarg Cartesian_kwds: Optional, additional C{Cartesian}
1012 keyword arguments.
1014 @return: The C{B{Cartesian}} instance.
1015 '''
1016 return _v2Cls(self, Cartesian, Cartesian_kwds)
1018 def to3Tuple(self):
1019 '''Reduce this tuple to a L{Vector3Tuple}.
1021 @return: A L{Vector3Tuple}C{(x, y, z)}.
1022 '''
1023 return self.xyz
1025 @property_RO
1026 def xyz(self):
1027 '''Get X, Y and Z components (L{Vector3Tuple}).
1028 '''
1029 return Vector3Tuple(*self.xyz3)
1031 @property_RO
1032 def xyz3(self):
1033 '''Get X, Y and Z components as C{3-tuple}.
1034 '''
1035 return tuple(self[:3])
1038class Vector4Tuple(_xyzh_Tuple): # .nvector.py
1039 '''4-Tuple C{(x, y, z, h)} of (geocentric) components, all
1040 in C{meter} or the same C{units}.
1041 '''
1042 _Names_ = _xyzh_Tuple._Names_
1043 _Units_ = _xyzh_Tuple._Units_
1046def _isinside(x, y, eps, bounds4): # in pyaxqg, pybelbg
1047 '''(INTERNAL) Is C{x} and C{y} inside a bounds 4-tuple?
1049 @return: C{False} if C{x} or C{y} is C{NAN} or outside, C{True} otherwise.
1050 '''
1051 # _xinstanceof(Bounds4Tuple, LB4Tuple, RD4Tuple, bounds4=bound4)
1052 L, B, R, T = bounds4
1053 return ((L - x) <= eps and (x - R) <= eps and
1054 (B - y) <= eps and (y - T) <= eps) if eps else \
1055 (L <= x <= R and B <= y <= T)
1058def _resize2(lo, hi, eps):
1059 '''(INTERNAL) Resize 2-tuple C{(lo, hi)} by C{eps}.
1060 '''
1061 # assert lo <= hi
1062 if eps:
1063 lo -= eps
1064 hi += eps
1065 if lo > hi:
1066 lo = hi = (lo + hi) * _0_5
1067 return lo, hi
1070def _resize4(bounds4, eps): # in pyaxqg, pybelbg
1071 '''(INTERNAL) Resize a bounds 4-tuple by C{eps}.
1072 '''
1073 # _xinstanceof(Bounds4Tuple, LB4Tuple, RD4Tuple, bounds4=bounds4)
1074 L, B, R, T = bounds4
1075 if eps:
1076 L, R = _resize2(L, R, eps)
1077 B, T = _resize2(B, T, eps)
1078 return bounds4.classof(L, B, R, T, name=typename(bounds4.resize))
1081def _v2Cls(v, Cls, Cartesian_kwds): # in .vector3d
1082 if issubclassof(Cls, _MODS.cartesianBase.CartesianBase): # _MODS.vector3d.Vector3d)
1083 return Cls(v, **Cartesian_kwds)
1084 raise _TypeError(Cartesian=Cls, **Cartesian_kwds)
1086# **) MIT License
1087#
1088# Copyright (C) 2016-2026 -- mrJean1 at Gmail -- All Rights Reserved.
1089#
1090# Permission is hereby granted, free of charge, to any person obtaining a
1091# copy of this software and associated documentation files (the "Software"),
1092# to deal in the Software without restriction, including without limitation
1093# the rights to use, copy, modify, merge, publish, distribute, sublicense,
1094# and/or sell copies of the Software, and to permit persons to whom the
1095# Software is furnished to do so, subject to the following conditions:
1096#
1097# The above copyright notice and this permission notice shall be included
1098# in all copies or substantial portions of the Software.
1099#
1100# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1101# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1102# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1103# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
1104# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1105# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
1106# OTHER DEALINGS IN THE SOFTWARE.