Python bindings for H3, a hierarchical hexagonal geospatial indexing system
84
Operations for working with H3's hierarchical structure, including parent-child relationships, cell compaction, and resolution changes. H3's multi-resolution hierarchy enables efficient storage and analysis across different scales.
Navigate up and down the hierarchical structure between different resolution levels.
def cell_to_parent(h: str, res: int = None) -> str:
"""
Get the parent cell at a coarser resolution.
Args:
h: H3 cell identifier
res: Target parent resolution. If None, uses resolution(h) - 1
Returns:
Parent H3 cell identifier at target resolution
Raises:
H3CellInvalidError: If h is not a valid H3 cell
H3ResDomainError: If res < 0 or res >= resolution(h)
Note:
Parent resolution must be coarser (lower number) than child.
Each cell has exactly one parent at each coarser resolution.
"""
def cell_to_children(h: str, res: int = None) -> list[str]:
"""
Get all child cells at a finer resolution.
Args:
h: H3 cell identifier
res: Target child resolution. If None, uses resolution(h) + 1
Returns:
List of child H3 cell identifiers at target resolution
Raises:
H3CellInvalidError: If h is not a valid H3 cell
H3ResDomainError: If res > 15 or res <= resolution(h)
Note:
Child resolution must be finer (higher number) than parent.
Most cells have 7 children, but some have different counts due to
the spherical geometry and pentagon locations.
"""
def cell_to_children_size(h: str, res: int = None) -> int:
"""
Get the count of child cells at a finer resolution.
Args:
h: H3 cell identifier
res: Target child resolution. If None, uses resolution(h) + 1
Returns:
Number of child cells at target resolution
Raises:
H3CellInvalidError: If h is not a valid H3 cell
H3ResDomainError: If res > 15 or res <= resolution(h)
Note:
More efficient than len(cell_to_children(h, res)) for large child counts.
"""
def cell_to_center_child(h: str, res: int = None) -> str:
"""
Get the center child cell at a finer resolution.
The center child is the child cell whose center is closest to
the parent cell's center.
Args:
h: H3 cell identifier
res: Target child resolution. If None, uses resolution(h) + 1
Returns:
Center child H3 cell identifier
Raises:
H3CellInvalidError: If h is not a valid H3 cell
H3ResDomainError: If res > 15 or res <= resolution(h)
"""Work with child position indices within parent cells.
def cell_to_child_pos(child: str, res_parent: int) -> int:
"""
Get the position index of a child cell within its parent.
Args:
child: Child H3 cell identifier
res_parent: Resolution of the parent cell
Returns:
Integer position index of child relative to parent
Raises:
H3CellInvalidError: If child is not a valid H3 cell
H3ResDomainError: If res_parent >= resolution(child)
Note:
Position indices are stable: the same child always has the same
position within its parent across different operations.
"""
def child_pos_to_cell(parent: str, res_child: int, child_pos: int) -> str:
"""
Get a specific child cell by position index.
Args:
parent: Parent H3 cell identifier
res_child: Resolution of desired child cell
child_pos: Position index of desired child
Returns:
Child H3 cell identifier at specified position
Raises:
H3CellInvalidError: If parent is not a valid H3 cell
H3ResDomainError: If res_child <= resolution(parent) or res_child > 15
ValueError: If child_pos is outside valid range for this parent
Note:
Valid child_pos range is 0 to cell_to_children_size(parent, res_child) - 1.
"""Optimize cell collections by combining child cells into parents where possible.
def compact_cells(cells: list[str]) -> list[str]:
"""
Compact a collection of cells by combining children into parents.
When all children of a parent cell are present in the input collection,
they are replaced by their parent cell. This process continues recursively
up the hierarchy until no further compaction is possible.
Args:
cells: Collection of H3 cell identifiers (must all be same resolution)
Returns:
Compacted list of H3 cell identifiers (mixed resolutions)
Raises:
H3ResMismatchError: If input cells have different resolutions
H3CellInvalidError: If any cell identifier is invalid
Note:
Input cells must all be at the same resolution.
Output may contain cells at various resolutions.
Compaction reduces storage and improves query performance.
"""
def uncompact_cells(cells: list[str], res: int) -> list[str]:
"""
Expand a collection of cells to a uniform resolution.
All cells in the collection are expanded to their children at the
target resolution. This reverses the compact_cells operation.
Args:
cells: Collection of H3 cell identifiers (mixed resolutions allowed)
res: Target resolution for all output cells
Returns:
List of H3 cell identifiers all at target resolution
Raises:
H3ResDomainError: If res > 15
H3CellInvalidError: If any cell identifier is invalid
ValueError: If any input cell has resolution > res
Note:
All input cells must have resolution <= target resolution.
Output cells provide complete coverage of input area at uniform resolution.
"""Get information about cells at specific resolutions and system structure.
def get_res0_cells() -> list[str]:
"""
Get all H3 cells at resolution 0 (base cells).
Returns:
List of all 122 base H3 cells covering the Earth
Note:
These are the coarsest cells in the H3 system.
All other cells are descendants of these base cells.
Order is not guaranteed.
"""
def get_pentagons(res: int) -> list[str]:
"""
Get all pentagon cells at a given resolution.
Args:
res: H3 resolution (0-15)
Returns:
List of all 12 pentagon H3 cells at the specified resolution
Raises:
H3ResDomainError: If res < 0 or res > 15
Note:
There are exactly 12 pentagons at every resolution level.
All other cells at that resolution are hexagons.
Pentagons occur at the 12 vertices of the icosahedron.
"""import h3
# Start with a high-resolution cell
child_cell = h3.latlng_to_cell(37.7749, -122.4194, 9)
print(f"Child cell (res 9): {child_cell}")
print(f"Resolution: {h3.get_resolution(child_cell)}")
# Get parent at resolution 7
parent = h3.cell_to_parent(child_cell, res=7)
print(f"Parent cell (res 7): {parent}")
# Get all children of the parent back at resolution 9
children = h3.cell_to_children(parent, res=9)
print(f"Parent has {len(children)} children at resolution 9")
print(f"Original child in children: {child_cell in children}") # True
# Get just the center child
center_child = h3.cell_to_center_child(parent, res=9)
print(f"Center child: {center_child}")
print(f"Center child is original: {center_child == child_cell}")import h3
# Trace a cell through multiple resolution levels
original = h3.latlng_to_cell(51.5074, -0.1278, 10) # London, res 10
print("Hierarchy from coarse to fine:")
current = original
# Go up to resolution 0
while h3.get_resolution(current) > 0:
parent = h3.cell_to_parent(current)
res = h3.get_resolution(parent)
print(f" Resolution {res}: {parent}")
current = parent
print(f" Resolution 0: {current} (base cell)")
# Go back down showing child counts
current = h3.cell_to_parent(original, res=5) # Start from res 5
print(f"\nChild expansion from resolution 5:")
for target_res in range(6, 11):
children = h3.cell_to_children(current, res=target_res)
print(f" Res {target_res}: {len(children)} children")
# For next iteration, use the center child
if target_res < 10:
current = h3.cell_to_center_child(current, res=target_res)import h3
# Work with child positions
parent = h3.latlng_to_cell(40.7589, -73.9851, 6) # NYC, res 6
children = h3.cell_to_children(parent)
print(f"Parent: {parent}")
print(f"Children ({len(children)}):")
# Show each child with its position
positions = []
for child in children:
pos = h3.cell_to_child_pos(child, res_parent=6)
positions.append((child, pos))
print(f" Position {pos}: {child}")
# Sort by position and verify we can reconstruct
positions.sort(key=lambda x: x[1])
print(f"\nReconstructing children by position:")
for child, pos in positions:
reconstructed = h3.child_pos_to_cell(parent, res_child=7, child_pos=pos)
print(f" Position {pos}: {reconstructed} (matches: {child == reconstructed})")import h3
import random
# Create a region with some complete families of children
center = h3.latlng_to_cell(37.7749, -122.4194, 6)
region_cells = h3.grid_disk(center, k=2) # Get area around center
# Expand all to resolution 8
expanded = h3.uncompact_cells(region_cells, res=8)
print(f"Original region: {len(region_cells)} cells at res 6")
print(f"Expanded region: {len(expanded)} cells at res 8")
# Add some random cells at resolution 8 to create incomplete families
random_cells = []
for _ in range(20):
lat = 37.7749 + random.uniform(-0.1, 0.1)
lng = -122.4194 + random.uniform(-0.1, 0.1)
cell = h3.latlng_to_cell(lat, lng, 8)
random_cells.append(cell)
all_cells = expanded + random_cells
print(f"With random additions: {len(all_cells)} cells")
# Compact back down
compacted = h3.compact_cells(all_cells)
print(f"After compaction: {len(compacted)} cells")
# Verify coverage is preserved by expanding back
verification = h3.uncompact_cells(compacted, res=8)
original_set = set(all_cells)
verification_set = set(verification)
print(f"Coverage preserved: {original_set == verification_set}")
print(f"Compression ratio: {len(compacted) / len(all_cells):.2f}")import h3
# Analyze pentagons across resolutions
print("Pentagon analysis:")
for res in range(0, 6):
pentagons = h3.get_pentagons(res)
print(f"Resolution {res}: {len(pentagons)} pentagons")
# Show some pentagon properties
pentagon = pentagons[0] # Pick first pentagon
neighbors = h3.grid_ring(pentagon, k=1)
print(f" Example pentagon {pentagon}:")
print(f" Has {len(neighbors)} neighbors (vs 6 for hexagons)")
print(f" Boundary has {len(h3.cell_to_boundary(pentagon))} vertices")
# Find pentagon children behavior
res0_pentagon = h3.get_pentagons(0)[0]
children = h3.cell_to_children(res0_pentagon, res=1)
print(f"\nResolution 0 pentagon has {len(children)} children at res 1")
# Compare to a hexagon at res 0
res0_cells = h3.get_res0_cells()
res0_hexagons = [cell for cell in res0_cells if not h3.is_pentagon(cell)]
hexagon = res0_hexagons[0]
hex_children = h3.cell_to_children(hexagon, res=1)
print(f"Resolution 0 hexagon has {len(hex_children)} children at res 1")import h3
# Analyze the base H3 system structure
base_cells = h3.get_res0_cells()
print(f"Total base cells: {len(base_cells)}")
pentagon_count = sum(1 for cell in base_cells if h3.is_pentagon(cell))
hexagon_count = len(base_cells) - pentagon_count
print(f" Pentagons: {pentagon_count}")
print(f" Hexagons: {hexagon_count}")
# Show base cell numbers
print(f"\nBase cell number range:")
base_numbers = [h3.get_base_cell_number(cell) for cell in base_cells]
print(f" Min: {min(base_numbers)}")
print(f" Max: {max(base_numbers)}")
print(f" Unique count: {len(set(base_numbers))}")
# Verify all base cells are resolution 0
resolutions = [h3.get_resolution(cell) for cell in base_cells]
print(f"\nAll resolution 0: {all(res == 0 for res in resolutions)}")
# Show some base cell coverage
print(f"\nSample base cells with centers:")
for i, cell in enumerate(base_cells[:5]):
lat, lng = h3.cell_to_latlng(cell)
is_pent = "pentagon" if h3.is_pentagon(cell) else "hexagon"
base_num = h3.get_base_cell_number(cell)
print(f" Base {base_num}: {cell} -> {lat:.1f}, {lng:.1f} ({is_pent})")Install with Tessl CLI
npx tessl i tessl/pypi-h3docs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10