Skip to content

Commit

Permalink
Use std::collections naming conventions
Browse files Browse the repository at this point in the history
  • Loading branch information
JayKickliter committed Sep 16, 2022
1 parent d8bd069 commit ca7d1da
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 122 deletions.
9 changes: 5 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ version = "0.1.0"
authors = ["Jay Kickliter <[email protected]>", "Andrew Thompson <[email protected]>"]
categories = ["science", "data-structures"]
edition = "2018"
exclude = ["/test/*"]
exclude = [".github/*", "/assets/*", "/test/*"]
readme = "README.md"
keywords = ["geo", "geography", "geospatial", "gis", "h3"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/jaykickliter/hexmap"
homepage = "https://github.com/jaykickliter/HexTree"
repository = "https://github.com/jaykickliter/HexTree"
description = """
Location to value mapping.
"""
Expand All @@ -25,9 +26,9 @@ h3ron = { version = "0.15" }
serde = { version = "1", optional = true, features = ["derive"] }

[dev-dependencies]
byteorder = "*"
byteorder = "1"
criterion = { version = "0.3", features = ["html_reports"] }
geo-types = "*"
geo-types = "0.7"

[dev-dependencies.h3-lorawan-regions]
git = "https://github.com/JayKickliter/h3-lorawan-regions.git"
Expand Down
52 changes: 38 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,45 @@
# HexSet
[![CI](https://github.com/JayKickliter/HexTree/actions/workflows/rust.yml/badge.svg)](https://github.com/JayKickliter/HexTree/actions/workflows/rust.yml) [![Documentation](https://docs.rs/hextree/badge.svg)](https://docs.rs/hextree)

A HexSet is a data structure for efficient [point-in-polygon] testing
of geographic regions.
# HexTree

You, the user, create a set by inserting [H3 cells] into a
HexSet. Internally, HexSet decomposes cells into a tree of
resolution-0 cells at the root, branching through intermediate
resolutions until reaching the leaf cells you inserted.
hextree provides tree structures that represent geographic regions
with [H3 cell]s.

HexSet automatically coalesces: on insert, any complete intermediate
cell in the tree is turned into a leaf cell. "Complete" is defined as
having all possibly child cells. Coalescing a cell allows for
optimized search, as any child cell of the coalesced cell is known to
be contained in the set.
The primary structures are:

[point-in-polygon]: https://en.wikipedia.org/wiki/Point_in_polygon
[H3 cells]: https://h3geo.org/docs/core-library/h3Indexing
- **HexTreeMap**: an H3 cell-to-value map.
- **HexTreeSet**: an H3 cell set for hit-testing.

You can think of `HexTreeMap` vs. `HexTreeSet` as [`HashMap`] vs. [`HashSet`].

## How is this different from `HashMap<H3Cell, V>`?

The key feature of a hextree is that its keys (H3 cells) are
hierarchical. For instance, if you previously inserted an entry for a
low-res hex, but later query for a higher-res child hex, the tree
returns the value for the lower res hex. Additionally, with
[compaction], trees can automatically coalesce adjacent high-res
hexagons into their parent hex. For very large regions, the compaction
process _can_ continue to lowest resolution cells (res-0), possibly
removing millions of redundant cells from the tree. For example, a set
of 4,795,661 res-7 cells representing North America coalesces [into a
42,383 element `HexTreeSet`][us915].

A hextree's internal structure exactly matches the semantics of an [H3
cell]. The root of the tree has 122 resolution-0 nodes, followed by 15
levels of 7-ary nodes. The level of an occupied node, or leaf node, is
the same as its corresponding H3 cell resolution.

## Features

* **`serde-support`**: support for serialization via [serde].

[`HashMap`]: https://doc.rust-lang.org/std/collections/struct.HashMap.html
[`HashSet`]: https://doc.rust-lang.org/std/collections/struct.HashSet.html
[H3 cell]: https://h3geo.org/docs/core-library/h3Indexing
[serde]: https://docs.rs/serde/latest/serde
[compaction]: crate::compaction
[us915]: https://www.google.com/maps/d/u/0/edit?mid=15wRzxmtmyzqf6fHU3yuW4hJAM9MoxLJs

## License

Expand Down
52 changes: 26 additions & 26 deletions benches/benches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ use geo_types::coord;
use h3_lorawan_regions::{
compact::US915 as COMPACT_US915_INDICES, nocompact::US915 as PLAIN_US915_INDICES,
};
use hextree::{compaction::EqCompactor, h3ron::H3Cell, HexMap, HexSet};
use hextree::{compaction::EqCompactor, h3ron::H3Cell, HexTreeMap, HexTreeSet};
use std::convert::TryFrom;

fn hexset_lookup(c: &mut Criterion) {
let mut group = c.benchmark_group("US915 HexSet lookup");
fn set_lookup(c: &mut Criterion) {
let mut group = c.benchmark_group("US915 HexTreeSet lookup");

let us915_hexset: HexSet = PLAIN_US915_INDICES
let us915_set: HexTreeSet = PLAIN_US915_INDICES
.iter()
.map(|&idx| H3Cell::try_from(idx).unwrap())
.collect();
Expand All @@ -26,23 +26,23 @@ fn hexset_lookup(c: &mut Criterion) {
group.bench_with_input(
BenchmarkId::new("Tarpon Spring", resolution),
&tarpon_springs,
|b, &cell| b.iter(|| us915_hexset.contains(cell)),
|b, &cell| b.iter(|| us915_set.contains(cell)),
);

group.bench_with_input(
BenchmarkId::new("Gulf of Mexico", resolution),
&gulf_of_mexico,
|b, &cell| b.iter(|| us915_hexset.contains(cell)),
|b, &cell| b.iter(|| us915_set.contains(cell)),
);

group.bench_with_input(BenchmarkId::new("Paris", resolution), &paris, |b, &cell| {
b.iter(|| us915_hexset.contains(cell))
b.iter(|| us915_set.contains(cell))
});
}
}

fn hexset_construction(c: &mut Criterion) {
let mut group = c.benchmark_group("US915 HexSet construction");
fn set_construction(c: &mut Criterion) {
let mut group = c.benchmark_group("US915 HexTreeSet construction");

let precompacted_us915_cells: Vec<H3Cell> = COMPACT_US915_INDICES
.iter()
Expand All @@ -54,26 +54,26 @@ fn hexset_construction(c: &mut Criterion) {
.collect();

group.bench_function("pre-compacted", |b| {
b.iter(|| precompacted_us915_cells.iter().collect::<HexSet>())
b.iter(|| precompacted_us915_cells.iter().collect::<HexTreeSet>())
});

group.bench_function("plain", |b| {
b.iter(|| plain_us915_cells.iter().collect::<HexSet>())
b.iter(|| plain_us915_cells.iter().collect::<HexTreeSet>())
});
}

fn hexmap_lookup(c: &mut Criterion) {
fn map_lookup(c: &mut Criterion) {
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[allow(dead_code)]
enum Region {
EU868,
US915,
}

let mut group = c.benchmark_group("US915 HexMap lookup");
let mut group = c.benchmark_group("US915 HexTreeMap lookup");

let mut us915_hexmap = HexMap::with_compactor(EqCompactor);
us915_hexmap.extend(
let mut us915_map = HexTreeMap::with_compactor(EqCompactor);
us915_map.extend(
PLAIN_US915_INDICES
.iter()
.map(|&idx| H3Cell::try_from(idx).unwrap())
Expand All @@ -92,22 +92,22 @@ fn hexmap_lookup(c: &mut Criterion) {
group.bench_with_input(
BenchmarkId::new("Tarpon Spring", resolution),
&tarpon_springs,
|b, &cell| b.iter(|| us915_hexmap.get(cell)),
|b, &cell| b.iter(|| us915_map.get(cell)),
);

group.bench_with_input(
BenchmarkId::new("Gulf of Mexico", resolution),
&gulf_of_mexico,
|b, &cell| b.iter(|| us915_hexmap.get(cell)),
|b, &cell| b.iter(|| us915_map.get(cell)),
);

group.bench_with_input(BenchmarkId::new("Paris", resolution), &paris, |b, &cell| {
b.iter(|| us915_hexmap.get(cell))
b.iter(|| us915_map.get(cell))
});
}
}

fn hexmap_construction(c: &mut Criterion) {
fn map_construction(c: &mut Criterion) {
// The value type for the map
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[allow(dead_code)]
Expand All @@ -116,7 +116,7 @@ fn hexmap_construction(c: &mut Criterion) {
US915,
}

let mut group = c.benchmark_group("US915 HexMap construction");
let mut group = c.benchmark_group("US915 HexTreeMap construction");

let precompacted_us915_cells: Vec<H3Cell> = COMPACT_US915_INDICES
.iter()
Expand All @@ -129,7 +129,7 @@ fn hexmap_construction(c: &mut Criterion) {

group.bench_function("pre-compacted", |b| {
b.iter(|| {
let mut map = HexMap::with_compactor(EqCompactor);
let mut map = HexTreeMap::with_compactor(EqCompactor);
map.extend(
precompacted_us915_cells
.iter()
Expand All @@ -142,7 +142,7 @@ fn hexmap_construction(c: &mut Criterion) {

group.bench_function("plain", |b| {
b.iter(|| {
let mut map = HexMap::with_compactor(EqCompactor);
let mut map = HexTreeMap::with_compactor(EqCompactor);
map.extend(
plain_us915_cells
.iter()
Expand All @@ -156,9 +156,9 @@ fn hexmap_construction(c: &mut Criterion) {

criterion_group!(
benches,
hexset_lookup,
hexmap_lookup,
hexset_construction,
hexmap_construction,
set_lookup,
map_lookup,
set_construction,
map_construction,
);
criterion_main!(benches);
30 changes: 15 additions & 15 deletions src/entry.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
//! `HexMap`'s Entry API.
//! `HexTreeMap`'s Entry API.

use crate::{compaction::Compactor, h3ron::H3Cell, HexMap};
use crate::{compaction::Compactor, h3ron::H3Cell, HexTreeMap};

/// A view into a single entry in a map, which may either be vacant or
/// occupied.
///
/// This enum is constructed from the [entry][HexMap::entry] method on
/// [HexMap][HexMap].
/// This enum is constructed from the [entry][HexTreeMap::entry]
/// method on [HexTreeMap][HexTreeMap].
pub enum Entry<'a, V, C> {
/// An occupied entry.
Occupied(OccupiedEntry<'a, V>),
/// A vacant entry.
Vacant(VacantEntry<'a, V, C>),
}

/// A view into an occupied entry in a `HexMap`. It is part of the
/// A view into an occupied entry in a `HexTreeMap`. It is part of the
/// [`Entry`] enum.
pub struct OccupiedEntry<'a, V> {
pub(crate) hex: H3Cell,
pub(crate) value: &'a mut V,
}

/// A view into a vacant entry in a `HexMap`. It is part of the
/// A view into a vacant entry in a `HexTreeMap`. It is part of the
/// [`Entry`] enum.
pub struct VacantEntry<'a, V, C> {
pub(crate) hex: H3Cell,
pub(crate) map: &'a mut HexMap<V, C>,
pub(crate) map: &'a mut HexTreeMap<V, C>,
}

impl<'a, V, C> Entry<'a, V, C>
Expand All @@ -38,9 +38,9 @@ where
/// # Examples
///
/// ```
/// use hextree::{h3ron::{H3Cell, Index}, HexMap};
/// use hextree::{h3ron::{H3Cell, Index}, HexTreeMap};
///
/// let mut map = HexMap::new();
/// let mut map = HexTreeMap::new();
/// let eiffel_tower_res12 = H3Cell::new(0x8c1fb46741ae9ff);
///
/// map.entry(eiffel_tower_res12)
Expand Down Expand Up @@ -73,9 +73,9 @@ where
/// # Examples
///
/// ```
/// use hextree::{h3ron::{H3Cell, Index}, HexMap};
/// use hextree::{h3ron::{H3Cell, Index}, HexTreeMap};
///
/// let mut map = HexMap::new();
/// let mut map = HexTreeMap::new();
/// let eiffel_tower_res12 = H3Cell::new(0x8c1fb46741ae9ff);
///
/// map.entry(eiffel_tower_res12)
Expand All @@ -100,9 +100,9 @@ where
/// # Examples
///
/// ```
/// use hextree::{h3ron::{H3Cell, Index}, HexMap};
/// use hextree::{h3ron::{H3Cell, Index}, HexTreeMap};
///
/// let mut map = HexMap::new();
/// let mut map = HexTreeMap::new();
/// let eiffel_tower_res12 = H3Cell::new(0x8c1fb46741ae9ff);
///
/// map.entry(eiffel_tower_res12)
Expand Down Expand Up @@ -136,9 +136,9 @@ where
/// # Examples
///
/// ```
/// use hextree::{h3ron::{H3Cell, Index}, HexMap};
/// use hextree::{h3ron::{H3Cell, Index}, HexTreeMap};
///
/// let mut map: HexMap<Option<&str>> = HexMap::new();
/// let mut map: HexTreeMap<Option<&str>> = HexTreeMap::new();
/// let eiffel_tower_res12 = H3Cell::new(0x8c1fb46741ae9ff);
///
/// map.entry(eiffel_tower_res12).or_default();
Expand Down
Loading

0 comments on commit ca7d1da

Please sign in to comment.