Coverage for pyrdnap / rd0.py: 98%

211 statements  

« prev     ^ index     » next       coverage.py v7.14.0, created at 2026-08-21 15:30 -0400

1 

2# -*- coding: utf-8 -*- 

3 

4u'''(INTERNAL) RijksDriehoeksmeting C{_RD} and reference C{_RD0} 

5constants and classes C{RDNAP7Tuple} and C{LqRD}. 

6''' 

7# make sure int/int division yields float quotient in Py2 

8from __future__ import division as _; del _ # noqa: E702 ; 

9 

10from pyrdnap.v_grids import _v_assert 

11from pyrdnap.__pygeodesy import (_0_5, _1_0, _2_0, # PYCHOK used! 

12 _isNAN, _isNAN0, _xinstanceof, _xsubclassof, 

13 _LLEB, _xkwds, 

14 _COMMASPACE_, _datum_, _h_, _N_, 

15 _ALL_OTHER, _FOR_DOCS, 

16 _H_lat_lon_height4Tuple, _NamedTuple, _Pass) 

17from pygeodesy import (map1, map2, NAN, NN, # basics, "consterns" 

18 Datum, Datums, Similarity, # datums 

19 Ellipsoid, Ellipsoids, LqRD as _LqRD, # ellipsoids, ltp 

20 deprecated_property_RO, Property_RO, # props 

21 property_RO, property_ROver, pairs, # props, streprs 

22 Lam, Lamd, Lat, Lon, Meter, Phi, Phid, # units 

23 sincos2, tanPI_2_2) # utily 

24 

25from math import atan2, ceil, fabs, floor, log, sin, sqrt 

26 

27__all__ = () 

28__version__ = '26.08.18' 

29 

30_LQRD0 = _LqRD() # get Amersfoort, region4, etc. (deleted below) 

31 

32 

33def _c_f_N_f3(*deg_SW_D): 

34 # return int(ceil) and int(floor) of Normalized 

35 # and (Normalized less floor) of C{deg} degrees 

36 N = _degN(*deg_SW_D) 

37 # assert N >= 0, N 

38 f = floor(N) 

39 return int(ceil(N)), int(f), (N - f) 

40 

41 

42def _degN(deg, degSW, deg_D): 

43 # return C{deg} Normalized 

44 return (deg - degSW) * deg_D 

45 

46 

47class _RDbase(object): 

48 '''(INTERNAL) Base. 

49 ''' 

50 def _preDict(self, _pred, **d): 

51 # return updated dict C{d} 

52 for n in self.__class__.__dict__.keys(): 

53 if _pred(n): 

54 d[n] = getattr(self, n) 

55 return d 

56 

57 def toStr(self, prec=9, **fmt_ints): 

58 # return this C{_RDx} as string 

59 d = self._toDict() # PYCHOK OK 

60 t = pairs(d, prec=prec, **fmt_ints) 

61 return _COMMASPACE_(*t) 

62 

63 

64class _RD(_RDbase): 

65 '''(INTERNAL) Bounds, constants for RDNAP2018 (ASCII.txt). 

66 ''' 

67 lat_D = Lat(lat_D=80.0) # degrees, all 

68 lon_D = Lon(lon_D=50.0) 

69 

70# latD = Lat(latD=1 / lat_D) # degrees, all 

71# lonD = Lon(lonD=1 / lon_D) 

72 

73 def __init__(self): 

74 S, W, N, E = self._region4 

75 nlat = _degN(N, S, self.lat_D) + _1_0 # 2.3.2g n-phi 

76 nlon = _degN(E, W, self.lon_D) + _1_0 # 2.3.2g n-lambda 

77 _v_assert(map1(int, nlat, nlon)) 

78 

79 _bounds4 = _LQRD0.bounds4() # in .rdnap2018 Bounds4Tuple 

80 

81 def _c_f_N_f6(self, lat, lon): 

82 # return (int(ceil), int(floor), Normalized less floor) of C{lat}) + \ 

83 # (int(ceil), int(floor), Normalized less floor) of C{lon}) 

