polytope-python 1.0.34__tar.gz → 1.0.36__tar.gz

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 (120) hide show
  1. {polytope_python-1.0.34/polytope_python.egg-info → polytope_python-1.0.36}/PKG-INFO +1 -1
  2. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/backends/fdb.py +13 -8
  3. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_mappers/datacube_mappers.py +3 -5
  4. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix.py +8 -5
  5. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix_nested.py +36 -46
  6. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/local_regular.py +8 -5
  7. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/octahedral.py +10 -7
  8. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_gaussian.py +8 -16
  9. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_ll.py +8 -5
  10. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/regular.py +8 -5
  11. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/tree_encoding.py +2 -1
  12. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/engine/hullslicer.py +16 -5
  13. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/polytope.py +7 -0
  14. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/shapes.py +47 -8
  15. polytope_python-1.0.36/polytope_feature/version.py +1 -0
  16. {polytope_python-1.0.34 → polytope_python-1.0.36/polytope_python.egg-info}/PKG-INFO +1 -1
  17. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_python.egg-info/SOURCES.txt +1 -0
  18. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_fdb_unmap_tree.py +9 -9
  19. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_healpix_mapper.py +1 -1
  20. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_mappers.py +3 -3
  21. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_point_shape.py +9 -3
  22. polytope_python-1.0.36/tests/test_point_union.py +148 -0
  23. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_regular_grid.py +1 -1
  24. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_regular_reduced_grid.py +3 -0
  25. polytope_python-1.0.34/polytope_feature/version.py +0 -1
  26. {polytope_python-1.0.34 → polytope_python-1.0.36}/LICENSE +0 -0
  27. {polytope_python-1.0.34 → polytope_python-1.0.36}/MANIFEST.in +0 -0
  28. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/__init__.py +0 -0
  29. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/__init__.py +0 -0
  30. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/backends/__init__.py +0 -0
  31. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/backends/datacube.py +0 -0
  32. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/backends/mock.py +0 -0
  33. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/backends/xarray.py +0 -0
  34. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/datacube_axis.py +0 -0
  35. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/index_tree_pb2.py +0 -0
  36. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/tensor_index_tree.py +0 -0
  37. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/__init__.py +0 -0
  38. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_cyclic/__init__.py +0 -0
  39. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_cyclic/datacube_cyclic.py +0 -0
  40. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_mappers/__init__.py +0 -0
  41. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/__init__.py +0 -0
  42. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_merger/__init__.py +0 -0
  43. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_merger/datacube_merger.py +0 -0
  44. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_reverse/__init__.py +0 -0
  45. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_reverse/datacube_reverse.py +0 -0
  46. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_transformations.py +0 -0
  47. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_type_change/__init__.py +0 -0
  48. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/datacube/transformations/datacube_type_change/datacube_type_change.py +0 -0
  49. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/engine/__init__.py +0 -0
  50. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/engine/engine.py +0 -0
  51. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/options.py +0 -0
  52. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/utility/__init__.py +0 -0
  53. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/utility/combinatorics.py +0 -0
  54. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/utility/exceptions.py +0 -0
  55. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/utility/geometry.py +0 -0
  56. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/utility/list_tools.py +0 -0
  57. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_feature/utility/profiling.py +0 -0
  58. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_python.egg-info/dependency_links.txt +0 -0
  59. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_python.egg-info/not-zip-safe +0 -0
  60. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_python.egg-info/requires.txt +0 -0
  61. {polytope_python-1.0.34 → polytope_python-1.0.36}/polytope_python.egg-info/top_level.txt +0 -0
  62. {polytope_python-1.0.34 → polytope_python-1.0.36}/pyproject.toml +0 -0
  63. {polytope_python-1.0.34 → polytope_python-1.0.36}/requirements.txt +0 -0
  64. {polytope_python-1.0.34 → polytope_python-1.0.36}/setup.cfg +0 -0
  65. {polytope_python-1.0.34 → polytope_python-1.0.36}/setup.py +0 -0
  66. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_axis_mappers.py +0 -0
  67. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_bad_request_error.py +0 -0
  68. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_combinatorics.py +0 -0
  69. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_cyclic_axis_over_negative_vals.py +0 -0
  70. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_cyclic_axis_slicer_not_0.py +0 -0
  71. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_cyclic_axis_slicing.py +0 -0
  72. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_cyclic_nearest.py +0 -0
  73. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_cyclic_simple.py +0 -0
  74. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_cyclic_snapping.py +0 -0
  75. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_datacube_axes_init.py +0 -0
  76. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_datacube_mock.py +0 -0
  77. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_datacube_xarray.py +0 -0
  78. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_date_time_unmerged.py +0 -0
  79. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_ecmwf_oper_data_fdb.py +0 -0
  80. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_engine_slicer.py +0 -0
  81. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_fdb_datacube.py +0 -0
  82. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_float_type.py +0 -0
  83. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_healpix_nested_grid.py +0 -0
  84. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_hull_slicer.py +0 -0
  85. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_hullslicer_engine.py +0 -0
  86. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_incomplete_tree_fdb.py +0 -0
  87. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_local_grid_cyclic.py +0 -0
  88. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_local_regular_grid.py +0 -0
  89. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_local_swiss_grid.py +0 -0
  90. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_merge_cyclic_octahedral.py +0 -0
  91. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_merge_octahedral_one_axis.py +0 -0
  92. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_merge_transformation.py +0 -0
  93. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_multiple_param_fdb.py +0 -0
  94. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_octahedral_grid.py +0 -0
  95. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_override_md5_hash_options.py +0 -0
  96. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_point_nearest.py +0 -0
  97. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_profiling_requesttree.py +0 -0
  98. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_reduced_ll_grid.py +0 -0
  99. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_request_tree.py +0 -0
  100. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_request_trees_after_slicing.py +0 -0
  101. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_reverse_transformation.py +0 -0
  102. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_shapes.py +0 -0
  103. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_slice_date_range_fdb.py +0 -0
  104. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_slice_date_range_fdb_v2.py +0 -0
  105. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_slice_fdb_box.py +0 -0
  106. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_slicer_engine.py +0 -0
  107. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_slicer_era5.py +0 -0
  108. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_slicer_xarray.py +0 -0
  109. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_slicing_unsliceable_axis.py +0 -0
  110. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_slicing_xarray_3D.py +0 -0
  111. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_slicing_xarray_4D.py +0 -0
  112. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_snapping.py +0 -0
  113. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_snapping_real_data.py +0 -0
  114. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_tree_protobuf.py +0 -0
  115. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_tree_protobuf_encoding.py +0 -0
  116. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_tree_protobuf_encoding_fdb.py +0 -0
  117. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_type_change_transformation.py +0 -0
  118. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_union_gj.py +0 -0
  119. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_union_point_box.py +0 -0
  120. {polytope_python-1.0.34 → polytope_python-1.0.36}/tests/test_wave_spectra_data.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: polytope-python
