Geodetic::Coordinate::H3¶
H3 Hexagonal Hierarchical Index¶
H3 is Uber's hierarchical geospatial indexing system that divides the globe into hexagonal cells (and 12 pentagons per resolution level) using an icosahedron projection. Each cell is identified by a 64-bit integer, typically displayed as a 15-character hex string like 872a1072bffffff.
H3 requires the libh3 C library installed on your system. Without it, all other coordinate systems work normally; H3 operations raise a clear error message with installation instructions.
Prerequisites¶
# macOS (Homebrew)
brew install h3
# Linux (build from source)
git clone https://github.com/uber/h3.git
cd h3
cmake -B build -DCMAKE_INSTALL_PREFIX=/usr/local
cmake --build build
sudo cmake --install build
You can also set the LIBH3_PATH environment variable to specify a custom library path:
Geodetic searches these paths automatically:
- /opt/homebrew/lib/libh3.dylib (macOS ARM Homebrew)
- /usr/local/lib/libh3.dylib (macOS Intel Homebrew)
- /usr/lib/libh3.so (Linux system)
- /usr/local/lib/libh3.so (Linux local install)
Key Differences from Other Spatial Hashes¶
| Feature | GH/OLC/GARS/GEOREF/HAM | H3 |
|---|---|---|
| Cell shape | BoundingBox | Hexagon (6 vertices) |
to_area returns |
Areas::BoundingBox |
Areas::Polygon |
neighbors returns |
Hash with 8 cardinal keys | Array of 6 cells |
| Code format | String | 64-bit integer (hex string) |
| Dependency | None (pure Ruby) | libh3 (C library via fiddle) |
H3 is a 2D coordinate system (no altitude). Conversions to/from other systems go through LLA as the intermediary. Each hex string represents a hexagonal cell; the coordinate's point value is the cell's centroid.
Constructor¶
# From a hex string
coord = Geodetic::Coordinate::H3.new("872a1072bffffff")
# From a hex string with 0x prefix
coord = Geodetic::Coordinate::H3.new("0x872a1072bffffff")
# From a 64-bit integer
coord = Geodetic::Coordinate::H3.new(0x872a1072bffffff)
# From any coordinate (converts via LLA)
coord = Geodetic::Coordinate::H3.new(lla_coord)
coord = Geodetic::Coordinate::H3.new(utm_coord, precision: 9)
| Parameter | Type | Default | Description |
|---|---|---|---|
source |
String, Integer, or Coord | -- | An H3 hex string, integer, or coordinate |
precision |
Integer | 7 | H3 resolution level (0-15) |
Raises ArgumentError if the source string is empty, contains invalid hex characters, or does not represent a valid H3 cell index. String input is case-insensitive (normalized to lowercase). The 0x prefix is stripped automatically.
Attributes¶
| Attribute | Type | Access | Description |
|---|---|---|---|
code |
String | read-only | The hex string representation |
h3_index |
Integer | read-only | The 64-bit H3 cell index |
H3 is immutable -- there are no setter methods.
Resolution¶
H3 uses "resolution" (0-15) instead of string-length precision. Higher resolution means smaller cells.
| Resolution | Approximate Cell Area | Approximate Edge Length |
|---|---|---|
| 0 | 4,357,449 km^2 | 1,108 km |
| 1 | 609,788 km^2 | 419 km |
| 2 | 86,801 km^2 | 158 km |
| 3 | 12,393 km^2 | 60 km |
| 4 | 1,770 km^2 | 23 km |
| 5 | 252 km^2 | 8.5 km |
| 6 | 36 km^2 | 3.2 km |
| 7 | 5.2 km^2 (default) | 1.2 km |
| 8 | 0.74 km^2 | 461 m |
| 9 | 0.105 km^2 | 174 m |
| 10 | 0.015 km^2 | 66 m |
| 11 | 0.002 km^2 | 25 m |
| 12 | 307 m^2 | 9.4 m |
| 13 | 43 m^2 | 3.6 m |
| 14 | 6.2 m^2 | 1.3 m |
| 15 | 0.9 m^2 | 0.5 m |
coord.resolution # => 7 (alias: coord.precision)
coord.cell_area # => 5182586.98 (square meters)
coord.precision_in_meters # => { lat: ~2276, lng: ~2276, area_m2: ~5182586 }
Checking Availability¶
Conversions¶
All conversions chain through LLA. The datum parameter defaults to Geodetic::WGS84.
Instance Methods¶
coord.to_lla # => LLA (centroid of the cell)
coord.to_ecef
coord.to_utm
coord.to_enu(reference_lla)
coord.to_ned(reference_lla)
coord.to_mgrs
coord.to_usng
coord.to_web_mercator
coord.to_ups
coord.to_state_plane(zone_code)
coord.to_bng
coord.to_gh36
coord.to_gh
coord.to_ham
coord.to_olc
coord.to_georef
coord.to_gars
Class Methods¶
H3.from_lla(lla_coord)
H3.from_ecef(ecef_coord)
H3.from_utm(utm_coord)
H3.from_web_mercator(wm_coord)
H3.from_gh(gh_coord)
H3.from_georef(georef_coord)
H3.from_gars(gars_coord)
# ... and all other coordinate systems
LLA Convenience Methods¶
lla = Geodetic::Coordinate::LLA.new(lat: 40.689167, lng: -74.044444)
h3 = lla.to_h3 # default resolution 7
h3 = lla.to_h3(precision: 9) # resolution 9
lla = Geodetic::Coordinate::LLA.from_h3(h3)
Serialization¶
to_s(format = nil)¶
Returns the hex string. Pass :integer to get the 64-bit integer value.
coord = H3.new("872a1072bffffff")
coord.to_s # => "872a1072bffffff"
coord.to_s(:integer) # => 608693941536498687
coord.h3_index # => 608693941536498687
to_a¶
Returns [lat, lng] of the cell centroid.
from_string / from_array¶
H3.from_string("872a1072bffffff") # from hex string
H3.from_array([40.689167, -74.044444]) # from [lat, lng]
Neighbors¶
Returns all adjacent cells as an Array of H3 instances. Hexagons have 6 neighbors; pentagons have 5.
Note: unlike the rectangular spatial hashes which return a directional Hash (:N, :S, etc.), H3 returns a flat Array because hexagonal cells do not have cardinal directions.
coord = H3.new("872a1072bffffff")
neighbors = coord.neighbors
# => [H3, H3, H3, H3, H3, H3]
neighbors.length # => 6
Grid Disk¶
The grid_disk(k) method returns all cells within k steps. This is a generalization of neighbors (which is grid_disk(1) minus self).
coord.grid_disk(0) # => [self] (1 cell)
coord.grid_disk(1) # => [self + 6 neighbors] (7 cells)
coord.grid_disk(2) # => 19 cells
Parent and Children¶
Navigate the H3 hierarchy by moving to coarser or finer resolution levels.
coord = H3.new("872a1072bffffff") # resolution 7
parent = coord.parent(5) # => H3 at resolution 5
parent.resolution # => 5
children = coord.children(8) # => Array of 7 H3 cells at resolution 8
children.length # => 7
children.first.resolution # => 8
parent raises ArgumentError if the target resolution is not coarser (lower number). children raises ArgumentError if the target resolution is not finer (higher number).
Area¶
The to_area method returns the hexagonal cell boundary as an Areas::Polygon with 6 vertices (5 for pentagons).
area = coord.to_area
# => Geodetic::Areas::Polygon
area.includes?(coord.to_lla) # => true (centroid is inside the cell)
area.boundary.length # => 7 (6 vertices + closing point)
Pentagon Detection¶
12 cells at each resolution level are pentagons (artifacts of the icosahedral projection). These have 5 neighbors and 5 boundary vertices instead of 6.
Cell Area¶
Equality¶
Two H3 instances are equal if their hex strings match exactly.
H3.new("872a1072bffffff") == H3.new("872a1072bffffff") # => true
H3.new("872a1072bffffff") == H3.new(0x872a1072bffffff) # => true (integer)
H3.new("872a1072bffffff") == H3.new("87195da49ffffff") # => false
valid?¶
Returns true if the H3 cell index is valid according to the H3 library.
Universal Distance and Bearing Methods¶
H3 supports all universal distance and bearing methods via the DistanceMethods and BearingMethods mixins:
a = H3.new("872a1072bffffff") # Statue of Liberty area
b = H3.new("87195da49ffffff") # London area
a.distance_to(b) # => Distance (~5,570 km)
a.straight_line_distance_to(b) # => Distance
a.bearing_to(b) # => Bearing
a.elevation_to(b) # => Float (degrees)
Well-Known H3 Cells¶
| Location | H3 Index (res 7) | Resolution |
|---|---|---|
| Statue of Liberty | 872a1072bffffff |
7 |
| London | 87195da49ffffff |
7 |
| Null Island (0, 0) | 87754e64dffffff |
7 |
Implementation Notes¶
Geodetic uses Ruby's fiddle (part of the standard library) to call the H3 v4 C API directly. No gem dependency beyond fiddle is required. The H3 C library must be installed separately.
The library search order is:
1. LIBH3_PATH environment variable
2. /opt/homebrew/lib/libh3.dylib (macOS ARM)
3. /usr/local/lib/libh3.dylib (macOS Intel)
4. /usr/lib/libh3.so (Linux)
5. /usr/local/lib/libh3.so (Linux local)
6. Architecture-specific Linux paths