polytope-python 1.0.31__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. polytope_feature/__init__.py +1 -0
  2. polytope_feature/datacube/__init__.py +1 -0
  3. polytope_feature/datacube/backends/__init__.py +1 -0
  4. polytope_feature/datacube/backends/datacube.py +171 -0
  5. polytope_feature/datacube/backends/fdb.py +399 -0
  6. polytope_feature/datacube/backends/mock.py +71 -0
  7. polytope_feature/datacube/backends/xarray.py +142 -0
  8. polytope_feature/datacube/datacube_axis.py +332 -0
  9. polytope_feature/datacube/index_tree_pb2.py +27 -0
  10. polytope_feature/datacube/tensor_index_tree.py +228 -0
  11. polytope_feature/datacube/transformations/__init__.py +1 -0
  12. polytope_feature/datacube/transformations/datacube_cyclic/__init__.py +1 -0
  13. polytope_feature/datacube/transformations/datacube_cyclic/datacube_cyclic.py +171 -0
  14. polytope_feature/datacube/transformations/datacube_mappers/__init__.py +1 -0
  15. polytope_feature/datacube/transformations/datacube_mappers/datacube_mappers.py +141 -0
  16. polytope_feature/datacube/transformations/datacube_mappers/mapper_types/__init__.py +5 -0
  17. polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix.py +147 -0
  18. polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix_nested.py +229 -0
  19. polytope_feature/datacube/transformations/datacube_mappers/mapper_types/local_regular.py +95 -0
  20. polytope_feature/datacube/transformations/datacube_mappers/mapper_types/octahedral.py +7896 -0
  21. polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_gaussian.py +1459 -0
  22. polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_ll.py +5128 -0
  23. polytope_feature/datacube/transformations/datacube_mappers/mapper_types/regular.py +75 -0
  24. polytope_feature/datacube/transformations/datacube_merger/__init__.py +1 -0
  25. polytope_feature/datacube/transformations/datacube_merger/datacube_merger.py +95 -0
  26. polytope_feature/datacube/transformations/datacube_reverse/__init__.py +1 -0
  27. polytope_feature/datacube/transformations/datacube_reverse/datacube_reverse.py +65 -0
  28. polytope_feature/datacube/transformations/datacube_transformations.py +96 -0
  29. polytope_feature/datacube/transformations/datacube_type_change/__init__.py +1 -0
  30. polytope_feature/datacube/transformations/datacube_type_change/datacube_type_change.py +124 -0
  31. polytope_feature/datacube/tree_encoding.py +132 -0
  32. polytope_feature/engine/__init__.py +1 -0
  33. polytope_feature/engine/engine.py +19 -0
  34. polytope_feature/engine/hullslicer.py +316 -0
  35. polytope_feature/options.py +77 -0
  36. polytope_feature/polytope.py +71 -0
  37. polytope_feature/shapes.py +405 -0
  38. polytope_feature/utility/__init__.py +0 -0
  39. polytope_feature/utility/combinatorics.py +48 -0
  40. polytope_feature/utility/exceptions.py +45 -0
  41. polytope_feature/utility/geometry.py +26 -0
  42. polytope_feature/utility/list_tools.py +41 -0
  43. polytope_feature/utility/profiling.py +14 -0
  44. polytope_feature/version.py +1 -0
  45. polytope_python-1.0.31.dist-info/LICENSE +201 -0
  46. polytope_python-1.0.31.dist-info/METADATA +21 -0
  47. polytope_python-1.0.31.dist-info/RECORD +49 -0
  48. polytope_python-1.0.31.dist-info/WHEEL +5 -0
  49. polytope_python-1.0.31.dist-info/top_level.txt +1 -0