3
- Version: 1.0.34
3
+ Version: 1.0.36
4
4
  Summary: Polytope datacube feature extraction library
5
5
  Home-page: https://github.com/ecmwf/polytope
6
6
  Author: ECMWF
@@ -227,17 +227,22 @@ class FDBDatacube(Datacube):
227
227
  first_ax_name = requests.children[0].axis.name
228
228
  second_ax_name = requests.children[0].children[0].axis.name
229
229
 
230
- if first_ax_name not in self.nearest_search.keys() or second_ax_name not in self.nearest_search.keys():
230
+ axes_in_nearest_search = [
231
+ first_ax_name not in self.nearest_search.keys(),
232
+ second_ax_name not in self.nearest_search.keys(),
233
+ ]
234
+
235
+ if all(not item for item in axes_in_nearest_search):
231
236
  raise Exception("nearest point search axes are wrong")
232
237
 
233
238
  second_ax = requests.children[0].children[0].axis
239
+ nearest_pts = self.nearest_search.get(first_ax_name, None)
240
+ if nearest_pts is None:
241
+ nearest_pts = self.nearest_search[second_ax_name]
234
242
 
235
- nearest_pts = [
236
- [lat_val, second_ax._remap_val_to_axis_range(lon_val)]
237
- for (lat_val, lon_val) in zip(
238
- self.nearest_search[first_ax_name][0], self.nearest_search[second_ax_name][0]
239
- )
240
- ]
243
+ transformed_nearest_pts = []
244
+ for point in nearest_pts:
245
+ transformed_nearest_pts.append([point[0], second_ax._remap_val_to_axis_range(point[1])])
241
246
 
242
247
  found_latlon_pts = []
243
248
  for lat_child in requests.children:
@@ -246,7 +251,7 @@ class FDBDatacube(Datacube):
246
251
 
247
252
  # now find the nearest lat lon to the points requested
248
253
  nearest_latlons = []
249
- for pt in nearest_pts:
254
+ for pt in transformed_nearest_pts:
250
255
  nearest_latlon = nearest_pt(found_latlon_pts, pt)
251
256
  nearest_latlons.append(nearest_latlon)
252
257
 
@@ -99,16 +99,14 @@ class DatacubeMapper(DatacubeAxisTransformation):
99
99
  return self.second_axis_vals(first_val)
100
100
 
101
101
  def unmap_path_key(self, key_value_path, leaf_path, unwanted_path, axis):
102
- value = key_value_path[axis.name]
102
+ values = key_value_path[axis.name]
103
103
  if axis.name == self._mapped_axes()[0]:
104
104
  unwanted_val = key_value_path[self._mapped_axes()[0]]
105
105
  unwanted_path[axis.name] = unwanted_val
106
106
  if axis.name == self._mapped_axes()[1]:
107
107
  first_val = unwanted_path[self._mapped_axes()[0]]
108
- unmapped_idx = []
109
- for val in value:
110
- unmapped_idx.append(self.unmap(first_val, (val,)))
111
- # unmapped_idx = self.unmap(first_val, value)
108
+ # unmapped_idx = [self.unmap(first_val, (val,)) for val in value]
109
+ unmapped_idx = self.unmap(first_val, values)
112
110
  leaf_path.pop(self._mapped_axes()[0], None)
113
111
  key_value_path.pop(axis.name)
114
112
  key_value_path[self.old_axis] = unmapped_idx
@@ -133,14 +133,17 @@ class HealpixGridMapper(DatacubeMapper):
133
133
  else:
134
134
  return idx
135
135
 