84 S, W, _, _ = self._region4 

85 return _c_f_N_f3(lat, S, self.lat_D) + \ 

86 _c_f_N_f3(lon, W, self.lon_D) 

87 

88 @property_ROver 

89 def _RDNAPv0(self): 

90 from pyrdnap.rdnap2018 import _RDNAPbase 

91 return _RDNAPbase() # singleton, instance! 

92 

93 _region4 = _LQRD0.region4() # in .rdnap2018 Bounds4Tuple 

94 

95 def _toDict(self): 

96 def _p(n): # lambda 

97 return any(map(n.endswith, '4DS')) 

98 

99 return self._preDict(_p) 

100 

101 @property_ROver 

102 def _xETRS2RD(self): # transform ETRS to RD-Bessel 

103 return Similarity(tx=-565.7346, ty=-50.4058, tz=-465.2895, s=-4.07242, 

104 rx=-1.91513, ry=1.60365, rz=-9.09546, name='_xETRS2RD') 

105 

106 @property_ROver 

107 def _xRD2ETRS(self): # transform RD-Bessel to ETRS 

108 return Similarity(tx=565.7381, ty=50.4018, tz=465.2904, s=4.07244, 

109 rx=1.91514, ry=-1.60363, rz=9.09546, name='_xRD2ETRS') 

110 

111 # % python -c "import pyrdnap; print(pyrdnap.rd0._RD.toStr())" 

112 # _bounds4=RD bounds (latS=50.75, lonW=2.539333, latN=55.765, lonE=7.22), 

113 # _region4=RD region (latS=50.0, lonW=2.0, latN=56.0, lonE=8.0), 

114 # _xETRS2RD=Similarity(name='_xETRS2RD', tx=-565.73, ty=-50.406, tz=-465.29, s=-4.0724, 

115 # rx=-1.9151, ry=1.6037, rz=-9.0955), 

116 # _xRD2ETRS=Similarity(name='_xRD2ETRS', tx=565.74, ty=50.402, tz=465.29, s=4.0724, 

117 # rx=1.9151, ry=-1.6036, rz=9.0955), 

118 # lat_D=80.0, lon_D=50.0 # latD=0.0125, lonD=0.02 

119 

120_RD = _RD() # PYCHOK singleton, in .test/testRndTrips 

121 

122 

123class _RD0(_RDbase): 

124 '''(INTERNAL) C{RD} Amersfoort, NL / C{RD New} constants for RDNAP2018 (ASCII.txt). 

125 

126 @see: U{EPSG:9809<https://EPSG.io/9809-method>}, U{"Oblique Stereographic" 

127 <https://PROJ.org/en/stable/operations/projections/sterea.html>} and 

128 <http://geotiff.maptools.org/proj_list/oblique_stereographic.html> 

129 ''' 

130 H0 = Meter(H0 =_LQRD0.height0) # Amersfoort.height0 0.0 m 

131 H0_ETRS = Meter(H0_ETRS=_LQRD0.height0_ETRS) # 43.0 m 

132 K0 = 0.9999079 # 2.4.1 scale factor 

133 LAT0 = Lat(LAT0=_LQRD0.Amersfoort.lat) # '52 9 22.178N' == 52.156160555555+° 

134 LON0 = Lon(LON0=_LQRD0.Amersfoort.lon) # ' 5 23 15.5E' == 5.387638888888+° 

135 LAM0 = Lamd(LAM0=LON0) # 𝜆0, 0.094032038 

136 LAM0C = Lam(LAM0C=LAM0) # 𝛬0 on sphere == 𝜆0 

137 PHI0 = Phid(PHI0=LAT0) # 𝜑0 0.910296727, PHI0C 𝛷0 set below 

138 X0 = Meter(X0=155000.0) # false Easting 155029.784? 

139 Y0 = Meter(Y0=463000.0) # false Norting 463109.889? 

140 

141# @property_ROver 

142# def C0(self): # c, sphere 

143# s, _ = self.sincos2PHI0 

