nim-h3

June 14, 2026 · View on GitHub

CI

High-quality Nim wrapper for the H3 v4 hierarchical hexagonal geospatial indexing system. Embeds the upstream C source — no system library dependencies required.

Quick Start

import h3

# ── Coordinate → cell → coordinate round-trip ──────────────────────
let sf = LatLng(lat: 37.7749, lng: -122.4194)
let cell = latLngToCell(sf, 9).value
echo cell                             # 0x89283082837ffff

let center = cellToLatLng(cell).value
echo center.lat, ", ", center.lng     # 37.77493..., -122.4194...

# ── Cell inspection ────────────────────────────────────────────────
echo cell.resolution                # 9
echo cell.isValidCell               # true
echo cell.cellToBoundary.value.len  # vertex count of hex boundary

# ── String parsing and hex display ─────────────────────────────────
echo $cell                          # 0x89283082837ffff
let parsed = parseHexCell("89283082837ffff").value
assert parsed == cell
echo cell.h3ToString.value          # 89283082837ffff

# ── Neighbors, distances, and paths ────────────────────────────────
for neighbor in gridDisk(cell, 2):   # all cells within 2 steps
  echo neighbor

echo gridDistance(cell, gridRing(cell, 1).value[0]).value   # 1

for c in gridPath(cell, gridRing(cell, 2).value[0]):        # shortest line
  echo c

# ── Resolution hierarchy ───────────────────────────────────────────
let parent = cellToParent(cell, 5).value
let children = cellToChildren(parent, 7).value   # 49 children
echo compactCells(children).value.len             # 7 compacted cells

# ── Polygon fill ───────────────────────────────────────────────────
let box = GeoPolygon(
  geoloop: @[
    LatLng(lat: 37.78, lng: -122.42),
    LatLng(lat: 37.78, lng: -122.41),
    LatLng(lat: 37.77, lng: -122.41),
    LatLng(lat: 37.77, lng: -122.42),
  ],
  holes: @[]
)
for c in polygonToCells(box, 10):    # fill polygon with cells
  echo c

# ── Directed edges ─────────────────────────────────────────────────
let neighbor = gridRing(cell, 1).value[0]
let edge = cellsToDirectedEdge(cell, neighbor).value
assert edge.directedEdgeOrigin.value == cell
assert edge.reverseDirectedEdge.value.directedEdgeOrigin.value == neighbor
echo edge.directedEdgeToBoundary.value.len   # 2

# ── The ? operator for ergonomic error propagation ─────────────────
proc cellWithNeighbors(latLng: LatLng, res: int): Result[seq[H3Cell], H3Error] =
  let origin = ?latLngToCell(latLng, res)
  let ring = ?gridRing(origin, 1)
  ok(@[origin] & ring)

let ring = cellWithNeighbors(sf, 9).valueOr:
  echo "failed: ", error
echo ring.len                          # 7 (origin + 6 neighbors)

Installation

nimble install https://github.com/ayman-albaz/nim-h3

Or in your .nimble file:

requires "h3 >= 0.1.0"

Key Features

  • No system library dependencies — H3 v4 C source is embedded in-tree
  • Result-based error handling — every fallible operation returns Result[T, H3Error]; the ? operator propagates errors ergonomically
  • Incremental iteratorscellToChildren and res0Cells use the C library's native state machines for true zero-allocation streaming; all iterators are safe to break out of
  • Full API coverage — indexing, inspection, traversal, hierarchy, regions, edges, vertexes, and measurement functions

Requirements

  • Nim >= 2.0.0
  • No system dependencies (all C source is embedded)

License

Apache-2.0 — same as Uber's H3 library.

Development

nimble test

Notes

  • LLMs were used to help create this wrapper library.
  • All code was reviewed by myself.