@@ -0,0 +1,147 @@
1
+ import bisect
2
+ import math
3
+
4
+ from ..datacube_mappers import DatacubeMapper
5
+
6
+
7
+ class HealpixGridMapper(DatacubeMapper):
8
+ def __init__(self, base_axis, mapped_axes, resolution, md5_hash=None, local_area=[], axis_reversed=None):
9
+ # TODO: if local area is not empty list, raise NotImplemented
10
+ self._mapped_axes = mapped_axes
11
+ self._base_axis = base_axis
12
+ self._resolution = resolution
13
+ self._axis_reversed = {mapped_axes[0]: True, mapped_axes[1]: False}
14
+ self._first_axis_vals = self.first_axis_vals()
15
+ self.compressed_grid_axes = [self._mapped_axes[1]]
16
+ if md5_hash is not None:
17
+ self.md5_hash = md5_hash
18
+ else:
19
+ self.md5_hash = _md5_hash.get(resolution, None)
20
+ if self._axis_reversed[mapped_axes[1]]:
21
+ raise NotImplementedError("Healpix grid with second axis in decreasing order is not supported")
22
+ if not self._axis_reversed[mapped_axes[0]]:
23
+ raise NotImplementedError("Healpix grid with first axis in increasing order is not supported")
24
+
25
+ def first_axis_vals(self):
26
+ rad2deg = 180 / math.pi
27
+ vals = [0] * (4 * self._resolution - 1)
28
+
29
+ # Polar caps
30
+ for i in range(1, self._resolution):
31
+ val = 90 - (rad2deg * math.acos(1 - (i * i / (3 * self._resolution * self._resolution))))
32
+ vals[i - 1] = val
33
+ vals[4 * self._resolution - 1 - i] = -val
34
+ # Equatorial belts
35
+ for i in range(self._resolution, 2 * self._resolution):
36
+ val = 90 - (rad2deg * math.acos((4 * self._resolution - 2 * i) / (3 * self._resolution)))
37
+ vals[i - 1] = val
38
+ vals[4 * self._resolution - 1 - i] = -val
39
+ # Equator
40
+ vals[2 * self._resolution - 1] = 0
41
+ return vals
42
+
43
+ def map_first_axis(self, lower, upper):
44
+ axis_lines = self._first_axis_vals
45
+ return_vals = [val for val in axis_lines if lower <= val <= upper]
46
+ return return_vals
47
+
48
+ def second_axis_vals(self, first_val):
49
+ tol = 1e-8
50
+ first_val = [i for i in self._first_axis_vals if first_val[0] - tol <= i <= first_val[0] + tol][0]
51
+ idx = self._first_axis_vals.index(first_val)
52
+
53
+ values = self.HEALPix_longitudes(idx)
54
+ return values
55
+
56
+ def second_axis_vals_from_idx(self, first_val_idx):
57
+ values = self.HEALPix_longitudes(first_val_idx)
58
+ return values
59
+
60
+ def HEALPix_nj(self, i):
61
+ assert self._resolution > 0
62
+ ni = 4 * self._resolution - 1
63
+ assert i < ni
64
+
65
+ if i < self._resolution:
66
+ return 4 * (i + 1)
67
+ elif i < 3 * self._resolution:
68
+ return 4 * self._resolution
69
+ else:
70
+ return self.HEALPix_nj(ni - 1 - i)
71
+
72
+ def HEALPix_longitudes(self, i):
73
+ Nj = self.HEALPix_nj(i)
74
+ step = 360.0 / Nj
75
+ start = (
76
+ step / 2.0 if i < self._resolution or 3 * self._resolution - 1 < i or (i + self._resolution) % 2 else 0.0
77
+ )
78
+
79
+ longitudes = [start + n * step for n in range(Nj)]
80
+
81
+ return longitudes
82
+
83
+ def map_second_axis(self, first_val, lower, upper):
84
+ axis_lines = self.second_axis_vals(first_val)
85
+ return_vals = [val for val in axis_lines if lower <= val <= upper]
86
+ return return_vals
87
+
88
+ def axes_idx_to_healpix_idx(self, first_idx, second_idx):
89
+ idx = 0
90
+ for i in range(self._resolution - 1):
91
+ if i != first_idx:
92
+ idx += 4 * (i + 1)
93
+ else:
94
+ idx += second_idx
95
+ return idx
96
+ for i in range(self._resolution - 1, 3 * self._resolution):
97
+ if i != first_idx:
98
+ idx += 4 * self._resolution
99
+ else:
100
+ idx += second_idx
101
+ return idx
102
+ for i in range(3 * self._resolution, 4 * self._resolution - 1):
103
+ if i != first_idx:
104
+ idx += 4 * (4 * self._resolution - 1 - i + 1)
105
+ else:
106
+ idx += second_idx
107
+ return idx
108
+
109
+ def find_second_idx(self, first_val, second_val):
110
+ tol = 1e-10
111
+ second_axis_vals = self.second_axis_vals(first_val)
112
+ second_idx = bisect.bisect_left(second_axis_vals, second_val - tol)
113
+ return second_idx
114
+
115
+ def unmap_first_val_to_start_line_idx(self, first_val):
116
+ tol = 1e-8
117
+ first_val = [i for i in self._first_axis_vals if first_val - tol <= i <= first_val + tol][0]
118
+ first_idx = self._first_axis_vals.index(first_val)
119
+ idx = 0
120
+ for i in range(self._resolution - 1):
121
+ if i != first_idx:
122
+ idx += 4 * (i + 1)
123
+ else:
124
+ return idx
125
+ for i in range(self._resolution - 1, 3 * self._resolution):
126
+ if i != first_idx:
127
+ idx += 4 * self._resolution
128
+ else:
129
+ return idx
130
+ for i in range(3 * self._resolution, 4 * self._resolution - 1):
131
+ if i != first_idx:
132
+ idx += 4 * (4 * self._resolution - 1 - i + 1)
133
+ else:
134
+ return idx
135
+
136
+ def unmap(self, first_val, second_val):
137
+ tol = 1e-8
138
+ first_value = [i for i in self._first_axis_vals if first_val[0] - tol <= i <= first_val[0] + tol][0]
139
+ first_idx = self._first_axis_vals.index(first_value)
140
+ second_val = [i for i in self.second_axis_vals(first_val) if second_val[0] - tol <= i <= second_val[0] + tol][0]
141
+ second_idx = self.second_axis_vals(first_val).index(second_val)
142
+ healpix_index = self.axes_idx_to_healpix_idx(first_idx, second_idx)
143
+ return healpix_index
144
+
145
+
146
+ # md5 grid hash in form {resolution : hash}
147
+ _md5_hash = {}
@@ -0,0 +1,229 @@
1
+ import bisect
2
+ import math
3
+
4
+ from ..datacube_mappers import DatacubeMapper
5
+
6
+
7
+ class NestedHealpixGridMapper(DatacubeMapper):
8
+ def __init__(self, base_axis, mapped_axes, resolution, md5_hash=None, local_area=[], axis_reversed=None):
9
+ # TODO: if local area is not empty list, raise NotImplemented
10
+ self._mapped_axes = mapped_axes
11
+ self._base_axis = base_axis
12
+ self._resolution = resolution
13
+ self._axis_reversed = {mapped_axes[0]: True, mapped_axes[1]: False}
14
+ self._first_axis_vals = self.first_axis_vals()
15
+ self.compressed_grid_axes = [self._mapped_axes[1]]
16
+ self.Nside = self._resolution
17
+ self.k = int(math.log2(self.Nside))
18
+ self.Npix = 12 * self.Nside * self.Nside
19
+ self.Ncap = (self.Nside * (self.Nside - 1)) << 1
20
+ if md5_hash is not None:
21
+ self.md5_hash = md5_hash
22
+ else:
23
+ self.md5_hash = _md5_hash.get(resolution, None)
24
+ if self._axis_reversed[mapped_axes[1]]:
25
+ raise NotImplementedError("Healpix grid with second axis in decreasing order is not supported")
26
+ if not self._axis_reversed[mapped_axes[0]]:
27
+ raise NotImplementedError("Healpix grid with first axis in increasing order is not supported")
28
+
29
+ def first_axis_vals(self):
30
+ rad2deg = 180 / math.pi
31
+ vals = [0] * (4 * self._resolution - 1)
32
+
33
+ # Polar caps
34
+ for i in range(1, self._resolution):
35
+ val = 90 - (rad2deg * math.acos(1 - (i * i / (3 * self._resolution * self._resolution))))
36
+ vals[i - 1] = val
37
+ vals[4 * self._resolution - 1 - i] = -val
38
+ # Equatorial belts
39
+ for i in range(self._resolution, 2 * self._resolution):
40
+ val = 90 - (rad2deg * math.acos((4 * self._resolution - 2 * i) / (3 * self._resolution)))
41
+ vals[i - 1] = val
42
+ vals[4 * self._resolution - 1 - i] = -val
43
+ # Equator
44
+ vals[2 * self._resolution - 1] = 0
45
+ return vals
46
+
47
+ def map_first_axis(self, lower, upper):
48
+ axis_lines = self._first_axis_vals
49
+ return_vals = [val for val in axis_lines if lower <= val <= upper]
50
+ return return_vals
51
+
52
+ def second_axis_vals(self, first_val):
53
+ tol = 1e-8
54
+ first_val = [i for i in self._first_axis_vals if first_val[0] - tol <= i <= first_val[0] + tol][0]
55
+ idx = self._first_axis_vals.index(first_val)
56
+
57
+ values = self.HEALPix_longitudes(idx)
58
+ return values
59
+
60
+ def second_axis_vals_from_idx(self, first_val_idx):
61
+ values = self.HEALPix_longitudes(first_val_idx)
62
+ return values
63
+
64
+ def HEALPix_nj(self, i):
65
+ assert self._resolution > 0
66
+ ni = 4 * self._resolution - 1
67
+ assert i < ni
68
+
69
+ if i < self._resolution:
70
+ return 4 * (i + 1)
71
+ elif i < 3 * self._resolution:
72
+ return 4 * self._resolution
73
+ else:
74
+ return self.HEALPix_nj(ni - 1 - i)
75
+
76
+ def HEALPix_longitudes(self, i):
77
+ Nj = self.HEALPix_nj(i)
78
+ step = 360.0 / Nj
79
+ start = (
80
+ step / 2.0 if i < self._resolution or 3 * self._resolution - 1 < i or (i + self._resolution) % 2 else 0.0
81
+ )
82
+
83
+ longitudes = [start + n * step for n in range(Nj)]
84
+
85
+ return longitudes
86
+
87
+ def map_second_axis(self, first_val, lower, upper):
88
+ axis_lines = self.second_axis_vals(first_val)
89
+ return_vals = [val for val in axis_lines if lower <= val <= upper]
90
+ return return_vals
91
+
92
+ def axes_idx_to_healpix_idx(self, first_idx, second_idx):
93
+ idx = 0
94
+ for i in range(self._resolution - 1):
95
+ if i != first_idx:
96
+ idx += 4 * (i + 1)
97
+ else:
98
+ idx += second_idx
99
+ return idx
100
+ for i in range(self._resolution - 1, 3 * self._resolution):
101
+ if i != first_idx:
102
+ idx += 4 * self._resolution
103
+ else:
104
+ idx += second_idx
105
+ return idx
106
+ for i in range(3 * self._resolution, 4 * self._resolution - 1):
107
+ if i != first_idx:
108
+ idx += 4 * (4 * self._resolution - 1 - i + 1)
109
+ else:
110
+ idx += second_idx
111
+ return idx
112
+
113
+ def find_second_idx(self, first_val, second_val):
114
+ tol = 1e-10
115
+ second_axis_vals = self.second_axis_vals(first_val)
116
+ second_idx = bisect.bisect_left(second_axis_vals, second_val - tol)
117
+ return second_idx
118
+
119
+ def unmap_first_val_to_start_line_idx(self, first_val):
120
+ tol = 1e-8
121
+ first_val = [i for i in self._first_axis_vals if first_val - tol <= i <= first_val + tol][0]
122
+ first_idx = self._first_axis_vals.index(first_val)
123
+ idx = 0
124
+ for i in range(self._resolution - 1):
125
+ if i != first_idx:
126
+ idx += 4 * (i + 1)
127
+ else:
128
+ return idx
129
+ for i in range(self._resolution - 1, 3 * self._resolution):
130
+ if i != first_idx:
131
+ idx += 4 * self._resolution
132
+ else:
133
+ return idx
134
+ for i in range(3 * self._resolution, 4 * self._resolution - 1):
135
+ if i != first_idx:
136
+ idx += 4 * (4 * self._resolution - 1 - i + 1)
137
+ else:
138
+ return idx
139
+
140
+ def unmap(self, first_val, second_val):
141
+ tol = 1e-8
142
+ first_value = [i for i in self._first_axis_vals if first_val[0] - tol <= i <= first_val[0] + tol][0]
143
+ first_idx = self._first_axis_vals.index(first_value)
144
+ second_val = [i for i in self.second_axis_vals(first_val) if second_val[0] - tol <= i <= second_val[0] + tol][0]
145
+ second_idx = self.second_axis_vals(first_val).index(second_val)
146
+ healpix_index = self.axes_idx_to_healpix_idx(first_idx, second_idx)
147
+ # TODO: here do conversion of ring to nested healpix representation before returning
148
+ healpix_index = self.ring_to_nested(healpix_index)
149
+ return healpix_index
150
+
151
+ def div_03(self, a, b):
152
+ t = 1 if a >= (b << 1) else 0
153
+ a -= t * (b << 1)
154
+ return (t << 1) + (1 if a >= b else 0)
155
+
156
+ def pll(self, f):
157
+ pll_values = [1, 3, 5, 7, 0, 2, 4, 6, 1, 3, 5, 7]
158
+ return pll_values[f]
159
+
160
+ def to_nest(self, f, ring, Nring, phi, shift):
161
+ r = int(((2 + (f >> 2)) << self.k) - ring - 1)
162
+ p = int(2 * phi - self.pll(f) * Nring - shift - 1)
163
+ if p >= 2 * self.Nside:
164
+ p -= 8 * self.Nside
165
+ i = int((r + p)) >> 1
166
+ j = int((r - p)) >> 1
167
+
168
+ return self.fij_to_nest(f, i, j, self.k)
169
+
170
+ def fij_to_nest(self, f, i, j, k):
171
+ return (f << (2 * k)) + self.nest_encode_bits(i) + (self.nest_encode_bits(j) << 1)
172
+
173
+ def nest_encode_bits(self, i):
174
+ __masks = [
175
+ 0x00000000FFFFFFFF,
176
+ 0x0000FFFF0000FFFF,
177
+ 0x00FF00FF00FF00FF,
178
+ 0x0F0F0F0F0F0F0F0F,
179
+ 0x3333333333333333,
180
+ 0x5555555555555555,
181
+ ]
182
+ i = int(i)
183
+ b = i & __masks[0]
184
+ b = (b ^ (b << 16)) & __masks[1]
185
+ b = (b ^ (b << 8)) & __masks[2]
186
+ b = (b ^ (b << 4)) & __masks[3]
187
+ b = (b ^ (b << 2)) & __masks[4]
188
+ b = (b ^ (b << 1)) & __masks[5]
189
+ return b
190
+
191
+ def ring_to_nested(self, idx):
192
+ if idx < self.Ncap:
193
+ # North polar cap
194
+ Nring = (1 + self.int_sqrt(2 * idx + 1)) >> 1
195
+ phi = 1 + idx - 2 * Nring * (Nring - 1)
196
+ f = self.div_03(phi - 1, Nring)
197
+ return self.to_nest(f, Nring, Nring, phi, 0)
198
+
199
+ if self.Npix - self.Ncap <= idx:
200
+ # South polar cap
201
+ Nring = (1 + self.int_sqrt(2 * self.Npix - 2 * idx - 1)) >> 1
202
+ phi = 1 + idx + 2 * Nring * (Nring - 1) + 4 * Nring - self.Npix
203
+ ring = 4 * self.Nside - Nring # (from South pole)
204
+ f = self.div_03(phi - 1, Nring) + 8
205
+ return self.to_nest(f, ring, Nring, phi, 0)
206
+ else:
207
+ # Equatorial belt
208
+ ip = idx - self.Ncap
209
+ tmp = ip >> (self.k + 2)
210
+
211
+ phi = ip - tmp * 4 * self.Nside + 1
212
+ ring = tmp + self.Nside
213
+
214
+ ifm = 1 + ((phi - 1 - ((1 + tmp) >> 1)) >> self.k)
215
+ ifp = 1 + ((phi - 1 - ((1 - tmp + 2 * self.Nside) >> 1)) >> self.k)
216
+ f = (ifp | 4) if ifp == ifm else (ifp if ifp < ifm else (ifm + 8))
217
+
218
+ return self.to_nest(f, ring, self.Nside, phi, ring & 1)
219
+
220
+ def int_sqrt(self, i):
221
+ return int(math.sqrt(i + 0.5))
222
+
223
+
224
+ # md5 grid hash in form {resolution : hash}
225
+ _md5_hash = {
226
+ 1024: "cbda19e48d4d7e5e22641154878b9b22",
227
+ 512: "47efaa0853e70948a41d5225e7653194",
228
+ 128: "f3dfeb7a5bbbdd13a20d10fdb3797c71",
229
+ }
@@ -0,0 +1,95 @@
1
+ import bisect
2
+
3
+ from ..datacube_mappers import DatacubeMapper
4
+
5
+
6
+ class LocalRegularGridMapper(DatacubeMapper):
7
+ def __init__(self, base_axis, mapped_axes, resolution, md5_hash=None, local_area=[], axis_reversed=None):
8
+ # TODO: if local area is not empty list, raise NotImplemented
9
+ self._mapped_axes = mapped_axes
10
+ self._base_axis = base_axis
11
+ self._first_axis_min = local_area[0]
12
+ self._first_axis_max = local_area[1]
13
+ self._second_axis_min = local_area[2]
14
+ self._second_axis_max = local_area[3]
15
+ if not isinstance(resolution, list):
16
+ self.first_resolution = resolution
17
+ self.second_resolution = resolution
18
+ if md5_hash is not None:
19
+ self.md5_hash = md5_hash
20
+ else:
21
+ self.md5_hash = _md5_hash.get(resolution, None)
22
+ else:
23
+ self.first_resolution = resolution[0]
24
+ self.second_resolution = resolution[1]
25
+ if md5_hash is not None:
26
+ self.md5_hash = md5_hash
27
+ else:
28
+ self.md5_hash = _md5_hash.get(tuple(resolution), None)
29
+ self._first_deg_increment = (local_area[1] - local_area[0]) / self.first_resolution
30
+ self._second_deg_increment = (local_area[3] - local_area[2]) / self.second_resolution
31
+ if axis_reversed is None:
32
+ self._axis_reversed = {mapped_axes[0]: False, mapped_axes[1]: False}
33
+ else:
34
+ assert set(axis_reversed.keys()) == set(mapped_axes)
35
+ self._axis_reversed = axis_reversed
36
+ self._first_axis_vals = self.first_axis_vals()
37
+ self.compressed_grid_axes = [self._mapped_axes[1]]
38
+ if self._axis_reversed[mapped_axes[1]]:
39
+ raise NotImplementedError("Local regular grid with second axis in decreasing order is not supported")
40
+
41
+ def first_axis_vals(self):
42
+ if self._axis_reversed[self._mapped_axes[0]]:
43
+ first_ax_vals = [
44
+ self._first_axis_max - i * self._first_deg_increment for i in range(self.first_resolution + 1)
45
+ ]
46
+ else:
47
+ first_ax_vals = [
48
+ self._first_axis_min + i * self._first_deg_increment for i in range(self.first_resolution + 1)
49
+ ]
50
+ return first_ax_vals
51
+
52
+ def map_first_axis(self, lower, upper):
53
+ axis_lines = self._first_axis_vals
54
+ return_vals = [val for val in axis_lines if lower <= val <= upper]
55
+ return return_vals
56
+
57
+ def second_axis_vals(self, first_val):
58
+ second_ax_vals = [
59
+ self._second_axis_min + i * self._second_deg_increment for i in range(self.second_resolution + 1)
60
+ ]
61
+ return second_ax_vals
62
+
63
+ def map_second_axis(self, first_val, lower, upper):
64
+ axis_lines = self.second_axis_vals(first_val)
65
+ return_vals = [val for val in axis_lines if lower <= val <= upper]
66
+ return return_vals
67
+
68
+ def axes_idx_to_regular_idx(self, first_idx, second_idx):
69
+ final_idx = first_idx * (self.second_resolution + 1) + second_idx
70
+ return final_idx
71
+
72
+ def find_second_idx(self, first_val, second_val):
73
+ tol = 1e-10
74
+ second_axis_vals = self.second_axis_vals(first_val)
75
+ second_idx = bisect.bisect_left(second_axis_vals, second_val - tol)
76
+ return second_idx
77
+
78
+ def unmap_first_val_to_start_line_idx(self, first_val):
79
+ tol = 1e-8
80
+ first_val = [i for i in self._first_axis_vals if first_val - tol <= i <= first_val + tol][0]
81
+ first_idx = self._first_axis_vals.index(first_val)
82
+ return first_idx * self.second_resolution
83
+
84
+ def unmap(self, first_val, second_val):
85
+ tol = 1e-8
86
+ first_val = [i for i in self._first_axis_vals if first_val[0] - tol <= i <= first_val[0] + tol][0]
87
+ first_idx = self._first_axis_vals.index(first_val)
88
+ second_val = [i for i in self.second_axis_vals(first_val) if second_val[0] - tol <= i <= second_val[0] + tol][0]
89
+ second_idx = self.second_axis_vals(first_val).index(second_val)
90
+ final_index = self.axes_idx_to_regular_idx(first_idx, second_idx)
91
+ return final_index
92
+
93
+
94
+ # md5 grid hash in form {resolution : hash}
95
+ _md5_hash = {}