Skip to main content

Crate mapterm

Crate mapterm 

Source
Expand description

mapterm: a map in your terminal. This is the library behind the mapterm viewer: a Map holds named sources and an ordered list of style layers, and draws as a ratatui widget with braille lines, cell-color fills and collision-free labels.

use mapterm::{Camera, Map, MapSource, StyleLayer};
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
use serde_json::json;

let mut map = Map::new();
map.add_source("sites", MapSource::geojson(&json!({
    "type": "Feature", "properties": {"name": "Washington Monument"},
    "geometry": {"type": "Point", "coordinates": [-77.0353, 38.8895]}
}))?)?;
map.add_layer(StyleLayer::symbol("sites", "sites").glyph("◆").text("{name}").color("cyan"))?;

// Draw it 60x10 cells, centered on the monument at zoom 15.
let area = Rect::new(0, 0, 60, 10);
let mut buf = Buffer::empty(area);
map.view(&Camera::new(-77.0353, 38.8895, 15.0)).render(area, &mut buf);
let row: String = (0..60).map(|x| buf[(x, 5)].symbol().to_string()).collect();
assert!(row.contains("◆ Washington Monument"));

For a basemap, Map::from_style loads any MapLibre style ("liberty" and the other OpenFreeMap styles by name, a URL or a file), or Map::terminal draws a vector tile Source with mapterm’s built-in terminal style. Tiles load with Map::load_tiles, or in the background with Map::tile_requests and Map::insert_loaded. examples/hello_map.rs is a complete pan-and-zoom app in about 60 lines.

§The map

§Interaction

  • interact: Controller, MapLibre-style mouse and keyboard handling (wheel zoom, drag with inertia, double-click and box zoom, eased keys, fly_to, fit_bounds).
  • events: layer click, enter and leave events, and hover highlights with feature state.
  • geocode: place search with Photon or Nominatim.

§Data

  • source: vector tiles from PMTiles (file or HTTP range requests), XYZ URLs or TileJSON, with timeouts, size caps and a disk cache.
  • raster: image tile sources (XYZ, TMS, PMTiles, named providers) for raster layers, drawn as kitty graphics (Map::kitty_commands) or as cell colors.
  • geojson: GeoJSON files or URLs, and ready-made overlay style layers for them.
  • formats: CSV, GPX, KML and FlatGeobuf (fgb) as GeoJSON.
  • index and cluster: the spatial index and point clustering behind GeoJSON sources.

§Output and the rest

  • export: a drawn map as plain text, ANSI-colored text or HTML.
  • smooth: opt-in pixel-smooth panning for the Ghostty smooth-scroll fork.
  • render: Camera, Viewport and Web Mercator helpers.
  • Lower level: mvt (vector tile decoding), pbf (protobuf), pmtiles (the archive format), store (decoded features), pick (hit testing and measurements).

Re-exports§

pub use interact::Click;
pub use interact::Controller;
pub use pick::Pick;
pub use layer::CustomLayer;
pub use layer::DrawContext;
pub use layer::Markers;
pub use layer::PickContext;
pub use map::KittyState;
pub use map::Map;
pub use map::MapSource;
pub use map::MapView;
pub use map::RasterMode;
pub use map::TileRequest;
pub use spec::StyleLayer;
pub use render::Camera;
pub use render::Overlay;
pub use render::Viewport;
pub use source::Source;
pub use store::TileId;

Modules§

