Coverage for pyrdnap / v_self.py: 95%

81 statements  

« prev     ^ index     » next       coverage.py v7.14.0, created at 2026-06-06 16:53 -0400

1 

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

3 

4u'''Function L{validation3} to run the C{RD NAP 2018} self-validation test set 

5C{.../RDNAPTRANS2018_v220627/.../Z001_ETRS89andRDNAP.txt} obtainable from U{NSGI.NL 

6<https://www.NSGI.NL/coordinatenstelsels-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} or C{-v2} transformer's L{reverse<pyrdnap.RDNAP2018v1.reverse>} 

14respectively L{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{RD NAP 2018} requirements, C{0.000000010 degrees} or 

21C{0.0010 meter} for each result. 

22 

23A test C{FAILED} if any C{reverse} or C{forward} result I{exceeds} the C{RD NAP 2018} 

24requirement 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 NN, map2, typename # NAN 

34 

35from math import fabs 

36import os.path as os_path 

37from time import time 

38 

39__all__ = () 

40__version__ = '26.06.04' 

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{RD NAP 2018} self-validation test set. 

64 

65 @arg self_txt: Name of the file containing the C{RD NAP 2018} self-validation tests 

66 (C{str}), C{.../RDNAPTRANS2018_v220627/.../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 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} 

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

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

73 and 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, 

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

79 region. 

80 ''' 

81 from pyrdnap import RDNAP2018v1, RDNAP2018v2, RDNAPError, _versions 

82 

83 _xinstanceof(str, bytes, self_txt=self_txt) 

84 _xinstanceof(RDNAP2018v1, RDNAP2018v2, R=R) 

85 R_ = typename(R) 

86 

87 nfailed = 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 req_d = _REQ_D 

93 diffs = [0] * len(req_d) # max |diff| of all 

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

95# if isinstance(R, RDNAP2018v2): 

96# # ignore RD-Bessel lat, lon 

97# req_d = (0, 0) + req_d[2:] 

98# ds[0] = ds[1] = NAN 

99 ln = t0 = 0 

100 for t in _readlines(self_txt): 

101 ln += 1 

102 if t0: 

103 ts = t.split() # list 

104 if ts[6] == _STAR_: 

105 ts[6] = _NAN_ 

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

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

108 F = NN # PASSED 

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

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

111 ntotal += len(rs) 

112 nin_out += 1 

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

114 if q > 0: 

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

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

117 diffs[i] = d 

118 if d > q: 

119 nfailed += 1 

120 F = 'FAILED' 

121 else: # PYCHOK no cover 

122 ntotal -= 1 

123# ds[i] = NAN 

124 if _printest and (F or all_): 

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

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

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

128 else: # 1st line 

129 if _print: 

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

131 t0 = time() 

132 

133 if _print: 

134 s = time() - t0 

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

136 if _printest: 

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

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

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

140 if nfailed: 

141 _print(R_, nfailed, 'of', ntotal, 'tests', 'FAILED,', t) 

142 else: 

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

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

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

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

147 else: # PYCHOK no cover 

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

149 if _print: 

150 _print(t) 

151 else: 

152 raise RDNAPError(t) 

153 nfailed = 1 

154 return nfailed, ntotal, nin_out 

155 

156 

157def _zfe4(floats): 

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

159 return _COMMASPACE_.join(t) 

160 

161 

162def _zfmt(floats): 

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

164 return _COMMASPACE_.join(t) 

165 

166 

167__all__ += _ALL_OTHER(validation3) 

168 

169# **) MIT License 

170# 

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

172# 

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

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

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

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

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

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

179# 

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

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

182# 

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

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

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

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

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

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

189# OTHER DEALINGS IN THE SOFTWARE.