136
- def unmap(self, first_val, second_val):
136
+ def unmap(self, first_val, second_vals):
137
137
  tol = 1e-8
138
138
  first_value = [i for i in self._first_axis_vals if first_val[0] - tol <= i <= first_val[0] + tol][0]
139
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
140
+ return_idxs = []
141
+ for second_val in second_vals:
142
+ second_val = [i for i in self.second_axis_vals(first_val) if second_val - tol <= i <= second_val + tol][0]
143
+ second_idx = self.second_axis_vals(first_val).index(second_val)
144
+ healpix_index = self.axes_idx_to_healpix_idx(first_idx, second_idx)
145
+ return_idxs.append(healpix_index)
146
+ return return_idxs
144
147
 
145
148
 
146
149
  # md5 grid hash in form {resolution : hash}
@@ -1,4 +1,3 @@
1
- import bisect
2
1
  import math
3
2
 
4
3
  from ..datacube_mappers import DatacubeMapper
@@ -14,9 +13,11 @@ class NestedHealpixGridMapper(DatacubeMapper):
14
13
  self._first_axis_vals = self.first_axis_vals()
15
14
  self.compressed_grid_axes = [self._mapped_axes[1]]
16
15
  self.Nside = self._resolution
16
+ self._cached_longitudes = {}
17
17
  self.k = int(math.log2(self.Nside))
18
18
  self.Npix = 12 * self.Nside * self.Nside
19
19
  self.Ncap = (self.Nside * (self.Nside - 1)) << 1
20
+ self._healpix_longitudes = {}
20
21
  if md5_hash is not None:
21
22
  self.md5_hash = md5_hash
22
23
  else:
@@ -58,7 +59,9 @@ class NestedHealpixGridMapper(DatacubeMapper):
58
59
  return values
59
60
 
60
61
  def second_axis_vals_from_idx(self, first_val_idx):
61
- values = self.HEALPix_longitudes(first_val_idx)
62
+ if first_val_idx not in self._healpix_longitudes:
63
+ self._healpix_longitudes[first_val_idx] = self.HEALPix_longitudes(first_val_idx)
64
+ values = self._healpix_longitudes[first_val_idx]
62
65
  return values
63
66
 
64
67
  def HEALPix_nj(self, i):
@@ -74,14 +77,19 @@ class NestedHealpixGridMapper(DatacubeMapper):
74
77
  return self.HEALPix_nj(ni - 1 - i)
75
78
 
76
79
  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
-
80
+ if i in self._cached_longitudes:
81
+ return self._cached_longitudes[i]
82
+ else:
83
+ Nj = self.HEALPix_nj(i)
84
+ step = 360.0 / Nj
85
+ start = (
86
+ step / 2.0
87
+ if i < self._resolution or 3 * self._resolution - 1 < i or (i + self._resolution) % 2
88
+ else 0.0
89
+ )
90
+
91
+ longitudes = [start + n * step for n in range(Nj)]
92
+ self._cached_longitudes[i] = longitudes
85
93
  return longitudes
86
94
 
87
95
  def map_second_axis(self, first_val, lower, upper):
@@ -110,43 +118,25 @@ class NestedHealpixGridMapper(DatacubeMapper):
110
118
  idx += second_idx
111
119
  return idx
112
120
 
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):
121
+ def unmap(self, first_val, second_vals):
141
122
  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
123
+ first_idx = next(
124
+ (i for i, val in enumerate(self._first_axis_vals) if first_val[0] - tol <= val <= first_val[0] + tol), None
125
+ )
126
+ if first_idx is None:
127
+ return None
128
+ second_axis_vals = self.second_axis_vals_from_idx(first_idx)
129
+
130
+ return_idxs = []
131
+ for second_val in second_vals:
132
+ second_idx = next(
133
+ (i for i, val in enumerate(second_axis_vals) if second_val - tol <= val <= second_val + tol), None
134
+ )
135
+ if second_idx is None:
136
+ return None
137
+ healpix_index = self.axes_idx_to_healpix_idx(first_idx, second_idx)
138
+ return_idxs.append(healpix_index)
139
+ return return_idxs
150
140
 
151
141
  def div_03(self, a, b):
152
142
  t = 1 if a >= (b << 1) else 0
@@ -81,14 +81,17 @@ class LocalRegularGridMapper(DatacubeMapper):
81
81
  first_idx = self._first_axis_vals.index(first_val)
82
82
  return first_idx * self.second_resolution
83
83
 
84
- def unmap(self, first_val, second_val):
84
+ def unmap(self, first_val, second_vals):
85
85
  tol = 1e-8
86
86
  first_val = [i for i in self._first_axis_vals if first_val[0] - tol <= i <= first_val[0] + tol][0]
87
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
88
+ return_idxs = []
89
+ for second_val in second_vals:
90
+ second_val = [i for i in self.second_axis_vals(first_val) if second_val - tol <= i <= second_val + tol][0]
91
+ second_idx = self.second_axis_vals(first_val).index(second_val)
92
+ final_index = self.axes_idx_to_regular_idx(first_idx, second_idx)
93
+ return_idxs.append(final_index)
94
+ return return_idxs
92
95
 
93
96
 
94
97
  # md5 grid hash in form {resolution : hash}