144# w = self._w1(s) 

145# c = (w - _1_0) / (w + _1_0) 

146# return (((self.N0 + s) * (_1_0 - c)) / 

147# ((self.N0 - s) * (_1_0 + c))) 

148 

149# def chilam(self, lat, lon): # EPSG:9809 

150# # return 2-tuple (chi, lam), conformal in radians 

151# s, _ = sincos2d(lat) 

152# w2 = self._w1(s) * self.C0 

153# s = (w2 - _1_0) / (w2 + _1_0) 

154# r = radians(lon - self.LON0) * self.N0 

155# return asin(s), r 

156 

157 @property_ROver 

158 def D0(self): # lazily 

159 return Datums.Bessel1841 

160 

161 @property_ROver 

162 def D80(self): # lazily 

163 return Datums.GRS80 

164 

165 @property_ROver 

166 def E0(self): # lazily 

167 return self.D0.ellipsoid 

168 

169 def log_e_2(self, phi): 

170 e = self.E0.e 

171 p = e * sin(phi) 

172 return log((_1_0 + p) / (_1_0 - p)) * (e * _0_5) 

173 

174 def log_tan(self, phi): 

175 return log(tanPI_2_2(phi)) # tan((phi + PI/2) / 2) 

176 

177 @property_ROver 

178 def M0(self): # 2.4.1 p 15 m 

179 return self.W0 - self.N0 * self.Q0 

180 

181 @property_ROver 

182 def N0(self): # 2.4.1 p 15 n, sphere 

183 E = self.E0 

184 _, c = self.sincos2PHI0 

185 return sqrt(c**4 * E.e2 / E.e21 + _1_0) 

186 

187 @property_ROver 

188 def PHI0C(self): # 2.4.1 p 15 𝛷0 on sphere 

189 m, n = self.Rmn2 

190 s, c = self.sincos2PHI0 

191 return Phi(PHI0C=atan2(m * s, n * c)) # atan((m / n) * tan(PHI0)) 

192 

193 @property_ROver 

194 def Q0(self): # 2.4.1 p 15 q0 

195 return self.log_tan(self.PHI0) - self.log_e_2(self.PHI0) 

196 

197 @property_ROver 

198 def R(self): # 2.4.1 p 15 R, radius conformal sphere 

199 m, n = self.Rmn2 

200 return m * n 

201 

202 @property_ROver 

203 def RK2(self): # 2.4.2 

204 return self.R * self.K0 * _2_0 

205 

206 @property_ROver 

207 def Rmn2(self): # 2.4.1 p 15 (sqrt(RsubM), sqrt(RsubN)) 

208 # RsubM, RsubN == RHO0, NU0 EPSG:9809 

209 E = self.E0 

210 s, _ = self.sincos2PHI0 

211 s = _1_0 - s**2 * E.e2 

212 # assert s > 0 

213 N = E.a / sqrt(s) 

214 # assert N > 0 

215 M = E.e21 * N / s 

216 # assert M > 0 

217 return map1(sqrt, M, N) # sqrt! 

218 

219 @property_ROver 

220 def sincos2PHI0(self): # 𝜑0 

221 return sincos2(self.PHI0) 

222 

223 @property_ROver 

224 def sincos2PHI0C(self): # 𝛷0 

225 return sincos2(self.PHI0C) 

226 

227 def _toDict(self): 

228 def _p(n): # lambda 

229 return n.endswith('0') or n.startswith('R') or \ 

230 n.endswith('0C') # _0_ 

231 

232 return self._preDict(_p, H0_ETRS=self.H0_ETRS) 

233 

234 @property_ROver 

235 def W0(self): # 2.4.1 p 15 w0 

236 return self.log_tan(self.PHI0C) # 𝛷0 

237 

238# def _w1(self, sphi): # EPSG:9809 

239# w1 = NAN 

240# if _1_0 > sphi > _N_1_0: 

241# e = self.E0.e 

242# S = (_1_0 + sphi) / (_1_0 - sphi) 

243# T = (_1_0 - sphi * e) / (_1_0 + sphi * e) 