cluster
Point clustering for GeoJSON sources, like MapLibre’s cluster: true (supercluster): for each zoom level up to max_zoom, points closer than radius merge into cluster features with cluster, cluster_id, point_count and point_count_abbreviated properties, built level by level from the one above so clusters nest. Other geometry passes through.
events
Layer events, MapLibre style: map.on("click", "layer", ...) and mouseenter / mouseleave, plus the usual hover highlight with feature state. Rust apps poll instead of registering callbacks: feed Events the pointer and clicks, and act on what it returns.
export
Render a drawn Buffer as plain text, ANSI-colored text or an HTML fragment, for snapshots, docs and web pages (mapterm --snapshot WxH --format html).
expr
MapLibre / Mapbox style expressions, the subset that makes sense in a terminal: get, has, zoom, geometry-type, comparisons, all/any/!, in, match, case, coalesce, interpolate (linear and exponential), step, string and number helpers, plus the legacy filter syntax (["==", "class", "park"], ["in", "class", ...], ["has", k]) and legacy zoom functions ({"stops": [[5, 1], [10, 4]], "base": 1.2}).
fgb
FlatGeobuf (.fgb) reading, as GeoJSON: the magic bytes, a FlatBuffers header (columns, geometry type, feature count, index node size, CRS), an optional packed Hilbert R-tree index (skipped here), then size-prefixed FlatBuffers features. Coordinates must be lon/lat (EPSG:4326, or a CRS that is lon/lat like NAD83). Spec: https://flatgeobuf.org (header.fbs, feature.fbs).
formats
Other vector data as GeoJSON: CSV / TSV with latitude and longitude columns (or a WKT column), GPX (waypoints, tracks, routes), KML (placemarks) and FlatGeobuf (crate::fgb). Each is turned into a GeoJSON FeatureCollection, so it draws, inspects and clusters like GeoJSON.
geocode
Place search (geocoding) with OpenStreetMap geocoders: Photon by default (free, no key, results biased toward the current view) or Nominatim. Both public servers ask for light use: search when the user asks (on Enter), not on every keystroke.
geojson
GeoJSON overlay layers: a file or URL becomes a layer of points, lines and polygons drawn over the map, with each feature’s properties kept for inspection. Spec: https://datatracker.ietf.org/doc/html/rfc7946
icons
Map icons as single terminal cells. A style’s icon-image names an icon in its sprite (hospital, airport_11, circle_11_black); mapterm can’t draw sprites, so it shows a one-cell symbol for the icons it knows: OpenMapTiles POI classes, Maki icon names, and Protomaps POI kinds. Only symbols that are one cell wide in every terminal are used (no emoji, whose width varies), so labels beside them line up.
index
A static spatial index over bounding boxes: a packed Hilbert R-tree (the Flatbush design). Items are sorted along a Hilbert curve by the centers of their boxes and grouped 16 at a time into a tree of boxes, built once; searches then visit only the parts of the tree that overlap the query. Used for GeoJSON sources, so drawing and picking look at the features in view instead of all of them.
interact
Map interaction following the MapLibre GL / Mapbox GL JS default handlers, adapted to terminal mouse reports (cells, or pixels with SGR-pixels mode):
kitty
The kitty terminal graphics protocol, for drawing raster tiles as real images underneath the text layer (Ghostty, kitty, WezTerm; herdr relays it). Every command sets q=2, so the terminal sends no replies that would show up as input. Spec: https://sw.kovidgoyal.net/kitty/graphics-protocol/
layer
Custom layers: code that draws through a DrawContext (lines, fills, markers, labels in world or lon/lat coordinates) and, optionally, says which of its features are under a spot. Add one to a crate::Map with add_custom_layer for anything style layers can’t express, the way MapLibre’s CustomLayerInterface works.
map
A map, MapLibre GL style: named sources (vector tiles, GeoJSON) plus an ordered list of style layers that say how to draw them, with custom layers for anything else. The app changes the map with set_data, set_paint, set_filter and friends, draws it with Map::view, and asks what’s under the pointer with Map::query_rendered_features.
mvt
Mapbox Vector Tile (MVT 2.x) decoding.
pbf
Minimal bounds-checked protobuf reader: just enough for vector tiles and PMTiles directories.
pick
Feature picking for inspection: which features are under a screen cell, like MapLibre’s queryRenderedFeatures, with their properties and some measurements for analysis.
pmtiles
PMTiles v3: header, Hilbert tile ids and directory lookup. Spec: https://github.com/protomaps/PMTiles/blob/main/spec/v3/spec.md
raster
Raster (image) tile layers: XYZ or TMS URLs of PNG/JPEG tiles, drawn underneath the vector map. Tiles are fetched and cached like vector tiles, decoded once, and laid out by giving every screen cell to exactly one tile, so blocks of cells can be drawn from a cropped tile (kitty graphics) or sampled into cell colors (any terminal).
render
Camera, projection and the ratatui map widget. Lines are drawn with braille dots (2x4 per cell); filled areas color the cell background; labels and markers are text on top.
smooth
Opt-in pixel smooth vertical panning for terminals that implement the experimental ESC _ smooth;... region protocol (tlpinney/ghostty, relayed by tlpinney/herdr). See SMOOTH.md on that fork’s smooth branch.
source
Where tiles come from: a PMTiles archive (local file or HTTP range requests), an XYZ URL template, or a TileJSON endpoint. Every network read has a timeout and a size cap, and decompression is capped too, so a hostile server can’t exhaust memory.
spec
Style layers, as in the MapLibre style spec: a layer draws features of one source (and one source-layer of a vector source) as fills, lines, circles or symbols, chosen by a filter and colored by paint properties that may be expressions.
store
Decoded vector tile features, kept for styling and inspection.
stylejson
Loading MapLibre style documents (style spec v8) into a Map: vector sources (TileJSON url, tiles templates, PMTiles), GeoJSON sources (inline or by URL) and the layers. Sprites, glyphs, terrain and light don’t apply in a terminal and are ignored.