@@ -7877,16 +7877,19 @@ class OctahedralGridMapper(DatacubeMapper):
7877
7877
  def find_second_axis_idx(self, first_val, second_val):
7878
7878
  (second_axis_spacing, first_idx) = self.second_axis_spacing(first_val)
7879
7879
  tol = 1e-8
7880
- if second_val[0] / second_axis_spacing > int(second_val[0] / second_axis_spacing) + 1 - tol:
7881
- second_idx = int(second_val[0] / second_axis_spacing) + 1
7880
+ if second_val / second_axis_spacing > int(second_val / second_axis_spacing) + 1 - tol:
7881
+ second_idx = int(second_val / second_axis_spacing) + 1
7882
7882
  else:
7883
- second_idx = int(second_val[0] / second_axis_spacing)
7883
+ second_idx = int(second_val / second_axis_spacing)
7884
7884
  return (first_idx, second_idx)
7885
7885
 
7886
- def unmap(self, first_val, second_val):
7887
- (first_idx, second_idx) = self.find_second_axis_idx(first_val, second_val)
7888
- octahedral_index = self.axes_idx_to_octahedral_idx(first_idx, second_idx)
7889
- return octahedral_index
7886
+ def unmap(self, first_val, second_vals):
7887
+ return_idxs = []
7888
+ for second_val in second_vals:
7889
+ (first_idx, second_idx) = self.find_second_axis_idx(first_val, second_val)
7890
+ octahedral_index = self.axes_idx_to_octahedral_idx(first_idx, second_idx)
7891
+ return_idxs.append(octahedral_index)
7892
+ return return_idxs
7890
7893
 
7891
7894
 
7892
7895
  # md5 grid hash in form {resolution : hash}
@@ -1432,25 +1432,17 @@ class ReducedGaussianGridMapper(DatacubeMapper):
1432
1432
  idx += second_idx
1433
1433
  return idx
1434
1434
 
1435
- # def find_second_idx(self, first_val, second_val):
1436
- # tol = 1e-10
1437
- # second_axis_vals = self.second_axis_vals(first_val)
1438
- # second_idx = bisect.bisect_left(second_axis_vals, second_val[0] - tol)
1439
- # return second_idx
1440
-
1441
- # def unmap(self, first_val, second_val):
1442
- # (first_idx, second_idx) = self.find_second_idx(first_val, second_val)
1443
- # octahedral_index = self.axes_idx_to_reduced_gaussian_idx(first_idx, second_idx)
1444
- # return octahedral_index
1445
-
1446
- def unmap(self, first_val, second_val):
1435
+ def unmap(self, first_val, second_vals):
1447
1436
  tol = 1e-8
1448
1437
  first_value = [i for i in self._first_axis_vals if first_val[0] - tol <= i <= first_val[0] + tol][0]
1449
1438
  first_idx = self._first_axis_vals.index(first_value)
1450
- second_val = [i for i in self.second_axis_vals(first_val) if second_val[0] - tol <= i <= second_val[0] + tol][0]
1451
- second_idx = self.second_axis_vals(first_val).index(second_val)
1452
- reduced_gaussian_index = self.axes_idx_to_reduced_gaussian_idx(first_idx, second_idx)
1453
- return reduced_gaussian_index
1439
+ return_idxs = []
1440
+ for second_val in second_vals:
1441
+ second_val = [i for i in self.second_axis_vals(first_val) if second_val - tol <= i <= second_val + tol][0]
1442
+ second_idx = self.second_axis_vals(first_val).index(second_val)
1443
+ reduced_gaussian_index = self.axes_idx_to_reduced_gaussian_idx(first_idx, second_idx)
1444
+ return_idxs.append(reduced_gaussian_index)
1445
+ return return_idxs
1454
1446
 
1455
1447
 
1456
1448
  # md5 grid hash in form {resolution : hash}
@@ -5114,14 +5114,17 @@ class ReducedLatLonMapper(DatacubeMapper):
5114
5114
  second_idx = bisect.bisect_left(second_axis_vals, second_val - tol)
5115
5115
  return second_idx
5116
5116
 
5117
- def unmap(self, first_val, second_val):
5117
+ def unmap(self, first_val, second_vals):
5118
5118
  tol = 1e-8
5119
5119
  first_value = [i for i in self._first_axis_vals if first_val[0] - tol <= i <= first_val[0] + tol][0]
5120
5120
  first_idx = self._first_axis_vals.index(first_value)
5121
- second_val = [i for i in self.second_axis_vals(first_val) if second_val[0] - tol <= i <= second_val[0] + tol][0]
5122
- second_idx = self.second_axis_vals(first_val).index(second_val)
5123
- reduced_ll_index = self.axes_idx_to_reduced_ll_idx(first_idx, second_idx)
5124
- return reduced_ll_index
5121
+ return_idxs = []
5122
+ for second_val in second_vals:
5123
+ second_val = [i for i in self.second_axis_vals(first_val) if second_val - tol <= i <= second_val + tol][0]
5124
+ second_idx = self.second_axis_vals(first_val).index(second_val)
5125
+ reduced_ll_index = self.axes_idx_to_reduced_ll_idx(first_idx, second_idx)
5126
+ return_idxs.append(reduced_ll_index)
5127
+ return return_idxs
5125
5128
 