244# w1 = pow(pow(T, e) * S, self.N0) 

245# return w1 

246 

247 # % python -c "import pyrdnap; print(pyrdnap.rd0._RD0.toStr())" 

248 # D0=Datum(name='Bessel1841', ellipsoid=Ellipsoids.Bessel1841, transform=Transforms.Bessel1841), 

249 # D80=Datum(name='GRS80', ellipsoid=Ellipsoids.GRS80, transform=Transforms.WGS84), 

250 # E0=Ellipsoid(name='Bessel1841', a=6377397.155, f=0.00334277, f_=299.1528128, b=6356078.962818), 

251 # H0=0.0, H0_ETRS=43.0, K0=0.9999079, LAM0=0.094032038, LAM0C=0.094032038, 

252 # LAT0=52.156160556, LON0=5.387638889, M0=0.003773954, N0=1.000475857, 

253 # PHI0=0.910296727, PHI0C=0.909684757, Q0=1.06531844, 

254 # R=6382644.571035411, RK2=12764113.458940838, Rmn2=(2524.794785679199, 2527.9854850929623), 

255 # sincos2PHI0=(0.7896858198001045, 0.6135114554811807), 

256 # sincos2PHI0C=(0.7893102212553742, 0.6139946047171686), 

257 # W0=1.069599332, X0=155000.0, Y0=463000.0 

258 

259_RD0 = _RD0() # PYCHOK singleton, in .test/testRndTrips 

260 

261 

262class RDNAP7Tuple(_H_lat_lon_height4Tuple): # in .v_self 

263 '''7-Tuple C{(RDx, RDy, H, lat, lon, height, datum)} with I{local} C{RDx}, C{RDy} 

264 and (orthometric) height C{H}, geodetic C{lat}, C{lon}, (ellipsoidal) C{height} 

265 and C{datum} with C{lat} and C{lon} in C{degrees} and with C{RDx}, C{RDy}, C{H} 

266 and C{height} in C{meter}, conventionally. 

267 

268 @note: I{By default} C{lat}, C{lon} and C{datum} are B{GRS80 (ETRS89)} when 

269 returned from L{RDNAP2018v1.reverse} but B{Bessel1841 (RD-Bessel)} 

270 from L{RDNAP2018v2.reverse}. 

271 ''' 

272 _Names_ = ('RDx', 'RDy') + _H_lat_lon_height4Tuple._Names_ + (_datum_,) 

273 _Units_ = ( Meter, Meter) + _H_lat_lon_height4Tuple._Units_ + (_Pass,) 

274 

275 def diff(self, other, datum=None, **name): 

276 '''Return the difference between this and an C{other} C{RDNAP7Tuple}. 

277 

278 @kwarg datum: Datum C{diff} (C{Datum}, None or NAN). 

279 @kwarg name: Optional name (C{str}). 

280 

281 @return: An L{RDNAP7Tuple} with the C{fabs(diff)} for each item, 

282 except C{datum} as B{C{datum}}. 

283 ''' 

284 def _diff(a, b): 

285 try: 

286 return fabs(a - b) 

287 except TypeError: 

288 return datum 

289 

290 _xinstanceof(RDNAP7Tuple, other=other) 

291 t = map2(_diff, self, other) 

292 return RDNAP7Tuple(t, **name) 

293 

294 @Property_RO 

295 def latlonheightdatum(self): 

296 '''Get the lat-, longitude in C{degrees} with height and datum (L{LatLon4Tuple}C{(lat, lon, height, datum)}). 

297 ''' 

298 return self.latlonheight.to4Tuple(self.datum) 

299 

300 @deprecated_property_RO 

301 def NAPh(self): 

302 '''DEPRECATED on 2026.07.21, use attribute C{H}.''' 

303 return self.H # PYCHOK H 

304 

305 @Property_RO 

306 def philamheightdatum(self): 

307 '''Get the lat-, longitude in C{radians} with height and datum (L{PhiLamn4Tuple}C{(phi, lam, height, datum)}). 

308 ''' 

