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.
- {polytope_python-2.1.3 → polytope_python-2.1.4}/PKG-INFO +4 -1
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/_version.py +1 -1
- polytope_python-2.1.4/polytope_feature/datacube/switching_grid_helper.py +215 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_type_change/datacube_type_change.py +47 -11
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/options.py +53 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_python.egg-info/PKG-INFO +4 -1
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_python.egg-info/SOURCES.txt +1 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_python.egg-info/requires.txt +4 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/pyproject.toml +5 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_type_change_transformation.py +11 -7
- {polytope_python-2.1.3 → polytope_python-2.1.4}/.github/ci-config.yml +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/.github/ci-hpc-config.yml +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/.github/workflows/cd.yml +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/.github/workflows/downstream-ci.yml +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/.github/workflows/label-public-pr.yml +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/.github/workflows/test-pypi.yml +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/.gitignore +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/.pre-commit-config.yaml +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/.readthedocs.yaml +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/ACKNOWLEDGEMENTS.rst +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/CONTRIBUTING.rst +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/LICENSE +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/Makefile +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/README.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/codecov.yml +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/API.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/Axis_types.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/Datacube.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/Overview.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/Slicer.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/images/Polytope_APIs_3.png +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/images/polytope_components_5.png +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/images/slicing_process.png +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/shapes.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Overview/Overview.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Overview/Polytope_at_ECMWF.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Overview/images_overview/ecmwf_datacube.png +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Overview/images_overview/ecmwf_polytope.png +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/User_Guide/Building_Features.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/User_Guide/Example.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/User_Guide/Getting_started.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/User_Guide/Overview.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/User_Guide/images_users/shipping_route.png +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Client/Overview.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Server/Design.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Server/Overview.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Data_Portfolio.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Design_doc.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_boundingbox_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_circle_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_country_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_polygon_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_position_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_timeseries_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_trajectory_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/OpenData/od_vertical_profile_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/boundingbox_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/circle_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/climate_dt_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/country_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/examples.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/extremes_dt_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/on-demand_dt_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/opendata_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/operational_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/polygon_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/position_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/timeseries_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/trajectory_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/vertical_profile_example.ipynb +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/boundingbox.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/circle.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/feature.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/polygon.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/position.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/timeseries.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/trajectory.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Features/vertical_profile.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Full_fields.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Installation.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Overview.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Quick_Start.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/extra.css +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/flight_path.png +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/greece.png +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/logo.gif +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/polytope_feature.png +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/polytope_logo_new_animated_AdobeExpress_3.gif +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/timeseries.png +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/timeseries_example.png +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/images/timeseries_qs.png +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/index.md +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/docs/requirements.txt +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/3D_shipping_route.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/3D_shipping_route_wave_model.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/4D_flight_path.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/country_slicing.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/cyclic_route_around_earth.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/EMODnet_HA_WindFarms_pg_20220324.shp +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/EMODnet_HA_WindFarms_pg_20220324.shx +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/Shipping-Lanes-v1.shp +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/Shipping-Lanes-v1.shx +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/World_Countries__Generalized_.shp +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/World_Countries__Generalized_.shx +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/earth_image.jpg +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/map_earth_4k.jpg +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/mars_req_9km_wind.req +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/mars_req_levels.req +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/mars_req_timeseries.req +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/output4.grib +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/output4.req +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/output8.grib +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/output8.req +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/temp_model_levels.grib +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/timeseries_t2m.grib +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/data/winds.grib +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/healpix_grid_box_example.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/octahedral_grid_box_example.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/octahedral_grid_country_example.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/plotting_country_data.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/read_me_example.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/requirements_examples.txt +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/slicing_all_ecmwf_countries.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/timeseries_example.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/examples/wind_farms.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/mkdocs.yml +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/fdb_performance.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/fdb_performance_3D.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/fdb_scalability_plot.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/fdb_slice_many_numbers_timeseries.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/performance_many_num_steps.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/plotting_scalability.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/scalability_test.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/performance/scalability_test_2.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/performance_unstructured/octahedral_vs_unstructured_slicing.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/performance_unstructured/plot_structured_vs_unstructured_slicing.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/__init__.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/__init__.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/backends/__init__.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/backends/catalogue_helper.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/backends/datacube.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/backends/fdb.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/backends/mock.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/backends/xarray.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/datacube_axis.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/index_tree.proto +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/index_tree_pb2.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/quadtree/quad_tree.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/quadtree/quadtree_additional_operations.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/tensor_index_tree.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/__init__.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_cyclic/__init__.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_cyclic/datacube_cyclic.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/__init__.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/datacube_mappers.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/__init__.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix_nested.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/irregular.py +0 -0
- {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
- {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
- {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
- {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
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/local_regular.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/octahedral.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_gaussian.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_ll.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/regular.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_merger/__init__.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_merger/datacube_merger.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_reverse/__init__.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_reverse/datacube_reverse.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_transformations.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/transformations/datacube_type_change/__init__.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/datacube/tree_encoding.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/__init__.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/engine.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/hullslicer.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/optimised_point_in_polygon_slicer.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/optimised_quadtree_slicer.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/point_in_polygon_slicer.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/quadtree_slicer.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/engine/slicing_tools.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/polytope.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/shapes.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/utility/__init__.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/utility/combinatorics.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/utility/engine_tools.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/utility/exceptions.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/utility/geometry.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/utility/list_tools.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_feature/utility/profiling.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_python.egg-info/dependency_links.txt +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/polytope_python.egg-info/top_level.txt +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/Cargo.toml +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/distance.rs +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/healpix_nested.rs +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/lambert_conformal.rs +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/lib.rs +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/list_tools.rs +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/octahedral.rs +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/point_in_polygon.rs +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/quadtree_mod.rs +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/rust/src/slicing_tools.rs +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/setup.cfg +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/setup.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/conftest.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/fdb_data/schema +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/helper_functions.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/profiled_quadtree.profile +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/quadtree_slicer_profiler.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_axis_mappers.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_bad_request_error.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_combinatorics.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_cyclic_axis_over_negative_vals.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_cyclic_axis_slicer_not_0.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_cyclic_axis_slicing.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_cyclic_nearest.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_cyclic_simple.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_cyclic_snapping.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_datacube_axes_init.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_datacube_mock.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_datacube_xarray.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_date_time_unmerged.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_ecmwf_oper_data_fdb.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_engine_slicer.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_fdb_datacube.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_fdb_unmap_tree.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_float_type.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_healpix_mapper.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_healpix_nested_grid.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_hull_slicer.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_hullslicer_engine.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_icon_grid_unstructured.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_icon_grid_unstructured_fdb.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_incomplete_tree_fdb.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_lambert_lam_grid_unstructured_fdb.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_lambert_lam_grid_unstructured_fdb_optimised_quadtree.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_local_grid_cyclic.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_local_regular_grid.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_local_swiss_grid.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_mappers.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_merge_cyclic_octahedral.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_merge_octahedral_one_axis.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_merge_transformation.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_multiple_param_fdb.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_octahedral_grid.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_orca_irregular_grid.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_orca_irregular_grid_optimised_point_in_polygon.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_orca_irregular_grid_point_in_polygon.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_override_md5_hash_options.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_point_nearest.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_point_shape.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_point_union.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_polytope_extract.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_polytope_extract_fdb.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_profiling_requesttree.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_quad_tree.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_quadtree_edge_cases.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_quadtree_indices.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_quadtree_optimisation.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_reduced_ll_grid.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_regular_grid.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_regular_reduced_grid.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_request_tree.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_request_trees_after_slicing.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_reverse_transformation.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_shapes.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_shapes_volume.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slice_date_range_fdb.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slice_date_range_fdb_v2.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slice_fdb_box.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slicer_engine.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slicer_era5.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slicer_xarray.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slicing_unsliceable_axis.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slicing_xarray_3D.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_slicing_xarray_4D.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_snapping.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_snapping_real_data.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_tree_protobuf.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_tree_protobuf_encoding.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_tree_protobuf_encoding_fdb.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_union_gj.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_union_point_box.py +0 -0
- {polytope_python-2.1.3 → polytope_python-2.1.4}/tests/test_wave_spectra_data.py +0 -0
- {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
|
+
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.
|
|
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(
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
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
|
+
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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
68
|
-
assert type_change_transform.make_str([pd.Timedelta(hours=
|
|
69
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Developer_Guide/Axis_types.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/Overview/Polytope_at_ECMWF.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/User_Guide/Building_Features.md
RENAMED
|
File without changes
|
|
File without changes
|
{polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Algorithm/User_Guide/Getting_started.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/boundingbox_example.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
{polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/climate_dt_example.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/extremes_dt_example.ipynb
RENAMED
|
File without changes
|
{polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/on-demand_dt_example.ipynb
RENAMED
|
File without changes
|
{polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/opendata_example.ipynb
RENAMED
|
File without changes
|
{polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/operational_example.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
{polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/position_example.ipynb
RENAMED
|
File without changes
|
{polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/timeseries_example.ipynb
RENAMED
|
File without changes
|
{polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/trajectory_example.ipynb
RENAMED
|
File without changes
|
{polytope_python-2.1.3 → polytope_python-2.1.4}/docs/Service/Examples/vertical_profile_example.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|