Skip to content

Tiles, images and output

map.load_tiles(&cam, area).await is fine for examples but makes the loop wait. The viewer asks for the missing tiles, loads them on tokio tasks, and stores them as they arrive:

use std::collections::HashSet;
use tokio::sync::mpsc;
let (tx, mut rx) = mpsc::unbounded_channel();
let mut pending = HashSet::new();
// Each frame, after drawing:
for req in map.tile_requests(&cam, area) {
if pending.insert((req.source.clone(), req.id)) {
let tx = tx.clone();
tokio::spawn(async move {
let loaded = req.load().await;
let _ = tx.send((req.source, req.id, loaded));
});
}
}
// When a tile arrives (e.g. in a tokio::select! with your input events):
while let Ok((source, id, loaded)) = rx.try_recv() {
pending.remove(&(source.clone(), id));
if let Err(e) = map.insert_loaded(&source, id, loaded) {
// logged; the map asks for this tile again later
}
}
  • tile_requests lists the tiles the view needs and doesn’t have, for the sources that shown layers use.
  • TileRequest::load does the fetching and decoding, off the map.
  • insert_loaded stores the result. Failed tiles are asked for again after a growing wait (2, 4, 8, 16 seconds, up to 60), and after five failures stored empty. Change it with map.retry (a RetryPolicy). A tile that doesn’t exist is stored empty at once.
  • Until a tile arrives, lower-zoom tiles stand in for it.

Raster layers draw as cell colors by default. On terminals with the kitty graphics protocol they can be real images under the text:

use mapterm::{KittyState, RasterMode};
if mapterm::kitty::supported() {
map.raster_mode = RasterMode::Kitty { cell_px: (cell_w, cell_h) }; // cell size in pixels
}
let mut kitty = KittyState::default();
// Each frame, after the cells, in the same synchronized update:
let cmds = map.kitty_commands(&cam, area, &mut kitty);
stdout.write_all(cmds.as_bytes())?;

Images are sent once and then only moved. KittyState tracks what the terminal holds and frees off-screen images first to stay within kitty.budget bytes (48 MiB by default), so the terminal never evicts a visible tile.

mapterm::export turns a drawn ratatui Buffer into a string. It’s what mapterm --snapshot uses:

use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
let area = Rect::new(0, 0, 120, 40);
map.load_tiles(&cam, area).await?;
let mut buf = Buffer::empty(area);
map.view(&cam).render(area, &mut buf);
let text = mapterm::export::to_text(&buf); // characters only
let ansi = mapterm::export::to_ansi(&buf); // 24-bit color escapes
let html = mapterm::export::to_html(&buf); // <pre class="mapterm"> with inline colors

Cells hidden under wide characters (CJK labels) are skipped, as a terminal would. See Snapshots and scripting for the CSS that makes HTML snapshots line up.