309 return self.philamheight.to4Tuple(self.datum) 

310 

311 def toDatum(self, datum2, name=NN): 

312 '''Convert this C{lat}, C{lon} and C{height} to B{C{datum2}}. 

313 

314 @arg datum2: Datum to convert I{to} (L{Datum}). 

315 @kwarg name: Optional name (C{str}), overriding this name. 

316 

317 @return: An L{RDNAP7Tuple} with transformed C{lat}, C{lon} and C{height} 

318 or this L{RDNAP7Tuple} if this.datum is B{C{datum2}}. 

319 

320 @note: This datum conversion is based on C{pygeodesy} which differs from 

321 C{RDNAPTRANS(tm)2018_v220627}. 

322 

323 @see: Methods L{RDNAP7Tuple.toETRS} and L{RDNAP7Tuple.toRD}. 

324 ''' 

325 _xinstanceof(Datum, datum2=datum2) 

326 if self.datum is datum2 or self.datum == datum2: # PYCHOK datum 

327 return self 

328 g = self.toLatLon(_LLEB).toDatum(datum2) 

329 h = NAN if _isNAN(self.height) else g.height # PYCHOK preserve height NAN 

330 return self.dup(lat=g.lat, lon=g.lon, datum=g.datum, height=h, 

331 name=name or self.name) 

332 

333 def toETRS(self, **name): 

334 '''Copy this L{RDNAP7Tuple} with C{lat} and C{lon} C{reverse3} transformed 

335 to ETRS89 (GRS80), provided this C{datum} is RD-Bessel (Bessel1841). 

336 

337 @kwarg name: Optional name (C{str}), overriding this name. 

338 

339 @see: Methods L{RDNAP7Tuple.toRD} and L{RDNAP7Tuple.toDatum}. 

340 ''' 

341 return self._toX(_RD0.D0, _RD._RDNAPv0.reverse3, **name) 

342 

343 def toLatLon(self, LatLon, **LatLon_kwds): 

344 '''Return this C{lat}, C{lon}, C{datum} and C{height} as B{C{LatLon}}. 

345 

346 @arg LatLon: An ellipsoidal C{LatLon} class (C{pygeodesy.ellipsoidal*}). 

347 @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword arguments. 

348 

349 @return: An B{C{LatLon}} instance. 

350 

351 @raise TypeError: B{C{LatLon}} not ellipsoidal or an other issue. 

352 ''' 

353 _xsubclassof(_LLEB, LatLon=LatLon) 

354 h = _isNAN0(self.height) # PYCHOK height 

355 kwds = _xkwds(LatLon_kwds, name=self.name, height=h) 

356 return LatLon(self.lat, self.lon, datum=self.datum, **kwds) # PYCHOK datum 

357 

358 def toRD(self, **name): 

359 '''Copy this L{RDNAP7Tuple} with C{lat} and C{lon} C{forward3} transformed 

360 to RD-Bessel (Bessel1841), provided this C{datum} is ETRS89 (GRS80). 

361 

362 @kwarg name: Optional name (C{str}), overriding this name. 

363 

364 @see: Methods L{RDNAP7Tuple.toETRS} and L{RDNAP7Tuple.toDatum}. 

365 ''' 

366 return self._toX(_RD0.D80, _RD._RDNAPv0.forward3, **name) 

367 

368 def _toX(self, datum, _xform, name=NN): 

369 # helper for C{toETRS} and C{toRD} 

370 if self.datum is datum or self.datum == datum: # PYCHOK datum 

371 lat, lon, d = _xform(*self.latlon) 

372 return self.dup(lat=lat, lon=lon, datum=d, name=name or self.name) 

373 return self 

374 

375 @deprecated_property_RO 

376 def xy(self): 

377 '''DEPRECATED on 2026.08.14, use attribute C{xyh}, C{xyH} or C{xyN}.''' 

378 from pygeodesy import Vector2Tuple 

379 return Vector2Tuple(self.RDx, self.RDy, name=self.name) # PYCHOK RDx, RDy 

