polytope-python 2.1.3__tar.gz → 2.1.4__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 (287) hide show
  1. {polytope_python-2.1.3 → polytope_python-2.1.4}/PKG-INFO +4 -1
  2. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/_version.py +1 -1
  3. polytope_python-2.1.4/polytope_feature/datacube/switching_grid_helper.py +215 -0
  4. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_type_change/datacube_type_change.py +47 -11
  5. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/options.py +53 -0
  6. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_python.egg-info/PKG-INFO +4 -1
  7. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_python.egg-info/SOURCES.txt +1 -0
  8. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_python.egg-info/requires.txt +4 -0
  9. {polytope_python-2.1.3 → polytope_python-2.1.4}/pyproject.toml +5 -0
  10. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_type_change_transformation.py +11 -7
  11. {polytope_python-2.1.3 → polytope_python-2.1.4}/.github/ci-config.yml +0 -0
  12. {polytope_python-2.1.3 → polytope_python-2.1.4}/.github/ci-hpc-config.yml +0 -0
  13. {polytope_python-2.1.3 → polytope_python-2.1.4}/.github/workflows/cd.yml +0 -0
  14. {polytope_python-2.1.3 → polytope_python-2.1.4}/.github/workflows/downstream-ci.yml +0 -0
  15. {polytope_python-2.1.3 → polytope_python-2.1.4}/.github/workflows/label-public-pr.yml +0 -0
  16. {polytope_python-2.1.3 → polytope_python-2.1.4}/.github/workflows/test-pypi.yml +0 -0
  17. {polytope_python-2.1.3 → polytope_python-2.1.4}/.gitignore +0 -0
  18. {polytope_python-2.1.3 → polytope_python-2.1.4}/.pre-commit-config.yaml +0 -0
  19. {polytope_python-2.1.3 → polytope_python-2.1.4}/.readthedocs.yaml +0 -0
  20. {polytope_python-2.1.3 → polytope_python-2.1.4}/ACKNOWLEDGEMENTS.rst +0 -0
  21. {polytope_python-2.1.3 → polytope_python-2.1.4}/CONTRIBUTING.rst +0 -0
  22. {polytope_python-2.1.3 → polytope_python-2.1.4}/LICENSE +0 -0
  23. {polytope_python-2.1.3 → polytope_python-2.1.4}/Makefile +0 -0
  24. {polytope_python-2.1.3 → polytope_python-2.1.4}/README.md +0 -0
  25. {polytope_python-2.1.3 → polytope_python-2.1.4}/codecov.yml +0 -0
  26. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/API.md +0 -0
  27. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/Axis_types.md +0 -0
  28. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/Datacube.md +0 -0
  29. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/Overview.md +0 -0
  30. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/Slicer.md +0 -0
  31. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/images/Polytope_APIs_3.png +0 -0
  32. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/images/polytope_components_5.png +0 -0
  33. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/images/slicing_process.png +0 -0
  34. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/shapes.md +0 -0
  35. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Overview/Overview.md +0 -0
  36. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Overview/Polytope_at_ECMWF.md +0 -0
  37. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Overview/images_overview/ecmwf_datacube.png +0 -0
  38. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Overview/images_overview/ecmwf_polytope.png +0 -0
  39. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/User_Guide/Building_Features.md +0 -0
  40. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/User_Guide/Example.md +0 -0
  41. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/User_Guide/Getting_started.md +0 -0
  42. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/User_Guide/Overview.md +0 -0
  43. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/User_Guide/images_users/shipping_route.png +0 -0
  44. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Client/Overview.md +0 -0
  45. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Server/Design.md +0 -0
  46. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Server/Overview.md +0 -0
  47. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Data_Portfolio.md +0 -0
  48. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Design_doc.md +0 -0
  49. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_boundingbox_example.ipynb +0 -0
  50. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_circle_example.ipynb +0 -0
  51. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_country_example.ipynb +0 -0
  52. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_polygon_example.ipynb +0 -0
  53. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_position_example.ipynb +0 -0
  54. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_timeseries_example.ipynb +0 -0
  55. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_trajectory_example.ipynb +0 -0
  56. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_vertical_profile_example.ipynb +0 -0
  57. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/boundingbox_example.ipynb +0 -0
  58. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/circle_example.ipynb +0 -0
  59. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/climate_dt_example.ipynb +0 -0
  60. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/country_example.ipynb +0 -0
  61. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/examples.md +0 -0
  62. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/extremes_dt_example.ipynb +0 -0
  63. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/on-demand_dt_example.ipynb +0 -0
  64. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/opendata_example.ipynb +0 -0
  65. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/operational_example.ipynb +0 -0
  66. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/polygon_example.ipynb +0 -0
  67. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/position_example.ipynb +0 -0
  68. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/timeseries_example.ipynb +0 -0
  69. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/trajectory_example.ipynb +0 -0
  70. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/vertical_profile_example.ipynb +0 -0
  71. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/boundingbox.md +0 -0
  72. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/circle.md +0 -0
  73. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/feature.md +0 -0
  74. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/polygon.md +0 -0
  75. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/position.md +0 -0
  76. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/timeseries.md +0 -0
  77. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/trajectory.md +0 -0
  78. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/vertical_profile.md +0 -0
  79. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Full_fields.md +0 -0
  80. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Installation.md +0 -0
  81. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Overview.md +0 -0
  82. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Quick_Start.md +0 -0
  83. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/extra.css +0 -0
  84. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/flight_path.png +0 -0
  85. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/greece.png +0 -0
  86. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/logo.gif +0 -0
  87. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/polytope_feature.png +0 -0
  88. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/polytope_logo_new_animated_AdobeExpress_3.gif +0 -0
  89. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/timeseries.png +0 -0
  90. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/timeseries_example.png +0 -0
  91. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/timeseries_qs.png +0 -0
  92. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/index.md +0 -0
  93. {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/requirements.txt +0 -0
  94. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/3D_shipping_route.py +0 -0
  95. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/3D_shipping_route_wave_model.py +0 -0
  96. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/4D_flight_path.py +0 -0
  97. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/country_slicing.py +0 -0
  98. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/cyclic_route_around_earth.py +0 -0
  99. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/EMODnet_HA_WindFarms_pg_20220324.shp +0 -0
  100. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/EMODnet_HA_WindFarms_pg_20220324.shx +0 -0
  101. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/Shipping-Lanes-v1.shp +0 -0
  102. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/Shipping-Lanes-v1.shx +0 -0
  103. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/World_Countries__Generalized_.shp +0 -0
  104. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/World_Countries__Generalized_.shx +0 -0
  105. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/earth_image.jpg +0 -0
  106. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/map_earth_4k.jpg +0 -0
  107. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/mars_req_9km_wind.req +0 -0
  108. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/mars_req_levels.req +0 -0
  109. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/mars_req_timeseries.req +0 -0
  110. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/output4.grib +0 -0
  111. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/output4.req +0 -0
  112. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/output8.grib +0 -0
  113. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/output8.req +0 -0
  114. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/temp_model_levels.grib +0 -0
  115. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/timeseries_t2m.grib +0 -0
  116. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/winds.grib +0 -0
  117. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/healpix_grid_box_example.py +0 -0
  118. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/octahedral_grid_box_example.py +0 -0
  119. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/octahedral_grid_country_example.py +0 -0
  120. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/plotting_country_data.py +0 -0
  121. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/read_me_example.py +0 -0
  122. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/requirements_examples.txt +0 -0
  123. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/slicing_all_ecmwf_countries.py +0 -0
  124. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/timeseries_example.py +0 -0
  125. {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/wind_farms.py +0 -0
  126. {polytope_python-2.1.3 → polytope_python-2.1.4}/mkdocs.yml +0 -0
  127. {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/fdb_performance.py +0 -0
  128. {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/fdb_performance_3D.py +0 -0
  129. {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/fdb_scalability_plot.py +0 -0
  130. {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/fdb_slice_many_numbers_timeseries.py +0 -0
  131. {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/performance_many_num_steps.py +0 -0
  132. {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/plotting_scalability.py +0 -0
  133. {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/scalability_test.py +0 -0
  134. {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/scalability_test_2.py +0 -0
  135. {polytope_python-2.1.3 → polytope_python-2.1.4}/performance_unstructured/octahedral_vs_unstructured_slicing.py +0 -0
  136. {polytope_python-2.1.3 → polytope_python-2.1.4}/performance_unstructured/plot_structured_vs_unstructured_slicing.py +0 -0
  137. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/__init__.py +0 -0
  138. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/__init__.py +0 -0
  139. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/backends/__init__.py +0 -0
  140. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/backends/catalogue_helper.py +0 -0
  141. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/backends/datacube.py +0 -0
  142. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/backends/fdb.py +0 -0
  143. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/backends/mock.py +0 -0
  144. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/backends/xarray.py +0 -0
  145. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/datacube_axis.py +0 -0
  146. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/index_tree.proto +0 -0
  147. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/index_tree_pb2.py +0 -0
  148. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/quadtree/quad_tree.py +0 -0
  149. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/quadtree/quadtree_additional_operations.py +0 -0
  150. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/tensor_index_tree.py +0 -0
  151. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/__init__.py +0 -0
  152. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_cyclic/__init__.py +0 -0
  153. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_cyclic/datacube_cyclic.py +0 -0
  154. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/__init__.py +0 -0
  155. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/datacube_mappers.py +0 -0
  156. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/__init__.py +0 -0
  157. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix.py +0 -0
  158. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix_nested.py +0 -0
  159. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/irregular.py +0 -0
  160. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/irregular_mapper_types/__init__.py +0 -0
  161. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/irregular_mapper_types/icon.py +0 -0
  162. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/irregular_mapper_types/lambert_conformal.py +0 -0
  163. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/irregular_mapper_types/unstructured.py +0 -0
  164. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/local_regular.py +0 -0
  165. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/octahedral.py +0 -0
  166. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_gaussian.py +0 -0
  167. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_ll.py +0 -0
  168. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/regular.py +0 -0
  169. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_merger/__init__.py +0 -0
  170. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_merger/datacube_merger.py +0 -0
  171. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_reverse/__init__.py +0 -0
  172. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_reverse/datacube_reverse.py +0 -0
  173. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_transformations.py +0 -0
  174. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_type_change/__init__.py +0 -0
  175. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/tree_encoding.py +0 -0
  176. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/__init__.py +0 -0
  177. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/engine.py +0 -0
  178. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/hullslicer.py +0 -0
  179. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/optimised_point_in_polygon_slicer.py +0 -0
  180. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/optimised_quadtree_slicer.py +0 -0
  181. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/point_in_polygon_slicer.py +0 -0
  182. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/quadtree_slicer.py +0 -0
  183. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/slicing_tools.py +0 -0
  184. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/polytope.py +0 -0
  185. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/shapes.py +0 -0
  186. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/utility/__init__.py +0 -0
  187. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/utility/combinatorics.py +0 -0
  188. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/utility/engine_tools.py +0 -0
  189. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/utility/exceptions.py +0 -0
  190. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/utility/geometry.py +0 -0
  191. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/utility/list_tools.py +0 -0
  192. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/utility/profiling.py +0 -0
  193. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_python.egg-info/dependency_links.txt +0 -0
  194. {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_python.egg-info/top_level.txt +0 -0
  195. {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/Cargo.toml +0 -0
  196. {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/distance.rs +0 -0
  197. {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/healpix_nested.rs +0 -0
  198. {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/lambert_conformal.rs +0 -0
  199. {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/lib.rs +0 -0
  200. {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/list_tools.rs +0 -0
  201. {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/octahedral.rs +0 -0
  202. {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/point_in_polygon.rs +0 -0
  203. {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/quadtree_mod.rs +0 -0
  204. {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/slicing_tools.rs +0 -0
  205. {polytope_python-2.1.3 → polytope_python-2.1.4}/setup.cfg +0 -0
  206. {polytope_python-2.1.3 → polytope_python-2.1.4}/setup.py +0 -0
  207. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/conftest.py +0 -0
  208. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/fdb_data/schema +0 -0
  209. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/helper_functions.py +0 -0
  210. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/profiled_quadtree.profile +0 -0
  211. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/quadtree_slicer_profiler.py +0 -0
  212. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_axis_mappers.py +0 -0
  213. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_bad_request_error.py +0 -0
  214. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_combinatorics.py +0 -0
  215. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_cyclic_axis_over_negative_vals.py +0 -0
  216. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_cyclic_axis_slicer_not_0.py +0 -0
  217. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_cyclic_axis_slicing.py +0 -0
  218. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_cyclic_nearest.py +0 -0
  219. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_cyclic_simple.py +0 -0
  220. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_cyclic_snapping.py +0 -0
  221. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_datacube_axes_init.py +0 -0
  222. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_datacube_mock.py +0 -0
  223. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_datacube_xarray.py +0 -0
  224. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_date_time_unmerged.py +0 -0
  225. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_ecmwf_oper_data_fdb.py +0 -0
  226. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_engine_slicer.py +0 -0
  227. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_fdb_datacube.py +0 -0
  228. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_fdb_unmap_tree.py +0 -0
  229. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_float_type.py +0 -0
  230. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_healpix_mapper.py +0 -0
  231. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_healpix_nested_grid.py +0 -0
  232. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_hull_slicer.py +0 -0
  233. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_hullslicer_engine.py +0 -0
  234. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_icon_grid_unstructured.py +0 -0
  235. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_icon_grid_unstructured_fdb.py +0 -0
  236. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_incomplete_tree_fdb.py +0 -0
  237. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_lambert_lam_grid_unstructured_fdb.py +0 -0
  238. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_lambert_lam_grid_unstructured_fdb_optimised_quadtree.py +0 -0
  239. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_local_grid_cyclic.py +0 -0
  240. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_local_regular_grid.py +0 -0
  241. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_local_swiss_grid.py +0 -0
  242. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_mappers.py +0 -0
  243. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_merge_cyclic_octahedral.py +0 -0
  244. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_merge_octahedral_one_axis.py +0 -0
  245. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_merge_transformation.py +0 -0
  246. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_multiple_param_fdb.py +0 -0
  247. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_octahedral_grid.py +0 -0
  248. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_orca_irregular_grid.py +0 -0
  249. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_orca_irregular_grid_optimised_point_in_polygon.py +0 -0
  250. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_orca_irregular_grid_point_in_polygon.py +0 -0
  251. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_override_md5_hash_options.py +0 -0
  252. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_point_nearest.py +0 -0
  253. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_point_shape.py +0 -0
  254. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_point_union.py +0 -0
  255. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_polytope_extract.py +0 -0
  256. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_polytope_extract_fdb.py +0 -0
  257. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_profiling_requesttree.py +0 -0
  258. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_quad_tree.py +0 -0
  259. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_quadtree_edge_cases.py +0 -0
  260. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_quadtree_indices.py +0 -0
  261. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_quadtree_optimisation.py +0 -0
  262. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_reduced_ll_grid.py +0 -0
  263. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_regular_grid.py +0 -0
  264. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_regular_reduced_grid.py +0 -0
  265. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_request_tree.py +0 -0
  266. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_request_trees_after_slicing.py +0 -0
  267. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_reverse_transformation.py +0 -0
  268. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_shapes.py +0 -0
  269. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_shapes_volume.py +0 -0
  270. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slice_date_range_fdb.py +0 -0
  271. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slice_date_range_fdb_v2.py +0 -0
  272. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slice_fdb_box.py +0 -0
  273. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slicer_engine.py +0 -0
  274. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slicer_era5.py +0 -0
  275. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slicer_xarray.py +0 -0
  276. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slicing_unsliceable_axis.py +0 -0
  277. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slicing_xarray_3D.py +0 -0
  278. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slicing_xarray_4D.py +0 -0
  279. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_snapping.py +0 -0
  280. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_snapping_real_data.py +0 -0
  281. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_tree_protobuf.py +0 -0
  282. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_tree_protobuf_encoding.py +0 -0
  283. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_tree_protobuf_encoding_fdb.py +0 -0
  284. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_union_gj.py +0 -0
  285. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_union_point_box.py +0 -0
  286. {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_wave_spectra_data.py +0 -0
  287. {polytope_python-2.1.3 → polytope_python-2.1.4}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polytope-python
3
- Version: 2.1.3
3
+ Version: 2.1.4
4
4
  Summary: Polytope datacube feature extraction library
5
5
  Author-email: "European Centre for Medium-Range Weather Forecasts (ECMWF)" <software.support@ecmwf.int>
6
6
  Maintainer-email: James Hawkes <James.Hawkes@ecmwf.int>, Mathilde Leuridan <Mathilde.Leuridan@ecmwf.int>
@@ -45,6 +45,9 @@ Provides-Extra: unstructured
45
45
  Requires-Dist: eckit; extra == "unstructured"
46
46
  Provides-Extra: catalogue
47
47
  Requires-Dist: qubed; extra == "catalogue"
48
+ Provides-Extra: switching-grids
49
+ Requires-Dist: eccodes; extra == "switching-grids"
50
+ Requires-Dist: pyfdb; extra == "switching-grids"
48
51
  Dynamic: license-file
49
52
 
50
53
 
@@ -1,2 +1,2 @@
1
1
  # Do not change! Do not track in version control!
2
- __version__ = "2.1.3"
2
+ __version__ = "2.1.4"
@@ -0,0 +1,215 @@
1
+ import json
2
+ import math
3
+ import os
4
+ import tempfile
5
+
6
+ import eccodes
7
+ import pyfdb
8
+
9
+ # from polytope_feature.options import MapperConfig
10
+
11
+
12
+ def get_first_grib_message(req):
13
+ fdb = pyfdb.FDB()
14
+
15
+ # Make sure that we are accessing a single georef so that the grid is consistent
16
+ assert "georef" in req.keys()
17
+
18
+ first_field = next(fdb.list(req, keys=True))["keys"]
19
+
20
+ field = fdb.retrieve(first_field)
21
+
22
+ # Normalize the retrieve() result into a plain `bytes` object
23
+ if hasattr(field, "read"):
24
+ # file-like object: read the contents
25
+ data = field.read()
26
+ else:
27
+ data = field
28
+
29
+ # Convert common buffer types to bytes
30
+ if isinstance(data, bytes):
31
+ msg_bytes = data
32
+ elif isinstance(data, bytearray):
33
+ msg_bytes = bytes(data)
34
+ elif isinstance(data, memoryview):
35
+ msg_bytes = data.tobytes()
36
+ else:
37
+ # last resort: try to construct bytes (may raise)
38
+ try:
39
+ msg_bytes = bytes(data)
40
+ except Exception as e:
41
+ raise TypeError(f"Unsupported GRIB message type: {type(data)!r}") from e
42
+
43
+ gid = eccodes.codes_new_from_message(msg_bytes)
44
+ return gid
45
+
46
+
47
+ def get_gridspec_lamebert_conformal(gid):
48
+ # Lambert lam grid
49
+
50
+ to_rad = math.pi / 180
51
+
52
+ md5hash = eccodes.codes_get(gid, "md5GridSection")
53
+
54
+ earth_round = (eccodes.codes_get(gid, "shapeOfTheEarth") == 0) or (
55
+ eccodes.codes_get(gid, "shapeOfTheEarth") == 6
56
+ )
57
+
58
+ if earth_round:
59
+ if eccodes.codes_get(gid, "shapeOfTheEarth") == 6:
60
+ radius = 6371229
61
+ elif eccodes.codes_get(gid, "shapeOfTheEarth") == 0:
62
+ radius = 6367470
63
+ else:
64
+ # TODO: set the earth major and minor axis accordingly
65
+ pass
66
+
67
+ nv = eccodes.codes_get(gid, "NV")
68
+ nx = eccodes.codes_get(gid, "Nx")
69
+ ny = eccodes.codes_get(gid, "Ny")
70
+ LoVInDegrees = eccodes.codes_get(gid, "LoV") / 1000000
71
+ Dx = eccodes.codes_get(gid, "Dx")
72
+ Dy = eccodes.codes_get(gid, "Dy")
73
+ latFirstInRadians = (
74
+ eccodes.codes_get(gid, "latitudeOfFirstGridPoint") / 1000000 * to_rad
75
+ )
76
+ lonFirstInRadians = (
77
+ eccodes.codes_get(gid, "longitudeOfFirstGridPoint") / 1000000 * to_rad
78
+ )
79
+ LoVInRadians = eccodes.codes_get(gid, "LoV") / 1000000 * to_rad
80
+ Latin1InRadians = eccodes.codes_get(gid, "Latin1") / 1000000 * to_rad
81
+ Latin2InRadians = eccodes.codes_get(gid, "Latin2") / 1000000 * to_rad
82
+ LaDInRadians = eccodes.codes_get(gid, "LaD") / 1000000 * to_rad
83
+
84
+ gridspec = {
85
+ "type": "lambert_conformal",
86
+ "earth_round": earth_round,
87
+ "radius": radius,
88
+ "nv": nv,
89
+ "nx": nx,
90
+ "ny": ny,
91
+ "LoVInDegrees": LoVInDegrees,
92
+ "Dx": Dx,
93
+ "Dy": Dy,
94
+ "latFirstInRadians": latFirstInRadians,
95
+ "lonFirstInRadians": lonFirstInRadians,
96
+ "LoVInRadians": LoVInRadians,
97
+ "Latin1InRadians": Latin1InRadians,
98
+ "Latin2InRadians": Latin2InRadians,
99
+ "LaDInRadians": LaDInRadians,
100
+ }
101
+ return (gridspec, md5hash)
102
+
103
+
104
+ def get_gridspec_icon(gid):
105
+ # ICON
106
+ # TODO: Need the following:
107
+ # uuid: Optional[str] = None
108
+ md5hash = eccodes.codes_get(gid, "md5GridSection")
109
+ gridspec = {}
110
+ return (gridspec, md5hash)
111
+
112
+
113
+ def get_gridspec_and_hash(gid):
114
+ grid_type = eccodes.codes_get(gid, "gridType")
115
+ if grid_type == "lambert_lam":
116
+ return get_gridspec_lamebert_conformal(gid)
117
+ elif grid_type == "icon":
118
+ return get_gridspec_icon(gid)
119
+ else:
120
+ raise ValueError(f"Unsupported grid type: {grid_type}")
121
+
122
+
123
+ # TODO: extract the right info and then write it to file, one for the grid hash and one for the actual config
124
+
125
+
126
+ def lookup_grid_config(req):
127
+ gid = get_first_grib_message(req)
128
+ req_georef = req["georef"]
129
+
130
+ # Cache file stored alongside this module
131
+ GRID_CACHE_FILE = os.path.join(os.path.dirname(__file__), "grid_cache.json")
132
+
133
+ def _load_cache():
134
+ try:
135
+ with open(GRID_CACHE_FILE, "r", encoding="utf-8") as fh:
136
+ return json.load(fh)
137
+ except FileNotFoundError:
138
+ return {}
139
+ except Exception:
140
+ return {}
141
+
142
+ def _save_cache(cache):
143
+ dirpath = os.path.dirname(GRID_CACHE_FILE)
144
+ os.makedirs(dirpath, exist_ok=True)
145
+ fd, tmp = tempfile.mkstemp(dir=dirpath, prefix=".grid_cache.")
146
+ try:
147
+ with os.fdopen(fd, "w", encoding="utf-8") as fh:
148
+ json.dump(cache, fh, indent=2, sort_keys=True)
149
+ os.replace(tmp, GRID_CACHE_FILE)
150
+ finally:
151
+ if os.path.exists(tmp):
152
+ try:
153
+ os.remove(tmp)
154
+ except Exception:
155
+ pass
156
+
157
+ # Use a stable serialization of the georef as the cache key
158
+ try:
159
+ cache_key = json.dumps(req_georef, sort_keys=True, default=str)
160
+ except Exception:
161
+ cache_key = str(req_georef)
162
+
163
+ cache = _load_cache()
164
+
165
+ try:
166
+ if cache_key in cache:
167
+ entry = cache[cache_key]
168
+ return (entry.get("gridspec"), entry.get("md5hash"))
169
+
170
+ gridspec, md5hash = get_gridspec_and_hash(gid)
171
+ cache[cache_key] = {"gridspec": gridspec, "md5hash": md5hash}
172
+ try:
173
+ _save_cache(cache)
174
+ except Exception:
175
+ # Swallow cache write errors but continue to return computed value
176
+ pass
177
+ return (gridspec, md5hash)
178
+ finally:
179
+ eccodes.codes_release(gid)
180
+
181
+
182
+ # def gridspec_to_grid_config(gridspec, md5hash):
183
+ # if gridspec.get("type") == "lambert_conformal":
184
+ # mc = MapperConfig(
185
+ # name="mapper",
186
+ # type="lambert_conformal",
187
+ # md5_hash=md5hash,
188
+ # is_spherical=gridspec.get("earth_round"),
189
+ # radius=gridspec.get("radius"),
190
+ # nv=gridspec.get("nv"),
191
+ # nx=gridspec.get("nx"),
192
+ # ny=gridspec.get("ny"),
193
+ # LoVInDegrees=gridspec.get("LoVInDegrees"),
194
+ # Dx=gridspec.get("Dx"),
195
+ # Dy=gridspec.get("Dy"),
196
+ # latFirstInRadians=gridspec.get("latFirstInRadians"),
197
+ # lonFirstInRadians=gridspec.get("lonFirstInRadians"),
198
+ # LoVInRadians=gridspec.get("LoVInRadians"),
199
+ # Latin1InRadians=gridspec.get("Latin1InRadians"),
200
+ # Latin2InRadians=gridspec.get("Latin2InRadians"),
201
+ # LaDInRadians=gridspec.get("LaDInRadians"),
202
+ # )
203
+ # return mc
204
+ # return None
205
+
206
+ # def replace_grid_config_in_options(options, req):
207
+ # gridspec, md5hash = lookup_grid_config(req)
208
+ # grid_config = gridspec_to_grid_config(gridspec, md5hash)
209
+ # if grid_config is not None:
210
+ # for axis_conf in options.axis_config:
211
+ # for idx, transformation in enumerate(axis_conf.transformations):
212
+ # if getattr(transformation, "name", None) == "mapper":
213
+ # axis_conf.transformations[idx] = grid_config
214
+ # return True
215
+ # return False
@@ -191,30 +191,66 @@ class TypeChangeSubHourlyTimeSteps(DatacubeAxisTypeChange):
191
191
  return pd.Timedelta(hours=int(value))
192
192
 
193
193
  if isinstance(value, str):
194
- # Extract hours and minutes using regex
194
+ # # Extract hours and minutes using regex
195
+ # h_match = re.search(r"(\d+)\s*h", value)
196
+ # m_match = re.search(r"(\d+)\s*m(?:in)?", value)
197
+
198
+ # hours = int(h_match.group(1)) if h_match else 0
199
+ # minutes = int(m_match.group(1)) if m_match else 0
200
+
201
+ # return pd.Timedelta(hours=hours, minutes=minutes)
202
+ # Extract days, hours, minutes and seconds using regex
203
+ d_match = re.search(r"(\d+)\s*d", value)
195
204
  h_match = re.search(r"(\d+)\s*h", value)
196
205
  m_match = re.search(r"(\d+)\s*m(?:in)?", value)
206
+ s_match = re.search(r"(\d+)\s*s(?:ec)?", value)
197
207
 
208
+ days = int(d_match.group(1)) if d_match else 0
198
209
  hours = int(h_match.group(1)) if h_match else 0
199
210
  minutes = int(m_match.group(1)) if m_match else 0
211
+ seconds = int(s_match.group(1)) if s_match else 0
200
212
 
201
- return pd.Timedelta(hours=hours, minutes=minutes)
213
+ return pd.Timedelta(
214
+ days=days, hours=hours, minutes=minutes, seconds=seconds
215
+ )
202
216
 
203
217
  raise ValueError(f"Unsupported timestep format: {value}")
204
218
 
205
219
  def make_str(self, value):
220
+ return_vals = []
206
221
  for val in value:
207
- total_minutes = int(val.total_seconds() // 60)
208
- hours, minutes = divmod(total_minutes, 60)
209
-
210
- if hours == 0 and minutes == 0:
211
- return "0"
222
+ total_seconds = int(val.total_seconds())
223
+ days, rem = divmod(total_seconds, 86400)
224
+ hours, rem = divmod(rem, 3600)
225
+ minutes, seconds = divmod(rem, 60)
226
+
227
+ if days == 0 and hours == 0 and minutes == 0 and seconds == 0:
228
+ return_vals.append("0")
229
+ # Prefer compact forms when possible to remain backwards compatible
230
+ elif days > 0:
231
+ parts = [f"{days}d"]
232
+ if hours > 0:
233
+ parts.append(f"{hours}h")
234
+ if minutes > 0:
235
+ parts.append(f"{minutes}m")
236
+ if seconds > 0:
237
+ parts.append(f"{seconds}s")
238
+ return_vals.append("".join(parts))
212
239
  elif hours == 0:
213
- return f"{minutes}m"
214
- elif minutes == 0:
215
- return f"{hours}"
240
+ if minutes == 0:
241
+ return_vals.append(f"{seconds}s")
242
+ elif seconds == 0:
243
+ return_vals.append(f"{minutes}m")
244
+ else:
245
+ return_vals.append(f"{minutes}m{seconds}s")
246
+ # hours > 0
247
+ elif minutes == 0 and seconds == 0:
248
+ return_vals.append(f"{hours}")
249
+ elif seconds == 0:
250
+ return_vals.append(f"{hours}h{minutes}m")
216
251
  else:
217
- return f"{hours}h{minutes}m"
252
+ return_vals.append(f"{hours}h{minutes}m{seconds}s")
253
+ return return_vals
218
254
 
219
255
 
220
256
  _type_to_datacube_type_change_lookup = {
@@ -5,6 +5,8 @@ from typing import Dict, List, Literal, Optional, Tuple, Union
5
5
  from conflator import ConfigModel, Conflator
6
6
  from pydantic import ConfigDict
7
7
 
8
+ from polytope_feature.datacube.switching_grid_helper import lookup_grid_config
9
+
8
10
 
9
11
  class TransformationConfig(ConfigModel):
10
12
  model_config = ConfigDict(extra="forbid")
@@ -86,6 +88,7 @@ class Config(ConfigModel):
86
88
  alternative_axes: Optional[List[GribJumpAxesConfig]] = []
87
89
  use_catalogue: Optional[bool] = False
88
90
  engine_options: Optional[Dict[str, str]] = {}
91
+ dynamic_grid: Optional[bool] = False
89
92
 
90
93
 
91
94
  class PolytopeOptions(ABC):
@@ -100,9 +103,21 @@ class PolytopeOptions(ABC):
100
103
  axis_config = config_options.axis_config
101
104
  compressed_axes_config = config_options.compressed_axes_config
102
105
  pre_path = config_options.pre_path
106
+ dynamic_grid = config_options.dynamic_grid
103
107
  alternative_axes = config_options.alternative_axes
104
108
  use_catalogue = config_options.use_catalogue
105
109
  engine_options = config_options.engine_options
110
+
111
+ if dynamic_grid:
112
+ # TODO: look at the pre-path and query the eccodes function to get the new grid option
113
+ # TODO: then change the grid option inside of the axis_config
114
+ try:
115
+ replaced = replace_grid_config_in_options(config_options, pre_path)
116
+ if replaced:
117
+ axis_config = config_options.axis_config
118
+ except Exception:
119
+ # Fail silently and continue with original config
120
+ pass
106
121
  return (
107
122
  axis_config,
108
123
  compressed_axes_config,
@@ -111,3 +126,41 @@ class PolytopeOptions(ABC):
111
126
  use_catalogue,
112
127
  engine_options,
113
128
  )
129
+
130
+
131
+ def gridspec_to_grid_config(gridspec, md5hash):
132
+ if gridspec.get("type") == "lambert_conformal":
133
+ mc = MapperConfig(
134
+ name="mapper",
135
+ type="lambert_conformal",
136
+ md5_hash=md5hash,
137
+ is_spherical=gridspec.get("earth_round"),
138
+ radius=gridspec.get("radius"),
139
+ nv=gridspec.get("nv"),
140
+ nx=gridspec.get("nx"),
141
+ ny=gridspec.get("ny"),
142
+ LoVInDegrees=gridspec.get("LoVInDegrees"),
143
+ Dx=gridspec.get("Dx"),
144
+ Dy=gridspec.get("Dy"),
145
+ latFirstInRadians=gridspec.get("latFirstInRadians"),
146
+ lonFirstInRadians=gridspec.get("lonFirstInRadians"),
147
+ LoVInRadians=gridspec.get("LoVInRadians"),
148
+ Latin1InRadians=gridspec.get("Latin1InRadians"),
149
+ Latin2InRadians=gridspec.get("Latin2InRadians"),
150
+ LaDInRadians=gridspec.get("LaDInRadians"),
151
+ axes=["latitude", "longitude"],
152
+ )
153
+ return mc
154
+ return None
155
+
156
+
157
+ def replace_grid_config_in_options(options, req):
158
+ gridspec, md5hash = lookup_grid_config(req)
159
+ grid_config = gridspec_to_grid_config(gridspec, md5hash)
160
+ if grid_config is not None:
161
+ for axis_conf in options.axis_config:
162
+ for idx, transformation in enumerate(axis_conf.transformations):
163
+ if getattr(transformation, "name", None) == "mapper":
164
+ axis_conf.transformations[idx] = grid_config
165
+ return True
166
+ return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polytope-python
3
- Version: 2.1.3
3
+ Version: 2.1.4
4
4
  Summary: Polytope datacube feature extraction library
5
5
  Author-email: "European Centre for Medium-Range Weather Forecasts (ECMWF)" <software.support@ecmwf.int>
6
6
  Maintainer-email: James Hawkes <James.Hawkes@ecmwf.int>, Mathilde Leuridan <Mathilde.Leuridan@ecmwf.int>
@@ -45,6 +45,9 @@ Provides-Extra: unstructured
45
45
  Requires-Dist: eckit; extra == "unstructured"
46
46
  Provides-Extra: catalogue
47
47
  Requires-Dist: qubed; extra == "catalogue"
48
+ Provides-Extra: switching-grids
49
+ Requires-Dist: eccodes; extra == "switching-grids"
50
+ Requires-Dist: pyfdb; extra == "switching-grids"
48
51
  Dynamic: license-file
49
52
 
50
53
 
@@ -136,6 +136,7 @@ polytope_feature/datacube/__init__.py
136
136
  polytope_feature/datacube/datacube_axis.py
137
137
  polytope_feature/datacube/index_tree.proto
138
138
  polytope_feature/datacube/index_tree_pb2.py
139
+ polytope_feature/datacube/switching_grid_helper.py
139
140
  polytope_feature/datacube/tensor_index_tree.py
140
141
  polytope_feature/datacube/tree_encoding.py
141
142
  polytope_feature/datacube/backends/__init__.py
@@ -10,6 +10,10 @@ protobuf
10
10
  [catalogue]
11
11
  qubed
12
12
 
13
+ [switching_grids]
14
+ eccodes
15
+ pyfdb
16
+
13
17
  [tests]
14
18
  pytest
15
19
  pytest-cov
@@ -62,6 +62,11 @@ catalogue = [
62
62
  "qubed"
63
63
  ]
64
64
 
65
+ switching_grids = [
66
+ "eccodes",
67
+ "pyfdb"
68
+ ]
69
+
65
70
  [project.urls]
66
71
  repository = "https://github.com/ecmwf/polytope"
67
72
  documentation = "https://polytope.readthedocs.io/en/latest/"
@@ -59,14 +59,18 @@ class TestIntTypeChangeTransformation:
59
59
  assert type_change_transform.transform_type("1h15m") == pd.Timedelta(
60
60
  hours=1, minutes=15
61
61
  )
62
-
63
- assert (
64
- type_change_transform.make_str([pd.Timedelta(hours=1, minutes=15)])
65
- == "1h15m"
62
+ assert type_change_transform.transform_type("1d2h30m15s") == pd.Timedelta(
63
+ days=1, hours=2, minutes=30, seconds=15
66
64
  )
67
- assert type_change_transform.make_str([pd.Timedelta(minutes=20)]) == "20m"
68
- assert type_change_transform.make_str([pd.Timedelta(hours=2)]) == "2"
69
- assert type_change_transform.make_str([pd.Timedelta(hours=0)]) == "0"
65
+
66
+ assert type_change_transform.make_str([pd.Timedelta(hours=1, minutes=15)]) == [
67
+ "1h15m"
68
+ ]
69
+ assert type_change_transform.make_str([pd.Timedelta(minutes=20)]) == ["20m"]
70
+ assert type_change_transform.make_str([pd.Timedelta(hours=2)]) == ["2"]
71
+ assert type_change_transform.make_str([pd.Timedelta(hours=0)]) == ["0"]
72
+ assert type_change_transform.make_str([pd.Timedelta(seconds=30)]) == ["30s"]
73
+ assert type_change_transform.make_str([pd.Timedelta(days=1)]) == ["1d"]
70
74
 
71
75
  def test_subhourly_step_compact_type_change_axis(self):
72
76
  type_change_transform = TypeChangeSubHourlyTimeStepsCompact(
File without changes