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
map:Map, its sources (MapSource: vector tiles, GeoJSON, raster), style and custom layers, feature state,Map::query_rendered_featuresandMap::query_features(every feature under a spot, drawn or not, asPicks).spec:StyleLayerand the style properties a terminal can show (a table in its docs).expr: MapLibre expressions and legacy filters.stylejson: MapLibre style documents and the built-in terminal styles.layer: custom layers drawing through aDrawContext;Markersis one.icons: style icons as one-cell symbols.
§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 askittygraphics (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.indexandcluster: 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,Viewportand 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 tomax_zoom, points closer thanradiusmerge into cluster features withcluster,cluster_id,point_countandpoint_count_abbreviatedproperties, built level by level from the one above so clusters nest. Other geometry passes through. - events
- Layer events, MapLibre style:
map.on("click", "layer", ...)andmouseenter/mouseleave, plus the usual hover highlight with feature state. Rust apps poll instead of registering callbacks: feedEventsthe pointer and clicks, and act on what it returns. - export
- Render a drawn
Bufferas 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-imagenames 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 acrate::Mapwithadd_custom_layerfor anything style layers can’t express, the way MapLibre’sCustomLayerInterfaceworks. - 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_filterand friends, draws it withMap::view, and asks what’s under the pointer withMap::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’ssmoothbranch. - 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 (TileJSONurl,tilestemplates, PMTiles), GeoJSON sources (inline or by URL) and the layers. Sprites, glyphs, terrain and light don’t apply in a terminal and are ignored.