5126
5129
 
5127
5130
  # md5 grid hash in form {resolution : hash}
@@ -61,14 +61,17 @@ class RegularGridMapper(DatacubeMapper):
61
61
  first_idx = self._first_axis_vals.index(first_val)
62
62
  return first_idx * 4 * self._resolution
63
63
 
64
- def unmap(self, first_val, second_val):
64
+ def unmap(self, first_val, second_vals):
65
65
  tol = 1e-8
66
66
  first_val = [i for i in self._first_axis_vals if first_val[0] - tol <= i <= first_val[0] + tol][0]
67
67
  first_idx = self._first_axis_vals.index(first_val)
68
- second_val = [i for i in self.second_axis_vals(first_val) if second_val[0] - tol <= i <= second_val[0] + tol][0]
69
- second_idx = self.second_axis_vals(first_val).index(second_val)
70
- final_index = self.axes_idx_to_regular_idx(first_idx, second_idx)
71
- return final_index
68
+ return_idxs = []
69
+ for second_val in second_vals:
70
+ second_val = [i for i in self.second_axis_vals(first_val) if second_val - tol <= i <= second_val + tol][0]
71
+ second_idx = self.second_axis_vals(first_val).index(second_val)
72
+ final_index = self.axes_idx_to_regular_idx(first_idx, second_idx)
73
+ return_idxs.append(final_index)
74
+ return return_idxs
72
75
 
73
76
 
74
77
  # md5 grid hash in form {resolution : hash}
