tui-globe 0.0.0

Proof-of-concept terminal renderer for the Mullvad 3D globe data
Documentation

tui-globe

A ratatui Braille-canvas globe widget. Geometry buffers under assets/geo/ are baked from public domain Natural Earth data and embedded at compile time via MapData::embedded().

Regenerating the geometry

The .gl buffers are generated by tools/build_geo.py. The script fetches shapefiles from naciscdn.org on demand, caches them, and writes the five .gl blobs to --out. The buffers are checked in, so you only need to re-run the script when bumping the data set or changing the build flags.

Flags

flag default meaning
--scale {10m,50m,110m} 50m Natural Earth resolution. Smaller numbers = denser mesh / larger binary.
--cultural-borders / --no-cultural-borders on With borders, triangulate ne_<scale>_admin_0_countries (coastlines + country borders) and overlay ne_<scale>_admin_1_states_provinces_lines. Without, triangulate ne_<scale>_land (coastlines only).
--data-dir PATH $XDG_CACHE_HOME/tui-globe-build-geo Where to cache downloaded .zip/.shp files.
--out PATH (required) Output directory for the five .gl files.
--ne-countries PATH auto-fetch Override: custom admin_0_countries .shp (cultural mode).
--ne-states PATH auto-fetch Override: custom admin_1_states_provinces_lines .shp (cultural mode).
--ne-land PATH auto-fetch Override: custom physical land .shp (--no-cultural-borders mode).

The override flags let you swap in locally modified shapefiles (e.g. simplified geometry, custom border tweaks). They take precedence over auto-fetch for that one input; the rest still come from the cache or are downloaded.

Run it

The script is packaged as a Python project at tools/pyproject.toml, declaring pyshp, mapbox-earcut, and numpy as dependencies. Pick whichever workflow you prefer:

With uv (no venv management needed):

uv run --project tui-globe/tools build-geo --scale 50m --out tui-globe/assets/geo

With pip into a venv:

python3 -m venv .venv
.venv/bin/pip install ./tui-globe/tools
.venv/bin/build-geo --scale 50m --out tui-globe/assets/geo

Without installing (manual deps):

pip install pyshp mapbox-earcut numpy
python3 tui-globe/tools/build_geo.py --scale 50m --out tui-globe/assets/geo

Examples

# Default: 50m with country and state/province borders
build-geo --scale 50m --out tui-globe/assets/geo

# High-detail land mesh with no borders
build-geo --scale 10m --no-cultural-borders --out tui-globe/assets/geo

# Tiny global-overview mesh
build-geo --scale 110m --out tui-globe/assets/geo

# Use custom / locally modified shapefiles (overrides auto-fetch)
build-geo --scale 50m \
    --ne-countries ./my-data/ne_50m_admin_0_countries.shp \
    --ne-states    ./my-data/ne_50m_admin_1_states_provinces_lines.shp \
    --out tui-globe/assets/geo

# Override just the country polygons; states still auto-fetched
build-geo --scale 50m \
    --ne-countries ./my-data/custom_countries.shp \
    --out tui-globe/assets/geo

# Custom physical-land shapefile, no borders
build-geo --no-cultural-borders \
    --ne-land ./my-data/custom_land.shp \
    --out tui-globe/assets/geo

Verify

cargo test -p tui-globe

The test suite asserts the buffers parse to a thin unit-sphere shell, that restart markers separate rings, and that no surviving chord exceeds the 0.05 threshold the renderer relies on.

Output format

Five little-endian binary blobs, the same format the desktop Mullvad client uploads into WebGL:

file dtype meaning
land_positions.gl f32 xyz unit-sphere vertices
land_triangle_indices.gl u32 GL_TRIANGLES indices
land_contour_indices.gl u32 GL_LINE_STRIP indices, 0xFFFFFFFF between rings
ocean_positions.gl f32 xyz level-4 icosphere vertices (2562)
ocean_indices.gl u32 GL_TRIANGLES indices (5120 tris)

Coordinate convention (matches lib.rs::project_point):

x = cos(lat) sin(lon)
y = sin(lat)
z = cos(lat) cos(lon)