Coverage for pyrdnap/v_grids.py: 100%
72 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-05-09 18:20 -0400
« prev ^ index » next coverage.py v7.10.7, created at 2026-05-09 18:20 -0400
2# -*- coding: utf-8 -*-
4u'''(INTERNAL) C{RD NAP 2018} v#grid utilities.
5'''
6from pyrdnap import pyrdnap_abspath # pyrdnap 1st
7from pyrdnap.__pygeodesy import _0_0s, _1_0, import_module
8from pygeodesy import print_
10from array import array
11import os.path as os_path
12import sys
14__all__ = ()
15__version__ = '26.05.08'
17_R_C = 481, 301 # shape: rows, cols
18_RxC = 144781 # total
21class _V_grid(tuple):
22 '''(INTERNAL) V_grid, an R-tuple of C-arrays of C{floats}.
23 '''
24 _scale = None
26 def __call__(self, i, j):
27 # R, C = _R_C
28 # assert isinstance(i, int) and 0 <= i < R
29 # assert isinstance(j, int) and 0 <= j < C
30 return self[i][j] * self._scale
32 def _assert(self, scale, ndigits, *len_min_0s_max):
33 self._scale = scale
34 z = self._assert0s(*_R_C)
35 n = sum(map(len, self)) # _RxC
36 t = (n, self._round(min, ndigits),
37 z, self._round(max, ndigits))
38 _v_assert(t, len_min_0s_max)
39 return self
41 def _assert0s(self, R, C):
42 _v_assert(len(self), R)
43 z = 0 # count zeros
44 try:
45 for i, r in enumerate(self):
46 _v_assert(type(r), array)
47 _v_assert(len(r), C)
48 z += C if r is _ZEROW else \
49 sum((0 if f else 1) for f in r)
50 except AssertionError as x:
51 raise AssertionError('row %s: %s' % (i, x))
52 return z
54 def _round(self, _op, ndigits):
55 # round(_op(f for a in self
56 # for f in a), ndigits)
57 return round(_op(map(_op, self)), ndigits)
60def _d_array(floats): # lat_/lon_corr_
61 return array('d', floats)
64def _f_array(floats): # _NAP_h, _ZEROW
65 return array('f', floats)
67_ZEROW = _f_array(_0_0s(301)) # PYCHOK singleton
70def _run(s, e, floats): # run of non-zeros
71 _v_assert(len(floats), e - s)
72 return s, e, floats
75def _v_assert(value, valid=_R_C):
76 if value != valid:
77 raise AssertionError('%r not %r' % (value, valid))
78 return True
81def _v1corr_grid(runs, ndigits, cmin, cmax, c0s=0, flen=_RxC, scale=1e-5):
82 '''(INTERNAL) Build a variant 1 C{lat_/lon_corr} _V_grid from C{s_e_runs},
83 each a run of non-zero floats preceeded by a start and end index.
84 '''
85 v = _V_grid(_v1corr_rows(runs, *_R_C))
86 return v._assert(scale, ndigits, flen, cmin, c0s, cmax)
89def _v1corr_rows(runs, R, C):
90 '''(INTERNAL) Yield R d-/f-arrays, each of C floats, see C{v1grid.__init__.py}.
91 '''
92 z = _ZEROW # f-array of C zeros
93 i = runs[0][0] # index of 1st non-zero
94 y, s = divmod(i, C)
95 for _ in range(y): # 28 leading ZEROWs
96 yield z
97 r = list(_0_0s(s)) # remainder, 170 zeros
98 for (s, e, t) in runs:
99 r.extend(_0_0s(s - i))
100 r.extend(t) # len(r) < 600
101 while len(r) >= C:
102 d = _d_array(r[:C])
103 r[:] = r[C:]
104 yield d if any(d) else z # just in case
105 y += 1
106 i = e
107 if r: # remaining, 216/219 non-zeros
108 # assert 0 < len(r) < C
109 r.extend(_0_0s(C - len(r)))
110 yield _d_array(r)
111 # r[:] = ()
112 y += 1
113 for _ in range(y, R): # 169 trailing ZEROWs
114 yield z
117def _v2corr_grid(d_arrays, ndigits, cmin, cmax, c0s=0, flen=_RxC, scale=0):
118 '''(INTERNAL) A variant 2 C{lat_/lon_corr} _V_grid.
119 '''
120 return _V_grid(d_arrays)._assert(scale, ndigits, flen, cmin, c0s, cmax)
123def _v_h_grid(f_arrays, hmin, hmax, flen=_RxC, scale=_1_0):
124 '''(INTERNAL) An C{NAP_h} _V_grid.
125 '''
126 return _V_grid(f_arrays)._assert(scale, 4, flen, hmin, 0, hmax)
129def _v_gridz3(v): # PHYCOK no cover
130 '''(INTERNAL) Return the fully-qualified path, directory and module.
131 '''
132 m = 'v%sgrid' % (v,)
133 z = m + 'z.zip'
134 d = pyrdnap_abspath
135 p = os_path.join(d, z)
136 return p, d, m
139def _v_gridz_import(v): # PHYCOK no cover
140 '''(INTERNAL) Try "from v#gridz.zip import v#grid"
141 '''
142 v_grid = None
143 p, _, m = _v_gridz3(v)
144 try: # Py 3.4+
145 # <https://RealPython.com/python-zip-import/
146 # #explore-pythons-zipimport-the-tool-behind-zip-imports>
147 from zipimport import zipimporter as Z # ZipImportError
148 # get an importer for zip file p and load module m
149 v_grid = Z(p).load_module(m) # Py3.14-
150 # XXX should use .exec_module but that fails and/or
151 # XXX needs .create_module, .find_spec, etc???
152 except (AttributeError, ImportError):
153 try: # trusted old-fashion way
154 sys.path.insert(0, p)
155 v_grid = import_module(m)
156# except ImportError:
157# v_grid = None
158 finally:
159 try:
160 sys.path.remove(p)
161 except ValueError:
162 pass # AssertionError
163# if v_grid:
164# sys.modules[m] = v_grid
165 return v_grid
168def _v_gridz_unzip(v, force=False, verbose=False): # PHYCOK no cover
169 '''(INTERNAL) Unzip a C{v#gridz.zip} file into the "pyrdnap" directory
170 '''
171 try:
172 from zipfile import BadZipFile, ZipFile
173 except ImportError: # Py3.3-
174 from zipfile import BadZipfile as BadZipFile, ZipFile
176 p, d, m = _v_gridz3(v)
177 t = os_path.join(d, m)
178 if (not force) and os_path.exists(t):
179 t = 'dir %r exists: %r' % (m, t)
180 raise OSError(t)
181 try:
182 with ZipFile(p, 'r') as z:
183 z.extractall(d)
184 except BadZipFile as x:
185 raise ValueError(str(x), cause=x)
186 if verbose:
187 print_('unzipped', repr(p))
188 print_(' into', repr(t))
190# **) MIT License
191#
192# Copyright (C) 2026-2026 -- mrJean1 at Gmail -- All Rights Reserved.
193#
194# Permission is hereby granted, free of charge, to any person obtaining a
195# copy of this software and associated documentation files (the "Software"),
196# to deal in the Software without restriction, including without limitation
197# the rights to use, copy, modify, merge, publish, distribute, sublicense,
198# and/or sell copies of the Software, and to permit persons to whom the
199# Software is furnished to do so, subject to the following conditions:
200#
201# The above copyright notice and this permission notice shall be included
202# in all copies or substantial portions of the Software.
203#
204# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
205# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
206# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
207# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
208# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
209# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
210# OTHER DEALINGS IN THE SOFTWARE.