Coverage for pygeodesy/namedTuples.py: 95%

240 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2025-05-04 12:01 -0400

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 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 

30 

31__all__ = _ALL_LAZY.namedTuples 

32__version__ = '24.11.22' 

33 

34# __DUNDER gets mangled in class 

35_closest_ = 'closest' 

36_destination_ = 'destination' 

37_elel_ = 'll' 

38_final_ = 'final' 

39_fraction_ = 'fraction' 

40 

41 

42class Bearing2Tuple(_NamedTuple): 

43 '''2-Tuple C{(initial, final)} bearings, both in compass C{degrees360}. 

44 ''' 

45 _Names_ = (_initial_, _final_) 

46 _Units_ = ( Bearing, Bearing) 

47 

48 

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) 

55 

56 

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) 

64 

65 def enclosures(self, S_other, *W_N_E): 

66 '''Get the enclosures of this around an other L{Bounds4Tuple}. 

67 

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}}. 

72 

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 

81 

82 def overlap(self, S_other, *W_N_E): 

83 '''Intersect this with an other L{Bounds4Tuple}. 

84 

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}}. 

89 

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)) 

97 

98 

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) 

105 

106 

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) 

114 

115 

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) 

122 

123 

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) 

130 

131 

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}}. 

138 

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) 

145 

146 

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) 

152 

153 

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) 

159 

160 

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[.] 

168 

169 

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) 

178 

179 

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) 

195 

196 

197class LatLon2Tuple(_NamedTuple): 

198 '''2-Tuple C{(lat, lon)} in C{degrees90} and C{degrees180}. 

199 ''' 

200 _Names_ = (_lat_, _lon_) 

201 _Units_ = ( Lat, Lon) 

202 

203 def to3Tuple(self, height, **name): 

204 '''Extend this L{LatLon2Tuple} to a L{LatLon3Tuple}. 

205 

206 @arg height: The height to add (C{scalar}). 

207 @kwarg name: Optional C{B{name}=NN} (C{str}), overriding 

208 this name. 

209 

210 @return: A L{LatLon3Tuple}C{(lat, lon, height)}. 

211 

212 @raise ValueError: Invalid B{C{height}}. 

213 ''' 

214 return self._xtend(LatLon3Tuple, height, **name) 

215 

216 def to4Tuple(self, height, datum, **name): 

217 '''Extend this L{LatLon2Tuple} to a L{LatLon4Tuple}. 

218 

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. 

223 

224 @return: A L{LatLon4Tuple}C{(lat, lon, height, datum)}. 

225 

226 @raise TypeError: If B{C{datum}} not a C{Datum}. 

227 

228 @raise ValueError: Invalid B{C{height}}. 

229 ''' 

230 return self.to3Tuple(height).to4Tuple(datum, **name) 

231 

232 

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) 

239 

240 def to4Tuple(self, datum, **name): 

241 '''Extend this L{LatLon3Tuple} to a L{LatLon4Tuple}. 

242 

243 @arg datum: The datum to add (C{Datum}). 

244 @kwarg name: Optional C{B{name}=NN} (C{str}), overriding 

245 this name. 

246 

247 @return: A L{LatLon4Tuple}C{(lat, lon, height, datum)}. 

248 

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) 

253 

254 

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) 

261 

262 

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 

281 

282 

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) 

289 

290 

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) 

297 

298 

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_) 

305 

306 def to5Tuple(self, height, radius, **name): 

307 '''Extend this L{LatLonPrec3Tuple} to a L{LatLonPrec5Tuple}. 

308 

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. 

313 

314 @return: A L{LatLonPrec5Tuple}C{(lat, lon, precision, 

315 height, radius)}. 

316 ''' 

317 return self._xtend(LatLonPrec5Tuple, height, radius, **name) 

318 

319 

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) 

327 

328 

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 

345 

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 

355 

356 

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) 

368 

369 

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) 

379 

380 

381# NearestOn4Tuple DEPRECATED, see .deprecated.classes.NearestOn4Tuple 

382 

383 

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) 

395 

396 

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) 

411 

412 

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:] 

421 

422 

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]}. 

426 

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) 

433 

434 def to3Tuple(self, height, **name): 

435 '''Extend this L{PhiLam2Tuple} to a L{PhiLam3Tuple}. 

436 

437 @arg height: The height to add (C{scalar}). 

438 @kwarg name: Optional C{B{name}=NN} (C{str}), 

439 overriding this name. 

440 

441 @return: A L{PhiLam3Tuple}C{(phi, lam, height)}. 

442 

443 @raise ValueError: Invalid B{C{height}}. 

444 ''' 

445 return self._xtend(PhiLam3Tuple, height, **name) 

446 

447 def to4Tuple(self, height, datum): 

448 '''Extend this L{PhiLam2Tuple} to a L{PhiLam4Tuple}. 

449 

450 @arg height: The height to add (C{scalar}). 

451 @arg datum: The datum to add (C{Datum}). 

452 

453 @return: A L{PhiLam4Tuple}C{(phi, lam, height, datum)}. 