380 

381 @Property_RO 

382 def xyh(self): 

383 '''Get the I{local} coordinates and ellipsoidal height (L{RDxyheight3Tuple}C{(RDx, RDy, h)}). 

384 ''' 

385 return RDxyheight3Tuple(self.RDx, self.RDy, self.height, name=self.name) 

386 

387 @Property_RO 

388 def xyH(self): 

389 '''Get the I{local} coordinates and orthometric height (L{RDxyHeight3Tuple}C{(RDx, RDy, H)}). 

390 ''' 

391 return RDxyHeight3Tuple(self.RDx, self.RDy, self.H, name=self.name) 

392 

393 @Property_RO 

394 def xyN(self): 

395 '''Get the I{local} coordinates and geoid height (L{RDxyNgeoid3Tuple}C{(RDx, RDy, N)}). 

396 ''' 

397 return RDxyNgeoid3Tuple(self.RDx, self.RDy, self.N, name=self.name) 

398 

399 @deprecated_property_RO 

400 def xyz(self): 

401 '''DEPRECATED on 2026.08.14, use attribute C{xyH}.''' 

402 from pygeodesy import Vector3Tuple 

403 return Vector3Tuple(self.RDx, self.RDy, self.H, name=self.name) # PYCHOK RDx, RDy 

404 

405 

406class LqRD(_LqRD): 

407 '''Like U{pygeodesy.LqRD<https://mrJean1.GitHub.io/PyGeodesy/docs/pygeodesy.ltp.LqRD-class.html>} 

408 but with methods C{forward} and C{reverse} returning an L{RDNAP7Tuple} with C{H} replaced 

409 by I{local} C{z}, the perpendicular distance to the local tangent plane (LTP). 

410 

411 This C{quasi-RD} transformer B{does not} implement any U{RD NAP<https://www.NSGI.NL/ 

412 coordinatenstelsels-en-transformaties/coordinatentransformaties/rdnap-etrs89-rdnaptrans>} 

413 specification and B{does not} provide I{Netherlands}' C{B{N}ormaal B{A}msterdams B{P}eil 

414 (NAP)} quasi-geodetic-height. 

415 ''' 

416 if _FOR_DOCS: 

417 __init__ = _LqRD.__init__ 

418 

419 def forward(self, lat_latlonh, lon=None, height=0, **name): # PYCHOK signature 

420 '''Convert I{geodetic} C{(lat, lon, height)} to I{local} C{quasi-RD (x, y, z)}. 

421 

422 @arg lat_latlonh: C{Scalar} (geodetic) latitude (C{degrees}) or a I{local} 

423 C{quasi-RD} L{RDNAP7Tuple}. 

424 @kwarg lon: C{Scalar} (geodetic) longitude (C{degrees}) iff B{C{lat_latlonh}} 

425 is C{scalar}, ignored otherwise. 

426 @kwarg height: Optional height (C{meter}, conventionally) perpendicular to and 

427 above (or below) the ellipsoid's surface, iff B{C{lat_latlonh}} 

428 is C{scalar}, ignored otherwise. 

429 @kwarg name: Optional C{B{name}=NN} (C{str}). 

430 

431 @return: An L{RDNAP7Tuple}C{(RDx, RDy, H, lat, lon, height, datum)} with C{H} 

432 set to I{local} C{z}. 

433 

434 @see: C{pygeodesy.LqRD.forward} for more information. 

435 ''' 

436 t = _LqRD.forward(self, lat_latlonh, lon=lon, height=height) 

437 return LqRD._l9t2r7t(t, **name) 

438 

439 def reverse(self, x_xyz, y=None, z=None, **name): # PYCHOK signature 

