1 2# -*- coding: utf-8 -*- 3 4u'''Named tuples. 5 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''' 10 11from pygeodesy.basics import _xinstanceof 12from pygeodesy.errors import _xkwds, _xkwds_not 13from pygeodesy.interns import NN, _a_, _A_, _angle_, _B_, _band_, _C_, \ 14 _convergence_, _datum_, _distance_, _E_, \ 15 _easting_, _epoch_, _h_, _height_, _hemipole_, \ 16 _lam_, _lat_, _lon_, _n_, _northing_, _number_, \ 17 _outside_, _phi_, _point_, _points_, _precision_, \ 18 _radius_, _reframe_, _scale_, _x_, _y_, _z_, \ 19 _zone_, _1_, _2_ 20from pygeodesy.lazily import _ALL_LAZY 21from pygeodesy.named import _NamedTuple, _Pass, _xnamed 22from pygeodesy.units import Band, Bearing, Degrees, Degrees2, Easting, \ 23 Height, Int, Lam, Lat, Lon, Meter, Meter2, \ 24 Northing, Number_, Phi, Precision_, \ 25 Radians, Radius, Scalar, Str 26 27__all__ = _ALL_LAZY.namedTuples 28__version__ = '21.09.09' 29 30# __DUNDER gets mangled in class 31_elel_ = 'll' 32_final_ = 'final' 33_initial_ = 'initial' 34 35 36class Bearing2Tuple(_NamedTuple): 37 '''2-Tuple C{(initial, final)} bearings, both in compass C{degrees360}. 38 ''' 39 _Names_ = (_initial_, _final_) 40 _Units_ = ( Bearing, Bearing) 41 42 43class Bounds2Tuple(_NamedTuple): # .geohash.py, .latlonBase.py, .points.py 44 '''2-Tuple C{(latlonSW, latlonNE)} with the bounds' lower-left and 45 upper-right corner as C{LatLon} instance. 46 ''' 47 _Names_ = ('latlonSW', 'latlonNE') 48 _Units_ = (_Pass, _Pass) 49 50 51class Bounds4Tuple(_NamedTuple): # .geohash.py, .points.py 52 '''4-Tuple C{(latS, lonW, latN, lonE)} with the bounds' lower-left 53 C{(LatS, LowW)} and upper-right C{(latN, lonE)} corner lat- and 54 longitudes. 55 ''' 56 _Names_ = ('latS', 'lonW', 'latN', 'lonE') 57 _Units_ = ( Lat, Lon, Lat, Lon) 58 59 60class Destination2Tuple(_NamedTuple): # .ellipsoidalKarney.py, -Vincenty.py 61 '''2-Tuple C{(destination, final)}, C{destination} in C{LatLon} 62 and C{final} bearing in compass C{degrees360}. 63 ''' 64 _Names_ = ('destination', _final_) 65 _Units_ = (_Pass, Bearing) 66 67 68class Destination3Tuple(_NamedTuple): # .karney.py 69 '''3-Tuple C{(lat, lon, final)}, destination C{lat}, C{lon} in 70 C{degrees90} respectively C{degrees180} and C{final} bearing 71 in compass C{degrees360}. 72 ''' 73 _Names_ = (_lat_, _lon_, _final_) 74 _Units_ = ( Lat, Lon, Bearing) 75 76 77class Distance2Tuple(_NamedTuple): # .datum.py, .ellipsoidalBase.py 78 '''2-Tuple C{(distance, initial)}, C{distance} in C{meter} and 79 C{initial} bearing in compass C{degrees360}. 80 ''' 81 _Names_ = (_distance_, _initial_) 82 _Units_ = ( Meter, Bearing) 83 84 85class Distance3Tuple(_NamedTuple): # .ellipsoidalKarney.py, -Vincenty.py 86 '''3-Tuple C{(distance, initial, final)}, C{distance} in C{meter} 87 and C{initial} and C{final} bearing, both in compass C{degrees360}. 88 ''' 89 _Names_ = (_distance_, _initial_, _final_) 90 _Units_ = ( Meter, Bearing, Bearing) 91 92 93class Distance4Tuple(_NamedTuple): # .formy.py, .points.py 94 '''4-Tuple C{(distance2, delta_lat, delta_lon, unroll_lon2)} with 95 the distance in C{degrees squared}, the latitudinal C{delta_lat 96 = B{lat2} - B{lat1}}, the wrapped, unrolled and adjusted 97 longitudinal C{delta_lon = B{lon2} - B{lon1}} and C{unroll_lon2}, 98 the unrolled or original B{C{lon2}}. 99 100 @note: Use Function L{degrees2m} to convert C{degrees squared} 101 to C{meter} as M{degrees2m(sqrt(distance2), ...)} or 102 M{degrees2m(hypot(delta_lat, delta_lon), ...)}. 103 ''' 104 _Names_ = ('distance2', 'delta_lat', 'delta_lon', 'unroll_lon2') 105 _Units_ = ( Degrees2, Degrees, Degrees, Degrees) 106 107 108class EasNor2Tuple(_NamedTuple): # .css.py, .osgr.py, .ups.py, .utm.py, .utmupsBase.py 109 '''2-Tuple C{(easting, northing)}, both in C{meter}, 110 conventionally. 111 ''' 112 _Names_ = (_easting_, _northing_) 113 _Units_ = ( Easting, Northing) 114 115 116class EasNor3Tuple(_NamedTuple): # .css.py, .lcc.py 117 '''3-Tuple C{(easting, northing, height)}, all in C{meter}, 118 conventionally. 119 ''' 120 _Names_ = (_easting_, _northing_, _height_) 121 _Units_ = ( Easting, Northing, Height) 122 123 124class Intersection3Tuple(_NamedTuple): # .css.py, .lcc.py 125 '''3-Tuple C{(point, outside1, outside2)} of an intersection 126 C{point} and C{outside1}, the position of the C{point}, 127 C{-1} if before the start, C{+1} if after the end and C{0} 128 if on or between the start and end point of the first line. 129 Similarly, C{outside2} is C{-2}, C{+2} or C{0} to indicate 130 the position of C{point} on the second line or path. If a 131 path was specified with an initial bearing instead of an 132 end point, C{outside1} and/or C{outside2} will be C{0} if 133 the intersection C{point} is on the start point or C{+1} 134 respectively C{+2} if the intersection C{point} is after 135 the start point, in the direction of the bearing. 136 ''' 137 _Names_ = (_point_, _outside_ + _1_, _outside_ + _2_) 138 _Units_ = (_Pass, Int, Int) 139 140 141class LatLon2Tuple(_NamedTuple): 142 '''2-Tuple C{(lat, lon)} in C{degrees90} and C{degrees180}. 143 ''' 144 _Names_ = (_lat_, _lon_) 145 _Units_ = ( Lat, Lon) 146 147 def to3Tuple(self, height): 148 '''Extend this L{LatLon2Tuple} to a L{LatLon3Tuple}. 149 150 @arg height: The height to add (C{scalar}). 151 152 @return: A L{LatLon3Tuple}C{(lat, lon, height)}. 153 154 @raise ValueError: Invalid B{C{height}}. 155 ''' 156 return self._xtend(LatLon3Tuple, height) 157 158 def to4Tuple(self, height, datum): 159 '''Extend this L{LatLon2Tuple} to a L{LatLon4Tuple}. 160 161 @arg height: The height to add (C{scalar}). 162 @arg datum: The datum to add (C{Datum}). 163 164 @return: A L{LatLon4Tuple}C{(lat, lon, height, datum)}. 165 166 @raise TypeError: If B{C{datum}} not a C{Datum}. 167 168 @raise ValueError: Invalid B{C{height}}. 169 ''' 170 return self.to3Tuple(height).to4Tuple(datum) 171 172 173class LatLon3Tuple(_NamedTuple): 174 '''3-Tuple C{(lat, lon, height)} in C{degrees90}, C{degrees180} 175 and C{meter}, conventionally. 176 ''' 177 _Names_ = (_lat_, _lon_, _height_) 178 _Units_ = ( Lat, Lon, Height) 179 180 def to4Tuple(self, datum): 181 '''Extend this L{LatLon3Tuple} to a L{LatLon4Tuple}. 182 183 @arg datum: The datum to add (C{Datum}). 184 185 @return: A L{LatLon4Tuple}C{(lat, lon, height, datum)}. 186 187 @raise TypeError: If B{C{datum}} not a C{Datum}. 188 ''' 189 from pygeodesy.datums import Datum 190 _xinstanceof(Datum, datum=datum) 191 return self._xtend(LatLon4Tuple, datum) 192 193 194class LatLon4Tuple(_NamedTuple): # .cartesianBase.py, .css.py, .ecef.py, .lcc.py 195 '''4-Tuple C{(lat, lon, height, datum)} in C{degrees90}, 196 C{degrees180}, C{meter} and L{Datum}. 197 ''' 198 _Names_ = (_lat_, _lon_, _height_, _datum_) 199 _Units_ = ( Lat, Lon, Height, _Pass) 200 201 202def _LL4Tuple(lat, lon, height, datum, LatLon, LatLon_kwds, inst=None, name=NN): 203 '''(INTERNAL) Return a L{LatLon4Tuple} or an B{C{LatLon}} instance. 204 ''' 205 if LatLon is None: 206 r = LatLon4Tuple(lat, lon, height, datum, name=name) 207 else: 208 r = {} if inst is None else _xkwds_not(None, 209 epoch= getattr(inst, _epoch_, None), 210 reframe=getattr(inst, _reframe_, None)) 211 kwds = _xkwds(LatLon_kwds, datum=datum, height=height, **r) 212 r = _xnamed(LatLon(lat, lon, **kwds), name) 213 return r 214 215 216class LatLonDatum3Tuple(_NamedTuple): # .lcc.py, .osgr.py 217 '''3-Tuple C{(lat, lon, datum)} in C{degrees90}, C{degrees180} 218 and L{Datum}. 219 ''' 220 _Names_ = (_lat_, _lon_, _datum_) 221 _Units_ = ( Lat, Lon, _Pass) 222 223 224class LatLonDatum5Tuple(_NamedTuple): # .ups.py, .utm.py, .utmupsBase.py 225 '''5-Tuple C{(lat, lon, datum, convergence, scale)} in 226 C{degrees90}, C{degrees180}, L{Datum}, C{degrees} 227 and C{float}. 228 ''' 229 _Names_ = (_lat_, _lon_, _datum_, _convergence_, _scale_) 230 _Units_ = ( Lat, Lon, _Pass, Degrees, Scalar) 231 232 233class LatLonPrec3Tuple(_NamedTuple): # .gars.py, .wgrs.py 234 '''3-Tuple C{(lat, lon, precision)} in C{degrees}, C{degrees} 235 and C{int}. 236 ''' 237 _Names_ = (_lat_, _lon_, _precision_) 238 _Units_ = ( Lat, Lon, Precision_) 239 240 def to5Tuple(self, height, radius): 241 '''Extend this L{LatLonPrec3Tuple} to a L{LatLonPrec5Tuple}. 242 243 @arg height: The height to add (C{float} or C{None}). 244 @arg radius: The radius to add (C{float} or C{None}). 245 246 @return: A L{LatLonPrec5Tuple}C{(lat, lon, precision, 247 height, radius)}. 248 ''' 249 return self._xtend(LatLonPrec5Tuple, height, radius) 250 251 252class LatLonPrec5Tuple(_NamedTuple): # .wgrs.py 253 '''5-Tuple C{(lat, lon, precision, height, radius)} in C{degrees}, 254 C{degrees}, C{int} and C{height} or C{radius} in C{meter} (or 255 C{None} if missing). 256 ''' 257 _Names_ = (_lat_, _lon_, _precision_, _height_, _radius_) 258 _Units_ = ( Lat, Lon, Precision_, Height, Radius) 259 260 261class NearestOn3Tuple(_NamedTuple): # .points.py, .sphericalTrigonometry.py 262 '''3-Tuple C{(closest, distance, angle)} of the C{closest} 263 point on the polygon, either a C{LatLon} instance or a 264 L{LatLon3Tuple}C{(lat, lon, height)} and the C{distance} 265 and C{angle} to the C{closest} point are in C{meter} 266 respectively compass C{degrees360}. 267 ''' 268 _Names_ = ('closest', _distance_, _angle_) 269 _Units_ = (_Pass, Meter, Degrees) 270 271 272class PhiLam2Tuple(_NamedTuple): # .frechet.py, .hausdorff.py, .latlonBase.py, .points.py, .vector3d.py 273 '''2-Tuple C{(phi, lam)} with latitude C{phi} in C{radians[PI_2]} 274 and longitude C{lam} in C{radians[PI]}. 275 276 @note: Using C{phi/lambda} for lat-/longitude in C{radians} 277 follows Chris Veness' U{convention 278 <https://www.Movable-Type.co.UK/scripts/latlong.html>}. 279 ''' 280 _Names_ = (_phi_, _lam_) 281 _Units_ = ( Phi, Lam) 282 283 def to3Tuple(self, height): 284 '''Extend this L{PhiLam2Tuple} to a L{PhiLam3Tuple}. 285 286 @arg height: The height to add (C{scalar}). 287 288 @return: A L{PhiLam3Tuple}C{(phi, lam, height)}. 289 290 @raise ValueError: Invalid B{C{height}}. 291 ''' 292 return self._xtend(PhiLam3Tuple, height) 293 294 def to4Tuple(self, height, datum): 295 '''Extend this L{PhiLam2Tuple} to a L{PhiLam4Tuple}. 296 297 @arg height: The height to add (C{scalar}). 298 @arg datum: The datum to add (C{Datum}). 299 300 @return: A L{PhiLam4Tuple}C{(phi, lam, height, datum)}. 301 302 @raise TypeError: If B{C{datum}} not a C{Datum}. 303 304 @raise ValueError: Invalid B{C{height}}. 305 ''' 306 return self.to3Tuple(height).to4Tuple(datum) 307 308 309class PhiLam3Tuple(_NamedTuple): # .nvector.py, extends -2Tuple 310 '''3-Tuple C{(phi, lam, height)} with latitude C{phi} in 311 C{radians[PI_2]}, longitude C{lam} in C{radians[PI]} and 312 C{height} in C{meter}. 313 314 @note: Using C{phi/lambda} for lat-/longitude in C{radians} 315 follows Chris Veness' U{convention 316 <https://www.Movable-Type.co.UK/scripts/latlong.html>}. 317 ''' 318 _Names_ = (_phi_, _lam_, _height_) 319 _Units_ = ( Phi, Lam, Height) 320 321 def to4Tuple(self, datum): 322 '''Extend this L{PhiLam3Tuple} to a L{PhiLam4Tuple}. 323 324 @arg datum: The datum to add (C{Datum}). 325 326 @return: A L{PhiLam4Tuple}C{(phi, lam, height, datum)}. 327 328 @raise TypeError: If B{C{datum}} not a C{Datum}. 329 ''' 330 from pygeodesy.datums import Datum 331 _xinstanceof(Datum, datum=datum) 332 return self._xtend(PhiLam4Tuple, datum) 333 334 335class PhiLam4Tuple(_NamedTuple): # extends -3Tuple 336 '''4-Tuple C{(phi, lam, height, datum)} with latitude C{phi} in 337 C{radians[PI_2]}, longitude C{lam} in C{radians[PI]}, C{height} 338 in C{meter} and L{Datum}. 339 340 @note: Using C{phi/lambda} for lat-/longitude in C{radians} 341 follows Chris Veness' U{convention 342 <https://www.Movable-Type.co.UK/scripts/latlong.html>}. 343 ''' 344 _Names_ = (_phi_, _lam_, _height_, _datum_) 345 _Units_ = ( Phi, Lam, Height, _Pass) 346 347 348class Point3Tuple(_NamedTuple): 349 '''3-Tuple C{(x, y, ll)} in C{meter}, C{meter} and C{LatLon}. 350 ''' 351 _Names_ = (_x_, _y_, _elel_) 352 _Units_ = ( Meter, Meter, _Pass) 353 354 355class Points2Tuple(_NamedTuple): # .formy.py, .latlonBase.py 356 '''2-Tuple C{(number, points)} with the C{number} of points 357 and -possible reduced- C{list} or C{tuple} of C{points}. 358 ''' 359 _Names_ = (_number_, _points_) 360 _Units_ = ( Number_, _Pass) 361 362 363class Triangle7Tuple(_NamedTuple): 364 '''7-Tuple C{(A, a, B, b, C, c, area)} with interior angles C{A}, 365 C{B} and C{C} in C{degrees}, spherical sides C{a}, C{b} and 366 C{c} in C{meter} and the C{area} of a spherical triangle in 367 I{square} C{meter}. 368 ''' 369 _Names_ = (_A_, _a_, _B_, 'b', _C_, 'c', 'area') 370 _Units_ = ( Degrees, Meter, Degrees, Meter, Degrees, Meter, Meter2) 371 372 373class Triangle8Tuple(_NamedTuple): 374 '''8-Tuple C{(A, a, B, b, C, c, D, E)} with interior angles C{A}, 375 C{B} and C{C}, spherical sides C{a}, C{b} and C{c}, I{spherical 376 deficit} C{D} and I{spherical excess} C{E} of a spherical 377 triangle, all in C{radians}. 378 ''' 379 _Names_ = (_A_, _a_, _B_, 'b', _C_, 'c', 'D', _E_) 380 _Units_ = ( Radians, Radians, Radians, Radians, Radians, Radians, Radians, Radians) 381 382 383class Trilaterate5Tuple(_NamedTuple): # .latlonBase.py, .nvector.py 384 '''5-Tuple C{(min, minPoint, max, maxPoint, n)} with C{min} and C{max} 385 in C{meter}, the corresponding trilaterated C{minPoint} and C{maxPoint} 386 as C{LatLon} and the number C{n}. For area overlap, C{min} and C{max} 387 are the smallest respectively largest overlap found. For perimeter 388 intersection, C{min} and C{max} represent the closest respectively 389 farthest intersection margin. Count C{n} is the total number of 390 trilaterated overlaps or intersections found, C{0, 1, 2...6} with 391 C{0} meaning concentric. 392 393 @see: The C{ellipsoidalKarney-}, C{ellipsoidalVincenty-} and 394 C{sphericalTrigonometry.LatLon.trilaterate5} method for further 395 details on corner cases, like concentric or single trilaterated 396 results. 397 ''' 398 _Names_ = (min.__name__, 'minPoint', max.__name__, 'maxPoint', _n_) 399 _Units_ = (Meter, _Pass, Meter, _Pass, Number_) 400 401 402class UtmUps2Tuple(_NamedTuple): # .epsg.py 403 '''2-Tuple C{(zone, hemipole)} as C{int} and C{str}, where 404 C{zone} is C{1..60} for UTM or C{0} for UPS and C{hemipole} 405 C{'N'|'S'} is the UTM hemisphere or the UPS pole. 406 ''' 407 _Names_ = (_zone_, _hemipole_) 408 _Units_ = ( Number_, Str) 409 410 411class UtmUps5Tuple(_NamedTuple): # .mgrs.py, .ups.py, .utm.py, .utmups.py 412 '''5-Tuple C{(zone, hemipole, easting, northing, band)} as C{int}, 413 C{str}, C{meter}, C{meter} and C{band} letter, where C{zone} 414 is C{1..60} for UTM or C{0} for UPS, C{hemipole} C{'N'|'S'} is 415 the UTM hemisphere or the UPS pole and C{band} is C{""} or the 416 (longitudinal) UTM band C{'C'|'D'..'W'|'X'} or the (polar) UPS 417 band C{'A'|'B'|'Y'|'Z'}. 418 ''' 419 _Names_ = (_zone_, _hemipole_, _easting_, _northing_, _band_) 420 _Units_ = ( Number_, Str, Easting, Northing, Band) 421 422 def __new__(cls, z, h, e, n, B, Error=None, name=NN): 423 if Error is not None: 424 e = Easting( e, Error=Error) 425 n = Northing(n, Error=Error) 426 return _NamedTuple.__new__(cls, z, h, e, n, B, name=name) 427 428 429class UtmUps8Tuple(_NamedTuple): # .ups.py, .utm.py, .utmups.py 430 '''8-Tuple C{(zone, hemipole, easting, northing, band, datum, 431 convergence, scale)} as C{int}, C{str}, C{meter}, C{meter}, 432 C{band} letter, C{Datum}, C{degrees} and C{scalar}, where 433 C{zone} is C{1..60} for UTM or C{0} for UPS, C{hemipole} 434 C{'N'|'S'} is the UTM hemisphere or the UPS pole and C{band} 435 is C{""} or the (longitudinal) UTM band C{'C'|'D'..'W'|'X'} 436 or the (polar) UPS band C{'A'|'B'|'Y'|'Z'}. 437 ''' 438 _Names_ = (_zone_, _hemipole_, _easting_, _northing_, 439 _band_, _datum_, _convergence_, _scale_) 440 _Units_ = ( Number_, Str, Easting, Northing, 441 Band, _Pass, Degrees, Scalar) 442 443 def __new__(cls, z, h, e, n, B, d, c, s, Error=None, name=NN): # PYCHOK 11 args 444 if Error is not None: 445 e = Easting( e, Error=Error) 446 n = Northing(n, Error=Error) 447 c = Degrees(convergence=c, Error=Error) 448 s = Scalar(scale=s, Error=Error) 449 return _NamedTuple.__new__(cls, z, h, e, n, B, d, c, s, name=name) 450 451 452class UtmUpsLatLon5Tuple(_NamedTuple): # .ups.py, .utm.py, .utmups.py 453 '''5-Tuple C{(zone, band, hemipole, lat, lon)} as C{int}, 454 C{str}, C{str}, C{degrees90} and C{degrees180}, where 455 C{zone} is C{1..60} for UTM or C{0} for UPS, C{band} is 456 C{""} or the (longitudinal) UTM band C{'C'|'D'..'W'|'X'} 457 or (polar) UPS band C{'A'|'B'|'Y'|'Z'} and C{hemipole} 458 C{'N'|'S'} is the UTM hemisphere or the UPS pole. 459 ''' 460 _Names_ = (_zone_, _band_, _hemipole_, _lat_, _lon_) 461 _Units_ = ( Number_, Band, Str, Lat, Lon) 462 463 def __new__(cls, z, B, h, lat, lon, Error=None, name=NN): 464 if Error is not None: 465 lat = Lat(lat, Error=Error) 466 lon = Lon(lon, Error=Error) 467 return _NamedTuple.__new__(cls, z, B, h, lat, lon, name=name) 468 469 470class Vector2Tuple(_NamedTuple): 471 '''2-Tuple C{(x, y)} of (geocentric) components, both in 472 C{meter} or C{units}. 473 ''' 474 _Names_ = (_x_, _y_) 475 _Units_ = ( Scalar, Scalar) 476 477 def to3Tuple(self, z): 478 '''Extend this L{Vector2Tuple} to a L{Vector3Tuple}. 479 480 @arg z: The Z component add (C{scalar}). 481 482 @return: A L{Vector3Tuple}C{(x, y, z)}. 483 484 @raise ValueError: Invalid B{C{z}}. 485 ''' 486 return self._xtend(Vector3Tuple, z) 487 488 489class Vector3Tuple(_NamedTuple): 490 '''3-Tuple C{(x, y, z)} of (geocentric) components, all in 491 C{meter} or C{units}. 492 ''' 493 _Names_ = (_x_, _y_, _z_) 494 _Units_ = ( Scalar, Scalar, Scalar) 495 496 def to4Tuple(self, h): 497 '''Extend this L{Vector3Tuple} to a L{Vector4Tuple}. 498 499 @arg h: The height to add (C{scalar}). 500 501 @return: A L{Vector4Tuple}C{(x, y, z, h)}. 502 503 @raise ValueError: Invalid B{C{h}}. 504 ''' 505 return self._xtend(Vector4Tuple, h) 506 507 508class Vector4Tuple(_NamedTuple): # .nvector.py 509 '''4-Tuple C{(x, y, z, h)} of (geocentric) components, all 510 in C{meter} or C{units}. 511 ''' 512 _Names_ = (_x_, _y_, _z_, _h_) 513 _Units_ = ( Scalar, Scalar, Scalar, Height) 514 515# **) MIT License 516# 517# Copyright (C) 2016-2021 -- mrJean1 at Gmail -- All Rights Reserved. 518# 519# Permission is hereby granted, free of charge, to any person obtaining a 520# copy of this software and associated documentation files (the "Software"), 521# to deal in the Software without restriction, including without limitation 522# the rights to use, copy, modify, merge, publish, distribute, sublicense, 523# and/or sell copies of the Software, and to permit persons to whom the 524# Software is furnished to do so, subject to the following conditions: 525# 526# The above copyright notice and this permission notice shall be included 527# in all copies or substantial portions of the Software. 528# 529# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 530# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 531# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 532# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 533# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 534# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 535# OTHER DEALINGS IN THE SOFTWARE. 536 537# % env PYGEODESY_FOR_DOCS=1 python -m pygeodesy.named 538# all 71 locals OK 539