454 

455 @raise TypeError: If B{C{datum}} not a C{Datum}. 

456 

457 @raise ValueError: Invalid B{C{height}}. 

458 ''' 

459 return self.to3Tuple(height).to4Tuple(datum) 

460 

461 

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}. 

466 

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) 

473 

474 def to4Tuple(self, datum, **name): 

475 '''Extend this L{PhiLam3Tuple} to a L{PhiLam4Tuple}. 

476 

477 @arg datum: The datum to add (C{Datum}). 

478 @kwarg name: Optional C{B{name}=NN} (C{str}), 

479 overriding this name. 

480 

481 @return: A L{PhiLam4Tuple}C{(phi, lam, height, datum)}. 

482 

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) 

487 

488 

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}. 

493 

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) 

500 

501 

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) 

507 

508 

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) 

515 

516 

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) 

525 

526 

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) 

535 

536 

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) 

545 

546 

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. 

556 

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_) 

564 

565 

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) 

573 

574 

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) 

584 

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) 

590 

591 

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) 

605 

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) 

613 

614 

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) 

625 

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) 

631 

632 

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) 

639 

640 def toCartesian(self, Cartesian, **Cartesian_kwds): 

641 '''Return this C{Vector2Tuple} as a C{Cartesian}. 

642 

643 @arg Cartesian: The C{Cartesian} class to use. 

644 @kwarg Cartesian_kwds: Optional, additional C{Cartesian} 

645 keyword arguments. 

646 

647 @return: The C{B{Cartesian}} instance with C{z=0}. 

648 ''' 

649 return _v2Cls(self.xyz, Cartesian, Cartesian_kwds) 

650 

651 def to3Tuple(self, z=INT0, **name): 

652 '''Extend this L{Vector2Tuple} to a L{Vector3Tuple}. 

653 

654 @kwarg z: The Z component add (C{scalar}). 

655 @kwarg name: Optional C{B{name}=NN} (C{str}), 

656 overriding this name. 

657 

658 @return: A L{Vector3Tuple}C{(x, y, z)}. 

659 

660 @raise ValueError: Invalid B{C{z}}. 

661 ''' 

662 return self._xtend(Vector3Tuple, z, **name) 

663 

664 @property_RO 

665 def xyz(self): 

666 '''Get X, Y and Z=0 components (C{Vector3Tuple}). 

667 ''' 

668 return Vector3Tuple(*self.xyz3) 

669 

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 

675 

676 

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) 

683 

684 def toCartesian(self, Cartesian, **Cartesian_kwds): 

685 '''Return this C{Vector3Tuple} as a C{Cartesian}. 

686 

687 @arg Cartesian: The C{Cartesian} class to use. 

688 @kwarg Cartesian_kwds: Optional, additional C{Cartesian} 

689 keyword arguments. 

690 

691 @return: The C{B{Cartesian}} instance. 

692 ''' 

693 return _v2Cls(self, Cartesian, Cartesian_kwds) 

694 

695 def to4Tuple(self, h=INT0, **name): 

696 '''Extend this L{Vector3Tuple} to a L{Vector4Tuple}. 

697 

698 @arg h: The height to add (C{scalar}). 

699 @kwarg name: Optional C{B{name}=NN} (C{str}), 

700 overriding this name. 

701 

702 @return: A L{Vector4Tuple}C{(x, y, z, h)}. 

703 

704 @raise ValueError: Invalid B{C{h}}. 

705 ''' 

706 return self._xtend(Vector4Tuple, h, **name) 

707 

708 @property_RO 

709 def xyz(self): 

710 '''Get X, Y and Z components (C{Vector3Tuple}). 

711 ''' 

712 return self 

713 

714 @property_RO 

715 def xyz3(self): 

716 '''Get X, Y and Z components as C{3-tuple}. 

717 ''' 

718 return tuple(self) 

719 

720 

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) 

727 

728 def toCartesian(self, Cartesian, **Cartesian_kwds): 

729 '''Return this C{Vector4Tuple} as a C{Cartesian}. 

730 

731 @arg Cartesian: The C{Cartesian} class to use. 

732 @kwarg Cartesian_kwds: Optional, additional C{Cartesian} 

733 keyword arguments. 

734 

735 @return: The C{B{Cartesian}} instance. 

736 ''' 

737 return _v2Cls(self, Cartesian, Cartesian_kwds) 

738 

739 def to3Tuple(self): 

740 '''Reduce this L{Vector4Tuple} to a L{Vector3Tuple}. 

741 

742 @return: A L{Vector3Tuple}C{(x, y, z)}. 

743 ''' 

744 return self.xyz 

745 

746 @property_RO 

747 def xyz(self): 

748 '''Get X, Y and Z components (L{Vector3Tuple}). 

749 ''' 

750 return Vector3Tuple(*self.xyz) 

751 

752 @property_RO 

753 def xyz3(self): 

754 '''Get X, Y and Z components as C{3-tuple}. 

755 ''' 

756 return tuple(self[:3]) 

757 

758 

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) 

763 

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.