1 unstable release

0.1.0 Feb 9, 2026

#5 in #2048

MIT license

79KB
170 lines

bgrid-rust

A Rust implementation of the BGrid hierarchical global grid, based on the BGrid specification described below. It converts coordinates (lat/lon) to multi-level BGrid indices, decodes indices back to cell centers/bounds, and optionally maps indices to BIP39 words.

What Is BGrid?

BGrid partitions the globe into 2048 cells per level. Each level alternates its split factors:

  • Odd levels: lon = 64, lat = 32
  • Even levels: lon = 32, lat = 64

A BGrid index at each level is a 1-based integer in [1..2048], computed from row/column. Levels can be chained (1 to N levels) to increase resolution.

Coordinate Encoding (DD → BGrid)

Given latitude/longitude in decimal degrees:

  1. Normalize to unit square:
    • x = (lon + 180) / 360
    • y = (90 - lat) / 180 (north is small y, same as JS)
  2. For each level:
    • col = floor(x * lonSplits)
    • row = floor(y * latSplits)
    • index = row * lonSplits + col + 1
  3. Refine x,y to the subcell residual and repeat.

This yields an array like [531, 1563, 1600] (or CSV if formatted).

Decoding (BGrid → Cell)

Given a grid index array, the cell bounds are refined level-by-level:

  • Start with [-180..180] and [-90..90].
  • For each level, compute the sub-rectangle based on row/col.
  • Lat bounds are derived from the top (north) downward, matching bgrid-js.

The output includes:

  • center (lat, lon)
  • bounds [[minLat, minLon], [maxLat, maxLon]]

BIP39 Word Mapping

BGrid indices can be displayed as words using BIP39 wordlists:

  • number_to_word(1..2048)
  • word_to_number(word) (case-insensitive)
  • grid_to_display(grid, Words, words)

Wordlists are loaded from JSON arrays of 2048 strings.

Install

cargo add bgrid

Quick Start

use std::path::Path;
use bgrid::{
    coords_to_bgrid, bgrid_to_cell, load_language,
    DisplayMode, grid_to_display,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let grid = coords_to_bgrid(40.7128, -74.0060, 4)?;
    let cell = bgrid_to_cell(&grid)?;

    println!("grid={:?}", grid);
    println!("center=({}, {})", cell.lat, cell.lon);

    let display = grid_to_display(&grid, DisplayMode::Numbers, None)?;
    println!("numbers={display}");

    let words = load_language("es", Path::new("bip39-wordlist"))?;
    let display_words = grid_to_display(&grid, DisplayMode::Words, Some(&words))?;
    println!("words={display_words}");

    Ok(())
}

Bundled Wordlists

This repo includes JSON wordlists under:

bip39-wordlist/
  bip39-cs.json
  bip39-en.json
  bip39-es.json
  bip39-fr.json
  bip39-it.json
  bip39-ja.json
  bip39-ko.json
  bip39-pt.json
  bip39-zh.json

API Overview

  • coords_to_bgrid(lat, lon, levels) -> Vec<u16>
  • bgrid_to_cell(grid) -> Cell
  • get_grid_cells(level, parent_grid, max_level) -> Vec<GridCell>
  • load_language(lang, base_path) -> Vec<String>
  • number_to_word(number, words) -> Option<&str>
  • word_to_number(word, words) -> Option<u16>
  • grid_to_display(grid, mode, words?) -> String

Error Model

All functions return Result<_, BGridError> with meaningful error variants such as:

  • InvalidLatLon
  • InvalidLevels
  • InvalidGrid
  • InvalidLanguage
  • InvalidWords

Tests

cargo test

Compatibility Notes

  • Lat orientation follows the BGrid spec: y = (90 - lat) / 180.
  • Indices are 1-based (1..2048) per level.
  • Word lookup is case-insensitive to match bgrid-js.

License

MIT

Dependencies

~0.3–0.8MB
~17K SLoC