├── .github └── workflows │ ├── audit.yml │ └── ci.yml ├── .gitignore ├── .rustfmt.toml ├── ARCHITECTURE.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE ├── README.md ├── benches └── h3 │ ├── are_neighbor_cells.rs │ ├── cell_area.rs │ ├── cell_range.rs │ ├── cell_to_boundary.rs │ ├── cell_to_center_child.rs │ ├── cell_to_child_pos.rs │ ├── cell_to_children.rs │ ├── cell_to_children_size.rs │ ├── cell_to_latlng.rs │ ├── cell_to_local_ij.rs │ ├── cell_to_parent.rs │ ├── cell_to_vertex.rs │ ├── cell_to_vertexes.rs │ ├── cells_to_directed_edge.rs │ ├── child_pos_to_cell.rs │ ├── compact_cells.rs │ ├── constants.rs │ ├── degs_to_rads.rs │ ├── directed_edge_to_boundary.rs │ ├── directed_edge_to_cells.rs │ ├── edge_length.rs │ ├── get_base_cell_number.rs │ ├── get_directed_edge_destination.rs │ ├── get_directed_edge_origin.rs │ ├── get_hexagon_area_avg.rs │ ├── get_hexagon_edge_length_avg.rs │ ├── get_icosahedron_faces.rs │ ├── get_num_cells.rs │ ├── get_pentagons.rs │ ├── get_res0_cells.rs │ ├── get_resolution.rs │ ├── great_circle_distance.rs │ ├── grid_disk.rs │ ├── grid_disk_distances.rs │ ├── grid_disk_distances_safe.rs │ ├── grid_disk_distances_unsafe.rs │ ├── grid_disk_unsafe.rs │ ├── grid_disks_unsafe.rs │ ├── grid_distance.rs │ ├── grid_path_cells.rs │ ├── grid_path_cells_size.rs │ ├── grid_ring_unsafe.rs │ ├── h3_set_lo_linked_geo.rs │ ├── h3_to_string.rs │ ├── is_pentagon.rs │ ├── is_res_class3.rs │ ├── is_valid_cell.rs │ ├── is_valid_directed_edge.rs │ ├── is_valid_vertex.rs │ ├── latlng_to_cell.rs │ ├── local_ij_to_cell.rs │ ├── main.rs │ ├── max_face_count.rs │ ├── max_grid_disk_size.rs │ ├── max_polygon_to_cells_size.rs │ ├── origin_to_directed_edges.rs │ ├── pentagon_count.rs │ ├── polygon_to_cells.rs │ ├── rads_to_degs.rs │ ├── res0_cell_count.rs │ ├── string_to_h3.rs │ ├── uncompact_cells.rs │ ├── uncompact_cells_size.rs │ ├── utils.rs │ └── vertex_to_latlng.rs ├── clippy.toml ├── dataset ├── Paris │ ├── cells-res10.txt │ ├── cells-res11.txt │ ├── cells-res5.txt │ ├── cells-res6.txt │ ├── cells-res7.txt │ ├── cells-res8.txt │ └── cells-res9.txt ├── avgEdgeLen.txt ├── cellToBoundary.txt ├── cellToLatLng.txt ├── latLngToCell.txt └── shapes │ ├── Empty.geojson │ ├── HalfWorld_1.geojson │ ├── HalfWorld_2.geojson │ ├── Holes.geojson │ ├── Paris.geojson │ ├── PrimeMeridian.geojson │ ├── Rabi.geojson │ ├── SanFrancisco.geojson │ ├── SanFranciscoHole.geojson │ ├── Transmeridian.geojson │ ├── TransmeridianComplex.geojson │ ├── TransmeridianHole.geojson │ ├── h3_issue136.geojson │ ├── h3java_issue138.geojson │ ├── h3js_issue67_1.geojson │ ├── h3js_issue67_2.geojson │ ├── h3o_issue21.geojson │ ├── h3o_issue23.geojson │ ├── h3py_issue343_1.geojson │ └── h3py_issue343_2.geojson ├── fuzz ├── .gitignore ├── Cargo.toml └── fuzz_targets │ ├── cell_index.rs │ ├── cell_index_hierarchy.rs │ ├── cell_index_pair.rs │ ├── cell_to_vertex.rs │ ├── cells_to_geom.rs │ ├── compact.rs │ ├── directed_edge_index.rs │ ├── grid.rs │ ├── index_io.rs │ ├── is_valid.rs │ ├── latltng_to_cell.rs │ ├── local_ij.rs │ ├── polygon_to_cells.rs │ ├── polygon_with_holes_to_cells.rs │ ├── uncompact.rs │ └── vertex_index.rs ├── src ├── base_cell.rs ├── boundary.rs ├── coord │ ├── cube.rs │ ├── faceijk.rs │ ├── ijk.rs │ ├── ijk_tests.rs │ ├── latlng.rs │ ├── latlng_tests.rs │ ├── localij.rs │ ├── localij_tests.rs │ ├── mod.rs │ ├── vec2d.rs │ ├── vec2d_tests.rs │ ├── vec3d.rs │ └── vec3d_tests.rs ├── direction.rs ├── error │ ├── compaction.rs │ ├── geom.rs │ ├── hex_grid.rs │ ├── invalid_value.rs │ ├── localij.rs │ ├── mod.rs │ ├── resolution_mismatch.rs │ └── tests.rs ├── face.rs ├── geom │ ├── mod.rs │ ├── plotter.rs │ ├── ring_hierarchy.rs │ ├── ring_hierarchy_tests.rs │ ├── solvent.rs │ ├── tiler.rs │ └── vertex_graph.rs ├── grid │ ├── algo.rs │ ├── iterator.rs │ ├── iterator_tests.rs │ └── mod.rs ├── index │ ├── bits.rs │ ├── bits_tests.rs │ ├── cell.rs │ ├── cell_tests.rs │ ├── edge.rs │ ├── edge_tests.rs │ ├── iterator.rs │ ├── mod.rs │ ├── mode.rs │ ├── triangle.rs │ ├── vertex.rs │ └── vertex_tests.rs ├── lib.rs ├── math-libm.rs ├── math-std.rs └── resolution.rs ├── tests ├── api │ ├── avg_edge_len.rs │ ├── base_cell.rs │ ├── boundary.rs │ ├── cell_index.rs │ ├── directed_edge_index.rs │ ├── direction.rs │ ├── edge.rs │ ├── face.rs │ ├── face_set.rs │ ├── geom │ │ ├── mod.rs │ │ ├── plotter.rs │ │ ├── solvent.rs │ │ ├── tiler.rs │ │ ├── to_geo.rs │ │ └── utils.rs │ ├── index_mode.rs │ ├── latlng.rs │ ├── localij.rs │ ├── mod.rs │ ├── resolution.rs │ ├── vertex.rs │ └── vertex_index.rs ├── h3 │ ├── are_neighbor_cells.rs │ ├── cell_area_km2.rs │ ├── cell_area_m2.rs │ ├── cell_area_rads2.rs │ ├── cell_to_boundary.rs │ ├── cell_to_center_child.rs │ ├── cell_to_child_pos.rs │ ├── cell_to_children.rs │ ├── cell_to_children_size.rs │ ├── cell_to_latlng.rs │ ├── cell_to_local_ij.rs │ ├── cell_to_parent.rs │ ├── cell_to_vertex.rs │ ├── cell_to_vertexes.rs │ ├── cells_to_directed_edge.rs │ ├── child_pos_to_cell.rs │ ├── compact_cells.rs │ ├── degs_to_rads.rs │ ├── directed_edge_to_boundary.rs │ ├── directed_edge_to_cells.rs │ ├── edge_length_km.rs │ ├── edge_length_m.rs │ ├── edge_length_rads.rs │ ├── get_base_cell_number.rs │ ├── get_directed_edge_destination.rs │ ├── get_directed_edge_origin.rs │ ├── get_hexagon_area_avg_km2.rs │ ├── get_hexagon_area_avg_m2.rs │ ├── get_hexagon_area_avg_rads2.rs │ ├── get_icosahedron_faces.rs │ ├── get_num_cells.rs │ ├── get_pentagons.rs │ ├── get_res0_cells.rs │ ├── get_resolution.rs │ ├── great_circle_distance_km.rs │ ├── great_circle_distance_m.rs │ ├── great_circle_distance_rads.rs │ ├── grid_disk.rs │ ├── grid_disk_distances.rs │ ├── grid_disk_distances_safe.rs │ ├── grid_disk_distances_unsafe.rs │ ├── grid_disk_unsafe.rs │ ├── grid_disks_unsafe.rs │ ├── grid_distance.rs │ ├── grid_path_cells.rs │ ├── grid_path_cells_size.rs │ ├── grid_ring_unsafe.rs │ ├── h3_to_string.rs │ ├── h3api.rs │ ├── is_pentagon.rs │ ├── is_res_class3.rs │ ├── is_valid_cell.rs │ ├── is_valid_directed_edge.rs │ ├── is_valid_vertex.rs │ ├── latlng_to_cell.rs │ ├── local_ij_to_cell.rs │ ├── max_face_count.rs │ ├── max_grid_disk_size.rs │ ├── mod.rs │ ├── origin_to_directed_edges.rs │ ├── pentagon_count.rs │ ├── rads_to_degs.rs │ ├── res0_cell_count.rs │ ├── string_to_h3.rs │ ├── uncompact_cells.rs │ ├── uncompact_cells_size.rs │ └── vertex_to_latlng.rs └── integration_tests.rs └── tools └── average_edge_length.rs /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | on: 3 | # Runs the audit on every push that update dependencies. 4 | push: 5 | paths: 6 | - '**/Cargo.toml' 7 | - '**/Cargo.lock' 8 | pull_request: 9 | paths: 10 | - '**/Cargo.toml' 11 | - '**/Cargo.lock' 12 | # Also runs periodically at midnight of each day, to catch new advisories. 13 | schedule: 14 | - cron: '0 0 * * *' 15 | jobs: 16 | audit: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@v3 21 | - name: Install Rust 22 | uses: actions-rs/toolchain@v1 23 | with: 24 | toolchain: stable 25 | profile: minimal 26 | override: true 27 | - uses: Swatinem/rust-cache@v2 28 | - uses: actions-rs/audit-check@v1 29 | with: 30 | token: ${{ secrets.GITHUB_TOKEN }} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | /coverage 3 | /target 4 | lcov.info 5 | lcov_correct.info 6 | *.swp 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | hard_tabs = false 3 | tab_spaces = 4 4 | newline_style = "Auto" 5 | use_small_heuristics = "Default" 6 | indent_style = "Block" 7 | wrap_comments = false 8 | comment_width = 100 9 | normalize_comments = false 10 | normalize_doc_attributes = false 11 | format_strings = false 12 | format_macro_matchers = false 13 | format_macro_bodies = true 14 | empty_item_single_line = true 15 | struct_lit_single_line = true 16 | fn_single_line = false 17 | where_single_line = false 18 | imports_indent = "Block" 19 | imports_layout = "Mixed" 20 | imports_granularity = "Crate" 21 | reorder_imports = true 22 | reorder_modules = true 23 | reorder_impl_items = true 24 | type_punctuation_density = "Wide" 25 | space_before_colon = false 26 | space_after_colon = true 27 | spaces_around_ranges = false 28 | binop_separator = "Front" 29 | remove_nested_parens = true 30 | combine_control_expr = true 31 | overflow_delimited_expr = false 32 | struct_field_align_threshold = 0 33 | enum_discrim_align_threshold = 0 34 | match_arm_blocks = true 35 | force_multiline_blocks = false 36 | brace_style = "SameLineWhere" 37 | control_brace_style = "AlwaysSameLine" 38 | trailing_semicolon = true 39 | trailing_comma = "Vertical" 40 | match_block_trailing_comma = false 41 | blank_lines_upper_bound = 1 42 | blank_lines_lower_bound = 0 43 | edition = "2021" 44 | version = "One" 45 | merge_derives = true 46 | use_try_shorthand = false 47 | use_field_init_shorthand = false 48 | force_explicit_abi = true 49 | condense_wildcard_suffixes = false 50 | color = "Auto" 51 | unstable_features = true 52 | disable_all_formatting = false 53 | skip_children = false 54 | hide_parse_errors = false 55 | error_on_line_overflow = false 56 | error_on_unformatted = false 57 | ignore = [] 58 | emit_mode = "Files" 59 | make_backup = false 60 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution guidelines 2 | 3 | First off, thank you for considering contributing to h3o. 4 | 5 | If your contribution is not straightforward, please first discuss the change you 6 | wish to make by creating a new issue before making the change. 7 | 8 | ## Reporting issues 9 | 10 | Before reporting an issue on the 11 | [issue tracker](https://github.com/HydroniumLabs/h3o/issues), 12 | please check that it has not already been reported by searching for some related 13 | keywords. 14 | 15 | ## Pull requests 16 | 17 | Try to do one pull request per change. 18 | 19 | ### Updating the changelog 20 | 21 | Update the changes you have made in 22 | [CHANGELOG](https://github.com/HydroniumLabs/h3o/blob/master/CHANGELOG.md) 23 | file under the **Unreleased** section. 24 | 25 | Add the changes of your pull request to one of the following subsections, 26 | depending on the types of changes defined by 27 | [Keep a changelog](https://keepachangelog.com/en/1.0.0/): 28 | 29 | - `Added` for new features. 30 | - `Changed` for changes in existing functionality. 31 | - `Deprecated` for soon-to-be removed features. 32 | - `Removed` for now removed features. 33 | - `Fixed` for any bug fixes. 34 | - `Security` in case of vulnerabilities. 35 | 36 | If the required subsection does not exist yet under **Unreleased**, create it! 37 | 38 | ## Developing 39 | 40 | ### Set up 41 | 42 | This is no different than other Rust projects. 43 | 44 | ```shell 45 | git clone https://github.com/HydroniumLabs/h3o 46 | cd h3o 47 | cargo test 48 | ``` 49 | 50 | ### Useful Commands 51 | 52 | - Run Clippy: 53 | 54 | ```shell 55 | cargo clippy --all-targets --all-features 56 | ``` 57 | 58 | - Run all tests: 59 | 60 | ```shell 61 | cargo test --all-features 62 | ``` 63 | 64 | - Check to see if there are code formatting issues 65 | 66 | ```shell 67 | cargo +nightly fmt --all -- --check 68 | ``` 69 | 70 | - Format the code in the project 71 | 72 | ```shell 73 | cargo +nightly fmt --all 74 | ``` 75 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Redistribution and use in source and binary forms, with or without modification, 2 | are permitted provided that the following conditions are met: 3 | 4 | 1. Redistributions of source code must retain the above copyright notice, this 5 | list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, 8 | this list of conditions and the following disclaimer in the documentation 9 | and/or other materials provided with the distribution. 10 | 11 | 3. Neither the name of the copyright holder nor the names of its contributors 12 | may be used to endorse or promote products derived from this software without 13 | specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 22 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # h3o 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/h3o.svg)](https://crates.io/crates/h3o) 4 | [![Docs.rs](https://docs.rs/h3o/badge.svg)](https://docs.rs/h3o) 5 | [![CI Status](https://github.com/HydroniumLabs/h3o/actions/workflows/ci.yml/badge.svg)](https://github.com/HydroniumLabs/h3o/actions) 6 | [![Coverage](https://img.shields.io/codecov/c/github/HydroniumLabs/h3o)](https://app.codecov.io/gh/HydroniumLabs/h3o) 7 | [![License](https://img.shields.io/badge/license-BSD-green)](https://opensource.org/licenses/BSD-3-Clause) 8 | 9 | [Rust](https://rustlang.org) implementation of the [H3](https://h3geo.org) 10 | geospatial indexing system. 11 | 12 | ## Design 13 | 14 | This is not a binding of the reference implementation, but a reimplementation 15 | from scratch. 16 | 17 | The goals are: 18 | - To be safer/harder to misuse by leveraging the strong typing of Rust. 19 | - To be 100% Rust (no C deps): painless compilation to WASM, easier LTO, … 20 | - To be as fast (or even faster when possible) than the reference library. 21 | 22 | ## Installation 23 | 24 | ### Cargo 25 | 26 | * Install the rust toolchain in order to have cargo installed by following 27 | [this](https://www.rust-lang.org/tools/install) guide. 28 | * run `cargo install h3o` 29 | 30 | ## Usage 31 | 32 | ```rust 33 | use h3o::{LatLng, Resolution}; 34 | 35 | let coord = LatLng::new(37.769377, -122.388903).expect("valid coord"); 36 | let cell = coord.to_cell(Resolution::Nine); 37 | ``` 38 | 39 | ## Why this name? 40 | 41 | Rust is an iron oxide. 42 | A Rust version of H3 is an H3 oxide, in other word $H_3O$ (a.k.a hydronium). 43 | Chemically speaking this is wrong ( $H_3O$ is produced by protonation of 44 | $H_2O$, not oxidation of $H_3$), but ¯\\_(ツ)_/¯ 45 | 46 | ## License 47 | 48 | [BSD 3-Clause](./LICENSE) 49 | -------------------------------------------------------------------------------- /benches/h3/are_neighbor_cells.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Bencher, Criterion}; 2 | use h3o::CellIndex; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | let mut group = c.benchmark_group("areNeighborCells"); 6 | 7 | let (origin, index) = (0x0890153a1017ffff, 0x890153a1003ffff); 8 | group.bench_function("h3o/SameParentCenter", |b| { 9 | bench_h3o(b, index, origin) 10 | }); 11 | group.bench_function("h3/SameParentCenter", |b| bench_h3(b, index, origin)); 12 | 13 | let (origin, index) = (0x0890153a1017ffff, 0x0890153a1013ffff); 14 | group 15 | .bench_function("h3o/SameParentOther", |b| bench_h3o(b, index, origin)); 16 | group.bench_function("h3/SameParentOther", |b| bench_h3(b, index, origin)); 17 | 18 | // This pair uses the fast unsafe implementation of grid disk. 19 | let (origin, index) = (0x0890153a1017ffff, 0x0890153a10bbffff); 20 | group 21 | .bench_function("h3o/DifferentParent", |b| bench_h3o(b, index, origin)); 22 | group.bench_function("h3/DifferentParent", |b| bench_h3(b, index, origin)); 23 | 24 | // This pair uses the slow safe implementation of grid disk. 25 | let (origin, index) = (0x08908000001bffff, 0x08908000000fffff); 26 | group.bench_function("h3o/DifferentParentFallback", |b| { 27 | bench_h3o(b, index, origin) 28 | }); 29 | group.bench_function("h3/DifferentParentFallback", |b| { 30 | bench_h3(b, index, origin) 31 | }); 32 | 33 | group.finish(); 34 | } 35 | 36 | // ----------------------------------------------------------------------------- 37 | 38 | fn bench_h3o(b: &mut Bencher<'_>, index: u64, origin: u64) { 39 | let origin = CellIndex::try_from(origin).expect("origin"); 40 | let index = CellIndex::try_from(index).expect("index"); 41 | b.iter(|| black_box(origin).is_neighbor_with(black_box(index))) 42 | } 43 | 44 | fn bench_h3(b: &mut Bencher<'_>, index: u64, origin: u64) { 45 | let mut out = 0; 46 | b.iter(|| unsafe { 47 | h3ron_h3_sys::areNeighborCells( 48 | black_box(origin), 49 | black_box(index), 50 | &mut out, 51 | ) 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /benches/h3/cell_to_boundary.rs: -------------------------------------------------------------------------------- 1 | use super::constants::{HEXAGONS, PENTAGONS}; 2 | use criterion::{black_box, Bencher, BenchmarkId, Criterion}; 3 | use h3o::CellIndex; 4 | 5 | pub fn bench(c: &mut Criterion) { 6 | let mut group = c.benchmark_group("cellToBoundary"); 7 | 8 | for (resolution, index) in HEXAGONS.iter().enumerate() { 9 | group.bench_with_input( 10 | BenchmarkId::new("h3o/Hexagon", resolution), 11 | index, 12 | bench_h3o, 13 | ); 14 | group.bench_with_input( 15 | BenchmarkId::new("h3/Hexagon", resolution), 16 | index, 17 | bench_h3, 18 | ); 19 | } 20 | 21 | for (resolution, index) in PENTAGONS.iter().enumerate() { 22 | group.bench_with_input( 23 | BenchmarkId::new("h3o/Pentagon", resolution), 24 | index, 25 | bench_h3o, 26 | ); 27 | group.bench_with_input( 28 | BenchmarkId::new("h3/Pentagon", resolution), 29 | index, 30 | bench_h3, 31 | ); 32 | } 33 | 34 | group.finish(); 35 | } 36 | 37 | // ----------------------------------------------------------------------------- 38 | 39 | fn bench_h3o(b: &mut Bencher<'_>, index: &u64) { 40 | let index = CellIndex::try_from(*index).expect("cell index"); 41 | b.iter(|| black_box(index).boundary()) 42 | } 43 | 44 | fn bench_h3(b: &mut Bencher<'_>, index: &u64) { 45 | let mut result = h3ron_h3_sys::CellBoundary { 46 | numVerts: 0, 47 | verts: [h3ron_h3_sys::LatLng { lat: 0., lng: 0. }; 10], 48 | }; 49 | b.iter(|| unsafe { 50 | h3ron_h3_sys::cellToBoundary(black_box(*index), &mut result); 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /benches/h3/cell_to_center_child.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, BenchmarkId, Criterion}; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | const INDEX: u64 = 0x8073fffffffffff; 6 | let mut group = c.benchmark_group("cellToCenterChild"); 7 | 8 | for resolution in 0..=15 { 9 | group.bench_with_input( 10 | BenchmarkId::new("h3o", resolution), 11 | &resolution, 12 | |b, &resolution| { 13 | let resolution = 14 | Resolution::try_from(resolution).expect("resolution"); 15 | let index = CellIndex::try_from(INDEX).expect("cell index"); 16 | 17 | b.iter(|| black_box(index).center_child(black_box(resolution))) 18 | }, 19 | ); 20 | 21 | group.bench_with_input( 22 | BenchmarkId::new("h3", resolution), 23 | &resolution, 24 | |b, &resolution| { 25 | let mut out: u64 = 0; 26 | b.iter(|| unsafe { 27 | h3ron_h3_sys::cellToCenterChild( 28 | black_box(INDEX), 29 | black_box(resolution), 30 | &mut out, 31 | ) 32 | }) 33 | }, 34 | ); 35 | } 36 | 37 | group.finish(); 38 | } 39 | -------------------------------------------------------------------------------- /benches/h3/cell_to_child_pos.rs: -------------------------------------------------------------------------------- 1 | use criterion::{ 2 | black_box, measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion, 3 | }; 4 | use h3o::{CellIndex, Resolution}; 5 | 6 | pub fn bench(c: &mut Criterion) { 7 | const HEXAGON: u64 = 0x8f73909728c5c58; 8 | const PENTAGON: u64 = 0x8f0812d5c1562e2; 9 | 10 | let mut group = c.benchmark_group("cellToChildPos"); 11 | 12 | for resolution in 0..=15 { 13 | bench_h3o(&mut group, "h3o/Hexagon", HEXAGON, resolution); 14 | bench_h3(&mut group, "h3/Hexagon", HEXAGON, resolution); 15 | 16 | bench_h3o(&mut group, "h3o/Pentagon", PENTAGON, resolution); 17 | bench_h3(&mut group, "h3/Pentagon", PENTAGON, resolution); 18 | } 19 | 20 | group.finish(); 21 | } 22 | 23 | // ----------------------------------------------------------------------------- 24 | 25 | fn bench_h3o( 26 | group: &mut BenchmarkGroup, 27 | name: &'static str, 28 | index: u64, 29 | resolution: u8, 30 | ) where 31 | T: Measurement, 32 | { 33 | let index = CellIndex::try_from(index).expect("cell index"); 34 | let resolution = Resolution::try_from(resolution).expect("resolution"); 35 | group.bench_with_input( 36 | BenchmarkId::new(name, resolution), 37 | &resolution, 38 | |b, &resolution| { 39 | b.iter(|| black_box(index).child_position(black_box(resolution))) 40 | }, 41 | ); 42 | } 43 | 44 | fn bench_h3( 45 | group: &mut BenchmarkGroup, 46 | name: &'static str, 47 | index: u64, 48 | resolution: u8, 49 | ) where 50 | T: Measurement, 51 | { 52 | group.bench_with_input( 53 | BenchmarkId::new(name, resolution), 54 | &resolution, 55 | |b, &resolution| { 56 | b.iter(|| { 57 | let mut out: i64 = 0; 58 | unsafe { 59 | h3ron_h3_sys::cellToChildPos( 60 | black_box(index), 61 | black_box(resolution.into()), 62 | &mut out, 63 | ) 64 | } 65 | }) 66 | }, 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /benches/h3/cell_to_children_size.rs: -------------------------------------------------------------------------------- 1 | use criterion::{ 2 | black_box, measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion, 3 | }; 4 | use h3o::{CellIndex, Resolution}; 5 | 6 | pub fn bench(c: &mut Criterion) { 7 | const HEXAGON: u64 = 0x8073fffffffffff; 8 | const PENTAGON: u64 = 0x8009fffffffffff; 9 | 10 | let mut group = c.benchmark_group("cellToChildrenSize"); 11 | 12 | for resolution in 0..=15 { 13 | bench_h3o(&mut group, "h3o/Hexagon", HEXAGON, resolution); 14 | bench_h3(&mut group, "h3/Hexagon", HEXAGON, resolution); 15 | 16 | bench_h3o(&mut group, "h3o/Pentagon", PENTAGON, resolution); 17 | bench_h3(&mut group, "h3/Pentagon", PENTAGON, resolution); 18 | } 19 | 20 | group.finish(); 21 | } 22 | 23 | // ----------------------------------------------------------------------------- 24 | 25 | fn bench_h3o( 26 | group: &mut BenchmarkGroup, 27 | name: &'static str, 28 | index: u64, 29 | resolution: u8, 30 | ) where 31 | T: Measurement, 32 | { 33 | let index = CellIndex::try_from(index).expect("cell index"); 34 | let resolution = Resolution::try_from(resolution).expect("resolution"); 35 | group.bench_with_input( 36 | BenchmarkId::new(name, resolution), 37 | &resolution, 38 | |b, &resolution| { 39 | b.iter(|| black_box(index).children_count(black_box(resolution))) 40 | }, 41 | ); 42 | } 43 | 44 | fn bench_h3( 45 | group: &mut BenchmarkGroup, 46 | name: &'static str, 47 | index: u64, 48 | resolution: u8, 49 | ) where 50 | T: Measurement, 51 | { 52 | group.bench_with_input( 53 | BenchmarkId::new(name, resolution), 54 | &resolution, 55 | |b, &resolution| { 56 | b.iter(|| { 57 | let mut out: i64 = 0; 58 | unsafe { 59 | h3ron_h3_sys::cellToChildrenSize( 60 | black_box(index), 61 | black_box(resolution.into()), 62 | &mut out, 63 | ) 64 | } 65 | }) 66 | }, 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /benches/h3/cell_to_latlng.rs: -------------------------------------------------------------------------------- 1 | use super::constants::{HEXAGONS, PENTAGONS}; 2 | use criterion::{black_box, Bencher, BenchmarkId, Criterion}; 3 | use h3o::{CellIndex, LatLng}; 4 | 5 | pub fn bench(c: &mut Criterion) { 6 | let mut group = c.benchmark_group("cellToLatLng"); 7 | 8 | for (resolution, index) in HEXAGONS.iter().copied().enumerate() { 9 | group.bench_with_input( 10 | BenchmarkId::new("h3o/Hexagon", resolution), 11 | &index, 12 | bench_h3o, 13 | ); 14 | group.bench_with_input( 15 | BenchmarkId::new("h3/Hexagon", resolution), 16 | &index, 17 | bench_h3, 18 | ); 19 | } 20 | 21 | for (resolution, index) in PENTAGONS.iter().copied().enumerate() { 22 | group.bench_with_input( 23 | BenchmarkId::new("h3o/Pentagon", resolution), 24 | &index, 25 | bench_h3o, 26 | ); 27 | group.bench_with_input( 28 | BenchmarkId::new("h3/Pentagon", resolution), 29 | &index, 30 | bench_h3, 31 | ); 32 | } 33 | 34 | group.finish(); 35 | } 36 | 37 | // ----------------------------------------------------------------------------- 38 | 39 | fn bench_h3o(b: &mut Bencher<'_>, index: &u64) { 40 | let index = CellIndex::try_from(*index).expect("cell index"); 41 | b.iter(|| LatLng::from(black_box(index))) 42 | } 43 | 44 | fn bench_h3(b: &mut Bencher<'_>, index: &u64) { 45 | let mut ll = h3ron_h3_sys::LatLng { lat: 0., lng: 0. }; 46 | b.iter(|| unsafe { h3ron_h3_sys::cellToLatLng(black_box(*index), &mut ll) }) 47 | } 48 | -------------------------------------------------------------------------------- /benches/h3/cell_to_local_ij.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Bencher, Criterion}; 2 | use h3o::CellIndex; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | let mut group = c.benchmark_group("cellToLocalIj"); 6 | 7 | let (anchor, index) = (0x823147fffffffff, 0x8230e7fffffffff); 8 | group.bench_function("h3o/Hexagon", |b| bench_h3o(b, anchor, index)); 9 | group.bench_function("h3/Hexagon", |b| bench_h3(b, anchor, index)); 10 | 11 | let (anchor, index) = (0x821f57fffffffff, 0x8208d7fffffffff); 12 | group.bench_function("h3o/Pentagon", |b| bench_h3o(b, anchor, index)); 13 | group.bench_function("h3/Pentagon", |b| bench_h3(b, anchor, index)); 14 | 15 | let (anchor, index) = (0x823147fffffffff, 0x8230e7fffffffff); 16 | group.bench_function("h3o/SameBase", |b| bench_h3o(b, anchor, index)); 17 | group.bench_function("h3/SameBase", |b| bench_h3(b, anchor, index)); 18 | 19 | group.finish(); 20 | } 21 | 22 | // ----------------------------------------------------------------------------- 23 | 24 | fn bench_h3o(b: &mut Bencher<'_>, anchor: u64, index: u64) { 25 | let anchor = CellIndex::try_from(anchor).expect("anchor"); 26 | let index = CellIndex::try_from(index).expect("index"); 27 | b.iter(|| black_box(index).to_local_ij(black_box(anchor))) 28 | } 29 | 30 | fn bench_h3(b: &mut Bencher<'_>, anchor: u64, index: u64) { 31 | let mut out = h3ron_h3_sys::CoordIJ { i: 0, j: 0 }; 32 | b.iter(|| unsafe { 33 | h3ron_h3_sys::cellToLocalIj( 34 | black_box(anchor), 35 | black_box(index), 36 | 0, 37 | &mut out, 38 | ) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /benches/h3/cell_to_parent.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, BenchmarkId, Criterion}; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | const INDEX: u64 = 0x8f734e64992d6d8; 6 | let mut group = c.benchmark_group("cellToParent"); 7 | 8 | for resolution in 0..=15 { 9 | group.bench_with_input( 10 | BenchmarkId::new("h3o", resolution), 11 | &resolution, 12 | |b, &resolution| { 13 | let resolution = 14 | Resolution::try_from(resolution).expect("resolution"); 15 | let index = CellIndex::try_from(INDEX).expect("cell index"); 16 | 17 | b.iter(|| black_box(index).parent(black_box(resolution))) 18 | }, 19 | ); 20 | 21 | group.bench_with_input( 22 | BenchmarkId::new("h3", resolution), 23 | &resolution, 24 | |b, &resolution| { 25 | let mut out: u64 = 0; 26 | b.iter(|| unsafe { 27 | h3ron_h3_sys::cellToParent( 28 | black_box(INDEX), 29 | black_box(resolution), 30 | &mut out, 31 | ) 32 | }) 33 | }, 34 | ); 35 | } 36 | 37 | group.finish(); 38 | } 39 | -------------------------------------------------------------------------------- /benches/h3/cell_to_vertex.rs: -------------------------------------------------------------------------------- 1 | use criterion::{ 2 | black_box, measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion, 3 | }; 4 | use h3o::{CellIndex, Vertex}; 5 | 6 | pub fn bench(c: &mut Criterion) { 7 | const HEXAGON: u64 = 0x084e_c69b_ffff_ffff; 8 | const PENTAGON: u64 = 0x0841_f265_ffff_ffff; 9 | 10 | let mut group = c.benchmark_group("cellToVertex"); 11 | 12 | for vertex_number in 0..6u8 { 13 | bench_h3o(&mut group, "h3o/Hexagon", HEXAGON, vertex_number); 14 | bench_h3(&mut group, "h3/Hexagon", HEXAGON, vertex_number); 15 | 16 | if vertex_number < 5 { 17 | bench_h3o(&mut group, "h3o/Pentagon", PENTAGON, vertex_number); 18 | bench_h3(&mut group, "h3/Pentagon", PENTAGON, vertex_number); 19 | } 20 | } 21 | 22 | group.finish(); 23 | } 24 | 25 | // ----------------------------------------------------------------------------- 26 | 27 | fn bench_h3o( 28 | group: &mut BenchmarkGroup, 29 | name: &'static str, 30 | index: u64, 31 | vertex_number: u8, 32 | ) where 33 | T: Measurement, 34 | { 35 | group.bench_with_input( 36 | BenchmarkId::new(name, vertex_number), 37 | &index, 38 | |b, &index| { 39 | let index = CellIndex::try_from(index).expect("cell index"); 40 | let vertex = Vertex::try_from(vertex_number).expect("vertex"); 41 | b.iter(|| black_box(index).vertex(black_box(vertex))) 42 | }, 43 | ); 44 | } 45 | 46 | fn bench_h3( 47 | group: &mut BenchmarkGroup, 48 | name: &'static str, 49 | index: u64, 50 | vertex_number: u8, 51 | ) where 52 | T: Measurement, 53 | { 54 | group.bench_with_input( 55 | BenchmarkId::new(name, vertex_number), 56 | &index, 57 | |b, &index| { 58 | let mut out: u64 = 0; 59 | b.iter(|| unsafe { 60 | h3ron_h3_sys::cellToVertex( 61 | black_box(index), 62 | black_box(vertex_number.into()), 63 | &mut out, 64 | ) 65 | }) 66 | }, 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /benches/h3/cell_to_vertexes.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Bencher, Criterion}; 2 | use h3o::CellIndex; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | const PENTAGON: u64 = 0x08f0_8000_0000_0000; 6 | const HEXAGON: u64 = 0x08f7_34e6_4992_d6d8; 7 | 8 | let mut group = c.benchmark_group("cellToVertexes"); 9 | 10 | group.bench_function("h3o/Hexagon", |b| bench_h3o(b, HEXAGON)); 11 | group.bench_function("h3/Hexagon", |b| bench_h3(b, HEXAGON)); 12 | 13 | group.bench_function("h3o/Pentagon", |b| bench_h3o(b, PENTAGON)); 14 | group.bench_function("h3/Pentagon", |b| bench_h3(b, PENTAGON)); 15 | 16 | group.finish(); 17 | } 18 | 19 | // ----------------------------------------------------------------------------- 20 | 21 | fn bench_h3o(b: &mut Bencher<'_>, index: u64) { 22 | let index = CellIndex::try_from(index).expect("cell index"); 23 | b.iter(|| black_box(index).vertexes().for_each(drop)) 24 | } 25 | 26 | fn bench_h3(b: &mut Bencher<'_>, index: u64) { 27 | let mut out = [0; 6]; 28 | b.iter(|| unsafe { 29 | h3ron_h3_sys::cellToVertexes(black_box(index), out.as_mut_ptr()) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /benches/h3/cells_to_directed_edge.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | use h3o::CellIndex; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | const ORIGIN: u64 = 0x0891_fb46_622f_ffff; 6 | const DESTINATION: u64 = 0x0891_fb46_622b_ffff; 7 | 8 | let mut group = c.benchmark_group("cellsToDirectedEdge"); 9 | 10 | group.bench_function("h3o", |b| { 11 | let origin = CellIndex::try_from(ORIGIN).expect("cell index"); 12 | let destination = CellIndex::try_from(DESTINATION).expect("cell index"); 13 | b.iter(|| black_box(origin).edge(black_box(destination))) 14 | }); 15 | group.bench_function("h3", |b| { 16 | let mut out: u64 = 0; 17 | b.iter(|| unsafe { 18 | h3ron_h3_sys::cellsToDirectedEdge( 19 | black_box(ORIGIN), 20 | black_box(DESTINATION), 21 | &mut out, 22 | ) 23 | }) 24 | }); 25 | 26 | group.finish(); 27 | } 28 | -------------------------------------------------------------------------------- /benches/h3/compact_cells.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, BatchSize, Bencher, Criterion}; 2 | use h3o::{CellIndex, Direction, Resolution}; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | const RESOLUTION: Resolution = Resolution::Three; 6 | 7 | let mut group = c.benchmark_group("compactCells"); 8 | 9 | let cells = CellIndex::base_cells() 10 | .flat_map(|index| index.children(RESOLUTION)) 11 | .collect::>(); 12 | group.bench_function("h3o/FullCompaction", |b| bench_h3o(b, &cells)); 13 | group.bench_function("h3/FullCompaction", |b| bench_h3(b, &cells)); 14 | 15 | let sparse = cells 16 | .iter() 17 | .copied() 18 | .enumerate() 19 | .filter_map(|(idx, cell)| (idx % 33 != 0).then_some(cell)) 20 | .collect::>(); 21 | group.bench_function("h3o/PartialCompaction", |b| bench_h3o(b, &sparse)); 22 | group.bench_function("h3/PartialCompaction", |b| bench_h3(b, &sparse)); 23 | 24 | let uncompactable = cells 25 | .iter() 26 | .copied() 27 | .filter(|cell| cell.direction_at(RESOLUTION) != Some(Direction::IK)) 28 | .collect::>(); 29 | group.bench_function("h3o/NoCompaction", |b| bench_h3o(b, &uncompactable)); 30 | group.bench_function("h3/NoCompaction", |b| bench_h3(b, &uncompactable)); 31 | 32 | group.finish(); 33 | } 34 | 35 | // ----------------------------------------------------------------------------- 36 | 37 | fn bench_h3o(b: &mut Bencher<'_>, indexes: &[CellIndex]) { 38 | b.iter_batched( 39 | || indexes.to_owned(), 40 | |mut indexes| { 41 | CellIndex::compact(black_box(&mut indexes)).expect("compacted set") 42 | }, 43 | BatchSize::SmallInput, 44 | ) 45 | } 46 | 47 | fn bench_h3(b: &mut Bencher<'_>, indexes: &[CellIndex]) { 48 | let indexes = indexes.iter().copied().map(u64::from).collect::>(); 49 | let mut out = vec![0; indexes.len()]; 50 | b.iter(|| unsafe { 51 | h3ron_h3_sys::compactCells( 52 | black_box(indexes.as_ptr()), 53 | out.as_mut_ptr(), 54 | indexes.len() as i64, 55 | ) 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /benches/h3/constants.rs: -------------------------------------------------------------------------------- 1 | pub const HEXAGONS: [u64; 16] = [ 2 | 0x801ffffffffffff, 3 | 0x811fbffffffffff, 4 | 0x821fb7fffffffff, 5 | 0x831fb4fffffffff, 6 | 0x841fb47ffffffff, 7 | 0x851fb467fffffff, 8 | 0x861fb4667ffffff, 9 | 0x871fb4662ffffff, 10 | 0x881fb46623fffff, 11 | 0x891fb46622fffff, 12 | 0x8a1fb46622dffff, 13 | 0x8b1fb46622d8fff, 14 | 0x8c1fb46622d85ff, 15 | 0x8d1fb46622d85bf, 16 | 0x8e1fb46622d8597, 17 | 0x8f1fb46622d8591, 18 | ]; 19 | 20 | pub const PENTAGONS: [u64; 16] = [ 21 | 0x8009fffffffffff, 22 | 0x81083ffffffffff, 23 | 0x820807fffffffff, 24 | 0x830800fffffffff, 25 | 0x8408001ffffffff, 26 | 0x85080003fffffff, 27 | 0x860800007ffffff, 28 | 0x870800000ffffff, 29 | 0x8808000001fffff, 30 | 0x89080000003ffff, 31 | 0x8a0800000007fff, 32 | 0x8b0800000000fff, 33 | 0x8c08000000001ff, 34 | 0x8d080000000003f, 35 | 0x8e0800000000007, 36 | 0x8f0800000000000, 37 | ]; 38 | -------------------------------------------------------------------------------- /benches/h3/degs_to_rads.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | 3 | pub fn bench(c: &mut Criterion) { 4 | const VALUE: f64 = 48.854501508844095; 5 | 6 | let mut group = c.benchmark_group("degsToRads"); 7 | 8 | group.bench_function("h3o", |b| b.iter(|| black_box(VALUE).to_radians())); 9 | group.bench_function("h3", |b| { 10 | b.iter(|| unsafe { h3ron_h3_sys::degsToRads(black_box(VALUE)) }) 11 | }); 12 | 13 | group.finish(); 14 | } 15 | -------------------------------------------------------------------------------- /benches/h3/directed_edge_to_cells.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | use h3o::DirectedEdgeIndex; 3 | 4 | const INPUT: u64 = 0x13f2834782b9c2ab; 5 | 6 | pub fn bench(c: &mut Criterion) { 7 | let mut group = c.benchmark_group("directedEdgeToCells"); 8 | 9 | group.bench_function("h3o", |b| { 10 | let index = DirectedEdgeIndex::try_from(INPUT).expect("edge index"); 11 | b.iter(|| black_box(index).cells()) 12 | }); 13 | group.bench_function("h3", |b| { 14 | let mut out = [0; 2]; 15 | b.iter(|| unsafe { 16 | h3ron_h3_sys::directedEdgeToCells( 17 | black_box(INPUT), 18 | out.as_mut_ptr(), 19 | ) 20 | }) 21 | }); 22 | 23 | group.finish(); 24 | } 25 | -------------------------------------------------------------------------------- /benches/h3/get_base_cell_number.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | use h3o::CellIndex; 3 | 4 | const INPUT: u64 = 0x8f734e64992d6d8; 5 | 6 | pub fn bench(c: &mut Criterion) { 7 | let mut group = c.benchmark_group("getBaseCellNumber"); 8 | 9 | group.bench_function("h3o", |b| { 10 | let index = CellIndex::try_from(INPUT).expect("cell index"); 11 | b.iter(|| black_box(index).base_cell()) 12 | }); 13 | group.bench_function("h3", |b| { 14 | b.iter(|| unsafe { h3ron_h3_sys::getBaseCellNumber(black_box(INPUT)) }) 15 | }); 16 | 17 | group.finish(); 18 | } 19 | -------------------------------------------------------------------------------- /benches/h3/get_directed_edge_destination.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Bencher, BenchmarkId, Criterion}; 2 | use h3o::DirectedEdgeIndex; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | let mut group = c.benchmark_group("getDirectedEdgeDestination"); 6 | 7 | for (resolution, (hexagon, pentagon)) in [ 8 | (0x150f_3fff_ffff_ffff, 0x1401_ffff_ffff_ffff), 9 | (0x111f_27ff_ffff_ffff, 0x161d_b3ff_ffff_ffff), 10 | (0x142f_367f_ffff_ffff, 0x162d_b37f_ffff_ffff), 11 | (0x143f_365f_ffff_ffff, 0x1531_f65f_ffff_ffff), 12 | (0x114e_c69b_ffff_ffff, 0x1441_f265_ffff_ffff), 13 | ] 14 | .iter() 15 | .enumerate() 16 | { 17 | group.bench_with_input( 18 | BenchmarkId::new("h3o/Hexagon", resolution), 19 | hexagon, 20 | bench_h3o, 21 | ); 22 | group.bench_with_input( 23 | BenchmarkId::new("h3/Hexagon", resolution), 24 | hexagon, 25 | bench_h3, 26 | ); 27 | 28 | group.bench_with_input( 29 | BenchmarkId::new("h3o/Pentagon", resolution), 30 | pentagon, 31 | bench_h3o, 32 | ); 33 | group.bench_with_input( 34 | BenchmarkId::new("h3/Pentagon", resolution), 35 | pentagon, 36 | bench_h3, 37 | ); 38 | } 39 | 40 | group.finish(); 41 | } 42 | 43 | // ----------------------------------------------------------------------------- 44 | 45 | fn bench_h3o(b: &mut Bencher<'_>, index: &u64) { 46 | let index = DirectedEdgeIndex::try_from(*index).expect("edge index"); 47 | b.iter(|| black_box(index).destination()) 48 | } 49 | 50 | fn bench_h3(b: &mut Bencher<'_>, index: &u64) { 51 | let mut out: u64 = 0; 52 | b.iter(|| unsafe { 53 | h3ron_h3_sys::getDirectedEdgeDestination(black_box(*index), &mut out) 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /benches/h3/get_directed_edge_origin.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | use h3o::DirectedEdgeIndex; 3 | 4 | const INPUT: u64 = 0x13f2834782b9c2ab; 5 | 6 | pub fn bench(c: &mut Criterion) { 7 | let mut group = c.benchmark_group("getDirectedEdgeOrigin"); 8 | 9 | group.bench_function("h3o", |b| { 10 | let index = DirectedEdgeIndex::try_from(INPUT).expect("edge index"); 11 | b.iter(|| black_box(index).origin()) 12 | }); 13 | group.bench_function("h3", |b| { 14 | let mut out: u64 = 0; 15 | b.iter(|| unsafe { 16 | h3ron_h3_sys::getDirectedEdgeOrigin(black_box(INPUT), &mut out) 17 | }) 18 | }); 19 | 20 | group.finish(); 21 | } 22 | -------------------------------------------------------------------------------- /benches/h3/get_hexagon_area_avg.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | use h3o::Resolution; 3 | 4 | pub fn bench_km2(c: &mut Criterion) { 5 | let mut group = c.benchmark_group("getHexagonAreaAvgKm2"); 6 | 7 | group.bench_function("h3o", |b| { 8 | b.iter(|| black_box(Resolution::Three).area_km2()) 9 | }); 10 | group.bench_function("h3", |b| { 11 | let mut out: f64 = 0.; 12 | b.iter(|| unsafe { 13 | h3ron_h3_sys::getHexagonAreaAvgKm2(black_box(3), &mut out) 14 | }) 15 | }); 16 | 17 | group.finish(); 18 | } 19 | 20 | pub fn bench_m2(c: &mut Criterion) { 21 | let mut group = c.benchmark_group("getHexagonAreaAvgM2"); 22 | 23 | group.bench_function("h3o", |b| { 24 | b.iter(|| black_box(Resolution::Three).area_m2()) 25 | }); 26 | group.bench_function("h3", |b| { 27 | let mut out: f64 = 0.; 28 | b.iter(|| unsafe { 29 | h3ron_h3_sys::getHexagonAreaAvgM2(black_box(3), &mut out) 30 | }) 31 | }); 32 | 33 | group.finish(); 34 | } 35 | -------------------------------------------------------------------------------- /benches/h3/get_hexagon_edge_length_avg.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | use h3o::Resolution; 3 | 4 | pub fn bench_km(c: &mut Criterion) { 5 | let mut group = c.benchmark_group("getHexagonEdgeLengthAvgKm"); 6 | 7 | group.bench_function("h3o", |b| { 8 | b.iter(|| black_box(Resolution::Three).edge_length_km()) 9 | }); 10 | group.bench_function("h3", |b| { 11 | let mut out: f64 = 0.; 12 | b.iter(|| unsafe { 13 | h3ron_h3_sys::getHexagonEdgeLengthAvgKm(black_box(3), &mut out) 14 | }) 15 | }); 16 | 17 | group.finish(); 18 | } 19 | 20 | pub fn bench_m(c: &mut Criterion) { 21 | let mut group = c.benchmark_group("getHexagonEdgeLengthAvgM"); 22 | 23 | group.bench_function("h3o", |b| { 24 | b.iter(|| black_box(Resolution::Three).edge_length_m()) 25 | }); 26 | group.bench_function("h3", |b| { 27 | let mut out: f64 = 0.; 28 | b.iter(|| unsafe { 29 | h3ron_h3_sys::getHexagonEdgeLengthAvgM(black_box(3), &mut out) 30 | }) 31 | }); 32 | 33 | group.finish(); 34 | } 35 | -------------------------------------------------------------------------------- /benches/h3/get_icosahedron_faces.rs: -------------------------------------------------------------------------------- 1 | use super::constants::{HEXAGONS, PENTAGONS}; 2 | use criterion::{ 3 | black_box, measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion, 4 | }; 5 | use h3o::CellIndex; 6 | 7 | pub fn bench(c: &mut Criterion) { 8 | let mut group = c.benchmark_group("getIcosahedronFaces"); 9 | 10 | for (resolution, index) in HEXAGONS.iter().copied().enumerate() { 11 | bench_h3o(&mut group, "h3o/Hexagon", index, resolution); 12 | bench_h3(&mut group, "h3/Hexagon", index, resolution); 13 | } 14 | 15 | for (resolution, index) in PENTAGONS.iter().copied().enumerate() { 16 | bench_h3o(&mut group, "h3o/Pentagon", index, resolution); 17 | bench_h3(&mut group, "h3/Pentagon", index, resolution); 18 | } 19 | 20 | group.finish(); 21 | } 22 | 23 | // ----------------------------------------------------------------------------- 24 | 25 | fn bench_h3o( 26 | group: &mut BenchmarkGroup, 27 | name: &'static str, 28 | index: u64, 29 | resolution: usize, 30 | ) where 31 | T: Measurement, 32 | { 33 | group.bench_with_input( 34 | BenchmarkId::new(name, resolution), 35 | &index, 36 | |b, &index| { 37 | let index = CellIndex::try_from(index).expect("cell index"); 38 | b.iter(|| black_box(index).icosahedron_faces()) 39 | }, 40 | ); 41 | } 42 | 43 | fn bench_h3( 44 | group: &mut BenchmarkGroup, 45 | name: &'static str, 46 | index: u64, 47 | resolution: usize, 48 | ) where 49 | T: Measurement, 50 | { 51 | group.bench_with_input( 52 | BenchmarkId::new(name, resolution), 53 | &index, 54 | |b, &index| { 55 | let mut result = vec![-1; 5]; 56 | b.iter(|| unsafe { 57 | h3ron_h3_sys::getIcosahedronFaces( 58 | black_box(index), 59 | result.as_mut_ptr(), 60 | ); 61 | }) 62 | }, 63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /benches/h3/get_num_cells.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, BenchmarkId, Criterion}; 2 | use h3o::Resolution; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | let mut group = c.benchmark_group("getNumCells"); 6 | 7 | for resolution in 0..=15 { 8 | group.bench_with_input( 9 | BenchmarkId::new("h3o", resolution), 10 | &resolution, 11 | |b, &resolution| { 12 | let resolution = 13 | Resolution::try_from(resolution).expect("resolution"); 14 | b.iter(|| black_box(resolution).cell_count()) 15 | }, 16 | ); 17 | group.bench_with_input( 18 | BenchmarkId::new("h3", resolution), 19 | &resolution, 20 | |b, &resolution| { 21 | let mut out: i64 = 0; 22 | b.iter(|| unsafe { 23 | h3ron_h3_sys::getNumCells(black_box(resolution), &mut out) 24 | }) 25 | }, 26 | ); 27 | } 28 | 29 | group.finish(); 30 | } 31 | -------------------------------------------------------------------------------- /benches/h3/get_pentagons.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, BenchmarkId, Criterion}; 2 | use h3o::Resolution; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | let mut group = c.benchmark_group("getPentagons"); 6 | 7 | for resolution in 0..=15 { 8 | group.bench_with_input( 9 | BenchmarkId::new("h3o", resolution), 10 | &resolution, 11 | |b, &resolution| { 12 | let resolution = 13 | Resolution::try_from(resolution).expect("resolution"); 14 | b.iter(|| { 15 | for pentagon in black_box(resolution).pentagons() { 16 | black_box(pentagon); 17 | } 18 | }) 19 | }, 20 | ); 21 | group.bench_with_input( 22 | BenchmarkId::new("h3", resolution), 23 | &resolution, 24 | |b, &resolution| { 25 | let mut out = [0; 12]; 26 | b.iter(|| unsafe { 27 | h3ron_h3_sys::getPentagons( 28 | black_box(resolution), 29 | out.as_mut_ptr(), 30 | ); 31 | for pentagon in out { 32 | black_box(pentagon); 33 | } 34 | }) 35 | }, 36 | ); 37 | } 38 | 39 | group.finish(); 40 | } 41 | -------------------------------------------------------------------------------- /benches/h3/get_res0_cells.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | use h3o::CellIndex; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | let mut group = c.benchmark_group("getRes0Cells"); 6 | 7 | group.bench_function("h3o", |b| { 8 | b.iter(|| { 9 | for base_cell in CellIndex::base_cells() { 10 | let _ = black_box(base_cell); 11 | } 12 | }) 13 | }); 14 | group.bench_function("h3", |b| { 15 | let mut out = [0; 122]; 16 | b.iter(|| unsafe { 17 | h3ron_h3_sys::getRes0Cells(black_box(out.as_mut_ptr())); 18 | for base_cell in out { 19 | let _ = black_box(base_cell); 20 | } 21 | }) 22 | }); 23 | 24 | group.finish(); 25 | } 26 | -------------------------------------------------------------------------------- /benches/h3/get_resolution.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | use h3o::CellIndex; 3 | 4 | const INPUT: u64 = 0x8f734e64992d6d8; 5 | 6 | pub fn bench(c: &mut Criterion) { 7 | let mut group = c.benchmark_group("getResolution"); 8 | 9 | group.bench_function("h3o", |b| { 10 | let index = CellIndex::try_from(INPUT).expect("cell index"); 11 | b.iter(|| black_box(index).resolution()) 12 | }); 13 | group.bench_function("h3", |b| { 14 | b.iter(|| unsafe { h3ron_h3_sys::getResolution(black_box(INPUT)) }) 15 | }); 16 | 17 | group.finish(); 18 | } 19 | -------------------------------------------------------------------------------- /benches/h3/grid_disk.rs: -------------------------------------------------------------------------------- 1 | use criterion::{ 2 | black_box, measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion, 3 | }; 4 | use h3o::CellIndex; 5 | use std::os::raw::c_int; 6 | 7 | const HEXAGON: u64 = 0x8b1fb46622defff; 8 | const PENTAGON: u64 = 0x8b0800000000fff; 9 | 10 | pub fn bench(c: &mut Criterion) { 11 | let mut group = c.benchmark_group("gridDisk"); 12 | 13 | for k in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 100] { 14 | bench_h3o(&mut group, "h3o/Hexagon", HEXAGON, k); 15 | bench_h3(&mut group, "h3/Hexagon", HEXAGON, k); 16 | 17 | bench_h3o(&mut group, "h3o/Pentagon", PENTAGON, k); 18 | bench_h3(&mut group, "h3/Pentagon", PENTAGON, k); 19 | } 20 | 21 | group.finish(); 22 | } 23 | 24 | // ----------------------------------------------------------------------------- 25 | 26 | fn bench_h3o( 27 | group: &mut BenchmarkGroup, 28 | name: &'static str, 29 | index: u64, 30 | k: u32, 31 | ) where 32 | T: Measurement, 33 | { 34 | let index = CellIndex::try_from(index).expect("hex index"); 35 | group.bench_with_input(BenchmarkId::new(name, k), &index, |b, &index| { 36 | b.iter_with_large_drop(|| { 37 | black_box(index).grid_disk::>(black_box(k)) 38 | }) 39 | }); 40 | } 41 | 42 | fn bench_h3( 43 | group: &mut BenchmarkGroup, 44 | name: &'static str, 45 | index: u64, 46 | k: u32, 47 | ) where 48 | T: Measurement, 49 | { 50 | let size = 51 | usize::try_from(h3o::max_grid_disk_size(k)).expect("grid too large"); 52 | 53 | group.bench_with_input(BenchmarkId::new(name, k), &index, |b, &index| { 54 | b.iter_with_large_drop(|| unsafe { 55 | let mut cells = vec![0u64; size]; 56 | 57 | h3ron_h3_sys::gridDisk( 58 | black_box(index), 59 | black_box(k as c_int), 60 | cells.as_mut_ptr(), 61 | ) 62 | }) 63 | }); 64 | } 65 | -------------------------------------------------------------------------------- /benches/h3/grid_disk_distances.rs: -------------------------------------------------------------------------------- 1 | use criterion::{ 2 | black_box, measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion, 3 | }; 4 | use h3o::CellIndex; 5 | use std::os::raw::c_int; 6 | 7 | const HEXAGON: u64 = 0x8b1fb46622defff; 8 | const PENTAGON: u64 = 0x8b0800000000fff; 9 | 10 | pub fn bench(c: &mut Criterion) { 11 | let mut group = c.benchmark_group("gridDiskDistances"); 12 | 13 | for k in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 100] { 14 | bench_h3o(&mut group, "h3o/Hexagon", HEXAGON, k); 15 | bench_h3(&mut group, "h3/Hexagon", HEXAGON, k); 16 | 17 | bench_h3o(&mut group, "h3o/Pentagon", PENTAGON, k); 18 | bench_h3(&mut group, "h3/Pentagon", PENTAGON, k); 19 | } 20 | 21 | group.finish(); 22 | } 23 | 24 | // ----------------------------------------------------------------------------- 25 | 26 | fn bench_h3o( 27 | group: &mut BenchmarkGroup, 28 | name: &'static str, 29 | index: u64, 30 | k: u32, 31 | ) where 32 | T: Measurement, 33 | { 34 | let index = CellIndex::try_from(index).expect("hex index"); 35 | group.bench_with_input(BenchmarkId::new(name, k), &index, |b, &index| { 36 | b.iter_with_large_drop(|| { 37 | black_box(index).grid_disk_distances::>(black_box(k)) 38 | }) 39 | }); 40 | } 41 | 42 | fn bench_h3( 43 | group: &mut BenchmarkGroup, 44 | name: &'static str, 45 | index: u64, 46 | k: u32, 47 | ) where 48 | T: Measurement, 49 | { 50 | let size = 51 | usize::try_from(h3o::max_grid_disk_size(k)).expect("grid too large"); 52 | 53 | group.bench_with_input( 54 | BenchmarkId::new(name, k), 55 | &index, 56 | |b, &pentagon| { 57 | b.iter_with_large_drop(|| unsafe { 58 | let mut cells = vec![0u64; size]; 59 | let mut distances: Vec = vec![0; size]; 60 | 61 | h3ron_h3_sys::gridDiskDistances( 62 | black_box(pentagon), 63 | black_box(k as c_int), 64 | cells.as_mut_ptr(), 65 | distances.as_mut_ptr(), 66 | ) 67 | }) 68 | }, 69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /benches/h3/grid_disk_distances_safe.rs: -------------------------------------------------------------------------------- 1 | use criterion::{ 2 | black_box, measurement::Measurement, BatchSize, BenchmarkGroup, 3 | BenchmarkId, Criterion, 4 | }; 5 | use h3o::CellIndex; 6 | use std::os::raw::c_int; 7 | 8 | const HEXAGON: u64 = 0x8b1fb46622defff; 9 | const PENTAGON: u64 = 0x8b0800000000fff; 10 | 11 | pub fn bench(c: &mut Criterion) { 12 | let mut group = c.benchmark_group("gridDiskDistancesSafe"); 13 | 14 | for k in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 100] { 15 | bench_h3o(&mut group, "h3o/Hexagon", HEXAGON, k); 16 | bench_h3(&mut group, "h3/Hexagon", HEXAGON, k); 17 | 18 | bench_h3o(&mut group, "h3o/Pentagon", PENTAGON, k); 19 | bench_h3(&mut group, "h3/Pentagon", PENTAGON, k); 20 | } 21 | 22 | group.finish(); 23 | } 24 | 25 | // ----------------------------------------------------------------------------- 26 | 27 | fn bench_h3o( 28 | group: &mut BenchmarkGroup, 29 | name: &'static str, 30 | index: u64, 31 | k: u32, 32 | ) where 33 | T: Measurement, 34 | { 35 | let index = CellIndex::try_from(index).expect("hex index"); 36 | group.bench_with_input(BenchmarkId::new(name, k), &index, |b, &index| { 37 | b.iter(|| { 38 | black_box(index) 39 | .grid_disk_distances_safe(black_box(k)) 40 | .for_each(drop) 41 | }) 42 | }); 43 | } 44 | 45 | fn bench_h3( 46 | group: &mut BenchmarkGroup, 47 | name: &'static str, 48 | index: u64, 49 | k: u32, 50 | ) where 51 | T: Measurement, 52 | { 53 | let size = 54 | usize::try_from(h3o::max_grid_disk_size(k)).expect("grid too large"); 55 | let cells = vec![0; size]; 56 | let distances: Vec = vec![0; size]; 57 | 58 | group.bench_with_input(BenchmarkId::new(name, k), &index, |b, &index| { 59 | b.iter_batched_ref( 60 | || (cells.clone(), distances.clone()), 61 | |(cells, distances)| unsafe { 62 | h3ron_h3_sys::gridDiskDistancesSafe( 63 | black_box(index), 64 | black_box(k as c_int), 65 | cells.as_mut_ptr(), 66 | distances.as_mut_ptr(), 67 | ) 68 | }, 69 | BatchSize::SmallInput, 70 | ) 71 | }); 72 | } 73 | -------------------------------------------------------------------------------- /benches/h3/grid_disk_distances_unsafe.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, BenchmarkId, Criterion}; 2 | use h3o::CellIndex; 3 | use std::os::raw::c_int; 4 | 5 | const HEXAGON: u64 = 0x08b1_fb46_622d_efff; 6 | 7 | pub fn bench(c: &mut Criterion) { 8 | let mut group = c.benchmark_group("gridDiskDistancesUnsafe"); 9 | 10 | for k in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 100] { 11 | let size = usize::try_from(h3o::max_grid_disk_size(k)) 12 | .expect("grid too large"); 13 | let mut cells = vec![0; size]; 14 | let mut distances: Vec = vec![0; size]; 15 | 16 | group.bench_with_input( 17 | BenchmarkId::new("h3o", k), 18 | &HEXAGON, 19 | |b, &hexagon| { 20 | let index = CellIndex::try_from(hexagon).expect("hex index"); 21 | b.iter(|| { 22 | black_box(index) 23 | .grid_disk_distances_fast(black_box(k)) 24 | .for_each(drop) 25 | }) 26 | }, 27 | ); 28 | group.bench_with_input( 29 | BenchmarkId::new("h3", k), 30 | &HEXAGON, 31 | |b, &hexagon| { 32 | b.iter(|| unsafe { 33 | h3ron_h3_sys::gridDiskDistancesUnsafe( 34 | black_box(hexagon), 35 | black_box(k as c_int), 36 | cells.as_mut_ptr(), 37 | distances.as_mut_ptr(), 38 | ) 39 | }) 40 | }, 41 | ); 42 | } 43 | 44 | group.finish(); 45 | } 46 | -------------------------------------------------------------------------------- /benches/h3/grid_disk_unsafe.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, BenchmarkId, Criterion}; 2 | use h3o::CellIndex; 3 | use std::os::raw::c_int; 4 | 5 | const HEXAGON: u64 = 0x08b1_fb46_622d_efff; 6 | 7 | pub fn bench(c: &mut Criterion) { 8 | let mut group = c.benchmark_group("gridDiskUnsafe"); 9 | 10 | for k in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 100] { 11 | let size = usize::try_from(h3o::max_grid_disk_size(k)) 12 | .expect("grid too large"); 13 | let mut cells = vec![0; size]; 14 | 15 | group.bench_with_input( 16 | BenchmarkId::new("h3o", k), 17 | &HEXAGON, 18 | |b, &hexagon| { 19 | let index = CellIndex::try_from(hexagon).expect("hex index"); 20 | b.iter(|| { 21 | black_box(index).grid_disk_fast(black_box(k)).for_each(drop) 22 | }) 23 | }, 24 | ); 25 | group.bench_with_input( 26 | BenchmarkId::new("h3", k), 27 | &HEXAGON, 28 | |b, &hexagon| { 29 | b.iter(|| unsafe { 30 | h3ron_h3_sys::gridDiskUnsafe( 31 | black_box(hexagon), 32 | black_box(k as c_int), 33 | cells.as_mut_ptr(), 34 | ) 35 | }) 36 | }, 37 | ); 38 | } 39 | 40 | group.finish(); 41 | } 42 | -------------------------------------------------------------------------------- /benches/h3/grid_disks_unsafe.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | use h3o::CellIndex; 3 | use std::os::raw::c_int; 4 | 5 | pub fn bench(c: &mut Criterion) { 6 | let mut group = c.benchmark_group("gridDisksUnsafe"); 7 | let mut indexes = vec![ 8 | 0x89283080ddbffff, 9 | 0x89283080c37ffff, 10 | 0x89283080c27ffff, 11 | 0x89283080d53ffff, 12 | 0x89283080dcfffff, 13 | 0x89283080dc3ffff, 14 | ]; 15 | 16 | group.bench_function("h3o", |b| { 17 | let indexes = indexes 18 | .iter() 19 | .map(|cell| CellIndex::try_from(*cell).expect("hex index")) 20 | .collect::>(); 21 | b.iter(|| { 22 | CellIndex::grid_disks_fast( 23 | black_box(indexes.iter().copied()), 24 | black_box(2), 25 | ) 26 | .for_each(drop) 27 | }) 28 | }); 29 | group.bench_function("h3", |b| { 30 | let size = indexes.len() 31 | * usize::try_from(h3o::max_grid_disk_size(2)) 32 | .expect("grid too large"); 33 | let mut cells = vec![0; size]; 34 | b.iter(|| unsafe { 35 | h3ron_h3_sys::gridDisksUnsafe( 36 | black_box(indexes.as_mut_ptr()), 37 | black_box(indexes.len() as c_int), 38 | black_box(2), 39 | cells.as_mut_ptr(), 40 | ) 41 | }) 42 | }); 43 | 44 | group.finish(); 45 | } 46 | -------------------------------------------------------------------------------- /benches/h3/grid_distance.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, BenchmarkId, Criterion}; 2 | use h3o::{LatLng, Resolution}; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | let src = LatLng::new(30.3157384429565, 104.15339644867949).expect("src"); 6 | let dst = LatLng::new(29.794972232093798, 106.56006993629623).expect("dst"); 7 | let mut group = c.benchmark_group("gridDistance"); 8 | 9 | for res in 0..=15 { 10 | let resolution = Resolution::try_from(res).expect("resolution"); 11 | let src = src.to_cell(resolution); 12 | let dst = dst.to_cell(resolution); 13 | 14 | group.bench_with_input( 15 | BenchmarkId::new("h3o", res), 16 | &(src, dst), 17 | |b, (src, dst)| { 18 | b.iter(|| black_box(src).grid_distance(black_box(*dst))) 19 | }, 20 | ); 21 | group.bench_with_input( 22 | BenchmarkId::new("h3", res), 23 | &(src, dst), 24 | |b, (src, dst)| { 25 | let mut out: i64 = 0; 26 | let src = u64::from(*src); 27 | let dst = u64::from(*dst); 28 | b.iter(|| unsafe { 29 | h3ron_h3_sys::gridDistance(src, dst, &mut out) 30 | }) 31 | }, 32 | ); 33 | } 34 | 35 | group.finish(); 36 | } 37 | -------------------------------------------------------------------------------- /benches/h3/grid_path_cells.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, BenchmarkId, Criterion}; 2 | use h3o::{LatLng, Resolution}; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | let src = LatLng::new(30.3157384429565, 104.15339644867949).expect("src"); 6 | let dst = LatLng::new(29.794972232093798, 106.56006993629623).expect("dst"); 7 | let mut group = c.benchmark_group("gridPathCells"); 8 | 9 | for res in 0..=15 { 10 | let resolution = Resolution::try_from(res).expect("resolution"); 11 | let src = src.to_cell(resolution); 12 | let dst = dst.to_cell(resolution); 13 | let size = src.grid_path_cells_size(dst).expect("path size"); 14 | 15 | group.bench_with_input( 16 | BenchmarkId::new("h3o", res), 17 | &(src, dst), 18 | |b, (src, dst)| { 19 | b.iter(|| { 20 | black_box(src) 21 | .grid_path_cells(black_box(*dst)) 22 | .expect("iter") 23 | .for_each(drop) 24 | }) 25 | }, 26 | ); 27 | group.bench_with_input( 28 | BenchmarkId::new("h3", res), 29 | &(src, dst), 30 | |b, (src, dst)| { 31 | let mut out = vec![0; size as usize]; 32 | let src = u64::from(*src); 33 | let dst = u64::from(*dst); 34 | b.iter(|| unsafe { 35 | h3ron_h3_sys::gridPathCells(src, dst, out.as_mut_ptr()) 36 | }) 37 | }, 38 | ); 39 | } 40 | 41 | group.finish(); 42 | } 43 | -------------------------------------------------------------------------------- /benches/h3/grid_path_cells_size.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, BenchmarkId, Criterion}; 2 | use h3o::{LatLng, Resolution}; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | let src = LatLng::new(30.3157384429565, 104.15339644867949).expect("src"); 6 | let dst = LatLng::new(29.794972232093798, 106.56006993629623).expect("dst"); 7 | 8 | let mut group = c.benchmark_group("gridPathCellsSize"); 9 | 10 | for res in 0..=15 { 11 | let resolution = Resolution::try_from(res).expect("resolution"); 12 | let src = src.to_cell(resolution); 13 | let dst = dst.to_cell(resolution); 14 | 15 | group.bench_with_input( 16 | BenchmarkId::new("h3o", res), 17 | &(src, dst), 18 | |b, (src, dst)| { 19 | b.iter(|| black_box(src).grid_path_cells_size(black_box(*dst))) 20 | }, 21 | ); 22 | group.bench_with_input( 23 | BenchmarkId::new("h3", res), 24 | &(src, dst), 25 | |b, (src, dst)| { 26 | let mut out: i64 = 0; 27 | let src = u64::from(*src); 28 | let dst = u64::from(*dst); 29 | b.iter(|| unsafe { 30 | h3ron_h3_sys::gridPathCellsSize(src, dst, &mut out) 31 | }) 32 | }, 33 | ); 34 | } 35 | 36 | group.finish(); 37 | } 38 | -------------------------------------------------------------------------------- /benches/h3/grid_ring_unsafe.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, BenchmarkId, Criterion}; 2 | use h3o::CellIndex; 3 | use std::os::raw::c_int; 4 | 5 | const HEXAGON: u64 = 0x08b1_fb46_622d_efff; 6 | 7 | pub fn bench(c: &mut Criterion) { 8 | let mut group = c.benchmark_group("gridRingUnsafe"); 9 | 10 | for k in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 100] { 11 | let size = usize::try_from(if k == 0 { 1 } else { 6 * k }) 12 | .expect("grid too large"); 13 | let mut cells = vec![0; size]; 14 | 15 | group.bench_with_input( 16 | BenchmarkId::new("h3o", k), 17 | &HEXAGON, 18 | |b, &hexagon| { 19 | let index = CellIndex::try_from(hexagon).expect("hex index"); 20 | b.iter(|| { 21 | black_box(index).grid_ring_fast(black_box(k)).for_each(drop) 22 | }) 23 | }, 24 | ); 25 | group.bench_with_input( 26 | BenchmarkId::new("h3", k), 27 | &HEXAGON, 28 | |b, &hexagon| { 29 | b.iter(|| unsafe { 30 | h3ron_h3_sys::gridRingUnsafe( 31 | black_box(hexagon), 32 | black_box(k as c_int), 33 | cells.as_mut_ptr(), 34 | ) 35 | }) 36 | }, 37 | ); 38 | } 39 | 40 | group.finish(); 41 | } 42 | -------------------------------------------------------------------------------- /benches/h3/h3_to_string.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | use h3o::CellIndex; 3 | use std::{ffi::CString, fmt::Write}; 4 | 5 | const INPUT: u64 = 0x8f734e64992d6d8; 6 | const SIZE: usize = 16; // u64 is at most a 16-char hexstring. 7 | 8 | pub fn bench(c: &mut Criterion) { 9 | let mut group = c.benchmark_group("h3ToString"); 10 | 11 | group.bench_function("h3o", |b| { 12 | let index = CellIndex::try_from(INPUT).expect("cell index"); 13 | let mut s = String::with_capacity(SIZE); 14 | b.iter(|| write!(&mut s, "{}", black_box(index))) 15 | }); 16 | group.bench_function("h3", |b| { 17 | let buf = CString::new(vec![1u8; SIZE]).expect("CString"); 18 | let ptr = buf.into_raw(); 19 | b.iter(|| unsafe { 20 | // +1 for the nul byte. 21 | h3ron_h3_sys::h3ToString(black_box(INPUT), ptr, SIZE + 1) 22 | }) 23 | }); 24 | 25 | group.finish(); 26 | } 27 | -------------------------------------------------------------------------------- /benches/h3/is_pentagon.rs: -------------------------------------------------------------------------------- 1 | use super::constants::PENTAGONS; 2 | use criterion::{black_box, BenchmarkId, Criterion}; 3 | use h3o::CellIndex; 4 | 5 | const HEXAGON: u64 = 0x8f734e64992d6d8; 6 | 7 | pub fn bench_hexagons(c: &mut Criterion) { 8 | let mut group = c.benchmark_group("isPentagon/Hexagon"); 9 | 10 | group.bench_function("h3o", |b| { 11 | let index = CellIndex::try_from(HEXAGON).expect("cell index"); 12 | b.iter(|| black_box(index).is_pentagon()) 13 | }); 14 | group.bench_function("h3", |b| { 15 | b.iter(|| unsafe { h3ron_h3_sys::isPentagon(black_box(HEXAGON)) }) 16 | }); 17 | 18 | group.finish(); 19 | } 20 | 21 | pub fn bench_pentagons(c: &mut Criterion) { 22 | let mut group = c.benchmark_group("isPentagon/Pentagon"); 23 | 24 | for (resolution, index) in PENTAGONS.iter().enumerate() { 25 | group.bench_with_input( 26 | BenchmarkId::new("h3o", resolution), 27 | index, 28 | |b, &index| { 29 | let index = CellIndex::try_from(index).expect("cell index"); 30 | b.iter(|| black_box(index).is_pentagon()) 31 | }, 32 | ); 33 | group.bench_with_input( 34 | BenchmarkId::new("h3", resolution), 35 | index, 36 | |b, &index| { 37 | b.iter(|| unsafe { h3ron_h3_sys::isPentagon(black_box(index)) }) 38 | }, 39 | ); 40 | } 41 | group.finish(); 42 | } 43 | -------------------------------------------------------------------------------- /benches/h3/is_res_class3.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | use h3o::CellIndex; 3 | 4 | const INPUT: u64 = 0x8f734e64992d6d8; 5 | 6 | pub fn bench(c: &mut Criterion) { 7 | let mut group = c.benchmark_group("isResClassIII"); 8 | 9 | group.bench_function("h3o", |b| { 10 | let resolution = 11 | CellIndex::try_from(INPUT).expect("cell index").resolution(); 12 | b.iter(|| black_box(resolution).is_class3()) 13 | }); 14 | group.bench_function("h3", |b| { 15 | b.iter(|| unsafe { h3ron_h3_sys::isResClassIII(black_box(INPUT)) }) 16 | }); 17 | 18 | group.finish(); 19 | } 20 | -------------------------------------------------------------------------------- /benches/h3/is_valid_cell.rs: -------------------------------------------------------------------------------- 1 | use super::constants::{HEXAGONS, PENTAGONS}; 2 | use criterion::{black_box, Bencher, BenchmarkId, Criterion}; 3 | use h3o::CellIndex; 4 | 5 | pub fn bench(c: &mut Criterion) { 6 | let mut group = c.benchmark_group("isValidCell"); 7 | 8 | for (resolution, index) in HEXAGONS.iter().enumerate() { 9 | group.bench_with_input( 10 | BenchmarkId::new("h3o/Hexagon", resolution), 11 | index, 12 | bench_h3o, 13 | ); 14 | group.bench_with_input( 15 | BenchmarkId::new("h3/Hexagon", resolution), 16 | index, 17 | bench_h3, 18 | ); 19 | } 20 | 21 | for (resolution, index) in PENTAGONS.iter().enumerate() { 22 | group.bench_with_input( 23 | BenchmarkId::new("h3o/Pentagon", resolution), 24 | index, 25 | bench_h3o, 26 | ); 27 | group.bench_with_input( 28 | BenchmarkId::new("h3/Pentagon", resolution), 29 | index, 30 | bench_h3, 31 | ); 32 | } 33 | 34 | group.finish(); 35 | } 36 | 37 | // ----------------------------------------------------------------------------- 38 | 39 | fn bench_h3o(b: &mut Bencher<'_>, index: &u64) { 40 | b.iter(|| CellIndex::try_from(black_box(*index))) 41 | } 42 | 43 | fn bench_h3(b: &mut Bencher<'_>, index: &u64) { 44 | b.iter(|| unsafe { h3ron_h3_sys::isValidCell(black_box(*index)) }) 45 | } 46 | -------------------------------------------------------------------------------- /benches/h3/is_valid_vertex.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Bencher, BenchmarkId, Criterion}; 2 | use h3o::VertexIndex; 3 | 4 | const HEXAGONS: [u64; 6] = [ 5 | 0x24b734e649928fff, 6 | 0x23b734e649928fff, 7 | 0x22b734e649929fff, 8 | 0x23b734e649929fff, 9 | 0x24b734e649929fff, 10 | 0x25b734e649929fff, 11 | ]; 12 | 13 | const PENTAGONS: [u64; 5] = [ 14 | 0x20b0800000000fff, 15 | 0x21b0800000000fff, 16 | 0x22b0800000000fff, 17 | 0x23b0800000000fff, 18 | 0x24b0800000000fff, 19 | ]; 20 | 21 | pub fn bench(c: &mut Criterion) { 22 | let mut group = c.benchmark_group("isValidVertex"); 23 | 24 | for (vertex, index) in HEXAGONS.iter().enumerate() { 25 | group.bench_with_input( 26 | BenchmarkId::new("h3o/Hexagon", vertex), 27 | index, 28 | bench_h3o, 29 | ); 30 | group.bench_with_input( 31 | BenchmarkId::new("h3/Hexagon", vertex), 32 | index, 33 | bench_h3, 34 | ); 35 | } 36 | 37 | for (vertex, index) in PENTAGONS.iter().enumerate() { 38 | group.bench_with_input( 39 | BenchmarkId::new("h3o/Pentagon", vertex), 40 | index, 41 | bench_h3o, 42 | ); 43 | group.bench_with_input( 44 | BenchmarkId::new("h3/Pentagon", vertex), 45 | index, 46 | bench_h3, 47 | ); 48 | } 49 | 50 | group.finish(); 51 | } 52 | 53 | // ----------------------------------------------------------------------------- 54 | 55 | fn bench_h3o(b: &mut Bencher<'_>, index: &u64) { 56 | b.iter(|| VertexIndex::try_from(black_box(*index))) 57 | } 58 | 59 | fn bench_h3(b: &mut Bencher<'_>, index: &u64) { 60 | b.iter(|| unsafe { h3ron_h3_sys::isValidVertex(black_box(*index)) }) 61 | } 62 | -------------------------------------------------------------------------------- /benches/h3/latlng_to_cell.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Bencher, BenchmarkId, Criterion}; 2 | use h3o::{LatLng, Resolution}; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | let mut group = c.benchmark_group("latLngToCell"); 6 | 7 | let ll = LatLng::new(48.85458622023985, 2.373012457671282).expect("hex"); 8 | for resolution in 0..=15 { 9 | group.bench_with_input( 10 | BenchmarkId::new("h3o/Hexagon", resolution), 11 | &resolution, 12 | |b, &resolution| bench_h3o(b, ll, resolution), 13 | ); 14 | group.bench_with_input( 15 | BenchmarkId::new("h3/Hexagon", resolution), 16 | &resolution, 17 | |b, &resolution| bench_h3(b, ll, resolution), 18 | ); 19 | } 20 | 21 | let ll = LatLng::new(64.70000012793489, 10.53619907546772).expect("pent"); 22 | for resolution in 0..=15 { 23 | group.bench_with_input( 24 | BenchmarkId::new("h3o/Pentagon", resolution), 25 | &resolution, 26 | |b, &resolution| bench_h3o(b, ll, resolution), 27 | ); 28 | group.bench_with_input( 29 | BenchmarkId::new("h3/Pentagon", resolution), 30 | &resolution, 31 | |b, &resolution| bench_h3(b, ll, resolution), 32 | ); 33 | } 34 | 35 | group.finish(); 36 | } 37 | 38 | // ----------------------------------------------------------------------------- 39 | 40 | fn bench_h3o(b: &mut Bencher<'_>, ll: LatLng, resolution: u8) { 41 | let resolution = Resolution::try_from(resolution).expect("resolution"); 42 | b.iter(|| black_box(ll).to_cell(black_box(resolution))) 43 | } 44 | 45 | fn bench_h3(b: &mut Bencher<'_>, ll: LatLng, resolution: u8) { 46 | let mut out: u64 = 0; 47 | let ll = h3ron_h3_sys::LatLng { 48 | lat: ll.lat_radians(), 49 | lng: ll.lng_radians(), 50 | }; 51 | b.iter(|| unsafe { 52 | h3ron_h3_sys::latLngToCell( 53 | black_box(&ll), 54 | black_box(resolution.into()), 55 | &mut out, 56 | ) 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /benches/h3/local_ij_to_cell.rs: -------------------------------------------------------------------------------- 1 | use super::constants::HEXAGONS; 2 | use criterion::{black_box, BenchmarkId, Criterion}; 3 | use h3o::{CellIndex, CoordIJ, LocalIJ}; 4 | 5 | pub fn bench(c: &mut Criterion) { 6 | let mut group = c.benchmark_group("localIjToCell"); 7 | 8 | for (resolution, index) in HEXAGONS.iter().enumerate() { 9 | group.bench_with_input( 10 | BenchmarkId::new("h3o", resolution), 11 | index, 12 | |b, &index| { 13 | let anchor = CellIndex::try_from(index).expect("anchor"); 14 | let coord = LocalIJ::new(anchor, CoordIJ::new(-4, -3)); 15 | b.iter(|| CellIndex::try_from(black_box(coord))) 16 | }, 17 | ); 18 | group.bench_with_input( 19 | BenchmarkId::new("h3", resolution), 20 | index, 21 | |b, &index| { 22 | let mut out: u64 = 0; 23 | let ij = h3ron_h3_sys::CoordIJ { i: -4, j: -3 }; 24 | b.iter(|| unsafe { 25 | h3ron_h3_sys::localIjToCell(index, &ij, 0, &mut out) 26 | }) 27 | }, 28 | ); 29 | } 30 | 31 | group.finish(); 32 | } 33 | -------------------------------------------------------------------------------- /benches/h3/max_face_count.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Bencher, Criterion}; 2 | use h3o::CellIndex; 3 | use std::os::raw::c_int; 4 | 5 | const PENTAGON: u64 = 0x8f0800000000000; 6 | const HEXAGON: u64 = 0x8f734e64992d6d8; 7 | 8 | pub fn bench(c: &mut Criterion) { 9 | let mut group = c.benchmark_group("maxFaceCount"); 10 | 11 | group.bench_function("h3o/Hexagon", |b| bench_h3o(b, HEXAGON)); 12 | group.bench_function("h3/Hexagon", |b| bench_h3(b, HEXAGON)); 13 | 14 | group.bench_function("h3o/Pentagon", |b| bench_h3o(b, PENTAGON)); 15 | group.bench_function("h3/Pentagon", |b| bench_h3(b, PENTAGON)); 16 | 17 | group.finish(); 18 | } 19 | 20 | // ----------------------------------------------------------------------------- 21 | 22 | fn bench_h3o(b: &mut Bencher<'_>, index: u64) { 23 | let index = CellIndex::try_from(index).expect("cell index"); 24 | b.iter(|| black_box(index).max_face_count()) 25 | } 26 | 27 | fn bench_h3(b: &mut Bencher<'_>, index: u64) { 28 | b.iter(|| { 29 | let mut out: c_int = 0; 30 | unsafe { h3ron_h3_sys::maxFaceCount(black_box(index), &mut out) } 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /benches/h3/max_grid_disk_size.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | use std::os::raw::c_int; 3 | 4 | const K: u32 = 42; 5 | 6 | pub fn bench(c: &mut Criterion) { 7 | let mut group = c.benchmark_group("maxGridDiskSize"); 8 | 9 | group.bench_function("h3o", |b| { 10 | b.iter(|| h3o::max_grid_disk_size(black_box(K))) 11 | }); 12 | group.bench_function("h3", |b| { 13 | b.iter(|| { 14 | let mut out: i64 = 0; 15 | unsafe { 16 | h3ron_h3_sys::maxGridDiskSize(black_box(K as c_int), &mut out) 17 | } 18 | }) 19 | }); 20 | 21 | group.finish(); 22 | } 23 | -------------------------------------------------------------------------------- /benches/h3/max_polygon_to_cells_size.rs: -------------------------------------------------------------------------------- 1 | use super::utils::load_polygon; 2 | use criterion::{black_box, Criterion}; 3 | use h3o::{geom::TilerBuilder, Resolution}; 4 | use std::os::raw::c_int; 5 | 6 | const RESOLUTION: Resolution = Resolution::Nine; 7 | 8 | pub fn bench(c: &mut Criterion) { 9 | let mut group = c.benchmark_group("maxPolygonToCellsSize"); 10 | let polygon = load_polygon("Paris"); 11 | 12 | group.bench_function("h3o", |b| { 13 | let mut tiler = TilerBuilder::new(RESOLUTION).build(); 14 | tiler.add(polygon.clone()).expect("valid polygon"); 15 | b.iter(|| black_box(&tiler).coverage_size_hint()) 16 | }); 17 | group.bench_function("h3", |b| { 18 | let mut coords = polygon 19 | .exterior() 20 | .coords() 21 | .map(|coord| h3ron_h3_sys::LatLng { 22 | lat: coord.y, 23 | lng: coord.x, 24 | }) 25 | .collect::>(); 26 | let geoloop = h3ron_h3_sys::GeoLoop { 27 | numVerts: coords.len() as c_int, 28 | verts: coords.as_mut_ptr(), 29 | }; 30 | let polygon = h3ron_h3_sys::GeoPolygon { 31 | geoloop, 32 | numHoles: 0, 33 | holes: std::ptr::null_mut(), 34 | }; 35 | let mut out = 0; 36 | b.iter(|| unsafe { 37 | h3ron_h3_sys::maxPolygonToCellsSize( 38 | black_box(&polygon), 39 | black_box(u8::from(RESOLUTION).into()), 40 | 0, 41 | &mut out, 42 | ); 43 | }) 44 | }); 45 | 46 | group.finish(); 47 | } 48 | -------------------------------------------------------------------------------- /benches/h3/origin_to_directed_edges.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Bencher, Criterion}; 2 | use h3o::CellIndex; 3 | 4 | const PENTAGON: u64 = 0x8f0800000000000; 5 | const HEXAGON: u64 = 0x8f734e64992d6d8; 6 | 7 | pub fn bench(c: &mut Criterion) { 8 | let mut group = c.benchmark_group("originToDirectedEdges"); 9 | 10 | group.bench_function("h3o/Hexagon", |b| bench_h3o(b, HEXAGON)); 11 | group.bench_function("h3/Hexagon", |b| bench_h3(b, HEXAGON)); 12 | 13 | group.bench_function("h3o/Pentagon", |b| bench_h3o(b, PENTAGON)); 14 | group.bench_function("h3/Pentagon", |b| bench_h3(b, PENTAGON)); 15 | group.finish(); 16 | } 17 | 18 | // ----------------------------------------------------------------------------- 19 | 20 | fn bench_h3o(b: &mut Bencher<'_>, index: u64) { 21 | let index = CellIndex::try_from(index).expect("cell index"); 22 | b.iter(|| { 23 | for edge in black_box(index).edges() { 24 | black_box(edge); 25 | } 26 | }); 27 | } 28 | 29 | fn bench_h3(b: &mut Bencher<'_>, index: u64) { 30 | let mut out = [0; 6]; 31 | b.iter(|| unsafe { 32 | h3ron_h3_sys::originToDirectedEdges(black_box(index), out.as_mut_ptr()); 33 | for edge in out { 34 | black_box(edge); 35 | } 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /benches/h3/pentagon_count.rs: -------------------------------------------------------------------------------- 1 | use criterion::Criterion; 2 | use h3o::Resolution; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | let mut group = c.benchmark_group("pentagonCount"); 6 | 7 | group.bench_function("h3o", |b| b.iter(Resolution::pentagon_count)); 8 | group.bench_function("h3", |b| { 9 | b.iter(|| unsafe { h3ron_h3_sys::pentagonCount() }) 10 | }); 11 | 12 | group.finish(); 13 | } 14 | -------------------------------------------------------------------------------- /benches/h3/rads_to_degs.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | 3 | const VALUE: f64 = 0.8377580409552; 4 | 5 | pub fn bench(c: &mut Criterion) { 6 | let mut group = c.benchmark_group("radsToDegs"); 7 | 8 | group.bench_function("h3o", |b| b.iter(|| black_box(VALUE).to_degrees())); 9 | group.bench_function("h3", |b| { 10 | b.iter(|| unsafe { h3ron_h3_sys::radsToDegs(black_box(VALUE)) }) 11 | }); 12 | 13 | group.finish(); 14 | } 15 | -------------------------------------------------------------------------------- /benches/h3/res0_cell_count.rs: -------------------------------------------------------------------------------- 1 | use criterion::Criterion; 2 | use h3o::BaseCell; 3 | 4 | pub fn bench(c: &mut Criterion) { 5 | let mut group = c.benchmark_group("res0CellCount"); 6 | 7 | group.bench_function("h3o", |b| b.iter(BaseCell::count)); 8 | group.bench_function("h3", |b| { 9 | b.iter(|| unsafe { h3ron_h3_sys::res0CellCount() }) 10 | }); 11 | 12 | group.finish(); 13 | } 14 | -------------------------------------------------------------------------------- /benches/h3/string_to_h3.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | use h3o::{CellIndex, DirectedEdgeIndex, VertexIndex}; 3 | use std::ffi::CString; 4 | 5 | const CELL_INDEX: &str = "08f734e64992d6d8"; 6 | const EDGE_INDEX: &str = "15f2834782b9c2ab"; 7 | const VERT_INDEX: &str = "23b734e649928fff"; 8 | 9 | pub fn bench_cell(c: &mut Criterion) { 10 | let mut group = c.benchmark_group("stringToH3"); 11 | 12 | group.bench_function("h3o/Cell", |b| { 13 | let s = CELL_INDEX.to_owned(); 14 | b.iter(|| black_box(&s).parse::()) 15 | }); 16 | group.bench_function("h3/Cell", |b| { 17 | let ptr = CString::new(CELL_INDEX).expect("CString").into_raw(); 18 | b.iter(|| { 19 | let mut out: h3ron_h3_sys::H3Index = 0; 20 | unsafe { h3ron_h3_sys::stringToH3(black_box(ptr), &mut out) } 21 | }) 22 | }); 23 | 24 | group.finish(); 25 | } 26 | 27 | pub fn bench_edge(c: &mut Criterion) { 28 | let mut group = c.benchmark_group("stringToH3"); 29 | 30 | group.bench_function("h3o/Edge", |b| { 31 | let s = EDGE_INDEX.to_owned(); 32 | b.iter(|| black_box(&s).parse::()) 33 | }); 34 | group.bench_function("h3/Edge", |b| { 35 | let ptr = CString::new(EDGE_INDEX).expect("CString").into_raw(); 36 | b.iter(|| { 37 | let mut out: h3ron_h3_sys::H3Index = 0; 38 | unsafe { h3ron_h3_sys::stringToH3(black_box(ptr), &mut out) } 39 | }) 40 | }); 41 | 42 | group.finish(); 43 | } 44 | 45 | pub fn bench_vertex(c: &mut Criterion) { 46 | let mut group = c.benchmark_group("stringToH3"); 47 | 48 | group.bench_function("h3o/Vertex", |b| { 49 | let s = VERT_INDEX.to_owned(); 50 | b.iter(|| black_box(&s).parse::()) 51 | }); 52 | group.bench_function("h3/Vertex", |b| { 53 | let ptr = CString::new(VERT_INDEX).expect("CString").into_raw(); 54 | b.iter(|| { 55 | let mut out: h3ron_h3_sys::H3Index = 0; 56 | unsafe { h3ron_h3_sys::stringToH3(black_box(ptr), &mut out) } 57 | }) 58 | }); 59 | 60 | group.finish(); 61 | } 62 | -------------------------------------------------------------------------------- /benches/h3/uncompact_cells.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | const RESOLUTION: Resolution = Resolution::Seven; 5 | 6 | pub fn bench(c: &mut Criterion) { 7 | let compacted = [ 8 | CellIndex::try_from(0x802bfffffffffff).unwrap(), // hexagon res 0. 9 | CellIndex::try_from(0x820807fffffffff).unwrap(), // pentagon res 1. 10 | CellIndex::try_from(0x83734efffffffff).unwrap(), // hexagon res 3. 11 | ]; 12 | let size = CellIndex::uncompact_size(compacted.iter().copied(), RESOLUTION); 13 | 14 | let mut group = c.benchmark_group("uncompactCells"); 15 | 16 | group.bench_function("h3o", |b| { 17 | b.iter(|| { 18 | let iter = compacted.iter().copied(); 19 | CellIndex::uncompact(black_box(iter), black_box(RESOLUTION)) 20 | .for_each(drop) 21 | }) 22 | }); 23 | group.bench_function("h3", |b| { 24 | let cells = 25 | compacted.iter().copied().map(u64::from).collect::>(); 26 | let mut out = vec![0; size as usize]; 27 | b.iter(|| unsafe { 28 | h3ron_h3_sys::uncompactCells( 29 | black_box(cells.as_ptr()), 30 | black_box(cells.len() as i64), 31 | out.as_mut_ptr(), 32 | black_box(size as i64), 33 | black_box(u8::from(RESOLUTION).into()), 34 | ) 35 | }) 36 | }); 37 | 38 | group.finish(); 39 | } 40 | -------------------------------------------------------------------------------- /benches/h3/uncompact_cells_size.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, Criterion}; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | const RESOLUTION: Resolution = Resolution::Seven; 5 | 6 | pub fn bench(c: &mut Criterion) { 7 | let compacted = [ 8 | CellIndex::try_from(0x802bfffffffffff).unwrap(), // hexagon res 0. 9 | CellIndex::try_from(0x820807fffffffff).unwrap(), // pentagon res 1. 10 | CellIndex::try_from(0x83734efffffffff).unwrap(), // hexagon res 3. 11 | ]; 12 | 13 | let mut group = c.benchmark_group("uncompactCellsSize"); 14 | 15 | group.bench_function("h3o", |b| { 16 | b.iter(|| { 17 | let iter = compacted.iter().copied(); 18 | CellIndex::uncompact_size(black_box(iter), black_box(RESOLUTION)) 19 | }) 20 | }); 21 | group.bench_function("h3", |b| { 22 | let cells = 23 | compacted.iter().copied().map(u64::from).collect::>(); 24 | let mut out: i64 = 0; 25 | b.iter(|| unsafe { 26 | h3ron_h3_sys::uncompactCellsSize( 27 | black_box(cells.as_ptr()), 28 | black_box(cells.len() as i64), 29 | black_box(u8::from(RESOLUTION).into()), 30 | &mut out, 31 | ) 32 | }) 33 | }); 34 | 35 | group.finish(); 36 | } 37 | -------------------------------------------------------------------------------- /benches/h3/utils.rs: -------------------------------------------------------------------------------- 1 | use geo::{Geometry, Polygon}; 2 | use h3o::CellIndex; 3 | use std::{ 4 | fs::File, 5 | io::{BufRead, BufReader}, 6 | path::PathBuf, 7 | }; 8 | 9 | pub fn load_cells(resolution: u32) -> Vec { 10 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 11 | let filepath = format!("dataset/Paris/cells-res{resolution}.txt"); 12 | path.push(filepath); 13 | 14 | let file = File::open(path).expect("open test dataset"); 15 | let reader = BufReader::new(file); 16 | 17 | reader 18 | .lines() 19 | .map(|line| { 20 | let line = line.expect("test input"); 21 | line.parse::().expect("cell index") 22 | }) 23 | .collect() 24 | } 25 | 26 | pub fn load_polygon(name: &str) -> Polygon { 27 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 28 | let filepath = format!("dataset/shapes/{name}.geojson"); 29 | path.push(filepath); 30 | 31 | let file = File::open(path).expect("open test dataset"); 32 | let reader = BufReader::new(file); 33 | 34 | let geojson = geojson::GeoJson::from_reader(reader).expect("GeoJSON"); 35 | let geometry = Geometry::try_from(geojson).expect("geometry"); 36 | Polygon::try_from(geometry).expect("polygon") 37 | } 38 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | # Furthermore, Rust types that are larger than 128 bytes are copied with memcpy 2 | # rather than inline code. [...] Shrinking these types to 128 bytes or less can 3 | # make the code faster by avoiding memcpy calls and reducing memory traffic. 4 | # 5 | # See https://nnethercote.github.io/perf-book/type-sizes.html 6 | enum-variant-size-threshold = 128 7 | -------------------------------------------------------------------------------- /dataset/Paris/cells-res5.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HydroniumLabs/h3o/ad2bebf52eab218d66b0bf213b14a2802bf616f7/dataset/Paris/cells-res5.txt -------------------------------------------------------------------------------- /dataset/Paris/cells-res6.txt: -------------------------------------------------------------------------------- 1 | 861fb462fffffff 2 | 861fb4667ffffff 3 | 861fb4677ffffff 4 | -------------------------------------------------------------------------------- /dataset/Paris/cells-res7.txt: -------------------------------------------------------------------------------- 1 | 871fb4628ffffff 2 | 871fb4663ffffff 3 | 871fb462dffffff 4 | 871fb4664ffffff 5 | 871fb4675ffffff 6 | 871fb4674ffffff 7 | 871fb4759ffffff 8 | 871fb4645ffffff 9 | 871fb4670ffffff 10 | 871fb4644ffffff 11 | 871fb4629ffffff 12 | 871fb475affffff 13 | 871fb4660ffffff 14 | 871fb464cffffff 15 | 871fb4673ffffff 16 | 871fb462bffffff 17 | 871fb475bffffff 18 | 871fb4662ffffff 19 | 871fb4641ffffff 20 | 871fb4671ffffff 21 | 871fb4661ffffff 22 | 871fb4665ffffff 23 | 871fb4666ffffff 24 | 871fb4676ffffff 25 | 871fb4646ffffff 26 | -------------------------------------------------------------------------------- /dataset/avgEdgeLen.txt: -------------------------------------------------------------------------------- 1 | 0.20110729345570325,1281.2560107413644,1281256.0107413644 2 | 0.07582111043884765,483.0568390711111,483056.8390711111 3 | 0.02864742595735945,182.51295648916735,182512.95648916735 4 | 0.01082705133253538,68.97922178775585,68979.22178775584 5 | 0.00409225087020209,26.07175968017739,26071.75968017739 6 | 0.0015467085046591636,9.854090989971207,9854.090989971206 7 | 0.0005846065718298453,3.7245326671400765,3724.5326671400767 8 | 0.0002207619176534713,1.4064757626435986,1406.4757626435987 9 | 0.0000834113029497395,0.5314140100625567,531.4140100625567 10 | 0.00003151560528974152,0.20078614761193547,200.78614761193546 11 | 0.000011907659293816194,0.07586378286883358,75.86378286883358 12 | 0.000004499115550979479,0.02866389748307224,28.66389748307224 13 | 0.000001699917695123959,0.010830187842605124,10.830187842605124 14 | 0.0000006422862754806202,0.004092010473292413,4.092010473292413 15 | 0.0000002426774312980401,0.001546099657446663,1.546099657446663 16 | 0.00000009169172362798202,0.0005841686296646656,0.5841686296646657 17 | -------------------------------------------------------------------------------- /dataset/shapes/Empty.geojson: -------------------------------------------------------------------------------- 1 | {"coordinates":[[[-122.40898669968212,37.81331899988944],[-122.40898669968784,37.81331899988944],[-122.40898669969356,37.81331899988944],[-122.40898669968212,37.81331899988944]]],"type":"Polygon"} 2 | -------------------------------------------------------------------------------- /dataset/shapes/HalfWorld_1.geojson: -------------------------------------------------------------------------------- 1 | {"coordinates":[[[-180,-90],[-180,90],[0,90],[0,-90],[-180,-90]]],"type":"Polygon"} 2 | -------------------------------------------------------------------------------- /dataset/shapes/HalfWorld_2.geojson: -------------------------------------------------------------------------------- 1 | {"coordinates":[[[0,-90],[0,90],[180,90],[180,-90],[0,-90]]],"type":"Polygon"} 2 | -------------------------------------------------------------------------------- /dataset/shapes/Holes.geojson: -------------------------------------------------------------------------------- 1 | {"type":"Polygon","coordinates":[[[-47.900390625,-14.944784875088372],[-51.591796875,-19.91138351415555],[-41.11083984375,-21.309846141087192],[-43.39599609375,-15.390135715305204],[-47.900390625,-14.944784875088372]],[[-46.6259765625,-17.14079039331664],[-47.548828125,-16.804541076383455],[-46.23046874999999,-16.699340234594537],[-45.3515625,-19.31114335506464],[-46.6259765625,-17.14079039331664]],[[-44.40673828125,-18.375379094031825],[-44.4287109375,-20.097206227083888],[-42.9345703125,-18.979025953255267],[-43.52783203125,-17.602139123350838],[-44.40673828125,-18.375379094031825]]]} 2 | -------------------------------------------------------------------------------- /dataset/shapes/PrimeMeridian.geojson: -------------------------------------------------------------------------------- 1 | {"type":"Polygon","coordinates":[[[0.5729577951308232,0.5729577951308232],[-0.5729577951308232,0.5729577951308232],[-0.5729577951308232,-0.5729577951308232],[0.5729577951308232,-0.5729577951308232],[0.5729577951308232,0.5729577951308232]]]} 2 | -------------------------------------------------------------------------------- /dataset/shapes/SanFrancisco.geojson: -------------------------------------------------------------------------------- 1 | {"coordinates":[[[-122.40898669969356,37.81331899988944],[-122.38054369969613,37.78663019990699],[-122.35447369969584,37.719806199904276],[-122.51234369969448,37.70761319990403],[-122.52471869969825,37.783587199903444],[-122.47987669969707,37.81515719990604],[-122.40898669969356,37.81331899988944]]],"type":"Polygon"} 2 | -------------------------------------------------------------------------------- /dataset/shapes/SanFranciscoHole.geojson: -------------------------------------------------------------------------------- 1 | {"type":"Polygon","coordinates":[[[-122.40898669969356,37.81331899988944],[-122.38054369969613,37.78663019990699],[-122.35447369969584,37.719806199904276],[-122.51234369969448,37.70761319990403],[-122.52471869969825,37.783587199903444],[-122.47987669969707,37.81515719990604],[-122.40898669969356,37.81331899988944]],[[-122.44711969969569,37.786980199908015],[-122.45907769969834,37.76641019990431],[-122.41370969969519,37.77106819990672],[-122.44711969969569,37.786980199908015]]]} 2 | -------------------------------------------------------------------------------- /dataset/shapes/Transmeridian.geojson: -------------------------------------------------------------------------------- 1 | {"type":"Polygon","coordinates":[[[-179.4270422048692,0.5729577951308232],[179.4270422048692,0.5729577951308232],[179.4270422048692,-0.5729577951308232],[-179.4270422048692,-0.5729577951308232],[-179.4270422048692,0.5729577951308232]]]} 2 | -------------------------------------------------------------------------------- /dataset/shapes/TransmeridianComplex.geojson: -------------------------------------------------------------------------------- 1 | {"type":"Polygon","coordinates":[[[-179.99942704220487,5.729577951308233],[179.99942704220487,5.729577951308233],[168.5408440973835,2.8647889756541165],[179.99942704220487,-5.729577951308233],[-179.99942704220487,-5.729577951308233],[-168.5408440973835,-2.8647889756541165],[-179.99942704220487,5.729577951308233]]]} 2 | -------------------------------------------------------------------------------- /dataset/shapes/TransmeridianHole.geojson: -------------------------------------------------------------------------------- 1 | {"type":"Polygon","coordinates":[[[-179.4270422048692,0.5729577951308232],[179.4270422048692,0.5729577951308232],[179.4270422048692,-0.5729577951308232],[-179.4270422048692,-0.5729577951308232],[-179.4270422048692,0.5729577951308232]],[[-179.71352110243458,0.2864788975654116],[179.71352110243458,0.2864788975654116],[179.71352110243458,-0.2864788975654116],[-179.71352110243458,-0.2864788975654116],[-179.71352110243458,0.2864788975654116]]]} 2 | -------------------------------------------------------------------------------- /dataset/shapes/h3_issue136.geojson: -------------------------------------------------------------------------------- 1 | {"coordinates":[[[51.1122595579185,5.76910652153309],[51.0844285870957,5.74843667987113],[51.0841156106137,5.74868565718131],[51.112087787177096,5.76939661923247],[51.1122595579185,5.76910652153309]]],"type":"Polygon"} 2 | -------------------------------------------------------------------------------- /dataset/shapes/h3java_issue138.geojson: -------------------------------------------------------------------------------- 1 | {"coordinates":[[[11.147991,64.758157],[11.443969,64.758157],[11.443969,64.845147],[11.147991,64.845147],[11.147991,64.758157]]],"type":"Polygon"} 2 | -------------------------------------------------------------------------------- /dataset/shapes/h3js_issue67_1.geojson: -------------------------------------------------------------------------------- 1 | {"coordinates":[[[-56.25,-33.13755119234615],[-56.25,-34.30714385628804],[-57.65625,-34.30714385628804],[-57.65625,-33.13755119234615],[-56.25,-33.13755119234615]]],"type":"Polygon"} 2 | -------------------------------------------------------------------------------- /dataset/shapes/h3js_issue67_2.geojson: -------------------------------------------------------------------------------- 1 | {"coordinates":[[[-57.65625,-34.30714385628804],[-57.65625,-35.4606699514953],[-59.0625,-35.4606699514953],[-59.0625,-34.30714385628804],[-57.65625,-34.30714385628804]]],"type":"Polygon"} 2 | -------------------------------------------------------------------------------- /dataset/shapes/h3o_issue21.geojson: -------------------------------------------------------------------------------- 1 | {"coordinates":[[[156.0,6.0],[156.0,3.0],[159.0,3.0],[159.0,6.0],[156.0,6.0]]],"type":"Polygon"} 2 | -------------------------------------------------------------------------------- /dataset/shapes/h3o_issue23.geojson: -------------------------------------------------------------------------------- 1 | {"coordinates":[[[0.0,5.0],[5.0,5.0],[5.0,0.0],[-179.0,-90.0],[0.0,5.0]]],"type":"Polygon"} 2 | -------------------------------------------------------------------------------- /dataset/shapes/h3py_issue343_1.geojson: -------------------------------------------------------------------------------- 1 | {"coordinates":[[[-141.001,61.895],[-140.996,61.895],[-140.996,64.848],[-141.001,64.848],[-141.001,61.895]]],"type":"Polygon"} 2 | -------------------------------------------------------------------------------- /dataset/shapes/h3py_issue343_2.geojson: -------------------------------------------------------------------------------- 1 | {"coordinates":[[[-141.001,61.895],[-140.996,61.895],[-140.996,65.848],[-141.001,65.848],[-141.001,61.895]]],"type":"Polygon"} 2 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | corpus 3 | artifacts 4 | coverage 5 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/cell_to_vertex.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use h3o::{CellIndex, Vertex, VertexIndex}; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | #[derive(Debug, arbitrary::Arbitrary)] 7 | pub struct Args { 8 | cell: CellIndex, 9 | vertex_num: Vertex, 10 | } 11 | 12 | fuzz_target!(|args: Args| { 13 | assert_eq!( 14 | args.cell.vertex(args.vertex_num), 15 | cell_to_vertex(args.cell, args.vertex_num), 16 | "cellToVertex" 17 | ); 18 | }); 19 | 20 | // H3 wrappers {{{ 21 | 22 | fn cell_to_vertex(cell: CellIndex, vertex: Vertex) -> Option { 23 | let mut out: u64 = 0; 24 | let res = unsafe { 25 | h3ron_h3_sys::cellToVertex( 26 | cell.into(), 27 | u8::from(vertex).into(), 28 | &mut out, 29 | ) 30 | }; 31 | (res == 0).then(|| VertexIndex::try_from(out).expect("vertex index")) 32 | } 33 | 34 | // }}} 35 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/cells_to_geom.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use h3o::{geom::SolventBuilder, CellIndex}; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | fuzz_target!(|data: Vec| { 7 | let cells = data 8 | .into_iter() 9 | .filter_map(|bits| CellIndex::try_from(bits).ok()) 10 | .take(1024) // Limit to 1024 cells to avoid looooooooong exec time. 11 | .collect::>(); 12 | let cell_count = cells.len(); 13 | let polygons = SolventBuilder::new().build().dissolve(cells); 14 | 15 | assert!(cell_count >= polygons.map(|mp| mp.0.len()).unwrap_or_default()); 16 | }); 17 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/compact.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use h3o::CellIndex; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | fuzz_target!(|cells: Vec| { 7 | // Filter out invalid cell indexes. 8 | let mut cells = cells 9 | .into_iter() 10 | .filter_map(|bits| CellIndex::try_from(bits).ok()) 11 | .collect::>(); 12 | 13 | let mut compacted = cells.clone(); 14 | if CellIndex::compact(&mut compacted).is_ok() { 15 | // Check that every input cell is present in the output either as itself 16 | // or as an ancestor. 17 | let mut i = 0; 18 | cells.sort_unstable(); 19 | for cell in cells { 20 | loop { 21 | let candidate = compacted[i]; 22 | if cell.parent(candidate.resolution()) == Some(candidate) { 23 | break; 24 | } 25 | i += 1; 26 | if i >= compacted.len() { 27 | panic!("{cell} missing"); 28 | } 29 | } 30 | } 31 | } 32 | }); 33 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/index_io.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use h3o::{CellIndex, DirectedEdgeIndex, VertexIndex}; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | fuzz_target!(|input: String| { 7 | if let Ok(index) = input.parse::() { 8 | assert_eq!( 9 | index.to_string().parse::(), 10 | Ok(index), 11 | "CellIndex" 12 | ); 13 | } 14 | if let Ok(index) = input.parse::() { 15 | assert_eq!( 16 | index.to_string().parse::(), 17 | Ok(index), 18 | "DirectedEdgeIndex" 19 | ); 20 | } 21 | if let Ok(index) = input.parse::() { 22 | assert_eq!( 23 | index.to_string().parse::(), 24 | Ok(index), 25 | "VertexIndex" 26 | ); 27 | } 28 | }); 29 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/is_valid.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use h3o::{CellIndex, DirectedEdgeIndex, VertexIndex}; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | fuzz_target!(|bits: u64| { 7 | assert_eq!( 8 | CellIndex::try_from(bits).is_ok(), 9 | is_valid_cell(bits), 10 | "isValidCell" 11 | ); 12 | assert_eq!( 13 | DirectedEdgeIndex::try_from(bits).is_ok(), 14 | is_valid_directed_edge(bits), 15 | "isValidDirectedEdge" 16 | ); 17 | assert_eq!( 18 | VertexIndex::try_from(bits).is_ok(), 19 | is_valid_vertex(bits), 20 | "isValidVertex" 21 | ); 22 | }); 23 | 24 | // H3 wrappers {{{ 25 | 26 | fn is_valid_cell(index: u64) -> bool { 27 | unsafe { h3ron_h3_sys::isValidCell(index) == 1 } 28 | } 29 | 30 | fn is_valid_directed_edge(index: u64) -> bool { 31 | unsafe { h3ron_h3_sys::isValidDirectedEdge(index) == 1 } 32 | } 33 | 34 | fn is_valid_vertex(index: u64) -> bool { 35 | unsafe { h3ron_h3_sys::isValidVertex(index) == 1 } 36 | } 37 | 38 | // }}} 39 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/latltng_to_cell.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use h3o::{CellIndex, LatLng, Resolution}; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | #[derive(Debug, arbitrary::Arbitrary)] 7 | pub struct Args { 8 | ll: LatLng, 9 | res: Resolution, 10 | } 11 | 12 | fuzz_target!(|args: Args| { 13 | assert_eq!(args.ll.to_cell(args.res), latlng_to_cell(args.ll, args.res)); 14 | }); 15 | 16 | // H3 wrappers {{{ 17 | 18 | fn latlng_to_cell(ll: LatLng, resolution: Resolution) -> CellIndex { 19 | let mut out: u64 = 0; 20 | let ll = h3ron_h3_sys::LatLng { 21 | lat: ll.lat_radians(), 22 | lng: ll.lng_radians(), 23 | }; 24 | unsafe { 25 | h3ron_h3_sys::latLngToCell(&ll, u8::from(resolution).into(), &mut out); 26 | } 27 | CellIndex::try_from(out).expect("cell index") 28 | } 29 | 30 | // }}} 31 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/local_ij.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use h3o::{CellIndex, CoordIJ, LocalIJ}; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | #[derive(Debug, arbitrary::Arbitrary)] 7 | pub struct Args { 8 | anchor: CellIndex, 9 | i: i32, 10 | j: i32, 11 | } 12 | 13 | fuzz_target!(|args: Args| { 14 | let local_ij = LocalIJ { 15 | anchor: args.anchor, 16 | coord: CoordIJ::new(args.i, args.j), 17 | }; 18 | 19 | assert_eq!( 20 | CellIndex::try_from(local_ij).ok(), 21 | local_ij_to_cell(local_ij), 22 | "localIjToCell" 23 | ); 24 | }); 25 | 26 | // H3 wrappers {{{ 27 | 28 | pub fn local_ij_to_cell(local_ij: LocalIJ) -> Option { 29 | let mut out: u64 = 0; 30 | let ij = h3ron_h3_sys::CoordIJ { 31 | i: local_ij.coord.i, 32 | j: local_ij.coord.j, 33 | }; 34 | let res = unsafe { 35 | h3ron_h3_sys::localIjToCell(local_ij.anchor.into(), &ij, 0, &mut out) 36 | }; 37 | 38 | (res == 0).then(|| CellIndex::try_from(out).expect("H3 index")) 39 | } 40 | 41 | // }}} 42 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/polygon_to_cells.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use geo_types::{Coord, LineString, Polygon}; 4 | use h3o::{geom::TilerBuilder, Resolution}; 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | #[derive(Debug, arbitrary::Arbitrary)] 8 | pub struct Args { 9 | resolution: Resolution, 10 | values: Vec, 11 | } 12 | 13 | fuzz_target!(|args: Args| { 14 | if args.values.len() < 6 { 15 | // Not enough point for a polygon. 16 | return; 17 | } 18 | 19 | let mut ring = LineString::new( 20 | args.values 21 | .chunks_exact(2) 22 | .map(|chunk| Coord { 23 | x: chunk[0], 24 | y: chunk[1], 25 | }) 26 | .collect(), 27 | ); 28 | ring.close(); 29 | // Can still return false if the first point contains NaN. 30 | if !ring.is_closed() { 31 | return; 32 | } 33 | let mut tiler = TilerBuilder::new(args.resolution).build(); 34 | if tiler.add(Polygon::new(ring, Vec::new())).is_ok() { 35 | if tiler.coverage_size_hint() > 4_000_000 { 36 | return; 37 | } 38 | tiler.into_coverage().for_each(drop); 39 | } 40 | }); 41 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/polygon_with_holes_to_cells.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use geo_types::{Coord, LineString, Polygon}; 4 | use h3o::{geom::TilerBuilder, Resolution}; 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | #[derive(Debug, arbitrary::Arbitrary)] 8 | pub struct Args { 9 | resolution: Resolution, 10 | values: Vec>, 11 | } 12 | 13 | fuzz_target!(|args: Args| { 14 | let mut rings = args 15 | .values 16 | .into_iter() 17 | .filter_map(|coords| { 18 | let mut ring = LineString::new( 19 | coords 20 | .chunks_exact(2) 21 | .map(|chunk| Coord { 22 | x: chunk[0], 23 | y: chunk[1], 24 | }) 25 | .collect(), 26 | ); 27 | ring.close(); 28 | ring.is_closed().then_some(ring) 29 | }) 30 | .collect::>(); 31 | 32 | if rings.len() < 2 { 33 | // Not enough loop for 1 ring and 1 hole. 34 | return; 35 | } 36 | let outer = rings.pop().expect("checked above"); 37 | rings.truncate(100); // Avoid too many holes. 38 | 39 | let mut tiler = TilerBuilder::new(args.resolution).build(); 40 | if tiler.add(Polygon::new(outer, rings)).is_ok() { 41 | if tiler.coverage_size_hint() > 4_000_000 { 42 | return; 43 | } 44 | tiler.into_coverage().for_each(drop); 45 | } 46 | }); 47 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/uncompact.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use h3o::{CellIndex, Resolution}; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | #[derive(Debug, arbitrary::Arbitrary)] 7 | pub struct Args { 8 | resolution: Resolution, 9 | cells: Vec, 10 | } 11 | 12 | fuzz_target!(|args: Args| { 13 | let cells = args 14 | .cells 15 | .into_iter() 16 | .filter_map(|bits| CellIndex::try_from(bits).ok()) 17 | .filter(|cell| cell.resolution() <= args.resolution) 18 | .collect::>(); 19 | 20 | // Skip inputs that would produce too many indexes. 21 | if CellIndex::uncompact_size(cells.iter().copied(), args.resolution) 22 | > 4_000_000 23 | { 24 | return; 25 | } 26 | 27 | let uncompacted = 28 | CellIndex::uncompact(cells.iter().copied(), args.resolution) 29 | .collect::>(); 30 | 31 | // Check that every ouput cell is present in the input either as itself 32 | // or as an ancestor. 33 | let mut i = 0; 34 | for cell in uncompacted { 35 | loop { 36 | let candidate = cells[i]; 37 | 38 | if cell.parent(candidate.resolution()) == Some(candidate) { 39 | break; 40 | } 41 | i += 1; 42 | if i >= cells.len() { 43 | panic!("{cell} missing"); 44 | } 45 | } 46 | } 47 | }); 48 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/vertex_index.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use h3o::{LatLng, VertexIndex}; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | fuzz_target!(|index: VertexIndex| { 7 | assert_eq!( 8 | LatLng::from(index), 9 | vertex_to_latlng(index), 10 | "vertexToLatLng" 11 | ); 12 | }); 13 | 14 | // H3 wrappers {{{ 15 | 16 | fn vertex_to_latlng(index: VertexIndex) -> LatLng { 17 | let mut ll = h3ron_h3_sys::LatLng { lat: 0., lng: 0. }; 18 | unsafe { 19 | h3ron_h3_sys::vertexToLatLng(index.into(), &mut ll); 20 | } 21 | LatLng::from_radians(ll.lat, ll.lng).expect("coordinate") 22 | } 23 | 24 | // }}} 25 | -------------------------------------------------------------------------------- /src/boundary.rs: -------------------------------------------------------------------------------- 1 | use crate::LatLng; 2 | use core::{fmt, ops::Deref}; 3 | 4 | /// Maximum number of cell boundary vertices. 5 | /// 6 | /// Worst case is pentagon: 5 original verts + 5 edge crossings. 7 | const MAX_BNDRY_VERTS: usize = 10; 8 | 9 | /// Boundary in latitude/longitude. 10 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Default)] 11 | pub struct Boundary { 12 | /// Vertices in CCW order. 13 | points: [LatLng; MAX_BNDRY_VERTS], 14 | /// Number of vertices. 15 | count: u8, 16 | } 17 | 18 | impl Boundary { 19 | /// Initializes a new empty cell boundary (test only) 20 | #[must_use] 21 | #[doc(hidden)] 22 | pub fn new() -> Self { 23 | Self::default() 24 | } 25 | 26 | /// Add a vertices to the boundary (test only). 27 | #[doc(hidden)] 28 | pub fn push(&mut self, ll: LatLng) { 29 | self.points[usize::from(self.count)] = ll; 30 | self.count += 1; 31 | } 32 | } 33 | 34 | impl Deref for Boundary { 35 | type Target = [LatLng]; 36 | 37 | /// Dereference to the slice of filled elements. 38 | fn deref(&self) -> &Self::Target { 39 | &self.points[..self.count.into()] 40 | } 41 | } 42 | 43 | impl fmt::Display for Boundary { 44 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 45 | write!(f, "[",)?; 46 | for (i, ll) in self.iter().enumerate() { 47 | if i != 0 { 48 | write!(f, "-")?; 49 | } 50 | write!(f, "{ll}")?; 51 | } 52 | write!(f, "]",) 53 | } 54 | } 55 | 56 | #[cfg(feature = "geo")] 57 | impl From for geo::LineString { 58 | fn from(value: Boundary) -> Self { 59 | Self::new(value.iter().copied().map(geo::Coord::from).collect()) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/coord/cube.rs: -------------------------------------------------------------------------------- 1 | use super::CoordIJK; 2 | use crate::math::{abs, round}; 3 | 4 | /// Cube coordinates. 5 | /// 6 | /// Cube coordinates are more suitable than `IJK` for linear interpolation. 7 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 8 | pub struct CoordCube { 9 | /// `i` component. 10 | pub i: i32, 11 | /// `j` component. 12 | pub j: i32, 13 | /// `k` component. 14 | pub k: i32, 15 | } 16 | 17 | impl CoordCube { 18 | /// Initializes a new cube coordinate with the specified component values. 19 | pub const fn new(i: i32, j: i32, k: i32) -> Self { 20 | Self { i, j, k } 21 | } 22 | 23 | /// Translate the coordinates by the specified offset. 24 | /// 25 | /// Algorithm from 26 | pub fn translate(&self, offsets: (f64, f64, f64)) -> Self { 27 | let i = f64::from(self.i) + offsets.0; 28 | let j = f64::from(self.j) + offsets.1; 29 | let k = f64::from(self.k) + offsets.2; 30 | 31 | #[expect(clippy::cast_possible_truncation, reason = "on purpose")] 32 | let (mut ri, mut rj, mut rk) = 33 | { (round(i) as i32, round(j) as i32, round(k) as i32) }; 34 | 35 | let i_diff = abs(f64::from(ri) - i); 36 | let j_diff = abs(f64::from(rj) - j); 37 | let k_diff = abs(f64::from(rk) - k); 38 | 39 | // Round, maintaining valid cube coords. 40 | if i_diff > j_diff && i_diff > k_diff { 41 | ri = -rj - rk; 42 | } else if j_diff > k_diff { 43 | rj = -ri - rk; 44 | } else { 45 | rk = -ri - rj; 46 | } 47 | 48 | Self::new(ri, rj, rk) 49 | } 50 | } 51 | 52 | impl From for CoordIJK { 53 | fn from(value: CoordCube) -> Self { 54 | Self::new(-value.i, value.j, 0).normalize() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/coord/localij_tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn from_ijk_zero() { 5 | let ijk = CoordIJK::new(0, 0, 0); 6 | let ij = CoordIJ::from(&ijk); 7 | 8 | assert_eq!(ij.i, 0, "ij.i zero"); 9 | assert_eq!(ij.j, 0, "ij.j zero"); 10 | } 11 | -------------------------------------------------------------------------------- /src/coord/vec2d_tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use float_eq::assert_float_eq; 3 | 4 | #[test] 5 | fn magnitude() { 6 | let v = Vec2d::new(3.0, 4.0); 7 | let expected = 5.0; 8 | 9 | let result = v.magnitude(); 10 | 11 | assert_float_eq!(result, expected, abs <= f64::EPSILON); 12 | } 13 | 14 | #[test] 15 | fn intersection() { 16 | let line1 = (Vec2d::new(2.0, 2.0), Vec2d::new(6.0, 6.0)); 17 | let line2 = (Vec2d::new(0.0, 4.0), Vec2d::new(10.0, 4.0)); 18 | let expected = Vec2d::new(4.0, 4.0); 19 | 20 | let result = Vec2d::intersection(line1, line2); 21 | 22 | assert_float_eq!( 23 | result.x, 24 | expected.x, 25 | abs <= f64::EPSILON, 26 | "x as expected" 27 | ); 28 | assert_float_eq!( 29 | result.y, 30 | expected.y, 31 | abs <= f64::EPSILON, 32 | "y as expected" 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/coord/vec3d.rs: -------------------------------------------------------------------------------- 1 | use crate::math::mul_add; 2 | 3 | /// 3D floating-point vector. 4 | #[derive(Debug, Clone, Copy, PartialEq)] 5 | pub struct Vec3d { 6 | /// `x` component. 7 | pub x: f64, 8 | /// `y` component. 9 | pub y: f64, 10 | /// `z` component. 11 | pub z: f64, 12 | } 13 | 14 | impl Vec3d { 15 | /// Initializes a new 3D vector with the specified component values. 16 | pub const fn new(x: f64, y: f64, z: f64) -> Self { 17 | Self { x, y, z } 18 | } 19 | 20 | /// Computes the square of the distance between two 3D coordinates. 21 | pub fn distance(&self, other: &Self) -> f64 { 22 | let x_diff = self.x - other.x; 23 | let y_diff = self.y - other.y; 24 | let z_diff = self.z - other.z; 25 | 26 | mul_add(x_diff, x_diff, mul_add(y_diff, y_diff, z_diff * z_diff)) 27 | } 28 | } 29 | 30 | #[cfg(test)] 31 | #[path = "./vec3d_tests.rs"] 32 | mod tests; 33 | -------------------------------------------------------------------------------- /src/coord/vec3d_tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use float_eq::assert_float_eq; 3 | 4 | #[test] 5 | fn distance() { 6 | let v1 = Vec3d::new(0., 0., 0.); 7 | let v2 = Vec3d::new(1., 0., 0.); 8 | let v3 = Vec3d::new(0., 1., 1.); 9 | let v4 = Vec3d::new(1., 1., 1.); 10 | let v5 = Vec3d::new(1., 1., 2.); 11 | 12 | assert_float_eq!( 13 | v1.distance(&v1), 14 | 0., 15 | abs <= f64::EPSILON, 16 | "distance to self is 0" 17 | ); 18 | assert_float_eq!( 19 | v1.distance(&v2), 20 | 1., 21 | abs <= f64::EPSILON, 22 | "distance to <1,0,0> is 1" 23 | ); 24 | assert_float_eq!( 25 | v1.distance(&v3), 26 | 2., 27 | abs <= f64::EPSILON, 28 | "distance to <0,1,1> is 2" 29 | ); 30 | assert_float_eq!( 31 | v1.distance(&v4), 32 | 3., 33 | abs <= f64::EPSILON, 34 | "distance to <1,1,1> is 3" 35 | ); 36 | assert_float_eq!( 37 | v1.distance(&v5), 38 | 6., 39 | abs <= f64::EPSILON, 40 | "distance to <1,1,2> is 6" 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /src/error/compaction.rs: -------------------------------------------------------------------------------- 1 | use core::{error::Error, fmt}; 2 | 3 | /// Errors occurring while compacting a set of cell indices. 4 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 5 | #[non_exhaustive] 6 | pub enum CompactionError { 7 | /// Input contains indices of heterogeneous resolutions. 8 | HeterogeneousResolution, 9 | /// Input set contains duplicate indices. 10 | DuplicateInput, 11 | } 12 | 13 | impl fmt::Display for CompactionError { 14 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 15 | match *self { 16 | Self::HeterogeneousResolution => { 17 | write!(f, "heterogeneous resolution") 18 | } 19 | Self::DuplicateInput => write!(f, "duplicate indices"), 20 | } 21 | } 22 | } 23 | 24 | impl Error for CompactionError { 25 | fn source(&self) -> Option<&(dyn Error + 'static)> { 26 | None 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/error/hex_grid.rs: -------------------------------------------------------------------------------- 1 | use core::{error::Error, fmt}; 2 | 3 | /// Errors related to the `IJK` coordinate system and its variants (e.g. 4 | /// [`LocalIJ`](crate::LocalIJ)). 5 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 6 | pub struct HexGridError { 7 | reason: &'static str, 8 | } 9 | 10 | impl HexGridError { 11 | /// Initializes a new [`HexGridError`] with the given error message. 12 | pub(crate) const fn new(reason: &'static str) -> Self { 13 | Self { reason } 14 | } 15 | } 16 | 17 | impl fmt::Display for HexGridError { 18 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 19 | write!(f, "{}", self.reason) 20 | } 21 | } 22 | 23 | impl Error for HexGridError { 24 | fn source(&self) -> Option<&(dyn Error + 'static)> { 25 | None 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/error/invalid_value.rs: -------------------------------------------------------------------------------- 1 | use core::{error::Error, fmt}; 2 | 3 | // Macro to declare type-specific InvalidValue error type. 4 | macro_rules! invalid_value_error { 5 | ($name:literal, $error:ident, $value_type:ty) => { 6 | #[doc = concat!("Invalid ", $name, ".")] 7 | #[derive(Clone, Copy, Debug, PartialEq)] 8 | #[allow( 9 | clippy::allow_attributes, 10 | clippy::derive_partial_eq_without_eq, 11 | reason = "value type may not be `Eq` (e.g. f64)" 12 | )] 13 | pub struct $error { 14 | /// The invalid value. 15 | pub value: $value_type, 16 | /// The reason why it's invalid. 17 | pub reason: &'static str, 18 | } 19 | 20 | impl $error { 21 | pub(crate) const fn new( 22 | value: $value_type, 23 | reason: &'static str, 24 | ) -> Self { 25 | Self { value, reason } 26 | } 27 | } 28 | 29 | impl fmt::Display for $error { 30 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 31 | write!( 32 | f, 33 | "invalid {} (got {:?}): {}", 34 | $name, self.value, self.reason 35 | ) 36 | } 37 | } 38 | 39 | impl Error for $error { 40 | fn source(&self) -> Option<&(dyn Error + 'static)> { 41 | None 42 | } 43 | } 44 | }; 45 | } 46 | 47 | invalid_value_error!("resolution", InvalidResolution, Option); 48 | invalid_value_error!("cell index", InvalidCellIndex, Option); 49 | invalid_value_error!("vertex index", InvalidVertexIndex, Option); 50 | invalid_value_error!( 51 | "directed edge index", 52 | InvalidDirectedEdgeIndex, 53 | Option 54 | ); 55 | invalid_value_error!("latitude/longitude", InvalidLatLng, f64); 56 | invalid_value_error!("cell edge", InvalidEdge, u8); 57 | invalid_value_error!("cell vertex", InvalidVertex, u8); 58 | invalid_value_error!("icosahedron face", InvalidFace, u8); 59 | invalid_value_error!("base cell", InvalidBaseCell, u8); 60 | invalid_value_error!("direction", InvalidDirection, u8); 61 | -------------------------------------------------------------------------------- /src/error/localij.rs: -------------------------------------------------------------------------------- 1 | use super::HexGridError; 2 | use core::{error::Error, fmt}; 3 | 4 | /// Errors occurring during [`LocalIJ`](crate::LocalIJ) coordinate system 5 | /// conversions. 6 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 7 | #[non_exhaustive] 8 | pub enum LocalIjError { 9 | /// Local origin and cell index have incompatible resolutions. 10 | ResolutionMismatch, 11 | /// Pentagon distortion was encountered and could not be handled. 12 | Pentagon, 13 | /// Error related to the `IJK` coordinate system. 14 | HexGrid(HexGridError), 15 | } 16 | 17 | impl fmt::Display for LocalIjError { 18 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 19 | match *self { 20 | Self::ResolutionMismatch => { 21 | write!(f, "resolution mismatch") 22 | } 23 | Self::Pentagon => write!(f, "pentagon distortion"), 24 | Self::HexGrid(err) => write!(f, "hex grid error: {err}"), 25 | } 26 | } 27 | } 28 | 29 | impl Error for LocalIjError { 30 | fn source(&self) -> Option<&(dyn Error + 'static)> { 31 | match *self { 32 | Self::ResolutionMismatch | Self::Pentagon => None, 33 | Self::HexGrid(ref err) => Some(err), 34 | } 35 | } 36 | } 37 | 38 | impl From for LocalIjError { 39 | fn from(value: HexGridError) -> Self { 40 | Self::HexGrid(value) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/error/mod.rs: -------------------------------------------------------------------------------- 1 | //! H3O error types. 2 | 3 | mod compaction; 4 | mod hex_grid; 5 | mod invalid_value; 6 | mod localij; 7 | mod resolution_mismatch; 8 | 9 | #[cfg(feature = "geo")] 10 | mod geom; 11 | 12 | #[cfg(test)] 13 | mod tests; 14 | 15 | pub use compaction::CompactionError; 16 | pub use hex_grid::HexGridError; 17 | pub use invalid_value::{ 18 | InvalidBaseCell, InvalidCellIndex, InvalidDirectedEdgeIndex, 19 | InvalidDirection, InvalidEdge, InvalidFace, InvalidLatLng, 20 | InvalidResolution, InvalidVertex, InvalidVertexIndex, 21 | }; 22 | pub use localij::LocalIjError; 23 | pub use resolution_mismatch::ResolutionMismatch; 24 | 25 | #[cfg(feature = "geo")] 26 | pub use geom::{DissolutionError, InvalidGeometry, PlotterError}; 27 | -------------------------------------------------------------------------------- /src/error/resolution_mismatch.rs: -------------------------------------------------------------------------------- 1 | use core::{error::Error, fmt}; 2 | 3 | /// Resolution mismatch between two cell indexes. 4 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 5 | pub struct ResolutionMismatch; 6 | 7 | impl fmt::Display for ResolutionMismatch { 8 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 9 | write!(f, "resolution mismatch") 10 | } 11 | } 12 | 13 | impl Error for ResolutionMismatch { 14 | fn source(&self) -> Option<&(dyn Error + 'static)> { 15 | None 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/geom/mod.rs: -------------------------------------------------------------------------------- 1 | //! Bridge between H3 entities and geometrical shapes. 2 | 3 | mod plotter; 4 | mod ring_hierarchy; 5 | mod solvent; 6 | mod tiler; 7 | mod vertex_graph; 8 | 9 | use ring_hierarchy::RingHierarchy; 10 | use vertex_graph::VertexGraph; 11 | 12 | pub use plotter::{Plotter, PlotterBuilder}; 13 | pub use solvent::{Solvent, SolventBuilder}; 14 | pub use tiler::{ContainmentMode, Tiler, TilerBuilder}; 15 | 16 | // Check that the coordinate are finite and in a legit range. 17 | fn coord_is_valid(coord: geo::Coord) -> bool { 18 | use crate::TWO_PI; 19 | use std::f64::consts::PI; 20 | 21 | coord.x.is_finite() 22 | && coord.y.is_finite() 23 | && coord.x >= -TWO_PI 24 | && coord.x <= TWO_PI 25 | && coord.y >= -PI 26 | && coord.y <= PI 27 | } 28 | 29 | // Return the immediate neighbors, no memory allocations. 30 | fn neighbors(cell: crate::CellIndex, scratchpad: &mut [u64]) -> usize { 31 | let mut count = 0; 32 | 33 | // Don't use `grid_disk` to avoid the allocation, 34 | // use the pre-allocated scratchpad memory instead. 35 | for candidate in cell.grid_disk_fast(1) { 36 | if let Some(neighbor) = candidate { 37 | scratchpad[count] = neighbor.into(); 38 | count += 1; 39 | } else { 40 | count = 0; 41 | break; 42 | } 43 | } 44 | 45 | // Unsafe version failed, fallback on the safe version. 46 | if count == 0 { 47 | for candidate in cell.grid_disk_safe(1) { 48 | scratchpad[count] = candidate.into(); 49 | count += 1; 50 | } 51 | } 52 | 53 | count 54 | } 55 | -------------------------------------------------------------------------------- /src/grid/iterator_tests.rs: -------------------------------------------------------------------------------- 1 | use core::convert::TryFrom; 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn stop_after_none() { 7 | let origin = CellIndex::try_from(0x8908000001bffff).expect("origin"); 8 | let iter = DiskDistancesUnsafe::new(origin, 1); 9 | 10 | assert_eq!(iter.count(), 5); 11 | } 12 | -------------------------------------------------------------------------------- /src/grid/mod.rs: -------------------------------------------------------------------------------- 1 | mod algo; 2 | mod iterator; 3 | 4 | pub use algo::{direction_for_neighbor, neighbor_rotations}; 5 | pub use iterator::{DiskDistancesSafe, DiskDistancesUnsafe, RingUnsafe}; 6 | -------------------------------------------------------------------------------- /src/index/bits_tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::{CellIndex, Resolution}; 3 | 4 | #[test] 5 | fn first_axe_none() { 6 | let index = 7 | CellIndex::try_from(0x8f2800000000000).expect("valid cell index"); 8 | let result = first_axe(index.into()); 9 | assert_eq!(result, None, "zero til the end"); 10 | 11 | let index = 12 | CellIndex::try_from(0x8029fffffffffff).expect("valid cell index"); 13 | let result = first_axe(index.into()); 14 | assert_eq!(result, None, "no cell"); 15 | 16 | let index = 17 | CellIndex::try_from(0x832800fffffffff).expect("valid cell index"); 18 | let result = first_axe(index.into()); 19 | assert_eq!(result, None, "some zero then unused"); 20 | } 21 | 22 | #[test] 23 | fn first_axe_some() { 24 | let resolutions = Resolution::range(Resolution::One, Resolution::Fifteen); 25 | let indexes = [ 26 | 0x8f287478ab9c2ab, // 20-1-6-4-3-6-1-2-5-6-3-4-1-2-5-3 27 | 0x8f283478ab9c2ab, // 20-0-6-4-3-6-1-2-5-6-3-4-1-2-5-3 28 | 0x8f280478ab9c2ab, // 20-0-0-4-3-6-1-2-5-6-3-4-1-2-5-3 29 | 0x8f280078ab9c2ab, // 20-0-0-0-3-6-1-2-5-6-3-4-1-2-5-3 30 | 0x8f280018ab9c2ab, // 20-0-0-0-0-6-1-2-5-6-3-4-1-2-5-3 31 | 0x8f280000ab9c2ab, // 20-0-0-0-0-0-1-2-5-6-3-4-1-2-5-3 32 | 0x8f2800002b9c2ab, // 20-0-0-0-0-0-0-2-5-6-3-4-1-2-5-3 33 | 0x8f2800000b9c2ab, // 20-0-0-0-0-0-0-0-5-6-3-4-1-2-5-3 34 | 0x8f280000019c2ab, // 20-0-0-0-0-0-0-0-0-6-3-4-1-2-5-3 35 | 0x8f280000001c2ab, // 20-0-0-0-0-0-0-0-0-0-3-4-1-2-5-3 36 | 0x8f28000000042ab, // 20-0-0-0-0-0-0-0-0-0-0-4-1-2-5-3 37 | 0x8f28000000002ab, // 20-0-0-0-0-0-0-0-0-0-0-0-1-2-5-3 38 | 0x8f28000000000ab, // 20-0-0-0-0-0-0-0-0-0-0-0-0-2-5-3 39 | 0x8f280000000002b, // 20-0-0-0-0-0-0-0-0-0-0-0-0-0-5-3 40 | 0x8f2800000000003, // 20-0-0-0-0-0-0-0-0-0-0-0-0-0-0-3 41 | ]; 42 | 43 | for (resolution, value) in resolutions.zip(indexes.into_iter()) { 44 | let index = CellIndex::try_from(value).expect("valid cells"); 45 | let expected = index.direction_at(resolution).and_then(Direction::axe); 46 | 47 | let result = first_axe(index.into()); 48 | 49 | assert_eq!(result, expected, "resolution {resolution:?}"); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/index/mod.rs: -------------------------------------------------------------------------------- 1 | //! H3 index types 2 | 3 | pub mod bits; 4 | mod cell; 5 | mod edge; 6 | mod iterator; 7 | mod mode; 8 | mod triangle; 9 | mod vertex; 10 | 11 | pub use cell::CellIndex; 12 | pub use edge::{DirectedEdgeIndex, Edge}; 13 | pub use mode::IndexMode; 14 | pub use vertex::{Vertex, VertexIndex}; 15 | 16 | pub use iterator::{Children, GridPathCells}; 17 | use triangle::Triangle; 18 | -------------------------------------------------------------------------------- /src/index/mode.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | /// H3 index modes. 4 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 5 | #[repr(u8)] 6 | #[non_exhaustive] 7 | #[cfg_attr( 8 | feature = "serde", 9 | derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr) 10 | )] 11 | pub enum IndexMode { 12 | /// An H3 Cell (Hexagon/Pentagon) index. 13 | Cell = 1, 14 | /// An H3 directed edge (Cell A -> Cell B) index. 15 | DirectedEdge = 2, 16 | /// An H3 undirected edge (Cell A <-> Cell B) index. 17 | UndirectedEdge = 3, 18 | /// An H3 Vertex (i.e. a single vertex of an H3 Cell). 19 | Vertex = 4, 20 | } 21 | 22 | impl From for u8 { 23 | fn from(value: IndexMode) -> Self { 24 | value as Self 25 | } 26 | } 27 | 28 | impl fmt::Display for IndexMode { 29 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 30 | write!( 31 | f, 32 | "{}", 33 | match *self { 34 | Self::Cell => "Cell", 35 | Self::DirectedEdge => "DirectedEdge", 36 | Self::UndirectedEdge => "UndirectedEdge", 37 | Self::Vertex => "Vertex", 38 | } 39 | ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/index/triangle.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | math::{atan, sqrt, tan}, 3 | LatLng, 4 | }; 5 | 6 | /// A triangle on unit sphere. 7 | pub struct Triangle { 8 | /// The first vertex coordinate. 9 | a: LatLng, 10 | /// The second vertex coordinate. 11 | b: LatLng, 12 | /// The third vertex coordinate. 13 | c: LatLng, 14 | } 15 | 16 | impl Triangle { 17 | /// Returns a triangle `ABC`. 18 | pub const fn new(a: LatLng, b: LatLng, c: LatLng) -> Self { 19 | Self { a, b, c } 20 | } 21 | 22 | /// Computes the area on unit sphere, in radians². 23 | pub fn area(&self) -> f64 { 24 | area_from_edges( 25 | self.a.distance_rads(self.b), 26 | self.b.distance_rads(self.c), 27 | self.c.distance_rads(self.a), 28 | ) 29 | } 30 | } 31 | 32 | /// Computes the area on unit sphere, in radians², from its edges. 33 | /// 34 | /// For the math, see [here](https://en.wikipedia.org/wiki/Spherical_trigonometry#Area_and_spherical_excess) 35 | /// 36 | /// # Arguments 37 | /// 38 | /// * `a` - length of triangle side `A`, in radians 39 | /// * `b` - length of triangle side `B`, in radians 40 | /// * `c` - length of triangle side `C`, in radians 41 | fn area_from_edges(mut a: f64, mut b: f64, mut c: f64) -> f64 { 42 | let mut s = (a + b + c) / 2.0; 43 | 44 | a = (s - a) / 2.; 45 | b = (s - b) / 2.; 46 | c = (s - c) / 2.; 47 | s /= 2.; 48 | 49 | 4. * atan(sqrt(tan(s) * tan(a) * tan(b) * tan(c))) 50 | } 51 | -------------------------------------------------------------------------------- /src/math-libm.rs: -------------------------------------------------------------------------------- 1 | #[inline] 2 | pub fn abs(x: f64) -> f64 { 3 | libm::fabs(x) 4 | } 5 | 6 | #[inline] 7 | pub fn sin(x: f64) -> f64 { 8 | libm::sin(x) 9 | } 10 | 11 | #[inline] 12 | pub fn cos(x: f64) -> f64 { 13 | libm::cos(x) 14 | } 15 | 16 | #[inline] 17 | pub fn tan(x: f64) -> f64 { 18 | libm::tan(x) 19 | } 20 | 21 | #[inline] 22 | pub fn asin(x: f64) -> f64 { 23 | libm::asin(x) 24 | } 25 | 26 | #[inline] 27 | pub fn acos(x: f64) -> f64 { 28 | libm::acos(x) 29 | } 30 | 31 | #[inline] 32 | pub fn atan(x: f64) -> f64 { 33 | libm::atan(x) 34 | } 35 | 36 | #[inline] 37 | pub fn atan2(y: f64, x: f64) -> f64 { 38 | libm::atan2(y, x) 39 | } 40 | 41 | #[inline] 42 | pub fn hypot(x: f64, y: f64) -> f64 { 43 | libm::hypot(x, y) 44 | } 45 | 46 | #[inline] 47 | pub fn sqrt(x: f64) -> f64 { 48 | libm::sqrt(x) 49 | } 50 | 51 | #[inline] 52 | pub fn round(x: f64) -> f64 { 53 | libm::round(x) 54 | } 55 | 56 | #[inline] 57 | pub fn mul_add(a: f64, b: f64, c: f64) -> f64 { 58 | (a * b) + c 59 | } 60 | -------------------------------------------------------------------------------- /src/math-std.rs: -------------------------------------------------------------------------------- 1 | #[inline] 2 | pub const fn abs(x: f64) -> f64 { 3 | x.abs() 4 | } 5 | 6 | #[inline] 7 | pub fn sin(x: f64) -> f64 { 8 | x.sin() 9 | } 10 | 11 | #[inline] 12 | pub fn cos(x: f64) -> f64 { 13 | x.cos() 14 | } 15 | 16 | #[inline] 17 | pub fn tan(x: f64) -> f64 { 18 | x.tan() 19 | } 20 | 21 | #[inline] 22 | pub fn asin(x: f64) -> f64 { 23 | x.asin() 24 | } 25 | 26 | #[inline] 27 | pub fn acos(x: f64) -> f64 { 28 | x.acos() 29 | } 30 | 31 | #[inline] 32 | pub fn atan(x: f64) -> f64 { 33 | x.atan() 34 | } 35 | 36 | #[inline] 37 | pub fn atan2(y: f64, x: f64) -> f64 { 38 | y.atan2(x) 39 | } 40 | 41 | #[inline] 42 | pub fn hypot(x: f64, y: f64) -> f64 { 43 | x.hypot(y) 44 | } 45 | 46 | #[inline] 47 | pub fn sqrt(x: f64) -> f64 { 48 | x.sqrt() 49 | } 50 | 51 | #[inline] 52 | pub fn round(x: f64) -> f64 { 53 | x.round() 54 | } 55 | 56 | #[inline] 57 | pub fn mul_add(a: f64, b: f64, c: f64) -> f64 { 58 | a.mul_add(b, c) 59 | } 60 | -------------------------------------------------------------------------------- /tests/api/avg_edge_len.rs: -------------------------------------------------------------------------------- 1 | use h3o::Resolution; 2 | use std::{ 3 | fs::File, 4 | io::{BufRead, BufReader}, 5 | path::PathBuf, 6 | }; 7 | 8 | #[test] 9 | fn golden_file() { 10 | // Load the file generated by tools/average_edge_length 11 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 12 | path.push("dataset/avgEdgeLen.txt"); 13 | 14 | let file = File::open(path).expect("open test dataset"); 15 | let reader = BufReader::new(file); 16 | let resolutions = Resolution::range(Resolution::Zero, Resolution::Fifteen); 17 | 18 | for (resolution, line) in resolutions.zip(reader.lines()) { 19 | let line = line.expect("test input"); 20 | let parts = line.split(',').collect::>(); 21 | let avg_rad = parts[0].parse::().expect("edge len in radians"); 22 | let avg_km = parts[1].parse::().expect("edge len in kilometers"); 23 | let avg_m = parts[2].parse::().expect("edge len in meters"); 24 | 25 | assert_eq!( 26 | resolution.edge_length_rads(), 27 | avg_rad, 28 | "avg edge len rad at {resolution}" 29 | ); 30 | assert_eq!( 31 | resolution.edge_length_km(), 32 | avg_km, 33 | "avg edge len km at {resolution}" 34 | ); 35 | assert_eq!( 36 | resolution.edge_length_m(), 37 | avg_m, 38 | "avg edge len m at {resolution}" 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/api/base_cell.rs: -------------------------------------------------------------------------------- 1 | use h3o::BaseCell; 2 | 3 | #[test] 4 | fn is_pentagon() { 5 | let cell = BaseCell::try_from(4).expect("pentagonal cell"); 6 | assert!(cell.is_pentagon(), "pentagon"); 7 | 8 | let cell = BaseCell::try_from(8).expect("hexagonal cell"); 9 | assert!(!cell.is_pentagon(), "hexagon"); 10 | } 11 | 12 | #[test] 13 | fn count() { 14 | assert_eq!(BaseCell::count(), 122); 15 | } 16 | 17 | #[test] 18 | fn try_from_u8() { 19 | assert!(BaseCell::try_from(0).is_ok(), "lower bound"); 20 | assert!(BaseCell::try_from(42).is_ok(), "valid value"); 21 | assert!(BaseCell::try_from(121).is_ok(), "upper bound"); 22 | 23 | assert!(BaseCell::try_from(122).is_err(), "out of range"); 24 | } 25 | 26 | #[test] 27 | fn into_u8() { 28 | let cell = BaseCell::try_from(0).expect("base cell"); 29 | assert_eq!(u8::from(cell), 0, "lower bound"); 30 | 31 | let cell = BaseCell::try_from(42).expect("base cell"); 32 | assert_eq!(u8::from(cell), 42, "valid value"); 33 | 34 | let cell = BaseCell::try_from(121).expect("base cell"); 35 | assert_eq!(u8::from(cell), 121, "upper bound"); 36 | } 37 | 38 | #[test] 39 | fn display() { 40 | let result = BaseCell::try_from(33).expect("base cell").to_string(); 41 | let expected = "33".to_owned(); 42 | 43 | assert_eq!(result, expected); 44 | } 45 | -------------------------------------------------------------------------------- /tests/api/boundary.rs: -------------------------------------------------------------------------------- 1 | use h3o::DirectedEdgeIndex; 2 | 3 | #[test] 4 | fn display() { 5 | let index = DirectedEdgeIndex::try_from(0x13a194e699ab7fff).expect("edge"); 6 | let result = index.boundary().to_string(); 7 | let expected = 8 | "[(51.5333297603, 0.0043462775)-(51.5328604873, 0.0051280949)]" 9 | .to_owned(); 10 | 11 | assert_eq!(result, expected); 12 | } 13 | -------------------------------------------------------------------------------- /tests/api/directed_edge_index.rs: -------------------------------------------------------------------------------- 1 | use h3o::DirectedEdgeIndex; 2 | 3 | #[test] 4 | fn try_from_str() { 5 | let result = "13a194e699ab7fff".parse::(); 6 | let expected = DirectedEdgeIndex::try_from(0x13a194e699ab7fff); 7 | assert_eq!(result, expected, "valid string"); 8 | 9 | let result = "no bueno".parse::(); 10 | assert!(result.is_err(), "invalid string"); 11 | } 12 | 13 | // Resolutions are displayed as numerical value. 14 | #[test] 15 | fn display() { 16 | let index = DirectedEdgeIndex::try_from(0x13a194e699ab7fff).expect("index"); 17 | 18 | // Default display is the lower hex one. 19 | let result = index.to_string(); 20 | let expected = "13a194e699ab7fff".to_owned(); 21 | assert_eq!(result, expected, "default display"); 22 | 23 | // Upper hex. 24 | let result = format!("{index:X}"); 25 | let expected = "13A194E699AB7FFF".to_owned(); 26 | assert_eq!(result, expected, "upper hex"); 27 | 28 | // Octal. 29 | let result = format!("{index:o}"); 30 | let expected = "116414516323152677777".to_owned(); 31 | assert_eq!(result, expected, "octal"); 32 | 33 | // Binary. 34 | let result = format!("{index:b}"); 35 | let expected = 36 | "1001110100001100101001110011010011001101010110111111111111111" 37 | .to_owned(); 38 | assert_eq!(result, expected, "binary"); 39 | } 40 | -------------------------------------------------------------------------------- /tests/api/direction.rs: -------------------------------------------------------------------------------- 1 | use h3o::Direction; 2 | 3 | #[test] 4 | fn try_from_u8() { 5 | assert!(Direction::try_from(0).is_ok(), "lower bound"); 6 | assert!(Direction::try_from(3).is_ok(), "valid value"); 7 | assert!(Direction::try_from(6).is_ok(), "upper bound"); 8 | 9 | assert!(Direction::try_from(7).is_err(), "out of range"); 10 | } 11 | 12 | #[test] 13 | fn into_u8() { 14 | assert_eq!(u8::from(Direction::Center), 0, "lower bound"); 15 | assert_eq!(u8::from(Direction::I), 4, "valid value"); 16 | assert_eq!(u8::from(Direction::IJ), 6, "upper bound"); 17 | } 18 | 19 | #[test] 20 | fn into_u64() { 21 | assert_eq!(u64::from(Direction::Center), 0, "lower bound"); 22 | assert_eq!(u64::from(Direction::I), 4, "valid value"); 23 | assert_eq!(u64::from(Direction::IJ), 6, "upper bound"); 24 | } 25 | 26 | // Directions are displayed as numerical value. 27 | #[test] 28 | fn display() { 29 | let result = Direction::J.to_string(); 30 | let expected = "2".to_owned(); 31 | 32 | assert_eq!(result, expected); 33 | } 34 | -------------------------------------------------------------------------------- /tests/api/edge.rs: -------------------------------------------------------------------------------- 1 | use h3o::Edge; 2 | 3 | #[test] 4 | fn try_from_u8() { 5 | assert!(Edge::try_from(1).is_ok(), "lower bound"); 6 | assert!(Edge::try_from(3).is_ok(), "valid value"); 7 | assert!(Edge::try_from(6).is_ok(), "upper bound"); 8 | 9 | assert!(Edge::try_from(0).is_err(), "out of range, low"); 10 | assert!(Edge::try_from(7).is_err(), "out of range, high"); 11 | } 12 | 13 | #[test] 14 | fn into_u8() { 15 | let edge = Edge::try_from(1).expect("edge"); 16 | assert_eq!(u8::from(edge), 1, "lower bound"); 17 | 18 | let edge = Edge::try_from(3).expect("edge"); 19 | assert_eq!(u8::from(edge), 3, "valid value"); 20 | 21 | let edge = Edge::try_from(6).expect("edge"); 22 | assert_eq!(u8::from(edge), 6, "upper bound"); 23 | } 24 | 25 | #[test] 26 | fn display() { 27 | let result = Edge::try_from(3).expect("edge").to_string(); 28 | let expected = "3".to_owned(); 29 | 30 | assert_eq!(result, expected); 31 | } 32 | -------------------------------------------------------------------------------- /tests/api/face.rs: -------------------------------------------------------------------------------- 1 | use h3o::Face; 2 | 3 | #[test] 4 | fn try_from_u8() { 5 | assert!(Face::try_from(0).is_ok(), "lower bound"); 6 | assert!(Face::try_from(11).is_ok(), "valid value"); 7 | assert!(Face::try_from(19).is_ok(), "upper bound"); 8 | 9 | assert!(Face::try_from(20).is_err(), "out of range"); 10 | } 11 | 12 | // Faces are displayed as numerical value. 13 | #[test] 14 | fn display() { 15 | let result = Face::try_from(2).expect("face").to_string(); 16 | let expected = "2".to_owned(); 17 | 18 | assert_eq!(result, expected); 19 | } 20 | -------------------------------------------------------------------------------- /tests/api/face_set.rs: -------------------------------------------------------------------------------- 1 | use h3o::{CellIndex, Face}; 2 | 3 | #[test] 4 | fn len() { 5 | let index = CellIndex::try_from(0x89283470803ffff).expect("cell"); 6 | let faces = index.icosahedron_faces(); 7 | 8 | assert_eq!(faces.len(), 1); 9 | } 10 | 11 | #[test] 12 | fn is_empty() { 13 | let index = CellIndex::try_from(0x89283470803ffff).expect("cell"); 14 | let faces = index.icosahedron_faces(); 15 | 16 | assert!(!faces.is_empty()); 17 | } 18 | 19 | #[test] 20 | fn contains() { 21 | let index = CellIndex::try_from(0x89283470803ffff).expect("cell"); 22 | let faces = index.icosahedron_faces(); 23 | 24 | assert!(faces.contains(Face::try_from(7).expect("face"))); 25 | assert!(!faces.contains(Face::try_from(2).expect("face"))); 26 | } 27 | 28 | #[test] 29 | fn display() { 30 | let index = CellIndex::try_from(0x8a1c00000007fff).expect("cell"); 31 | let faces = index.icosahedron_faces(); 32 | 33 | assert_eq!(faces.to_string(), "[1-2-6-7-11]".to_owned()); 34 | } 35 | -------------------------------------------------------------------------------- /tests/api/geom/mod.rs: -------------------------------------------------------------------------------- 1 | mod plotter; 2 | mod solvent; 3 | mod tiler; 4 | mod to_geo; 5 | mod utils; 6 | -------------------------------------------------------------------------------- /tests/api/geom/plotter.rs: -------------------------------------------------------------------------------- 1 | use geo::{coord, line_string, Line}; 2 | use h3o::{geom::PlotterBuilder, Resolution}; 3 | 4 | fn line_rads() -> Line { 5 | Line::new( 6 | coord! { x: -0.009526982062241713, y: 0.8285232894553574 }, 7 | coord! { x: 0.04142734140306332, y: 0.8525145186317127 }, 8 | ) 9 | } 10 | 11 | fn line_degs() -> Line { 12 | Line::new( 13 | coord! { x: -0.5458558636632915, y: 47.47088771408784 }, 14 | coord! { x: 2.373611818843102, y: 48.84548389122412 }, 15 | ) 16 | } 17 | 18 | #[test] 19 | fn add_rads() { 20 | let mut plotter = PlotterBuilder::new(Resolution::Two) 21 | .disable_radians_conversion() 22 | .build(); 23 | let result = plotter.add(line_rads()); 24 | 25 | assert!(result.is_ok()); 26 | } 27 | 28 | #[test] 29 | fn add_degs() { 30 | let mut plotter = PlotterBuilder::new(Resolution::Two).build(); 31 | let result = plotter.add(line_degs()); 32 | 33 | assert!(result.is_ok()); 34 | } 35 | 36 | #[test] 37 | fn add_batch() { 38 | let mut plotter = PlotterBuilder::new(Resolution::Two).build(); 39 | let result = plotter.add_batch( 40 | line_string![ 41 | (x: 2.363503198417334, y: 48.8203086545891), 42 | (x: 2.3730684893043588, y: 48.85398407690437), 43 | (x: 2.334964762310932, y: 48.870861968772914), 44 | ] 45 | .lines(), 46 | ); 47 | 48 | assert!(result.is_ok()); 49 | } 50 | 51 | #[test] 52 | fn invalid() { 53 | let mut plotter = PlotterBuilder::new(Resolution::Two).build(); 54 | let result = plotter.add(Line::new( 55 | coord! { x: 0., y: 0. }, 56 | coord! { x: f64::NAN, y: 0. }, 57 | )); 58 | 59 | assert!(result.is_err()); 60 | } 61 | 62 | #[test] 63 | fn plot() { 64 | let mut plotter = PlotterBuilder::new(Resolution::Ten).build(); 65 | plotter.add(line_degs()).expect("failed to add line"); 66 | 67 | let result = plotter 68 | .plot() 69 | .collect::, _>>() 70 | .expect("failed to plot") 71 | .len(); 72 | 73 | assert_eq!(result, 2423); 74 | } 75 | -------------------------------------------------------------------------------- /tests/api/geom/to_geo.rs: -------------------------------------------------------------------------------- 1 | use approx::assert_relative_eq; 2 | use geo::{coord, polygon, Line, Point, Polygon}; 3 | use h3o::{CellIndex, DirectedEdgeIndex, VertexIndex}; 4 | 5 | #[test] 6 | fn from_cell() { 7 | let index = CellIndex::try_from(0x89283470803ffff).expect("index"); 8 | let result = Polygon::from(index); 9 | let expected = polygon![ 10 | (x: -122.02648011977477, y: 37.38558967035685), 11 | (x: -122.02540378194031, y: 37.38727461225182), 12 | (x: -122.02665619162275, y: 37.38879129032762), 13 | (x: -122.02898493935817, y: 37.38862300294707), 14 | (x: -122.03006120911812, y: 37.38693806029814), 15 | (x: -122.02880879921976, y: 37.38542140578344), 16 | ]; 17 | 18 | assert_relative_eq!(result, expected, epsilon = 1e-6); 19 | } 20 | 21 | #[test] 22 | fn from_directed_edge() { 23 | let index = 24 | DirectedEdgeIndex::try_from(0x13a1_94e6_99ab_7fff).expect("index"); 25 | let result = Line::from(index); 26 | let expected = Line::new( 27 | coord!(x: 0.004346277485193205, y: 51.5333297602599), 28 | coord!(x: 0.005128094944356792, y: 51.53286048728922), 29 | ); 30 | 31 | assert_relative_eq!(result, expected, epsilon = 1e-6); 32 | } 33 | 34 | #[test] 35 | fn from_vertex() { 36 | let index = VertexIndex::try_from(0x2302_bfff_ffff_ffff).expect("index"); 37 | let result = Point::from(index); 38 | let expected = Point::new(-74.64046816708004, 30.219492199828117); 39 | 40 | assert_relative_eq!(result, expected, epsilon = 1e-6); 41 | } 42 | -------------------------------------------------------------------------------- /tests/api/geom/utils.rs: -------------------------------------------------------------------------------- 1 | use geo::{Geometry, Polygon}; 2 | use std::{fs::File, io::BufReader, path::PathBuf}; 3 | 4 | pub fn load_polygon(name: &str) -> Polygon { 5 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 6 | let filepath = format!("dataset/shapes/{name}.geojson"); 7 | path.push(filepath); 8 | 9 | let file = File::open(path).expect("open test dataset"); 10 | let reader = BufReader::new(file); 11 | 12 | let geojson = geojson::GeoJson::from_reader(reader).expect("GeoJSON"); 13 | let geometry = Geometry::try_from(geojson).expect("geometry"); 14 | 15 | Polygon::try_from(geometry).expect("polygon") 16 | } 17 | -------------------------------------------------------------------------------- /tests/api/index_mode.rs: -------------------------------------------------------------------------------- 1 | use h3o::IndexMode; 2 | 3 | #[test] 4 | fn into_u8() { 5 | let result = u8::from(IndexMode::Cell); 6 | let expected = 1; 7 | assert_eq!(result, expected); 8 | 9 | let result = u8::from(IndexMode::DirectedEdge); 10 | let expected = 2; 11 | assert_eq!(result, expected); 12 | 13 | let result = u8::from(IndexMode::UndirectedEdge); 14 | let expected = 3; 15 | assert_eq!(result, expected); 16 | 17 | let result = u8::from(IndexMode::Vertex); 18 | let expected = 4; 19 | assert_eq!(result, expected); 20 | } 21 | 22 | #[test] 23 | fn display() { 24 | let result = IndexMode::Cell.to_string(); 25 | let expected = "Cell".to_owned(); 26 | assert_eq!(result, expected); 27 | 28 | let result = IndexMode::DirectedEdge.to_string(); 29 | let expected = "DirectedEdge".to_owned(); 30 | assert_eq!(result, expected); 31 | 32 | let result = IndexMode::UndirectedEdge.to_string(); 33 | let expected = "UndirectedEdge".to_owned(); 34 | assert_eq!(result, expected); 35 | 36 | let result = IndexMode::Vertex.to_string(); 37 | let expected = "Vertex".to_owned(); 38 | assert_eq!(result, expected); 39 | } 40 | -------------------------------------------------------------------------------- /tests/api/localij.rs: -------------------------------------------------------------------------------- 1 | use h3o::{CellIndex, CoordIJ, LocalIJ}; 2 | 3 | #[test] 4 | fn display() { 5 | let anchor = CellIndex::try_from(0x8508282bfffffff).expect("cell"); 6 | let local_ij = LocalIJ::new(anchor, CoordIJ::new(-4, -3)); 7 | let expected = "8508282bfffffff (-4, -3)".to_owned(); 8 | 9 | assert_eq!(local_ij.to_string(), expected); 10 | } 11 | 12 | #[test] 13 | fn to_cell_overflow() { 14 | let origin_res2 = CellIndex::try_from(0x820407fffffffff).expect("cell"); 15 | 16 | let ij = CoordIJ::new(553648127, -2145378272); 17 | assert!(CellIndex::try_from(LocalIJ::new(origin_res2, ij)).is_err()); 18 | 19 | let ij = CoordIJ::new(i32::MAX - 10, -11); 20 | assert!(CellIndex::try_from(LocalIJ::new(origin_res2, ij)).is_err()); 21 | 22 | let origin_res3 = CellIndex::try_from(0x830400fffffffff).expect("cell"); 23 | let ij = CoordIJ::new(553648127, -2145378272); 24 | assert!(CellIndex::try_from(LocalIJ::new(origin_res3, ij)).is_err()); 25 | 26 | let ij = CoordIJ::new(i32::MAX - 10, -10); 27 | assert!(CellIndex::try_from(LocalIJ::new(origin_res3, ij)).is_err()); 28 | 29 | let ij = CoordIJ::new(i32::MAX - 10, -9); 30 | assert!(CellIndex::try_from(LocalIJ::new(origin_res3, ij)).is_err()); 31 | } 32 | -------------------------------------------------------------------------------- /tests/api/mod.rs: -------------------------------------------------------------------------------- 1 | mod avg_edge_len; 2 | mod base_cell; 3 | mod boundary; 4 | mod cell_index; 5 | mod directed_edge_index; 6 | mod direction; 7 | mod edge; 8 | mod face; 9 | mod face_set; 10 | #[cfg(feature = "geo")] 11 | mod geom; 12 | mod index_mode; 13 | mod latlng; 14 | mod localij; 15 | mod resolution; 16 | mod vertex; 17 | mod vertex_index; 18 | 19 | #[test] 20 | fn max_grid_disk_size_overflow() { 21 | assert_eq!(h3o::max_grid_disk_size(4294967295), 569_707_381_193_162); 22 | } 23 | -------------------------------------------------------------------------------- /tests/api/resolution.rs: -------------------------------------------------------------------------------- 1 | use h3o::Resolution; 2 | 3 | #[test] 4 | fn try_from_u8() { 5 | assert!(Resolution::try_from(0).is_ok(), "lower bound"); 6 | assert!(Resolution::try_from(11).is_ok(), "valid value"); 7 | assert!(Resolution::try_from(15).is_ok(), "upper bound"); 8 | 9 | assert!(Resolution::try_from(16).is_err(), "out of range"); 10 | } 11 | 12 | #[test] 13 | fn try_from_str() { 14 | assert!("0".parse::().is_ok(), "lower bound"); 15 | assert!("11".parse::().is_ok(), "valid value"); 16 | assert!("15".parse::().is_ok(), "upper bound"); 17 | 18 | assert!("One".parse::().is_err(), "invalid"); 19 | assert!("16".parse::().is_err(), "out of range"); 20 | } 21 | 22 | #[test] 23 | fn into_u8() { 24 | assert_eq!(u8::from(Resolution::Zero), 0, "lower bound"); 25 | assert_eq!(u8::from(Resolution::Eleven), 11, "valid value"); 26 | assert_eq!(u8::from(Resolution::Fifteen), 15, "upper bound"); 27 | } 28 | 29 | // Resolutions are displayed as numerical value. 30 | #[test] 31 | fn display() { 32 | let result = Resolution::Eleven.to_string(); 33 | let expected = "11".to_owned(); 34 | 35 | assert_eq!(result, expected); 36 | } 37 | -------------------------------------------------------------------------------- /tests/api/vertex.rs: -------------------------------------------------------------------------------- 1 | use h3o::Vertex; 2 | 3 | #[test] 4 | fn try_from_u8() { 5 | assert!(Vertex::try_from(0).is_ok(), "lower bound"); 6 | assert!(Vertex::try_from(3).is_ok(), "valid value"); 7 | assert!(Vertex::try_from(5).is_ok(), "upper bound"); 8 | 9 | assert!(Vertex::try_from(6).is_err(), "out of range: low"); 10 | } 11 | 12 | #[test] 13 | fn into_u8() { 14 | let vertex = Vertex::try_from(0).expect("vertex"); 15 | assert_eq!(u8::from(vertex), 0, "lower bound"); 16 | 17 | let vertex = Vertex::try_from(3).expect("vertex"); 18 | assert_eq!(u8::from(vertex), 3, "valid value"); 19 | 20 | let vertex = Vertex::try_from(5).expect("vertex"); 21 | assert_eq!(u8::from(vertex), 5, "upper bound"); 22 | } 23 | 24 | #[test] 25 | fn display() { 26 | let result = Vertex::try_from(3).expect("vertex").to_string(); 27 | let expected = "3".to_owned(); 28 | 29 | assert_eq!(result, expected); 30 | } 31 | -------------------------------------------------------------------------------- /tests/api/vertex_index.rs: -------------------------------------------------------------------------------- 1 | use h3o::VertexIndex; 2 | 3 | #[test] 4 | fn try_from_str() { 5 | let result = "2222597fffffffff".parse::(); 6 | let expected = VertexIndex::try_from(0x2222597fffffffff); 7 | assert_eq!(result, expected, "valid string"); 8 | 9 | let result = "no bueno".parse::(); 10 | assert!(result.is_err(), "invalid string"); 11 | } 12 | 13 | // Resolutions are displayed as numerical value. 14 | #[test] 15 | fn display() { 16 | let index = VertexIndex::try_from(0x2222597fffffffff).expect("index"); 17 | 18 | // Default display is the lower hex one. 19 | let result = index.to_string(); 20 | let expected = "2222597fffffffff".to_owned(); 21 | assert_eq!(result, expected, "default display"); 22 | 23 | // Upper hex. 24 | let result = format!("{index:X}"); 25 | let expected = "2222597FFFFFFFFF".to_owned(); 26 | assert_eq!(result, expected, "upper hex"); 27 | 28 | // Octal. 29 | let result = format!("{index:o}"); 30 | let expected = "210422627777777777777".to_owned(); 31 | assert_eq!(result, expected, "octal"); 32 | 33 | // Binary. 34 | let result = format!("{index:b}"); 35 | let expected = 36 | "10001000100010010110010111111111111111111111111111111111111111" 37 | .to_owned(); 38 | assert_eq!(result, expected, "binary"); 39 | } 40 | -------------------------------------------------------------------------------- /tests/h3/are_neighbor_cells.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! exhaustive_test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | for index in CellIndex::base_cells() 11 | .flat_map(|index| index.children(resolution)) 12 | { 13 | let result = index.is_neighbor_with(index).ok(); 14 | let reference = h3api::are_neighbor_cells(index, index); 15 | assert_eq!( 16 | result, reference, 17 | "self-neighbor check with {index}" 18 | ); 19 | 20 | for neighbor in index.grid_disk::>(3) { 21 | let result = index.is_neighbor_with(neighbor).ok(); 22 | let reference = h3api::are_neighbor_cells(index, neighbor); 23 | assert_eq!( 24 | result, reference, 25 | "areNeighborCells({index}, {neighbor})" 26 | ); 27 | } 28 | } 29 | } 30 | }; 31 | } 32 | 33 | exhaustive_test!(exhaustive_res0, 0); 34 | exhaustive_test!(exhaustive_res1, 1); 35 | exhaustive_test!(exhaustive_res2, 2); 36 | -------------------------------------------------------------------------------- /tests/h3/cell_to_center_child.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{LatLng, Resolution}; 3 | 4 | macro_rules! test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let ll = LatLng::new(56.66170660104207, 20.46973734604441) 9 | .expect("coordinate"); 10 | let resolution = 11 | Resolution::try_from($resolution).expect("index resolution"); 12 | let index = ll.to_cell(resolution); 13 | 14 | for res in 0..=15 { 15 | let child_res = 16 | Resolution::try_from(res).expect("child resolution"); 17 | let result = index.center_child(child_res); 18 | let reference = h3api::cell_to_center_child(index, child_res); 19 | 20 | assert_eq!(result, reference, "center child at {child_res:?}"); 21 | } 22 | } 23 | }; 24 | } 25 | 26 | test!(res0, 0); 27 | test!(res1, 1); 28 | test!(res2, 2); 29 | test!(res3, 3); 30 | test!(res4, 4); 31 | test!(res5, 5); 32 | test!(res6, 6); 33 | test!(res7, 7); 34 | test!(res8, 8); 35 | test!(res9, 9); 36 | test!(res10, 10); 37 | test!(res11, 11); 38 | test!(res12, 12); 39 | test!(res13, 13); 40 | test!(res14, 14); 41 | test!(res15, 15); 42 | -------------------------------------------------------------------------------- /tests/h3/cell_to_child_pos.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! exhaustive_test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | for index in CellIndex::base_cells() 11 | .flat_map(|index| index.children(resolution)) 12 | { 13 | for offset in 0..=3 { 14 | let children_res = 15 | Resolution::try_from(u8::from(resolution) + offset) 16 | .expect("valid resolution"); 17 | for child in index.children(children_res) { 18 | let result = child.child_position(resolution); 19 | let expected = 20 | h3api::cell_to_child_pos(child, resolution); 21 | 22 | assert_eq!(result, expected, "index:{child:?}"); 23 | } 24 | } 25 | } 26 | } 27 | }; 28 | } 29 | 30 | exhaustive_test!(exhaustive_res0, 0); 31 | exhaustive_test!(exhaustive_res1, 1); 32 | exhaustive_test!(exhaustive_res2, 2); 33 | -------------------------------------------------------------------------------- /tests/h3/cell_to_children.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let index = CellIndex::try_from($index).expect("cell index"); 9 | let resolution = 10 | Resolution::try_from($resolution).expect("index resolution"); 11 | let result = index.children(resolution).collect::>(); 12 | let reference = h3api::cell_to_children(index, resolution); 13 | 14 | assert_eq!(result, reference); 15 | } 16 | }; 17 | } 18 | 19 | test!(coarser_hexagon, 0x8b754e649929fff, 10); 20 | test!(coarser_pentagon, 0x8b0800000000fff, 10); 21 | test!(same_res11_hexagon, 0x8b754e649929fff, 11); 22 | test!(same_res11_pentagon, 0x8b0800000000fff, 11); 23 | test!(same_res0_hexagon, 0x8077fffffffffff, 0); 24 | test!(same_res0_pentagon, 0x8009fffffffffff, 0); 25 | test!(children_res12_hexagon, 0x8b754e649929fff, 12); 26 | test!(children_res12_pentagon, 0x8b0800000000fff, 12); 27 | test!(children_res1_hexagon, 0x8077fffffffffff, 1); 28 | test!(children_res1_pentagon, 0x8075fffffffffff, 1); 29 | test!(grand_children_res13_hexagon, 0x8b754e649929fff, 13); 30 | test!(grand_children_res13_pentagon, 0x8b0800000000fff, 13); 31 | test!(grand_children_res2_hexagon, 0x8077fffffffffff, 2); 32 | test!(grand_children_res2_pentagon, 0x8075fffffffffff, 2); 33 | test!(five_generation_res15_hexagon, 0x8b754e649929fff, 15); 34 | test!(five_generation_res15_pentagon, 0x8b0800000000fff, 15); 35 | test!(five_generation_res5_hexagon, 0x8077fffffffffff, 5); 36 | test!(five_generation_res5_pentagon, 0x8075fffffffffff, 5); 37 | -------------------------------------------------------------------------------- /tests/h3/cell_to_children_size.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let index = CellIndex::try_from($index).expect("cell index"); 9 | let resolution = 10 | Resolution::try_from($resolution).expect("index resolution"); 11 | let result = index.children_count(resolution); 12 | let reference = h3api::cell_to_children_size(index, resolution); 13 | 14 | assert_eq!(result, reference); 15 | } 16 | }; 17 | } 18 | 19 | test!(coarser_hexagon, 0x87283080dffffff, 3); 20 | test!(same_resolution_hexagon, 0x87283080dffffff, 7); 21 | test!(children_hexagon, 0x87283080dffffff, 8); 22 | test!(grand_children_hexagon, 0x87283080dffffff, 9); 23 | test!(highest_resolution_hexagon, 0x806dfffffffffff, 15); 24 | test!(coarser_pentagon, 0x870800000ffffff, 3); 25 | test!(same_resolution_pentagon, 0x870800000ffffff, 7); 26 | test!(children_pentagon, 0x870800000ffffff, 8); 27 | test!(grand_children_pentagon, 0x870800000ffffff, 9); 28 | test!(highest_resolution_pentagon, 0x8009fffffffffff, 15); 29 | -------------------------------------------------------------------------------- /tests/h3/cell_to_local_ij.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! exhaustive_test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | for index in CellIndex::base_cells() 11 | .flat_map(|index| index.children(resolution)) 12 | { 13 | // Test self. 14 | let result = index.to_local_ij(index).ok(); 15 | let reference = h3api::cell_to_local_ij(index, index); 16 | assert_eq!(result, reference, "anchor {index}, index {index}"); 17 | 18 | // Test neighbors. 19 | for neighbor in index.grid_disk_safe(5) { 20 | let result = neighbor.to_local_ij(index).ok(); 21 | let reference = h3api::cell_to_local_ij(index, neighbor); 22 | 23 | assert_eq!( 24 | result, reference, 25 | "anchor {index}, index {neighbor}" 26 | ); 27 | } 28 | } 29 | } 30 | }; 31 | } 32 | 33 | exhaustive_test!(exhaustive_res0, 0); 34 | exhaustive_test!(exhaustive_res1, 1); 35 | exhaustive_test!(exhaustive_res2, 2); 36 | -------------------------------------------------------------------------------- /tests/h3/cell_to_parent.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let index = CellIndex::try_from($index).expect("cell index"); 9 | let resolution = 10 | Resolution::try_from($resolution).expect("index resolution"); 11 | let result = index.parent(resolution); 12 | let reference = h3api::cell_to_parent(index, resolution); 13 | 14 | assert_eq!(result, reference); 15 | } 16 | }; 17 | } 18 | 19 | test!(res0, 0x8f734e64992d6d8, 0); 20 | test!(res1, 0x8f734e64992d6d8, 1); 21 | test!(res2, 0x8f734e64992d6d8, 2); 22 | test!(res3, 0x8f734e64992d6d8, 3); 23 | test!(res4, 0x8f734e64992d6d8, 4); 24 | test!(res5, 0x8f734e64992d6d8, 5); 25 | test!(res6, 0x8f734e64992d6d8, 6); 26 | test!(res7, 0x8f734e64992d6d8, 7); 27 | test!(res8, 0x8f734e64992d6d8, 8); 28 | test!(res9, 0x8f734e64992d6d8, 9); 29 | test!(res10, 0x8f734e64992d6d8, 10); 30 | test!(res11, 0x8f734e64992d6d8, 11); 31 | test!(res12, 0x8f734e64992d6d8, 12); 32 | test!(res13, 0x8f734e64992d6d8, 13); 33 | test!(res14, 0x8f734e64992d6d8, 14); 34 | test!(res15, 0x8f734e64992d6d8, 15); 35 | -------------------------------------------------------------------------------- /tests/h3/cell_to_vertex.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution, Vertex}; 3 | 4 | macro_rules! exhaustive_test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | for index in CellIndex::base_cells() 11 | .flat_map(|index| index.children(resolution)) 12 | { 13 | for vertex in 0..6 { 14 | let vertex = Vertex::try_from(vertex).expect("cell vertex"); 15 | let result = index.vertex(vertex); 16 | let reference = h3api::cell_to_vertex(index, vertex); 17 | 18 | assert_eq!( 19 | result, 20 | reference, 21 | "index {index}/vertex {}", 22 | u8::from(vertex) 23 | ); 24 | } 25 | } 26 | } 27 | }; 28 | } 29 | 30 | exhaustive_test!(exhaustive_res0, 0); 31 | exhaustive_test!(exhaustive_res1, 1); 32 | exhaustive_test!(exhaustive_res2, 2); 33 | -------------------------------------------------------------------------------- /tests/h3/cell_to_vertexes.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::CellIndex; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal) => { 6 | #[test] 7 | fn $name() { 8 | let index = CellIndex::try_from($index).expect("cell index"); 9 | let result = index.vertexes().collect::>(); 10 | let reference = h3api::cell_to_vertexes(index); 11 | 12 | assert_eq!(result, reference); 13 | } 14 | }; 15 | } 16 | 17 | test!(hexagon, 0x8f0800000000000); 18 | test!(pentagon, 0x8f734e64992d6d8); 19 | -------------------------------------------------------------------------------- /tests/h3/cells_to_directed_edge.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal) => { 6 | #[test] 7 | fn $name() { 8 | let index = CellIndex::try_from($index).expect("edge index"); 9 | for edge in index.edges() { 10 | let destination = edge.destination(); 11 | let result = index.edge(destination); 12 | let reference = 13 | h3api::cells_to_directed_edge(index, destination); 14 | 15 | assert_eq!(result, reference, "edge {edge}"); 16 | } 17 | } 18 | }; 19 | } 20 | 21 | test!(hexagon, 0x891fb46622fffff); 22 | test!(pentagon, 0x821c07fffffffff); 23 | 24 | macro_rules! exhaustive_test { 25 | ($name:ident, $resolution:literal) => { 26 | #[test] 27 | fn $name() { 28 | let resolution = 29 | Resolution::try_from($resolution).expect("index resolution"); 30 | for index in CellIndex::base_cells() 31 | .flat_map(|index| index.children(resolution)) 32 | { 33 | for edge in index.edges() { 34 | let destination = edge.destination(); 35 | let result = index.edge(destination); 36 | let reference = 37 | h3api::cells_to_directed_edge(index, destination); 38 | 39 | assert_eq!(result, reference, "edge {edge}"); 40 | } 41 | } 42 | } 43 | }; 44 | } 45 | 46 | exhaustive_test!(exhaustive_res0, 0); 47 | exhaustive_test!(exhaustive_res1, 1); 48 | exhaustive_test!(exhaustive_res2, 2); 49 | -------------------------------------------------------------------------------- /tests/h3/child_pos_to_cell.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! exhaustive_test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | for index in CellIndex::base_cells() 11 | .flat_map(|index| index.children(resolution)) 12 | { 13 | for offset in 0..=3 { 14 | let children_res = 15 | Resolution::try_from(u8::from(resolution) + offset) 16 | .expect("valid resolution"); 17 | for pos in 0..index.children_count(children_res) { 18 | let result = index.child_at(pos, children_res); 19 | let expected = 20 | h3api::child_pos_to_cell(index, pos, children_res); 21 | 22 | assert_eq!( 23 | result, expected, 24 | "parent:{index:?}, pos:{pos}, res:{children_res}" 25 | ); 26 | } 27 | } 28 | } 29 | } 30 | }; 31 | } 32 | 33 | exhaustive_test!(exhaustive_res0, 0); 34 | exhaustive_test!(exhaustive_res1, 1); 35 | exhaustive_test!(exhaustive_res2, 2); 36 | -------------------------------------------------------------------------------- /tests/h3/degs_to_rads.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use float_eq::assert_float_eq; 3 | 4 | macro_rules! test { 5 | ($name:ident, $angle:literal) => { 6 | #[test] 7 | fn $name() { 8 | let result = $angle.to_radians(); 9 | let reference = h3api::degs_to_rads($angle); 10 | 11 | assert_float_eq!(result, reference, abs <= f64::EPSILON); 12 | } 13 | }; 14 | } 15 | 16 | test!(positive, 48.854501508844095_f64); 17 | test!(negative, -48.854501508844095_f64); 18 | test!(large_positive, 448.8545015088441_f64); 19 | test!(large_negative, -448.8545015088441_f64); 20 | -------------------------------------------------------------------------------- /tests/h3/directed_edge_to_cells.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::DirectedEdgeIndex; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal) => { 6 | #[test] 7 | fn $name() { 8 | let index = 9 | DirectedEdgeIndex::try_from($index).expect("edge index"); 10 | let result = index.cells(); 11 | let reference = h3api::directed_edge_to_cells(index); 12 | 13 | assert_eq!(result, reference); 14 | } 15 | }; 16 | } 17 | 18 | test!(hexagon, 0x13f2834782b9c2ab); 19 | test!(pentagon, 0x1370800000ffffff); 20 | -------------------------------------------------------------------------------- /tests/h3/edge_length_km.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use float_eq::assert_float_eq; 3 | use h3o::CellIndex; 4 | 5 | macro_rules! test { 6 | ($name:ident, $index:literal) => { 7 | #[test] 8 | fn $name() { 9 | let index = CellIndex::try_from($index).expect("edge index"); 10 | for edge in index.edges() { 11 | let result = edge.length_km(); 12 | let reference = h3api::edge_length_km(edge); 13 | 14 | assert_float_eq!( 15 | result, 16 | reference, 17 | r2nd <= f64::from(f32::EPSILON), 18 | "edge {edge}", 19 | ); 20 | } 21 | } 22 | }; 23 | } 24 | 25 | test!(hexagon_res0, 0x8019fffffffffff); 26 | test!(hexagon_res1, 0x811fbffffffffff); 27 | test!(hexagon_res2, 0x821fb7fffffffff); 28 | test!(hexagon_res3, 0x831fb4fffffffff); 29 | test!(hexagon_res4, 0x841fb47ffffffff); 30 | test!(hexagon_res5, 0x851fb467fffffff); 31 | test!(hexagon_res6, 0x861fb4667ffffff); 32 | test!(hexagon_res7, 0x871fb4662ffffff); 33 | test!(hexagon_res8, 0x881fb46623fffff); 34 | test!(hexagon_res9, 0x891fb46622fffff); 35 | test!(hexagon_res10, 0x8a1fb46622dffff); 36 | test!(hexagon_res11, 0x8b1fb46622dcfff); 37 | test!(hexagon_res12, 0x8c1fb46622dc9ff); 38 | test!(hexagon_res13, 0x8d1fb46622dc83f); 39 | test!(hexagon_res14, 0x8e1fb46622dc81f); 40 | test!(hexagon_res15, 0x8f1fb46622dc81b); 41 | 42 | test!(pentagon_res0, 0x8031fffffffffff); 43 | test!(pentagon_res1, 0x81303ffffffffff); 44 | test!(pentagon_res2, 0x823007fffffffff); 45 | test!(pentagon_res3, 0x833000fffffffff); 46 | test!(pentagon_res4, 0x8430001ffffffff); 47 | test!(pentagon_res5, 0x85300003fffffff); 48 | test!(pentagon_res6, 0x863000007ffffff); 49 | test!(pentagon_res7, 0x873000000ffffff); 50 | test!(pentagon_res8, 0x8830000001fffff); 51 | test!(pentagon_res9, 0x89300000003ffff); 52 | test!(pentagon_res10, 0x8a3000000007fff); 53 | test!(pentagon_res11, 0x8b3000000000fff); 54 | test!(pentagon_res12, 0x8c30000000001ff); 55 | test!(pentagon_res13, 0x8d300000000003f); 56 | test!(pentagon_res14, 0x8e3000000000007); 57 | test!(pentagon_res15, 0x8f3000000000000); 58 | -------------------------------------------------------------------------------- /tests/h3/edge_length_m.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use float_eq::assert_float_eq; 3 | use h3o::CellIndex; 4 | 5 | macro_rules! test { 6 | ($name:ident, $index:literal) => { 7 | #[test] 8 | fn $name() { 9 | let index = CellIndex::try_from($index).expect("edge index"); 10 | for edge in index.edges() { 11 | let result = edge.length_m(); 12 | let reference = h3api::edge_length_m(edge); 13 | 14 | assert_float_eq!( 15 | result, 16 | reference, 17 | r2nd <= f64::from(f32::EPSILON), 18 | "edge {edge}", 19 | ); 20 | } 21 | } 22 | }; 23 | } 24 | 25 | test!(hexagon_res0, 0x8019fffffffffff); 26 | test!(hexagon_res1, 0x811fbffffffffff); 27 | test!(hexagon_res2, 0x821fb7fffffffff); 28 | test!(hexagon_res3, 0x831fb4fffffffff); 29 | test!(hexagon_res4, 0x841fb47ffffffff); 30 | test!(hexagon_res5, 0x851fb467fffffff); 31 | test!(hexagon_res6, 0x861fb4667ffffff); 32 | test!(hexagon_res7, 0x871fb4662ffffff); 33 | test!(hexagon_res8, 0x881fb46623fffff); 34 | test!(hexagon_res9, 0x891fb46622fffff); 35 | test!(hexagon_res10, 0x8a1fb46622dffff); 36 | test!(hexagon_res11, 0x8b1fb46622dcfff); 37 | test!(hexagon_res12, 0x8c1fb46622dc9ff); 38 | test!(hexagon_res13, 0x8d1fb46622dc83f); 39 | test!(hexagon_res14, 0x8e1fb46622dc81f); 40 | test!(hexagon_res15, 0x8f1fb46622dc81b); 41 | 42 | test!(pentagon_res0, 0x8031fffffffffff); 43 | test!(pentagon_res1, 0x81303ffffffffff); 44 | test!(pentagon_res2, 0x823007fffffffff); 45 | test!(pentagon_res3, 0x833000fffffffff); 46 | test!(pentagon_res4, 0x8430001ffffffff); 47 | test!(pentagon_res5, 0x85300003fffffff); 48 | test!(pentagon_res6, 0x863000007ffffff); 49 | test!(pentagon_res7, 0x873000000ffffff); 50 | test!(pentagon_res8, 0x8830000001fffff); 51 | test!(pentagon_res9, 0x89300000003ffff); 52 | test!(pentagon_res10, 0x8a3000000007fff); 53 | test!(pentagon_res11, 0x8b3000000000fff); 54 | test!(pentagon_res12, 0x8c30000000001ff); 55 | test!(pentagon_res13, 0x8d300000000003f); 56 | test!(pentagon_res14, 0x8e3000000000007); 57 | test!(pentagon_res15, 0x8f3000000000000); 58 | -------------------------------------------------------------------------------- /tests/h3/edge_length_rads.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use float_eq::assert_float_eq; 3 | use h3o::CellIndex; 4 | 5 | macro_rules! test { 6 | ($name:ident, $index:literal) => { 7 | #[test] 8 | fn $name() { 9 | let index = CellIndex::try_from($index).expect("edge index"); 10 | for edge in index.edges() { 11 | let result = edge.length_rads(); 12 | let reference = h3api::edge_length_rads(edge); 13 | 14 | assert_float_eq!( 15 | result, 16 | reference, 17 | r2nd <= f64::from(f32::EPSILON), 18 | "edge {edge}", 19 | ); 20 | } 21 | } 22 | }; 23 | } 24 | 25 | test!(hexagon_res0, 0x8019fffffffffff); 26 | test!(hexagon_res1, 0x811fbffffffffff); 27 | test!(hexagon_res2, 0x821fb7fffffffff); 28 | test!(hexagon_res3, 0x831fb4fffffffff); 29 | test!(hexagon_res4, 0x841fb47ffffffff); 30 | test!(hexagon_res5, 0x851fb467fffffff); 31 | test!(hexagon_res6, 0x861fb4667ffffff); 32 | test!(hexagon_res7, 0x871fb4662ffffff); 33 | test!(hexagon_res8, 0x881fb46623fffff); 34 | test!(hexagon_res9, 0x891fb46622fffff); 35 | test!(hexagon_res10, 0x8a1fb46622dffff); 36 | test!(hexagon_res11, 0x8b1fb46622dcfff); 37 | test!(hexagon_res12, 0x8c1fb46622dc9ff); 38 | test!(hexagon_res13, 0x8d1fb46622dc83f); 39 | test!(hexagon_res14, 0x8e1fb46622dc81f); 40 | test!(hexagon_res15, 0x8f1fb46622dc81b); 41 | 42 | test!(pentagon_res0, 0x8031fffffffffff); 43 | test!(pentagon_res1, 0x81303ffffffffff); 44 | test!(pentagon_res2, 0x823007fffffffff); 45 | test!(pentagon_res3, 0x833000fffffffff); 46 | test!(pentagon_res4, 0x8430001ffffffff); 47 | test!(pentagon_res5, 0x85300003fffffff); 48 | test!(pentagon_res6, 0x863000007ffffff); 49 | test!(pentagon_res7, 0x873000000ffffff); 50 | test!(pentagon_res8, 0x8830000001fffff); 51 | test!(pentagon_res9, 0x89300000003ffff); 52 | test!(pentagon_res10, 0x8a3000000007fff); 53 | test!(pentagon_res11, 0x8b3000000000fff); 54 | test!(pentagon_res12, 0x8c30000000001ff); 55 | test!(pentagon_res13, 0x8d300000000003f); 56 | test!(pentagon_res14, 0x8e3000000000007); 57 | test!(pentagon_res15, 0x8f3000000000000); 58 | -------------------------------------------------------------------------------- /tests/h3/get_base_cell_number.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::CellIndex; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal) => { 6 | #[test] 7 | fn $name() { 8 | let index = CellIndex::try_from($index).expect("cell index"); 9 | let result = index.base_cell(); 10 | let reference = h3api::get_base_cell_number(index); 11 | 12 | assert_eq!(result, reference); 13 | } 14 | }; 15 | } 16 | 17 | test!(hexagon, 0x87283080dffffff); 18 | test!(pentagon, 0x870800000ffffff); 19 | -------------------------------------------------------------------------------- /tests/h3/get_directed_edge_destination.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal) => { 6 | #[test] 7 | fn $name() { 8 | let index = CellIndex::try_from($index).expect("edge index"); 9 | for edge in index.edges() { 10 | let result = edge.destination(); 11 | let reference = h3api::get_directed_edge_destination(edge); 12 | 13 | assert_eq!(result, reference, "edge {edge}"); 14 | } 15 | } 16 | }; 17 | } 18 | 19 | test!(hexagon, 0x891fb46622fffff); 20 | test!(pentagon, 0x821c07fffffffff); 21 | 22 | macro_rules! exhaustive_test { 23 | ($name:ident, $resolution:literal) => { 24 | #[test] 25 | fn $name() { 26 | let resolution = 27 | Resolution::try_from($resolution).expect("index resolution"); 28 | for index in CellIndex::base_cells() 29 | .flat_map(|index| index.children(resolution)) 30 | { 31 | for edge in index.edges() { 32 | let result = edge.destination(); 33 | let reference = h3api::get_directed_edge_destination(edge); 34 | 35 | assert_eq!(result, reference, "edge {edge}"); 36 | } 37 | } 38 | } 39 | }; 40 | } 41 | 42 | exhaustive_test!(exhaustive_res0, 0); 43 | exhaustive_test!(exhaustive_res1, 1); 44 | exhaustive_test!(exhaustive_res2, 2); 45 | -------------------------------------------------------------------------------- /tests/h3/get_directed_edge_origin.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::DirectedEdgeIndex; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal) => { 6 | #[test] 7 | fn $name() { 8 | let index = 9 | DirectedEdgeIndex::try_from($index).expect("edge index"); 10 | let result = index.origin(); 11 | let reference = h3api::get_directed_edge_origin(index); 12 | 13 | assert_eq!(result, reference); 14 | } 15 | }; 16 | } 17 | 18 | test!(hexagon, 0x13f2834782b9c2ab); 19 | test!(pentagon, 0x1370800000ffffff); 20 | -------------------------------------------------------------------------------- /tests/h3/get_hexagon_area_avg_km2.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::Resolution; 3 | 4 | macro_rules! test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | let result = resolution.area_km2(); 11 | let reference = h3api::get_hexagon_area_avg_km2(resolution); 12 | 13 | assert_eq!(result, reference); 14 | } 15 | }; 16 | } 17 | 18 | test!(res0, 0); 19 | test!(res1, 1); 20 | test!(res2, 2); 21 | test!(res3, 3); 22 | test!(res4, 4); 23 | test!(res5, 5); 24 | test!(res6, 6); 25 | test!(res7, 7); 26 | test!(res8, 8); 27 | test!(res9, 9); 28 | test!(res10, 10); 29 | test!(res11, 11); 30 | test!(res12, 12); 31 | test!(res13, 13); 32 | test!(res14, 14); 33 | test!(res15, 15); 34 | -------------------------------------------------------------------------------- /tests/h3/get_hexagon_area_avg_m2.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::Resolution; 3 | 4 | macro_rules! test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | let result = resolution.area_m2(); 11 | let reference = h3api::get_hexagon_area_avg_m2(resolution); 12 | 13 | assert_eq!(result, reference); 14 | } 15 | }; 16 | } 17 | 18 | test!(res0, 0); 19 | test!(res1, 1); 20 | test!(res2, 2); 21 | test!(res3, 3); 22 | test!(res4, 4); 23 | test!(res5, 5); 24 | test!(res6, 6); 25 | test!(res7, 7); 26 | test!(res8, 8); 27 | test!(res9, 9); 28 | test!(res10, 10); 29 | test!(res11, 11); 30 | test!(res12, 12); 31 | test!(res13, 13); 32 | test!(res14, 14); 33 | test!(res15, 15); 34 | -------------------------------------------------------------------------------- /tests/h3/get_hexagon_area_avg_rads2.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::Resolution; 3 | 4 | macro_rules! test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | let result = resolution.area_rads2(); 11 | let reference = h3api::get_hexagon_area_avg_km2(resolution) 12 | / (6371.007180918475_f64 * 6371.007180918475_f64); 13 | 14 | assert_eq!(result, reference); 15 | } 16 | }; 17 | } 18 | 19 | test!(res0, 0); 20 | test!(res1, 1); 21 | test!(res2, 2); 22 | test!(res3, 3); 23 | test!(res4, 4); 24 | test!(res5, 5); 25 | test!(res6, 6); 26 | test!(res7, 7); 27 | test!(res8, 8); 28 | test!(res9, 9); 29 | test!(res10, 10); 30 | test!(res11, 11); 31 | test!(res12, 12); 32 | test!(res13, 13); 33 | test!(res14, 14); 34 | test!(res15, 15); 35 | -------------------------------------------------------------------------------- /tests/h3/get_icosahedron_faces.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal) => { 6 | #[test] 7 | fn $name() { 8 | let index = CellIndex::try_from($index).expect("cell index"); 9 | let result = index.icosahedron_faces().iter().collect::>(); 10 | let reference = h3api::get_icosahedron_faces(index); 11 | 12 | assert_eq!(result, reference); 13 | } 14 | }; 15 | } 16 | 17 | // Class II pentagon neighbor - one face, two adjacent vertices on edge. 18 | test!(hexagon_with_edge_vertices, 0x821c37fffffffff); 19 | // Class III pentagon neighbor, distortion across faces. 20 | test!(hexagon_with_distortion, 0x831c06fffffffff); 21 | // Class II hex with two vertices on edge. 22 | test!(hexagon_crossing_faces, 0x821ce7fffffffff); 23 | 24 | test!(class3_pentagon, 0x81083ffffffffff); 25 | test!(class2_pentagon, 0x820807fffffffff); 26 | test!(resolution15_pentagon, 0x8f0800000000000); 27 | 28 | #[test] 29 | fn base_cells() { 30 | for base_cell in CellIndex::base_cells() { 31 | let result = base_cell.icosahedron_faces().iter().collect::>(); 32 | let reference = h3api::get_icosahedron_faces(base_cell); 33 | 34 | assert_eq!(result, reference, "base cell {base_cell}"); 35 | } 36 | } 37 | 38 | macro_rules! test_cells_at_res { 39 | ($name:ident, $base_cell:literal, $resolution:literal) => { 40 | #[test] 41 | fn $name() { 42 | let resolution = 43 | Resolution::try_from($resolution).expect("index resolution"); 44 | let base_cell = 45 | CellIndex::try_from($base_cell).expect("cell index"); 46 | 47 | for index in base_cell.children(resolution) { 48 | let result = 49 | index.icosahedron_faces().iter().collect::>(); 50 | let reference = h3api::get_icosahedron_faces(index); 51 | 52 | assert_eq!(result, reference, "cell {index}"); 53 | } 54 | } 55 | }; 56 | } 57 | 58 | // Base cell 16 is at the center of an icosahedron face, so all children should 59 | // have the same face. 60 | test_cells_at_res!(single_face_hexes_res2, 0x8021fffffffffff, 2); 61 | test_cells_at_res!(single_face_hexes_res3, 0x8021fffffffffff, 3); 62 | -------------------------------------------------------------------------------- /tests/h3/get_num_cells.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::Resolution; 3 | 4 | macro_rules! test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | let result = resolution.cell_count(); 11 | let reference = h3api::get_num_cells(resolution); 12 | 13 | assert_eq!(result, reference); 14 | } 15 | }; 16 | } 17 | 18 | test!(res0, 0); 19 | test!(res1, 1); 20 | test!(res2, 2); 21 | test!(res3, 3); 22 | test!(res4, 4); 23 | test!(res5, 5); 24 | test!(res6, 6); 25 | test!(res7, 7); 26 | test!(res8, 8); 27 | test!(res9, 9); 28 | test!(res10, 10); 29 | test!(res11, 11); 30 | test!(res12, 12); 31 | test!(res13, 13); 32 | test!(res14, 14); 33 | test!(res15, 15); 34 | -------------------------------------------------------------------------------- /tests/h3/get_pentagons.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::Resolution; 3 | 4 | macro_rules! test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | let result = resolution.pentagons().collect::>(); 11 | let reference = h3api::get_pentagons(resolution); 12 | 13 | assert_eq!(result, reference); 14 | } 15 | }; 16 | } 17 | 18 | test!(res0, 0); 19 | test!(res1, 1); 20 | test!(res2, 2); 21 | test!(res3, 3); 22 | test!(res4, 4); 23 | test!(res5, 5); 24 | test!(res6, 6); 25 | test!(res7, 7); 26 | test!(res8, 8); 27 | test!(res9, 9); 28 | test!(res10, 10); 29 | test!(res11, 11); 30 | test!(res12, 12); 31 | test!(res13, 13); 32 | test!(res14, 14); 33 | test!(res15, 15); 34 | -------------------------------------------------------------------------------- /tests/h3/get_res0_cells.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::CellIndex; 3 | 4 | #[test] 5 | fn value() { 6 | let result = CellIndex::base_cells().collect::>(); 7 | let reference = h3api::get_res0_cells(); 8 | 9 | assert_eq!(result, reference); 10 | } 11 | -------------------------------------------------------------------------------- /tests/h3/get_resolution.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::CellIndex; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal) => { 6 | #[test] 7 | fn $name() { 8 | let index = CellIndex::try_from($index).expect("cell index"); 9 | let result = index.resolution(); 10 | let reference = h3api::get_resolution(index); 11 | 12 | assert_eq!(result, reference); 13 | } 14 | }; 15 | } 16 | 17 | test!(res0, 0x8075fffffffffff); 18 | test!(res1, 0x81757ffffffffff); 19 | test!(res2, 0x82754ffffffffff); 20 | test!(res3, 0x83754efffffffff); 21 | test!(res4, 0x84754a9ffffffff); 22 | test!(res5, 0x85754e67fffffff); 23 | test!(res6, 0x86754e64fffffff); 24 | test!(res7, 0x87754e64dffffff); 25 | test!(res8, 0x88754e6499fffff); 26 | test!(res9, 0x89754e64993ffff); 27 | test!(res10, 0x8a754e64992ffff); 28 | test!(res11, 0x8b754e649929fff); 29 | test!(res12, 0x8c754e649929dff); 30 | test!(res13, 0x8d754e64992d6ff); 31 | test!(res14, 0x8e754e64992d6df); 32 | test!(res15, 0x8f754e64992d6d8); 33 | -------------------------------------------------------------------------------- /tests/h3/great_circle_distance_km.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use float_eq::assert_float_eq; 3 | use h3o::LatLng; 4 | 5 | const ICECREAM: (f64, f64) = (48.854501508844095_f64, 2.3729695423293613_f64); 6 | const BOCAMEXA: (f64, f64) = (48.854091837885264_f64, 2.3708719883564124_f64); 7 | const GOOGLE: (f64, f64) = (37.422747247604335_f64, -122.08389658095136_f64); 8 | 9 | macro_rules! test { 10 | ($name:ident, $src:ident, $dst:ident) => { 11 | #[test] 12 | fn $name() { 13 | let src = LatLng::new($src.0, $src.1).expect("valid location"); 14 | let dst = LatLng::new($dst.0, $dst.1).expect("valid location"); 15 | let result = src.distance_km(dst); 16 | let reference = h3api::great_circle_distance_km(&src, &dst); 17 | 18 | assert_float_eq!(result, reference, r2nd <= f64::EPSILON); 19 | } 20 | }; 21 | } 22 | 23 | test!(zero, ICECREAM, ICECREAM); 24 | test!(close, ICECREAM, BOCAMEXA); 25 | test!(far, ICECREAM, GOOGLE); 26 | -------------------------------------------------------------------------------- /tests/h3/great_circle_distance_m.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use float_eq::assert_float_eq; 3 | use h3o::LatLng; 4 | 5 | const ICECREAM: (f64, f64) = (48.854501508844095_f64, 2.3729695423293613_f64); 6 | const BOCAMEXA: (f64, f64) = (48.854091837885264_f64, 2.3708719883564124_f64); 7 | const GOOGLE: (f64, f64) = (37.422747247604335_f64, -122.08389658095136_f64); 8 | 9 | macro_rules! test { 10 | ($name:ident, $src:ident, $dst:ident) => { 11 | #[test] 12 | fn $name() { 13 | let src = LatLng::new($src.0, $src.1).expect("valid location"); 14 | let dst = LatLng::new($dst.0, $dst.1).expect("valid location"); 15 | let result = src.distance_m(dst); 16 | let reference = h3api::great_circle_distance_m(&src, &dst); 17 | 18 | assert_float_eq!(result, reference, r2nd <= f64::EPSILON); 19 | } 20 | }; 21 | } 22 | 23 | test!(zero, ICECREAM, ICECREAM); 24 | test!(close, ICECREAM, BOCAMEXA); 25 | test!(far, ICECREAM, GOOGLE); 26 | -------------------------------------------------------------------------------- /tests/h3/great_circle_distance_rads.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use float_eq::assert_float_eq; 3 | use h3o::LatLng; 4 | 5 | const ICECREAM: (f64, f64) = (48.854501508844095_f64, 2.3729695423293613_f64); 6 | const BOCAMEXA: (f64, f64) = (48.854091837885264_f64, 2.3708719883564124_f64); 7 | const GOOGLE: (f64, f64) = (37.422747247604335_f64, -122.08389658095136_f64); 8 | 9 | macro_rules! test { 10 | ($name:ident, $src:ident, $dst:ident) => { 11 | #[test] 12 | fn $name() { 13 | let src = LatLng::new($src.0, $src.1).expect("valid location"); 14 | let dst = LatLng::new($dst.0, $dst.1).expect("valid location"); 15 | let result = src.distance_rads(dst); 16 | let reference = h3api::great_circle_distance_rads(&src, &dst); 17 | 18 | assert_float_eq!(result, reference, abs <= f64::EPSILON); 19 | } 20 | }; 21 | } 22 | 23 | test!(zero, ICECREAM, ICECREAM); 24 | test!(close, ICECREAM, BOCAMEXA); 25 | test!(far, ICECREAM, GOOGLE); 26 | -------------------------------------------------------------------------------- /tests/h3/grid_disk.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! exhaustive_test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | for index in CellIndex::base_cells() 11 | .flat_map(|index| index.children(resolution)) 12 | { 13 | for k in 0..=5 { 14 | let mut result = index.grid_disk::>(k); 15 | let mut reference = h3api::grid_disk(index, k); 16 | 17 | result.sort_unstable(); 18 | reference.sort_unstable(); 19 | 20 | assert_eq!(result, reference, "origin {index}, k {k}"); 21 | } 22 | } 23 | } 24 | }; 25 | } 26 | 27 | exhaustive_test!(exhaustive_res0, 0); 28 | exhaustive_test!(exhaustive_res1, 1); 29 | exhaustive_test!(exhaustive_res2, 2); 30 | -------------------------------------------------------------------------------- /tests/h3/grid_disk_distances.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! exhaustive_test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | for index in CellIndex::base_cells() 11 | .flat_map(|index| index.children(resolution)) 12 | { 13 | for k in 0..=5 { 14 | let mut result = index.grid_disk_distances::>(k); 15 | let mut reference = h3api::grid_disk_distances(index, k); 16 | 17 | result.sort_unstable_by_key(|(cell, _)| *cell); 18 | reference.sort_unstable_by_key(|(cell, _)| *cell); 19 | 20 | assert_eq!(result, reference, "origin {index}, k {k}"); 21 | } 22 | } 23 | } 24 | }; 25 | } 26 | 27 | exhaustive_test!(exhaustive_res0, 0); 28 | exhaustive_test!(exhaustive_res1, 1); 29 | exhaustive_test!(exhaustive_res2, 2); 30 | -------------------------------------------------------------------------------- /tests/h3/grid_disk_distances_safe.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! exhaustive_test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | for index in CellIndex::base_cells() 11 | .flat_map(|index| index.children(resolution)) 12 | { 13 | for k in 0..=5 { 14 | let mut result = 15 | index.grid_disk_distances_safe(k).collect::>(); 16 | let mut reference = 17 | h3api::grid_disk_distances_safe(index, k); 18 | 19 | result.sort_unstable_by_key(|(cell, _)| *cell); 20 | reference.sort_unstable_by_key(|(cell, _)| *cell); 21 | 22 | assert_eq!(result, reference, "origin {index}, k {k}"); 23 | } 24 | } 25 | } 26 | }; 27 | } 28 | 29 | exhaustive_test!(exhaustive_res0, 0); 30 | exhaustive_test!(exhaustive_res1, 1); 31 | exhaustive_test!(exhaustive_res2, 2); 32 | -------------------------------------------------------------------------------- /tests/h3/grid_disk_distances_unsafe.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! exhaustive_test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | for index in CellIndex::base_cells() 11 | .flat_map(|index| index.children(resolution)) 12 | { 13 | for k in 0..=7 { 14 | let result = index 15 | .grid_disk_distances_fast(k) 16 | .collect::>>(); 17 | let reference = h3api::grid_disk_distances_unsafe(index, k); 18 | 19 | assert_eq!(result, reference, "origin {index}, k {k}"); 20 | } 21 | } 22 | } 23 | }; 24 | } 25 | 26 | exhaustive_test!(exhaustive_res0, 0); 27 | exhaustive_test!(exhaustive_res1, 1); 28 | exhaustive_test!(exhaustive_res2, 2); 29 | -------------------------------------------------------------------------------- /tests/h3/grid_disk_unsafe.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! exhaustive_test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | for index in CellIndex::base_cells() 11 | .flat_map(|index| index.children(resolution)) 12 | { 13 | for k in 0..=7 { 14 | let result = 15 | index.grid_disk_fast(k).collect::>>(); 16 | let reference = h3api::grid_disk_unsafe(index, k); 17 | 18 | assert_eq!(result, reference, "origin {index}, k {k}"); 19 | } 20 | } 21 | } 22 | }; 23 | } 24 | 25 | exhaustive_test!(exhaustive_res0, 0); 26 | exhaustive_test!(exhaustive_res1, 1); 27 | exhaustive_test!(exhaustive_res2, 2); 28 | -------------------------------------------------------------------------------- /tests/h3/grid_distance.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! exhaustive_test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | for index in CellIndex::base_cells() 11 | .flat_map(|index| index.children(resolution)) 12 | { 13 | let result = index.grid_distance(index).unwrap_or(-1); 14 | let reference = 15 | h3api::grid_distance(index, index).unwrap_or(-1); 16 | assert_eq!(result, reference, "distance to self for {index}"); 17 | 18 | for neighbor in index.grid_disk::>(5) { 19 | let result = index.grid_distance(neighbor).unwrap_or(-1); 20 | let reference = 21 | h3api::grid_distance(index, neighbor).unwrap_or(-1); 22 | assert_eq!( 23 | result, reference, 24 | "distance from {index} to {neighbor}" 25 | ); 26 | } 27 | } 28 | } 29 | }; 30 | } 31 | 32 | exhaustive_test!(exhaustive_res0, 0); 33 | exhaustive_test!(exhaustive_res1, 1); 34 | exhaustive_test!(exhaustive_res2, 2); 35 | -------------------------------------------------------------------------------- /tests/h3/grid_path_cells.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! exhaustive_test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | for index in CellIndex::base_cells() 11 | .flat_map(|index| index.children(resolution)) 12 | { 13 | let result = index 14 | .grid_path_cells(index) 15 | .ok() 16 | .and_then(|iter| iter.collect::, _>>().ok()); 17 | let reference = h3api::grid_path_cells(index, index); 18 | assert_eq!(result, reference, "path to self for {index}"); 19 | 20 | for neighbor in index.grid_disk::>(5) { 21 | let result = 22 | index.grid_path_cells(neighbor).ok().and_then(|iter| { 23 | iter.collect::, _>>().ok() 24 | }); 25 | let reference = h3api::grid_path_cells(index, neighbor); 26 | assert_eq!( 27 | result, reference, 28 | "path from {index} to {neighbor}" 29 | ); 30 | } 31 | } 32 | } 33 | }; 34 | } 35 | 36 | exhaustive_test!(exhaustive_res0, 0); 37 | exhaustive_test!(exhaustive_res1, 1); 38 | exhaustive_test!(exhaustive_res2, 2); 39 | -------------------------------------------------------------------------------- /tests/h3/grid_path_cells_size.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! exhaustive_test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | for index in CellIndex::base_cells() 11 | .flat_map(|index| index.children(resolution)) 12 | { 13 | let result = index.grid_path_cells_size(index).unwrap_or(-1); 14 | let reference = 15 | h3api::grid_path_cells_size(index, index).unwrap_or(-1); 16 | assert_eq!(result, reference, "distance to self for {index}"); 17 | 18 | for neighbor in index.grid_disk::>(5) { 19 | let result = 20 | index.grid_path_cells_size(neighbor).unwrap_or(-1); 21 | let reference = 22 | h3api::grid_path_cells_size(index, neighbor) 23 | .unwrap_or(-1); 24 | assert_eq!( 25 | result, reference, 26 | "distance from {index} to {neighbor}" 27 | ); 28 | } 29 | } 30 | } 31 | }; 32 | } 33 | 34 | exhaustive_test!(exhaustive_res0, 0); 35 | exhaustive_test!(exhaustive_res1, 1); 36 | exhaustive_test!(exhaustive_res2, 2); 37 | -------------------------------------------------------------------------------- /tests/h3/grid_ring_unsafe.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | 3 | use super::h3api; 4 | use h3o::{CellIndex, Resolution}; 5 | 6 | macro_rules! exhaustive_test { 7 | ($name:ident, $resolution:literal) => { 8 | #[test] 9 | fn $name() { 10 | let resolution = 11 | Resolution::try_from($resolution).expect("index resolution"); 12 | for index in CellIndex::base_cells() 13 | .flat_map(|index| index.children(resolution)) 14 | { 15 | for k in 0..=7 { 16 | let result = 17 | index.grid_ring_fast(k).collect::>>(); 18 | let reference = h3api::grid_ring_unsafe(index, k); 19 | 20 | assert_eq!(result, reference, "origin {index}, k {k}"); 21 | } 22 | } 23 | } 24 | }; 25 | } 26 | 27 | exhaustive_test!(exhaustive_res0, 0); 28 | exhaustive_test!(exhaustive_res1, 1); 29 | exhaustive_test!(exhaustive_res2, 2); 30 | -------------------------------------------------------------------------------- /tests/h3/h3_to_string.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, DirectedEdgeIndex, VertexIndex}; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:expr) => { 6 | #[test] 7 | fn $name() { 8 | let index = $index.expect("H3 index"); 9 | let result = index.to_string(); 10 | let reference = h3api::h3_to_string(index); 11 | 12 | assert_eq!(result, reference); 13 | } 14 | }; 15 | } 16 | 17 | test!(cell_index_res0, CellIndex::try_from(0x802bfffffffffff)); 18 | test!(cell_index_res12, CellIndex::try_from(0x8c2bae305336bff)); 19 | test!(cell_index_res15, CellIndex::try_from(0x8f2834782b9c2ab)); 20 | test!( 21 | edge_index_res0, 22 | DirectedEdgeIndex::try_from(0x1302bfffffffffff) 23 | ); 24 | test!( 25 | edge_index_res12, 26 | DirectedEdgeIndex::try_from(0x13c2bae305336bff) 27 | ); 28 | test!( 29 | edge_index_res15, 30 | DirectedEdgeIndex::try_from(0x15f2834782b9c2ab) 31 | ); 32 | test!(vertex_index_res0, VertexIndex::try_from(0x2302bfffffffffff)); 33 | test!( 34 | vertex_index_res12, 35 | VertexIndex::try_from(0x23f2834782b9c2a8) 36 | ); 37 | test!( 38 | vertex_index_res15, 39 | VertexIndex::try_from(0x21c2bae305330dff) 40 | ); 41 | -------------------------------------------------------------------------------- /tests/h3/is_pentagon.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::CellIndex; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal) => { 6 | #[test] 7 | fn $name() { 8 | let index = CellIndex::try_from($index).expect("cell index"); 9 | let result = index.is_pentagon(); 10 | let reference = h3api::is_pentagon(index); 11 | 12 | assert_eq!(result, reference); 13 | } 14 | }; 15 | } 16 | 17 | test!(hexagon_res0, 0x8009fffffffffff); 18 | test!(hexagon_res1, 0x81083ffffffffff); 19 | test!(hexagon_res2, 0x820807fffffffff); 20 | test!(hexagon_res3, 0x830800fffffffff); 21 | test!(hexagon_res4, 0x8408001ffffffff); 22 | test!(hexagon_res5, 0x85080003fffffff); 23 | test!(hexagon_res6, 0x860800007ffffff); 24 | test!(hexagon_res7, 0x870800000ffffff); 25 | test!(hexagon_res8, 0x8808000001fffff); 26 | test!(hexagon_res9, 0x89080000003ffff); 27 | test!(hexagon_res10, 0x8a0800000007fff); 28 | test!(hexagon_res11, 0x8b0800000000fff); 29 | test!(hexagon_res12, 0x8c08000000001ff); 30 | test!(hexagon_res13, 0x8d080000000003f); 31 | test!(hexagon_res14, 0x8e0800000000007); 32 | test!(hexagon_res15, 0x8f0800000000000); 33 | 34 | test!(pentagon_res0, 0x8073fffffffffff); 35 | test!(pentagon_res1, 0x81737ffffffffff); 36 | test!(pentagon_res2, 0x82734ffffffffff); 37 | test!(pentagon_res3, 0x83734efffffffff); 38 | test!(pentagon_res4, 0x84734a9ffffffff); 39 | test!(pentagon_res5, 0x85734e67fffffff); 40 | test!(pentagon_res6, 0x86734e64fffffff); 41 | test!(pentagon_res7, 0x87734e64dffffff); 42 | test!(pentagon_res8, 0x88734e6499fffff); 43 | test!(pentagon_res9, 0x89734e64993ffff); 44 | test!(pentagon_res10, 0x8a734e64992ffff); 45 | test!(pentagon_res11, 0x8b734e649929fff); 46 | test!(pentagon_res12, 0x8c734e649929dff); 47 | test!(pentagon_res13, 0x8d734e64992d6ff); 48 | test!(pentagon_res14, 0x8e734e64992d6df); 49 | test!(pentagon_res15, 0x8f734e64992d6d8); 50 | -------------------------------------------------------------------------------- /tests/h3/is_res_class3.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::Resolution; 3 | 4 | macro_rules! test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | let result = resolution.is_class3(); 11 | let reference = h3api::is_res_class3(resolution); 12 | 13 | assert_eq!(result, reference); 14 | } 15 | }; 16 | } 17 | 18 | test!(res0, 0); 19 | test!(res1, 1); 20 | test!(res2, 2); 21 | test!(res3, 3); 22 | test!(res4, 4); 23 | test!(res5, 5); 24 | test!(res6, 6); 25 | test!(res7, 7); 26 | test!(res8, 8); 27 | test!(res9, 9); 28 | test!(res10, 10); 29 | test!(res11, 11); 30 | test!(res12, 12); 31 | test!(res13, 13); 32 | test!(res14, 14); 33 | test!(res15, 15); 34 | -------------------------------------------------------------------------------- /tests/h3/is_valid_cell.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::CellIndex; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal) => { 6 | #[test] 7 | fn $name() { 8 | let result = CellIndex::try_from($index).is_ok(); 9 | let reference = h3api::is_valid_cell($index); 10 | 11 | assert_eq!(result, reference); 12 | } 13 | }; 14 | } 15 | 16 | test!(res0, 0x8075fffffffffff); 17 | test!(res1, 0x81757ffffffffff); 18 | test!(res2, 0x82754ffffffffff); 19 | test!(res3, 0x83754efffffffff); 20 | test!(res4, 0x84754a9ffffffff); 21 | test!(res5, 0x85754e67fffffff); 22 | test!(res6, 0x86754e64fffffff); 23 | test!(res7, 0x87754e64dffffff); 24 | test!(res8, 0x88754e6499fffff); 25 | test!(res9, 0x89754e64993ffff); 26 | test!(res10, 0x8a754e64992ffff); 27 | test!(res11, 0x8b754e649929fff); 28 | test!(res12, 0x8c754e649929dff); 29 | test!(res13, 0x8d754e64992d6ff); 30 | test!(res14, 0x8e754e64992d6df); 31 | test!(res15, 0x8f754e64992d6d8); 32 | 33 | test!(high_bit_set, 0x88c2bae305336bff); 34 | test!(invalid_mode, 0x28c2bae305336bff); 35 | test!(tainted_reserved_bits, 0xac2bae305336bff); 36 | test!(invalid_base_cell, 0x80fffffffffffff); 37 | 38 | test!(unexpected_unused_first, 0x8c2bee305336bff); 39 | test!(unexpected_unused_middle, 0x8c2bae33d336bff); 40 | test!(unexpected_unused_last, 0x8c2bae305336fff); 41 | 42 | test!(missing_unused_first, 0x8c0fae305336aff); 43 | test!(missing_unused_middle, 0x8c0fae305336fef); 44 | test!(missing_unused_last, 0x81757fffffffffe); 45 | 46 | test!(deleted_subsequence_hexagon1, 0x81887ffffffffff); 47 | test!(deleted_subsequence_pentagon1, 0x81087ffffffffff); 48 | test!(deleted_subsequence_hexagon2, 0x8804000011fffff); 49 | test!(deleted_subsequence_pentagon2, 0x8808000011fffff); 50 | -------------------------------------------------------------------------------- /tests/h3/is_valid_directed_edge.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::DirectedEdgeIndex; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal) => { 6 | #[test] 7 | fn $name() { 8 | let result = DirectedEdgeIndex::try_from($index).is_ok(); 9 | let reference = h3api::is_valid_directed_edge($index); 10 | 11 | assert_eq!(result, reference); 12 | } 13 | }; 14 | } 15 | 16 | test!(res0, 0x15075fffffffffff); 17 | test!(res1, 0x151757ffffffffff); 18 | test!(res2, 0x152754ffffffffff); 19 | test!(res3, 0x153754efffffffff); 20 | test!(res4, 0x154754a9ffffffff); 21 | test!(res5, 0x155754e67fffffff); 22 | test!(res6, 0x156754e64fffffff); 23 | test!(res7, 0x157754e64dffffff); 24 | test!(res8, 0x158754e6499fffff); 25 | test!(res9, 0x159754e64993ffff); 26 | test!(res10, 0x15a754e64992ffff); 27 | test!(res11, 0x15b754e649929fff); 28 | test!(res12, 0x15c754e649929dff); 29 | test!(res13, 0x15d754e64992d6ff); 30 | test!(res14, 0x15e754e64992d6df); 31 | test!(res15, 0x15f754e64992d6d8); 32 | 33 | test!(high_bit_set, 0x95c2bae305336bff); 34 | test!(invalid_mode, 0x1dc2bae305336bff); 35 | test!(invalid_edge, 0x10c2bae305336bff); 36 | test!(deleted_pentagon_edge, 0x111083ffffffffff); 37 | test!(invalid_base_cell, 0x150fffffffffffff); 38 | 39 | test!(unexpected_unused_first, 0x15c2bee305336bff); 40 | test!(unexpected_unused_middle, 0x15c2bae33d336bff); 41 | test!(unexpected_unused_last, 0x15c2bae305336fff); 42 | 43 | test!(missing_unused_first, 0x15c0fae305336aff); 44 | test!(missing_unused_middle, 0x15c0fae305336fef); 45 | test!(missing_unused_last, 0x151757fffffffffe); 46 | 47 | test!(deleted_subsequence_hexagon1, 0x151887ffffffffff); 48 | test!(deleted_subsequence_pentagon1, 0x151087ffffffffff); 49 | test!(deleted_subsequence_hexagon2, 0x15804000011fffff); 50 | test!(deleted_subsequence_pentagon2, 0x15808000011fffff); 51 | -------------------------------------------------------------------------------- /tests/h3/is_valid_vertex.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::VertexIndex; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal) => { 6 | #[test] 7 | fn $name() { 8 | let result = VertexIndex::try_from($index).is_ok(); 9 | let reference = h3api::is_valid_vertex($index); 10 | 11 | assert_eq!(result, reference); 12 | } 13 | }; 14 | } 15 | 16 | test!(valid, 0x2222597fffffffff); 17 | test!(hexagon_vertex0, 0x2222597fffffffff); 18 | test!(hexagon_vertex1, 0x2523d47fffffffff); 19 | test!(hexagon_vertex2, 0x2423d47fffffffff); 20 | test!(hexagon_vertex3, 0x21224b7fffffffff); 21 | test!(hexagon_vertex4, 0x20224b7fffffffff); 22 | test!(hexagon_vertex5, 0x2322597fffffffff); 23 | 24 | test!(high_bit_set, 0xa322597fffffffff); 25 | test!(invalid_owner, 0x2222597ffffffffe); 26 | // Vertex 0 belong to 0x80000016f57b1f0, not 0x823d6ffffffffff. 27 | test!(wrong_owner, 0x2023d6ffffffffff); 28 | test!(cell_index, 0x80000016f57b1f0); 29 | test!(invalid_hexagon_vertex, 0x2622597fffffffff); 30 | test!(invalid_pentagon_vertex, 0x2523007fffffffff); 31 | -------------------------------------------------------------------------------- /tests/h3/local_ij_to_cell.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! exhaustive_test { 5 | ($name:ident, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let resolution = 9 | Resolution::try_from($resolution).expect("index resolution"); 10 | for index in CellIndex::base_cells() 11 | .flat_map(|index| index.children(resolution)) 12 | { 13 | // Test self. 14 | let coord = 15 | index.to_local_ij(index).expect("self local IJ coord"); 16 | let result = CellIndex::try_from(coord).ok(); 17 | let reference = h3api::local_ij_to_cell(coord); 18 | assert_eq!(result, reference, "anchor {index}, index {index}"); 19 | 20 | // Test neighbors. 21 | for neighbor in index.grid_disk_safe(5) { 22 | if let Ok(coord) = neighbor.to_local_ij(index) { 23 | let result = CellIndex::try_from(coord).ok(); 24 | let reference = h3api::local_ij_to_cell(coord); 25 | 26 | assert_eq!( 27 | result, reference, 28 | "anchor {index}, index {neighbor}" 29 | ); 30 | } 31 | } 32 | } 33 | } 34 | }; 35 | } 36 | 37 | exhaustive_test!(exhaustive_res0, 0); 38 | exhaustive_test!(exhaustive_res1, 1); 39 | exhaustive_test!(exhaustive_res2, 2); 40 | -------------------------------------------------------------------------------- /tests/h3/max_face_count.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::CellIndex; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal) => { 6 | #[test] 7 | fn $name() { 8 | let index = CellIndex::try_from($index).expect("cell index"); 9 | let result = index.max_face_count(); 10 | let reference = h3api::max_face_count(index); 11 | 12 | assert_eq!(result, reference); 13 | } 14 | }; 15 | } 16 | 17 | test!(hexagon, 0x87283080dffffff); 18 | test!(pentagon, 0x870800000ffffff); 19 | -------------------------------------------------------------------------------- /tests/h3/max_grid_disk_size.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | 3 | macro_rules! test { 4 | ($name:ident, $k:literal) => { 5 | #[test] 6 | fn $name() { 7 | let result = h3o::max_grid_disk_size($k); 8 | let reference = h3api::max_grid_disk_size($k); 9 | 10 | assert_eq!(result, reference); 11 | } 12 | }; 13 | } 14 | 15 | test!(zero, 0); 16 | test!(one, 1); 17 | test!(many, 42); 18 | -------------------------------------------------------------------------------- /tests/h3/mod.rs: -------------------------------------------------------------------------------- 1 | mod are_neighbor_cells; 2 | mod cell_area_km2; 3 | mod cell_area_m2; 4 | mod cell_area_rads2; 5 | mod cell_to_boundary; 6 | mod cell_to_center_child; 7 | mod cell_to_child_pos; 8 | mod cell_to_children; 9 | mod cell_to_children_size; 10 | mod cell_to_latlng; 11 | mod cell_to_local_ij; 12 | mod cell_to_parent; 13 | mod cell_to_vertex; 14 | mod cell_to_vertexes; 15 | mod cells_to_directed_edge; 16 | mod child_pos_to_cell; 17 | mod compact_cells; 18 | mod degs_to_rads; 19 | mod directed_edge_to_boundary; 20 | mod directed_edge_to_cells; 21 | mod edge_length_km; 22 | mod edge_length_m; 23 | mod edge_length_rads; 24 | mod get_base_cell_number; 25 | mod get_directed_edge_destination; 26 | mod get_directed_edge_origin; 27 | mod get_hexagon_area_avg_km2; 28 | mod get_hexagon_area_avg_m2; 29 | mod get_hexagon_area_avg_rads2; 30 | mod get_icosahedron_faces; 31 | mod get_num_cells; 32 | mod get_pentagons; 33 | mod get_res0_cells; 34 | mod get_resolution; 35 | mod great_circle_distance_km; 36 | mod great_circle_distance_m; 37 | mod great_circle_distance_rads; 38 | mod grid_disk; 39 | mod grid_disk_distances; 40 | mod grid_disk_distances_safe; 41 | mod grid_disk_distances_unsafe; 42 | mod grid_disk_unsafe; 43 | mod grid_disks_unsafe; 44 | mod grid_distance; 45 | mod grid_path_cells; 46 | mod grid_path_cells_size; 47 | mod grid_ring_unsafe; 48 | mod h3_to_string; 49 | mod h3api; 50 | mod is_pentagon; 51 | mod is_res_class3; 52 | mod is_valid_cell; 53 | mod is_valid_directed_edge; 54 | mod is_valid_vertex; 55 | mod latlng_to_cell; 56 | mod local_ij_to_cell; 57 | mod max_face_count; 58 | mod max_grid_disk_size; 59 | mod origin_to_directed_edges; 60 | mod pentagon_count; 61 | mod rads_to_degs; 62 | mod res0_cell_count; 63 | mod string_to_h3; 64 | mod uncompact_cells; 65 | mod uncompact_cells_size; 66 | mod vertex_to_latlng; 67 | -------------------------------------------------------------------------------- /tests/h3/origin_to_directed_edges.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::CellIndex; 3 | 4 | macro_rules! test { 5 | ($name:ident, $index:literal) => { 6 | #[test] 7 | fn $name() { 8 | let index = CellIndex::try_from($index).expect("cell index"); 9 | let result = index.edges().collect::>(); 10 | let reference = h3api::origin_to_directed_edges(index); 11 | 12 | assert_eq!(result, reference); 13 | } 14 | }; 15 | } 16 | 17 | test!(hexagon, 0x8f0800000000000); 18 | test!(pentagon, 0x8f734e64992d6d8); 19 | -------------------------------------------------------------------------------- /tests/h3/pentagon_count.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::Resolution; 3 | 4 | #[test] 5 | fn value() { 6 | let result = Resolution::pentagon_count(); 7 | let reference = h3api::pentagon_count(); 8 | 9 | assert_eq!(result, reference); 10 | } 11 | -------------------------------------------------------------------------------- /tests/h3/rads_to_degs.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use float_eq::assert_float_eq; 3 | 4 | macro_rules! test { 5 | ($name:ident, $angle:literal) => { 6 | #[test] 7 | fn $name() { 8 | let result = $angle.to_degrees(); 9 | let reference = h3api::rads_to_degs($angle); 10 | 11 | assert_float_eq!(result, reference, abs <= f64::EPSILON); 12 | } 13 | }; 14 | } 15 | 16 | test!(positive, 0.8526719057477519_f64); 17 | test!(negative, -0.8526719057477519_f64); 18 | test!(large_positive, 7.833988913707753_f64); 19 | test!(large_negative, -7.833988913707753_f64); 20 | -------------------------------------------------------------------------------- /tests/h3/res0_cell_count.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::BaseCell; 3 | 4 | #[test] 5 | fn value() { 6 | let result = BaseCell::count(); 7 | let reference = h3api::res0_cell_count(); 8 | 9 | assert_eq!(result, reference); 10 | } 11 | -------------------------------------------------------------------------------- /tests/h3/string_to_h3.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, DirectedEdgeIndex, VertexIndex}; 3 | 4 | macro_rules! test { 5 | ($name:ident, $string:literal, $ty:ty) => { 6 | #[test] 7 | fn $name() { 8 | let result = $string.parse::<$ty>().expect("cell index"); 9 | let reference = 10 | h3api::string_to_h3::<$ty>($string).expect("cell index"); 11 | 12 | assert_eq!(result, reference); 13 | } 14 | }; 15 | } 16 | 17 | test!(cell_index_res0, "802bfffffffffff", CellIndex); 18 | test!(cell_index_res12, "8c2bae305336bff", CellIndex); 19 | test!(cell_index_res15, "8f2834782b9c2ab", CellIndex); 20 | test!(edge_index_res0, "1302bfffffffffff", DirectedEdgeIndex); 21 | test!(edge_index_res12, "13c2bae305336bff", DirectedEdgeIndex); 22 | test!(edge_index_res15, "15f2834782b9c2ab", DirectedEdgeIndex); 23 | test!(vertex_index_res0, "2302bfffffffffff", VertexIndex); 24 | test!(vertex_index_res12, "23f2834782b9c2a8", VertexIndex); 25 | test!(vertex_index_res15, "21c2bae305330dff", VertexIndex); 26 | -------------------------------------------------------------------------------- /tests/h3/uncompact_cells.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! test { 5 | ($name:ident, $compacted:expr, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let compacted = $compacted 9 | .into_iter() 10 | .map(|value| CellIndex::try_from(value).expect("cell index")) 11 | .collect::>(); 12 | let resolution = 13 | Resolution::try_from($resolution).expect("index resolution"); 14 | let result = 15 | CellIndex::uncompact(compacted.iter().copied(), resolution) 16 | .collect::>(); 17 | let reference = h3api::uncompact_cells(&compacted, resolution); 18 | 19 | assert_eq!(result, reference); 20 | } 21 | }; 22 | } 23 | 24 | test!(single_hexagon, vec![0x802bfffffffffff], 5); 25 | test!(single_pentagon, vec![0x820807fffffffff], 5); 26 | test!( 27 | mix, 28 | vec![0x802bfffffffffff, 0x820807fffffffff, 0x83734efffffffff], 29 | 5 30 | ); 31 | -------------------------------------------------------------------------------- /tests/h3/uncompact_cells_size.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use h3o::{CellIndex, Resolution}; 3 | 4 | macro_rules! test { 5 | ($name:ident, $compacted:expr, $resolution:literal) => { 6 | #[test] 7 | fn $name() { 8 | let compacted = $compacted 9 | .into_iter() 10 | .map(|value| CellIndex::try_from(value).expect("cell index")) 11 | .collect::>(); 12 | let resolution = 13 | Resolution::try_from($resolution).expect("index resolution"); 14 | let result = CellIndex::uncompact_size( 15 | compacted.iter().copied(), 16 | resolution, 17 | ); 18 | let reference = h3api::uncompact_cells_size(&compacted, resolution); 19 | 20 | assert_eq!(result, reference); 21 | } 22 | }; 23 | } 24 | 25 | test!(single_hexagon, vec![0x802bfffffffffff], 5); 26 | test!(single_pentagon, vec![0x820807fffffffff], 5); 27 | test!( 28 | mix, 29 | vec![0x802bfffffffffff, 0x820807fffffffff, 0x83734efffffffff], 30 | 5 31 | ); 32 | -------------------------------------------------------------------------------- /tests/h3/vertex_to_latlng.rs: -------------------------------------------------------------------------------- 1 | use super::h3api; 2 | use float_eq::assert_float_eq; 3 | use h3o::{CellIndex, LatLng, Resolution, Vertex}; 4 | 5 | macro_rules! exhaustive_test { 6 | ($name:ident, $resolution:literal) => { 7 | #[test] 8 | fn $name() { 9 | let resolution = 10 | Resolution::try_from($resolution).expect("index resolution"); 11 | for index in CellIndex::base_cells() 12 | .flat_map(|index| index.children(resolution)) 13 | { 14 | for vertex in 0..6 { 15 | let vertex = Vertex::try_from(vertex).expect("cell vertex"); 16 | if let Some(index) = index.vertex(vertex) { 17 | let result = LatLng::from(index); 18 | let reference = h3api::vertex_to_latlng(index); 19 | 20 | assert_float_eq!( 21 | result.lat_radians(), 22 | reference.lat_radians(), 23 | abs <= f64::from(f32::EPSILON), 24 | "latitude (vertex {index})" 25 | ); 26 | assert_float_eq!( 27 | result.lng_radians(), 28 | reference.lng_radians(), 29 | abs <= f64::from(f32::EPSILON), 30 | "longitude (vertex {index})" 31 | ); 32 | } 33 | } 34 | } 35 | } 36 | }; 37 | } 38 | 39 | exhaustive_test!(exhaustive_res0, 0); 40 | exhaustive_test!(exhaustive_res1, 1); 41 | exhaustive_test!(exhaustive_res2, 2); 42 | -------------------------------------------------------------------------------- /tests/integration_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate alloc; 2 | 3 | mod api; 4 | // Test against the reference implementation. 5 | mod h3; 6 | -------------------------------------------------------------------------------- /tools/average_edge_length.rs: -------------------------------------------------------------------------------- 1 | //! Compute average H3 cell edge length. 2 | //! 3 | //! Inspired from https://gist.github.com/mciethan/3e10802c1f41972831c325994d97ef27 4 | use h3o::{CellIndex, DirectedEdgeIndex, Resolution}; 5 | use polyfit_rs::polyfit_rs::polyfit; 6 | 7 | fn main() { 8 | // Compute the exact average for the resolution 0 to 6. 9 | let mut averages = Resolution::range(Resolution::Zero, Resolution::Six) 10 | .map(avg_edge_len_at_res) 11 | .collect::>(); 12 | // Extrapolate values for finer resolutions. 13 | let x = [0., 1., 2., 3., 4., 5., 6.]; 14 | let y = averages.iter().map(|value| value.ln()).collect::>(); 15 | let coeffs = polyfit(&x, &y, 1).expect("polynomial coefficients"); 16 | for resolution in 7..=15_u8 { 17 | averages.push((coeffs[1] * f64::from(resolution) + coeffs[0]).exp()) 18 | } 19 | for avg in averages { 20 | let avg_km = avg * h3o::EARTH_RADIUS_KM; 21 | let avg_m = avg_km * 1000.; 22 | println!("{avg},{avg_km},{avg_m}"); 23 | } 24 | } 25 | 26 | /// Returns every edge at the given resolution. 27 | fn edges_at_res( 28 | resolution: Resolution, 29 | ) -> impl Iterator { 30 | CellIndex::uncompact(CellIndex::base_cells(), resolution) 31 | .flat_map(|cell| cell.edges()) 32 | } 33 | 34 | /// Compute the average edge length (in radians) at the given resolution. 35 | fn avg_edge_len_at_res(resolution: Resolution) -> f64 { 36 | let (count, sum) = edges_at_res(resolution) 37 | .fold((0, 0.), |(count, sum), edge| { 38 | (count + 1, sum + edge.length_rads()) 39 | }); 40 | sum / count as f64 41 | } 42 | --------------------------------------------------------------------------------