@@ -38,7 +38,8 @@ def encode_child(tree: TensorIndexTree, child: TensorIndexTree, node, result_siz
38
38
 
39
39
  if child.hidden:
40
40
  # add indexes to parent and add also indexes size...
41
- node.indexes.extend(tree.indexes)
41
+ for tree_indexes in tree.indexes:
42
+ node.indexes.extend(tree_indexes)
42
43
  break_tag = False
43
44
  return break_tag
44
45
 
@@ -8,7 +8,7 @@ import scipy.spatial
8
8
  from ..datacube.backends.datacube import Datacube
9
9
  from ..datacube.datacube_axis import UnsliceableDatacubeAxis
10
10
  from ..datacube.tensor_index_tree import TensorIndexTree
11
- from ..shapes import ConvexPolytope
11
+ from ..shapes import ConvexPolytope, Product
12
12
  from ..utility.combinatorics import group, tensor_product
13
13
  from ..utility.exceptions import UnsliceableShapeError
14
14
  from ..utility.geometry import lerp
@@ -76,8 +76,6 @@ class HullSlicer(Engine):
76
76
  upper = ax.from_float(upper + tol)
77
77
  flattened = node.flatten()
78
78
  method = polytope.method
79
- if method == "nearest":
80
- datacube.nearest_search[ax.name] = polytope.points
81
79
 
82
80
  # NOTE: caching
83
81
  # Create a coupled_axes list inside of datacube and add to it during axis formation, then here
@@ -214,7 +212,11 @@ class HullSlicer(Engine):
214
212
 
215
213
  # Convert the polytope points to float type to support triangulation and interpolation
216
214
  for p in polytopes:
217
- self._unique_continuous_points(p, datacube)
215
+ if isinstance(p, Product):
216
+ for poly in p.polytope():
217
+ self._unique_continuous_points(poly, datacube)
218
+ else:
219
+ self._unique_continuous_points(p, datacube)
218
220
 
219
221
  groups, input_axes = group(polytopes)
220
222
  datacube.validate(input_axes)
@@ -233,7 +235,16 @@ class HullSlicer(Engine):
233
235
  new_c.extend(combi)
234
236
  else:
235
237
  new_c.append(combi)
236
- r["unsliced_polytopes"] = set(new_c)
238
+ # NOTE TODO: here some of the polys in new_c can be a Product shape instead of a ConvexPolytope
239
+ # -> need to go through the polytopes in new_c and replace the Products with their sub-ConvexPolytopes
240
+ final_polys = []
241
+ for poly in new_c:
242
+ if isinstance(poly, Product):
243
+ final_polys.extend(poly.polytope())
244
+ else:
245
+ final_polys.append(poly)
246
+ # r["unsliced_polytopes"] = set(new_c)
247
+ r["unsliced_polytopes"] = set(final_polys)
237
248
  current_nodes = [r]
238
249
  for ax in datacube.axes.values():
239
250
  next_nodes = []
@@ -64,6 +64,13 @@ class Polytope:
64
64
  """Higher-level API which takes a request and uses it to slice the datacube"""
65
65
  logging.info("Starting request for %s ", self.context)
66
66
  self.datacube.check_branching_axes(request)
67
+ for polytope in request.polytopes():
68
+ method = polytope.method
69
+ if method == "nearest":
70
+ if self.datacube.nearest_search.get(polytope.axes()[0], None) is None:
71
+ self.datacube.nearest_search[polytope.axes()[0]] = polytope.values
72
+ else:
73
+ self.datacube.nearest_search[polytope.axes()[0]].append(polytope.values[0])
67
74
  request_tree = self.engine.extract(self.datacube, request.polytopes())
68
75
  logging.info("Created request tree for %s ", self.context)
69
76
  self.datacube.get(request_tree, self.context)
@@ -61,6 +61,42 @@ class ConvexPolytope(Shape):
61
61
  return [self]
62
62
 
63
63
 
64
+ class Product(Shape):
65
+ """Shape that takes two polytopes and 'multiplies' them together to obtain higher-dimensional shape"""
66
+
67
+ def __init__(self, *polytopes, method, value):
68
+ # TODO
69
+ all_axes = []
70
+ for poly in polytopes:
71
+ all_axes.extend(poly.axes())
72
+ self._axes = list(set(all_axes))
73
+ # Check there weren't any duplicates in the polytopes' axes
74
+ assert len(self._axes) == len(all_axes)
75
+
76
+ self._polytopes = []
77
+ for poly in polytopes:
78
+ self._polytopes.extend(poly.polytope())
79
+
80
+ self.is_in_union = False
81
+ self.method = method
82
+ self.values = value
83
+
84
+ self.is_orthogonal = False
85
+
86
+ polys_orthogonal = [poly.is_orthogonal for poly in polytopes]
87
+ if all(polys_orthogonal):
88
+ self.is_orthogonal = True
89
+
90
+ def add_to_union(self):
91
+ self.is_in_union = True
92
+
93
+ def axes(self):
94
+ return self._axes
95
+
96
+ def polytope(self):
97
+ return self._polytopes
98
+
99
+
64
100
  # This is the only shape which can slice on axes without a discretizer or interpolator
65
101
  class Select(Shape):
66
102
  """Matches several discrete values"""
@@ -89,19 +125,22 @@ class Point(Shape):
89
125
  self._axes = axes
90
126
  self.values = values
91
127
  self.method = method
92
- self.polytopes = []
93
- if method == "nearest":
94
- assert len(self.values) == 1
95
- for i in range(len(axes)):
96
- polytope_points = [v[i] for v in self.values]
97
- self.polytopes.extend(
98
- [ConvexPolytope([axes[i]], [[point]], self.method, is_orthogonal=True) for point in polytope_points]
99
- )
128
+ assert len(values) == 1
100
129
 
101
130
  def axes(self):
102
131
  return self._axes
103
132
 
104
133
  def polytope(self):
134
+ # TODO: change this to use the Product instead and return a Product here of the two 1D selects
135
+
136
+ polytopes = []
137
+ for point in self.values:
138
+ poly_to_mult = []
139
+ for i in range(len(self._axes)):
140
+ poly_to_mult.append(ConvexPolytope([self._axes[i]], [[point[i]]], self.method, is_orthogonal=True))
141
+ polytopes.append(Product(*poly_to_mult, method=self.method, value=[point]))
142
+ self.polytopes = polytopes
143
+
105
144
  return self.polytopes
106
145
 
107
146
  def __repr__(self):
@@ -0,0 +1 @@
1
+ __version__ = "1.0.36"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: polytope-python
3
- Version: 1.0.34
3
+ Version: 1.0.36
4
4
  Summary: Polytope datacube feature extraction library
5
5
  Home-page: https://github.com/ecmwf/polytope
6
6
  Author: ECMWF
@@ -88,6 +88,7 @@ tests/test_octahedral_grid.py
88
88
  tests/test_override_md5_hash_options.py
89
89
  tests/test_point_nearest.py
90
90
  tests/test_point_shape.py
91
+ tests/test_point_union.py
91
92
  tests/test_profiling_requesttree.py
92
93
  tests/test_reduced_ll_grid.py
93
94
  tests/test_regular_grid.py
@@ -73,13 +73,13 @@ class TestSlicingFDBDatacube:
73
73
  assert leaf.hidden
74
74
  assert leaf._parent.hidden
75
75
  assert leaf._parent._parent.indexes == [
76
- 3294704,
77
- 3294705,
78
- 3294706,
79
- 3289572,
80
- 3289573,
81
- 3289574,
82
- 3284444,
83
- 3284445,
84
- 3284446,
76
+ [3294704],
77
+ [3294705],
78
+ [3294706],
79
+ [3289572],
80
+ [3289573],
81
+ [3289574],
82
+ [3284444],
83
+ [3284445],
84
+ [3284446],
85
85
  ]
@@ -70,7 +70,7 @@ class TestHealpixGrid:
70
70
  eccodes_result = nearest_points[0][0]["value"]
71
71
 
72
72
  mapper = HealpixGridMapper("base", ["base1", "base2"], 32)
73
- assert nearest_points[0][0]["index"] == mapper.unmap((lat,), (lon,))
73
+ assert nearest_points[0][0]["index"] == mapper.unmap((lat,), (lon,))[0]
74
74
  assert eccodes_lat - tol <= lat
75
75
  assert lat <= eccodes_lat + tol
76
76
  assert eccodes_lon - tol <= lon
@@ -102,6 +102,6 @@ class TestMapper:
102
102
  base_axis = "base"
103
103
  resolution = 1280
104
104
  octahedral_mapper = OctahedralGridMapper(base_axis, mapped_axes, resolution)
105
- assert octahedral_mapper.unmap((89.94618771566562,), (0,)) == 0
106
- assert octahedral_mapper.unmap((0.035149384215604956,), (0,)) == 3299840 - 5136
107
- assert octahedral_mapper.unmap((-0.035149384215604956,), (0,)) == 3299840
105
+ assert octahedral_mapper.unmap((89.94618771566562,), (0,))[0] == 0
106
+ assert octahedral_mapper.unmap((0.035149384215604956,), (0,))[0] == 3299840 - 5136
107
+ assert octahedral_mapper.unmap((-0.035149384215604956,), (0,))[0] == 3299840
@@ -4,7 +4,7 @@ import xarray as xr
4
4
 
5
5
  from polytope_feature.engine.hullslicer import HullSlicer
6
6
  from polytope_feature.polytope import Polytope, Request
7
- from polytope_feature.shapes import Point, Select
7
+ from polytope_feature.shapes import Point, Select, Union
8
8
 
9
9
 
10
10
  class TestSlicing3DXarrayDatacube:
@@ -30,20 +30,26 @@ class TestSlicing3DXarrayDatacube:
30
30
  assert result.leaves[0].axis.name == "level"
31
31
 
32
32
  def test_multiple_points(self):
33
- request = Request(Point(["step", "level"], [[3, 10], [3, 12]]), Select("date", ["2000-01-01"]))
33
+ # request = Request(Point(["step", "level"], [[3, 10], [3, 12]]), Select("date", ["2000-01-01"]))
34
+ request = Request(
35
+ Union(["step", "level"], Point(["step", "level"], [[3, 10]]), Point(["step", "level"], [[3, 12]])),
36
+ Select("date", ["2000-01-01"]),
37
+ )
34
38
  result = self.API.retrieve(request)
35
39
  result.pprint()
36
- assert len(result.leaves) == 1
40
+ assert len(result.leaves) == 2
37
41
  assert result.leaves[0].axis.name == "level"
38
42
 
39
43
  def test_point_surrounding_step(self):
40
44
  request = Request(Point(["step", "level"], [[2, 10]], method="surrounding"), Select("date", ["2000-01-01"]))
41
45
  result = self.API.retrieve(request)
46
+ result.pprint()
42
47
  assert len(result.leaves) == 1
43
48
  assert np.shape(result.leaves[0].result[1]) == (1, 2, 3)
44
49
 
45
50
  def test_point_surrounding_exact_step(self):
46
51
  request = Request(Point(["step", "level"], [[3, 10]], method="surrounding"), Select("date", ["2000-01-01"]))
47
52
  result = self.API.retrieve(request)
53
+ result.pprint()
48
54
  assert len(result.leaves) == 1
49
55
  assert np.shape(result.leaves[0].result[1]) == (1, 3, 3)
@@ -0,0 +1,148 @@
1
+ import pandas as pd
2
+ import pytest
3
+
4
+ from polytope_feature.engine.hullslicer import HullSlicer
5
+ from polytope_feature.polytope import Polytope, Request
6
+ from polytope_feature.shapes import Point, Select, Span, Union
7
+
8
+
9
+ class TestSlicingFDBDatacube:
10
+ def setup_method(self, method):
11
+ # Create a dataarray with 3 labelled axes using different index types
12
+ self.options = {
13
+ "axis_config": [
14
+ {"axis_name": "number", "transformations": [{"name": "type_change", "type": "int"}]},
15
+ {"axis_name": "step", "transformations": [{"name": "type_change", "type": "int"}]},
16
+ {
17
+ "axis_name": "date",
18
+ "transformations": [{"name": "merge", "other_axis": "time", "linkers": ["T", "00"]}],
19
+ },
20
+ {
21
+ "axis_name": "values",
22
+ "transformations": [
23
+ {"name": "mapper", "type": "octahedral", "resolution": 1280, "axes": ["latitude", "longitude"]}
24
+ ],
25
+ },
26
+ {"axis_name": "latitude", "transformations": [{"name": "reverse", "is_reverse": True}]},
27
+ {"axis_name": "longitude", "transformations": [{"name": "cyclic", "range": [0, 360]}]},
28
+ ],
29
+ "pre_path": {"class": "od", "expver": "0001", "levtype": "sfc", "stream": "oper"},
30
+ "compressed_axes_config": [
31
+ "longitude",
32
+ "latitude",
33
+ "levtype",
34
+ "step",
35
+ "date",
36
+ "domain",
37
+ "expver",
38
+ "param",
39
+ "class",
40
+ "stream",
41
+ "type",
42
+ ],
43
+ }
44
+
45
+ # Testing different shapes
46
+ @pytest.mark.fdb
47
+ def test_fdb_datacube(self):
48
+ import pygribjump as gj
49
+
50
+ request = Request(
51
+ Select("step", [0]),
52
+ Select("levtype", ["sfc"]),
53
+ Span("date", pd.Timestamp("20230625T120000"), pd.Timestamp("20230626T120000")),
54
+ Select("domain", ["g"]),
55
+ Select("expver", ["0001"]),
56
+ Select("param", ["167"]),
57
+ Select("class", ["od"]),
58
+ Select("stream", ["oper"]),
59
+ Select("type", ["an"]),
60
+ Union(
61
+ ["latitude", "longitude"],
62
+ Point(["latitude", "longitude"], [[20, 20]], method="nearest"),
63
+ Point(["latitude", "longitude"], [[0, 0]], method="nearest"),
64
+ Point(["latitude", "longitude"], [[0, 20]], method="nearest"),
65
+ Point(["latitude", "longitude"], [[25, 30]], method="nearest"),
66
+ Point(["latitude", "longitude"], [[-30, 90]], method="nearest"),
67
+ Point(["latitude", "longitude"], [[-60, -30]], method="nearest"),
68
+ Point(["latitude", "longitude"], [[-15, -45]], method="nearest"),
69
+ Point(["latitude", "longitude"], [[20, 0]], method="nearest"),
70
+ ),
71
+ )
72
+
73
+ self.fdbdatacube = gj.GribJump()
74
+ self.slicer = HullSlicer()
75
+ self.API = Polytope(
76
+ datacube=self.fdbdatacube,
77
+ engine=self.slicer,
78
+ options=self.options,
79
+ )
80
+ result = self.API.retrieve(request)
81
+ result.pprint()
82
+ assert len(result.leaves) == 8
83
+
84
+ @pytest.mark.fdb
85
+ def test_fdb_datacube_surrounding(self):
86
+ import pygribjump as gj
87
+
88
+ request = Request(
89
+ Select("step", [0]),
90
+ Select("levtype", ["sfc"]),
91
+ Span("date", pd.Timestamp("20230625T120000"), pd.Timestamp("20230626T120000")),
92
+ Select("domain", ["g"]),
93
+ Select("expver", ["0001"]),
94
+ Select("param", ["167"]),
95
+ Select("class", ["od"]),
96
+ Select("stream", ["oper"]),
97
+ Select("type", ["an"]),
98
+ Union(
99
+ ["latitude", "longitude"],
100
+ Point(["latitude", "longitude"], [[25, 30]], method="surrounding"),
101
+ Point(["latitude", "longitude"], [[-15, -45]], method="surrounding"),
102
+ ),
103
+ )
104
+
105
+ self.fdbdatacube = gj.GribJump()
106
+ self.slicer = HullSlicer()
107
+ self.API = Polytope(
108
+ datacube=self.fdbdatacube,
109
+ engine=self.slicer,
110
+ options=self.options,
111
+ )
112
+ result = self.API.retrieve(request)
113
+ result.pprint()
114
+ assert len(result.leaves) == 4
115
+ tot_leaves = 0
116
+ for leaf in result.leaves:
117
+ tot_leaves += len(leaf.result)
118
+ assert tot_leaves == 9
119
+
120
+ # @pytest.mark.fdb
121
+ # def test_fdb_datacube_mix_methods(self):
122
+ # import pygribjump as gj
123
+
124
+ # request = Request(
125
+ # Select("step", [0]),
126
+ # Select("levtype", ["sfc"]),
127
+ # Span("date", pd.Timestamp("20230625T120000"), pd.Timestamp("20230626T120000")),
128
+ # Select("domain", ["g"]),
129
+ # Select("expver", ["0001"]),
130
+ # Select("param", ["167"]),
131
+ # Select("class", ["od"]),
132
+ # Select("stream", ["oper"]),
133
+ # Select("type", ["an"]),
134
+ # Union(["latitude", "longitude"],
135
+ # Point(["latitude", "longitude"], [[25, 30]], method="nearest"),
136
+ # Point(["latitude", "longitude"], [[-15, -45]], method="surrounding"))
137
+ # )
138
+
139
+ # self.fdbdatacube = gj.GribJump()
140
+ # self.slicer = HullSlicer()
141
+ # self.API = Polytope(
142
+ # datacube=self.fdbdatacube,
143
+ # engine=self.slicer,
144
+ # options=self.options,
145
+ # )
146
+ # result = self.API.retrieve(request)
147
+ # result.pprint()
148
+ # assert len(result.leaves) == 6
@@ -116,7 +116,7 @@ class TestRegularGrid:
116
116
  eccodes_lats.append(eccodes_lat)
117
117
 
118
118
  mapper = RegularGridMapper("base", ["base1", "base2"], 30)
119
- assert nearest_points[121][0]["index"] == mapper.unmap((lat,), (lon,))
119
+ assert nearest_points[121][0]["index"] == mapper.unmap((lat,), (lon,))[0]
120
120
 
121
121
  assert eccodes_lat - tol <= lat
122
122
  assert lat <= eccodes_lat + tol
@@ -10,6 +10,8 @@ from polytope_feature.shapes import Box, Select
10
10
  class TestSlicingFDBDatacube:
11
11
  def setup_method(self, method):
12
12
  # Create a dataarray with 3 labelled axes using different index types
13
+ # TODO: This uses the wrong fdb/schema/data so the hash for the grid is wrong
14
+ # BUT the validation against eccodes is OK because the GRIB file is the same grid as Polytope returns
13
15
  self.options = {
14
16
  "axis_config": [
15
17
  {"axis_name": "step", "transformations": [{"name": "type_change", "type": "int"}]},
@@ -25,6 +27,7 @@ class TestSlicingFDBDatacube:
25
27
  "type": "reduced_gaussian",
26
28
  "resolution": 320,
27
29
  "axes": ["latitude", "longitude"],
30
+ "md5_hash": "158db321ae8e773681eeb40e0a3d350f",
28
31
  }
29
32
  ],
30
33
  },
@@ -1 +0,0 @@
1
- __version__ = "1.0.34"