Coverage for pyrdnap / v_self.py: 95%

83 statements  

« prev     ^ index     » next       coverage.py v7.14.0, created at 2026-06-26 11:46 -0400

1 

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

3 

4u'''Function L{validation3} to run the C{RDNAPTRANS(tm)2018_v220627} self-validation 

5test set C{.../Z001_ETRS89andRDNAP.txt} obtainable from U{NSGI.NL<https://www.NSGI.NL/ 

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

7after registration. 

8 

9For each test point, 3 lines are produced: the 1st showing the point C{id}, the original 

10(ETRS89) C{lat}, C{lon} and C{height} and the expected C{RDx}, C{RDy} and C{NAPh} values. 

11 

12The 2nd line shows the C{lat}, C{lon} and C{height} and C{RDx}, C{RDy} and C{NAPh} results 

13from the L{RDNAP2018v1.reverse<pyrdnap.RDNAP2018v1.reverse>} respectively L{-.forward 

14<pyrdnap._RDNAPbase.forward>} method. 

15 

16The 3rd line contains the (absolute value) of the difference between each result on the 

172nd line and the corresponding, original test point value on the 1st line. 

18 

19The final lines of the output are the C{maximum} of all (absolute value) differences in 

202 formats and a line with the C{RDNAPTRANS(tm)2018} requirements, C{0.000000010 degrees} 

21or C{0.0010 meter} for each result. 

22 

23A test is considered C{FAILED} if any C{reverse} or C{forward} result I{exceeds} the 

24C{RDNAPTRANS(tm)2018} requirement for that result. 

25 

26For points with C{NAPh} marked C{"*"}, C{NAPh} is set to C{NAN}. 

27 

28See main module L{pyrdnap<pyrdnap.__main__>} for some examples invoking L{validation3}. 

29''' 

30from pyrdnap.rd0 import RDNAP7Tuple 

31from pyrdnap.__pygeodesy import (_all_OTHER, _COMMASPACE_, _NAN_, _NL_, 

32 _SPACE_, _STAR_, _secs2str, 

33 _xinstanceof, RDNAPError) 

34from pygeodesy import NAN, NN, map2, typename 

35 

36from math import fabs 

37import os.path as os_path 

38from time import time 

39 

40__all__ = () 

41__version__ = '26.06.18' 

42 

43_NAMES = RDNAP7Tuple._Names_[3:6] + RDNAP7Tuple._Names_[0:3] 

44# (lat lon height RDx RDy NAPh) 

45_REQ_D = (1e-8, 1e-8, 1e-3, 1e-3, 1e-3, 1e-3) # 0 == ignore 

46_NDECS = (11, 11, 6, 8, 8, 8) # fmt precision 

47_NDecs = tuple((_ - 4) for _ in _NDECS) # fe4 precision 

48 

49 

50def _line(ln): # in .__main__ 

51 return ' (line %s)' % (ln,) 

52 

53 

54def _readlines(filename): # in .__main__ 

55 # yield each line as str, not bytes 

56 with open(filename, 'rb') as f: 

57 t = f.readline() 

58 while t: 

59 yield t.strip().decode('utf-8') 

60 t = f.readline() 

61 

62 

63def validation3(self_txt, R, all_=False, in_out=True, _print=None, _printest=None): # MCCABE 18 

64 '''Run the C{RDNAPTRANS(tm)2018_v220627} self-validation test set. 

65 

66 @arg self_txt: Path of the file containing the self-validation test set (C{str, 

67 .../Z001_ETRS89andRDNAP.txt}). 

68 @arg R: An RDNAP2018v# transformer (L{RDNAP2018v1} or L{RDNAP2018v2} instance). 

69 @kwarg all_: If C{True} print all tests and test results, otherwise print only failing 

70 tests (C{bool}). 

71 @kwarg in_out: If C{True} test only points C{inside} the C{RD} region, if C{False} only 

72 points C{outside} (C{bool}). 

73 @kwarg _print: A Python 3+ C{print}-like callable or C{None} to not print the header and 

74 the final, summary lines. 

75 @kwarg _printest: A Python 3+ C{print}-like callable or C{None} to not print B{C{all_}} 

76 B{C{in_out}} tests or only the failing ones. 

77 

78 @return: 3-Tuple C{(failed, total, in_outside)} with the number of C{FAILED} tests, the 

79 C{total} number of tests and the number of points B{C{in_out}} the C{RD} region. 

80 ''' 

