///|
/// The maximum grid disk radius accepted by this implementation.
pub const MAX_GRID_DISK_RADIUS : Int = 64
///|
fn geometric_neighbor(index : H3Index, direction : Int) -> H3Index? {
let original = index_to_face_ijk(index.raw())
let resolution = index.resolution()
let moved = coord_neighbor(original.1, direction)
let mut adjusted_resolution = resolution
let mut current = moved
if is_resolution_class_iii(adjusted_resolution) {
current = coord_down_ap7r(current)
adjusted_resolution += 1
}
let (adjusted, overage) = adjust_overage_class_ii(
FaceIJK(original.0, current),
adjusted_resolution,
false,
)
let selected = if overage == NoOverage {
moved
} else if adjusted_resolution != resolution {
coord_up_ap7r(adjusted.1)
} else {
adjusted.1
}
let face = if overage == NoOverage { original.0 } else { adjusted.0 }
match face_ijk_to_h3(FaceIJK(face, selected), resolution) {
Ok(value) =>
match H3Index::from_u64(value) {
Ok(cell) => Some(cell)
Err(_) => None
}
Err(_) => None
}
}
///|
/// Returns the neighboring cell in an H3 index direction from 1 through 6.
///
/// Pentagons have one unavailable direction. In that case,
/// `UnavailableNeighbor` is returned.
pub fn H3Index::neighbor(
self : H3Index,
direction : Int,
) -> Result[H3Index, H3Error] {
if direction < 1 || direction > 6 {
return Err(InvalidDirection(direction))
}
match geometric_neighbor(self, direction) {
Some(neighbor) => Ok(neighbor)
None => Err(UnavailableNeighbor(direction))
}
}
///|
/// Returns whether two cells share an edge.
pub fn H3Index::is_neighbor_of(self : H3Index, other : H3Index) -> Bool {
if self.resolution() != other.resolution() {
return false
}
let mut direction = 1
while direction <= 6 {
match geometric_neighbor(self, direction) {
Some(neighbor) => if neighbor == other { return true }
None => ()
}
direction += 1
}
false
}
///|
/// Returns the direction from this cell to `other`, when they are neighbors.
pub fn H3Index::direction_to(self : H3Index, other : H3Index) -> Int? {
if self.resolution() != other.resolution() {
return None
}
let mut direction = 1
while direction <= 6 {
match geometric_neighbor(self, direction) {
Some(neighbor) => if neighbor == other { return Some(direction) }
None => ()
}
direction += 1
}
None
}
///|
fn H3Index::grid_disk_entries(
self : H3Index,
radius : Int,
) -> Result[Array[(H3Index, Int)], H3Error] {
if radius < 0 || radius > MAX_GRID_DISK_RADIUS {
return Err(InvalidGridDiskRadius(radius))
}
let visited : @hashmap.HashMap[H3Index, Unit] = @hashmap.HashMap([])
let result : Array[(H3Index, Int)] = []
let mut frontier : Array[H3Index] = [self]
visited.set(self, ())
let mut distance = 0
while distance <= radius {
for cell in frontier {
result.push((cell, distance))
}
if distance == radius {
break
}
let next : Array[H3Index] = []
for cell in frontier {
for direction in 1..<=6 {
match geometric_neighbor(cell, direction) {
Some(neighbor) =>
if !visited.contains(neighbor) {
visited.set(neighbor, ())
next.push(neighbor)
}
None => ()
}
}
}
frontier = next
distance += 1
}
Ok(result)
}
///|
/// Returns the origin, its direct neighbors, and all cells within `radius`
/// grid steps.
///
/// The result does not contain duplicates and is ordered by increasing grid
/// distance from the origin.
pub fn H3Index::grid_disk(
self : H3Index,
radius : Int,
) -> Result[Array[H3Index], H3Error] {
let entries = match self.grid_disk_entries(radius) {
Ok(entries) => entries
Err(error) => return Err(error)
}
let result : Array[H3Index] = []
for entry in entries {
result.push(entry.0)
}
Ok(result)
}
///|
/// Returns each cell within `radius` together with its grid distance.
pub fn H3Index::grid_disk_distances(
self : H3Index,
radius : Int,
) -> Result[Array[(H3Index, Int)], H3Error] {
self.grid_disk_entries(radius)
}
///|
/// Returns only the cells exactly `radius` grid steps from the origin.
pub fn H3Index::grid_ring(
self : H3Index,
radius : Int,
) -> Result[Array[H3Index], H3Error] {
let entries = match self.grid_disk_entries(radius) {
Ok(entries) => entries
Err(error) => return Err(error)
}
let result : Array[H3Index] = []
for entry in entries {
if entry.1 == radius {
result.push(entry.0)
}
}
Ok(result)
}
///|
/// Returns the exact grid distance to another cell within the supported search
/// radius.
pub fn H3Index::grid_distance_to(
self : H3Index,
other : H3Index,
) -> Result[Int, H3Error] {
if self.resolution() != other.resolution() {
return Err(ResolutionMismatch(other.resolution(), self.resolution()))
}
if self == other {
return Ok(0)
}
let entries = match self.grid_disk_entries(MAX_GRID_DISK_RADIUS) {
Ok(entries) => entries
Err(error) => return Err(error)
}
for entry in entries {
if entry.0 == other {
return Ok(entry.1)
}
}
Err(GridDistanceOutOfRange)
}