Coverage for pyrdnap / v_self.py: 95%

82 statements  

« prev     ^ index     » next       coverage.py v7.14.0, created at 2026-06-14 13:03 -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 {NAPh} results 

13from the L{RDNAP2018v1} transformer's L{reverse<pyrdnap.RDNAP2018v1.reverse>} respectively 

14L{forward<pyrdnap.RDNAP2018v1.forward>} method. 

15 

16The 3rd line contains the (absolute value) of the differences for each result on the 2nd 

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

28@see: Module L{pyrdnap<pyrdnap.__main__>} for examples to invoke L{validation3}. 

29''' 

30from pyrdnap.rd0 import RDNAP7Tuple 

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

32 _SPACE_, _STAR_, _secs2str, _xinstanceof) 

33from pygeodesy import NAN, NN, map2, typename 

34 

35from math import fabs 

36import os.path as os_path 

37from time import time 

38 

39__all__ = () 

40__version__ = '26.06.12' 

41 

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

43# (lat lon height RDx RDy NAPh) 

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

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

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

47 

48 

49def _line(ln): # in .__main__ 

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

51 

52 

53def _readlines(filename): # in .__main__ 

54 # yield each line as str, not bytes 

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

56 t = f.readline() 

57 while t: 

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

59 t = f.readline() 

60 

61 

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

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

64 

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

66 .../Z001_ETRS89andRDNAP.txt}). 

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

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

69 tests (C{bool}). 

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

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

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

73 the final, summary lines. 

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

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

76 

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

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

79 ''' 

80 from pyrdnap import RDNAP2018v1, RDNAP2018v2, RDNAPError, _versions 

81 

82 _xinstanceof(str, bytes, self_txt=self_txt) 

83 _xinstanceof(RDNAP2018v1, RDNAP2018v2, R=R) 

84 R_ = typename(R) 

85 

86 nfailed = nlines = ntotal = nin_out = 0 

87 if _print: 

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

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

90 if self_txt and os_path.exists(self_txt): 

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

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

93 ln = t0 = 0 

94 for t in _readlines(self_txt): 

95 ln += 1 

96 if t0: 

97 ts = t.split() # list 

98 if ts[6] == _STAR_: 

99 ts[6] = _NAN_ 

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

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

102 F = NN # PASSED 

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

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

105 ntotal += len(rs) 

106 nin_out += 1 

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

108 if q > 0: 

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

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

111 diffs[i] = d 

112 if d > q: 

113 nfailed += 1 

114 F = 'FAILED' 

115 else: # PYCHOK no cover 

116 ntotal -= 1 

117 ds[i] = NAN 

118 if _printest and (F or all_): 

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

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

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

122 if F: 

123 nlines += 1 

124 else: # 1st line 

125 if _print: 

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

127 t0 = time() 

128 

129 if _print: 

130 s = time() - t0 

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

132 if _printest: 

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

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

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

136 if nfailed: 

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

138 else: 

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

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

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

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

143 else: # PYCHOK no cover 

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

145 if _print: 

146 _print(t) 

147 else: 

148 raise RDNAPError(t) 

149 nfailed = 1 

150 return nfailed, ntotal, nin_out 

151 

152 

153def _zfe4(floats): 

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

155 return _COMMASPACE_.join(t) 

156 

157 

158def _zfmt(floats): 

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

160 return _COMMASPACE_.join(t) 

161 

162 

163__all__ += _ALL_OTHER(validation3) 

164 

165# **) MIT License 

166# 

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

168# 

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

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

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

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

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

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

175# 

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

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

178# 

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

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

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

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

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

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

185# OTHER DEALINGS IN THE SOFTWARE.