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):
With pip into a venv:
Without installing (manual deps):
Examples
# Default: 50m with country and state/province borders
# High-detail land mesh with no borders
# Tiny global-overview mesh
# Use custom / locally modified shapefiles (overrides auto-fetch)
# Override just the country polygons; states still auto-fetched
# Custom physical-land shapefile, no borders
Verify
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)