81 from pyrdnap import RDNAP2018v1, RDNAP2018v2, _versions 

82 

83 _xinstanceof(str, bytes, self_txt=self_txt) 

84 _xinstanceof(RDNAP2018v1, RDNAP2018v2, R=R) 

85 R_ = typename(R) 

86 

87 nfailed = nlines = ntotal = nin_out = 0 

88 if _print: 

89 _print('testing', repr(R)) 

90 _print(' using', repr(self_txt)) 

91 if self_txt and os_path.exists(self_txt): 

92 diffs = [0] * len(_REQ_D) # max |diff| of all 

93 ds = list(diffs) # |diff| per line 

94 ln = t0 = 0 

95 for t in _readlines(self_txt): 

96 ln += 1 

97 if t0: 

98 ts = t.split() # list 

99 if ts[6] == _STAR_: 

100 ts[6] = _NAN_ 

101 lat, lon, h, RDx, RDy, NAPh = xs = map2(float, ts[1:]) 

102 if in_out == bool(R.isinside(lat, lon)): 

103 F = NN # PASSED 

104 rs = R.reverse(RDx, RDy, NAPh).latlonheight + \ 

105 R.forward(lat, lon, h).xyz 

106 ntotal += len(rs) 

107 nin_out += 1 

108 for i, (m, q, r, x) in enumerate(zip(diffs, _REQ_D, rs, xs)): 

109 if q > 0: 

110 ds[i] = d = fabs(r - x) 

111 if d > m: # new max |diff| 

112 diffs[i] = d 

113 if d > q: 

114 nfailed += 1 

115 F = 'FAILED' 

116 else: # PYCHOK no cover 

117 ntotal -= 1 

118 ds[i] = NAN 

119 if _printest and (F or all_): 

120 _printest('id', _SPACE_(*ts), _line(ln)) 

121 _printest(R_, _zfmt(rs), F) 

122 _printest(' |diff|', _zfe4(ds), F, _NL_) 

123 if F: 

124 nlines += 1 

125 else: # 1st line 

126 if _print: 

127 _print(' header', repr(t), _line(ln), _NL_) 

128 t0 = time() 

129 

130 if _print: 

131 s = time() - t0 

132 t = '-inside' if in_out else '-outside' 

133 if _printest: 

134 t += ' -all' if all_ else ' -failed' 

135 t = '%s of %s points %s' % (nin_out, (ln - 1), t) 

136 t = '%s (%s) %s' % (t, _versions(), _secs2str(s)) 

137 if nfailed: 

138 _print(R_, nfailed, 'of', ntotal, 'tests', 'FAILED for', nlines, 'of', t) 

139 else: 

140 _print(R_, 'all', ntotal, 'tests', 'PASSED,', t) 

141 for n, _z, fs in (('req', _zfmt, _REQ_D), ('max', _zfe4, diffs), 

142 ('max', _zfmt, diffs)): 

143 _print(R_, n, '|diff|', _z(fs)) 

144 else: # PYCHOK no cover 

145 t = "doesn't exist: %r" % (self_txt,) 

146 if _print: 

147 _print(t) 

148 else: 

149 raise RDNAPError(t) 

150 nfailed = 1 

151 return nfailed, ntotal, nin_out 

152 

153 

154def _zfe4(floats): 

155 t = ('%s %.*e' % t for t in zip(_NAMES, _NDecs, floats)) 

156 return _COMMASPACE_.join(t) 

157 

158 

159def _zfmt(floats): 

160 t = ('%s %.*f' % t for t in zip(_NAMES, _NDECS, floats)) 

161 return _COMMASPACE_.join(t) 

162 

163 

164__all__ += _all_OTHER(validation3) 

165del _all_OTHER 

166 

167# **) MIT License 

168# 

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

170# 

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

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

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

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

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

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

177# 

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

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

180# 

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

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

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

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

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

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

187# OTHER DEALINGS IN THE SOFTWARE.