440 '''Convert I{local} C{quasi-RD (x, y, z)} to I{geodetic} C{(lat, lon, height)}. 

441 

442 @arg x_xyz: Local C{quasi-RD x} coordinate (C{scalar}) or a I{local} 

443 C{quasi-RD} L{RDNAP7Tuple}. 

444 @kwarg y: Local C{quasi-RD y} coordinate (C{meter}) iff B{C{x_xyz}} is 

445 C{scalar}, ignored otherwise. 

446 @kwarg z: Local C{z} coordinate (C{meter}) iff B{C{x_xyz}} is C{scalar}, 

447 ignored otherwise. 

448 @kwarg name: Optional C{B{name}=NN} (C{str}). 

449 

450 @return: An L{RDNAP7Tuple}C{(RDx, RDy, H, lat, lon, height, datum)} with C{H} 

451 set to I{local} B{C{z}}. 

452 

453 @see: C{pygeodesy.LqRD.reverse} for more information. 

454 ''' 

455 t = _LqRD.reverse(self, x_xyz, y=y, z=z) 

456 return LqRD._l9t2r7t(t, **name) 

457 

458 @staticmethod 

459 def _l9t2r7t(t, name=NN, **unused): # M=False 

460 return RDNAP7Tuple(t.x, t.y, t.z, # H = t.z 

461 t.lat, t.lon, t.height, t.ecef.datum, name=name or t.name) 

462 

463 

464class _RDxy3Tuple(_NamedTuple): # provides .x, .y and .z 

465 

466 @property_RO 

467 def x(self): 

468 '''Get I{local} C{RDx} (C{Meter}). 

469 ''' 

470 return self[0] # PYCHOK RDx 

471 

472 @property_RO 

473 def y(self): 

474 '''Get I{local} C{RDy} (C{Meter}). 

475 ''' 

476 return self[1] # PYCHOK RDy 

477 

478 @property_RO 

479 def z(self): 

480 '''Get the ellipsoidal, geoid I{or} orthometric height (C{Meter}). 

481 ''' 

482 return self[2] # PYCHOK height, H or N 

483 

484 

485class RDxyHeight3Tuple(_RDxy3Tuple): 

486 '''3-tuple C{(RDx, RDy, H)} with orthometric height C{H}, all in C{meter}. 

487 ''' 

488 _Names_ = RDNAP7Tuple._Names_[:3] 

489 _Units_ = RDNAP7Tuple._Units_[:3] 

490 

491 

492class RDxyheight3Tuple(_RDxy3Tuple): 

493 '''3-tuple C{(RDx, RDy, h)} with ellipsoidal height C{h}, all in C{meter}. 

494 ''' 

495 _Names_ = RDxyHeight3Tuple._Names_[:2] + (_h_,) 

496 _Units_ = RDxyHeight3Tuple._Units_ 

497 

498 

499class RDxyNgeoid3Tuple(_RDxy3Tuple): 

500 '''3-tuple C{(RDx, RDy, N)} with geoid height C{N}, all in C{meter}. 

501 ''' 

502 _Names_ = RDxyHeight3Tuple._Names_[:2] + (_N_,) 

503 _Units_ = RDxyHeight3Tuple._Units_ 

504 

505 

506__all__ += _ALL_OTHER(RDNAP7Tuple, RDxyheight3Tuple, RDxyHeight3Tuple, 

507 RDxyNgeoid3Tuple, LqRD, # passed along from PyGeodesy 

508 Datum, Datums, Ellipsoid, Ellipsoids, Similarity) 

509del _ALL_OTHER, _LQRD0 

510 

511# **) MIT License 

512# 

513# Copyright (C) 2026-2026 -- mrJean1 at Gmail -- All Rights Reserved. 

514# 

515# Permission is hereby granted, free of charge, to any person obtaining a 

516# copy of this software and associated documentation files (the "Software"), 

517# to deal in the Software without restriction, including without limitation 

518# the rights to use, copy, modify, merge, publish, distribute, sublicense, 

519# and/or sell copies of the Software, and to permit persons to whom the 

520# Software is furnished to do so, subject to the following conditions: 

521# 

522# The above copyright notice and this permission notice shall be included 

523# in all copies or substantial portions of the Software. 

524# 

525# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 

526# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 

527# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 

528# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 

529# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 

530# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 

531# OTHER DEALINGS IN THE SOFTWARE.