tiletea

package module
v0.0.0-...-15e8353 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2026 License: MIT Imports: 14 Imported by: 0

README

tiletea

A Bubble Tea (v2) component that renders an interactive slippy map in the terminal using the Kitty graphics protocol.

tiletea

It's a thin, embeddable wrapper around github.com/akhenakh/maprender, which fetches Mapbox Vector Tiles and rasterises them according to a Mapbox GL Style.

Requirements

Installation

go get github.com/akhenakh/tiletea

Important: see Ultraviolet version caveat before upgrading charm.land/bubbletea/v2.

Quick start

Use the component standalone:

package main

import (
	"fmt"
	"os"

	tea "charm.land/bubbletea/v2"
	"github.com/akhenakh/tiletea"
)

func main() {
	m := tiletea.New(40.7128, -74.0060, 14, // NYC, zoom 14
		tiletea.WithMarker(40.7128, -74.0060),
	)

	p := tea.NewProgram(m)
	if _, err := p.Run(); err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
}

Run it:

go run .

A complete example lives in cmd/browse, including debug logging:

DEBUG=1 go run ./cmd/browse   # writes render logs to debug.log

Embedding in a larger app

tiletea.Map implements tea.Model, so it can be embedded like any other component. Return it from your own model's Init, Update, and View:

type app struct {
	m *tiletea.Map
}

func (a app) Init() tea.Cmd           { return a.m.Init() }
func (a app) View() tea.View          { return a.m.View() }

func (a app) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	m, cmd := a.m.Update(msg)
	a.m = m.(*tiletea.Map)
	return a, cmd
}

Configuration

New(lat, lng float64, zoom int, opts ...Option) accepts the following functional options:

Option Description
WithMarker(lat, lng) Place a marker at the given coordinates.
WithOverlays(overlays...) Draw maprender.Overlay geometries on top of the map.
WithFitOverlays() Center and zoom the map to fit the overlays on first render.
WithStyle(style) Supply a pre-fetched *maprender.MapStyle, skipping style fetching.
WithStyleURL(url) Override the Mapbox GL style URL (default DefaultStyleURL).
WithTileSource(url) Override the TileJSON endpoint used to resolve the tile URL (default DefaultSourceURL).
WithTileURLTemplate(tmpl) Supply the {z}/{x}/{y} tile URL template directly, bypassing TileJSON.
WithSourceMaxZoom(z) Set the source max zoom, used for overzoom when a template is supplied directly.
WithTileCache(dir, ttl) Configure the on-disk tile cache (default ~/.cache/maprender, 2-week expiry). Empty dir keeps the default; non-positive ttl disables expiry.
WithLogger(l) Set the *slog.Logger used for render/debug output.
WithAltScreen(bool) Enable or disable the alternate screen buffer (enabled by default).

Useful methods:

  • Center() (lat, lng float64) and Zoom() int read the current view.
  • SetMarker(lat, lng *float64) sets or clears (nil) the marker.
  • SetOverlays(overlays ...maprender.Overlay) replaces the drawn overlays.
  • FitOverlays() tea.Cmd recenters and rezooms to fit the current overlays.

Geometry view

GeomView is a Bubble Tea model that renders geometry on a map fitted to its bounds. It accepts GeoJSON, WKT, WKB, or a geom.Geometry:

package main

import (
	"fmt"
	"os"

	tea "charm.land/bubbletea/v2"
	"github.com/akhenakh/tiletea"
)

func main() {
	gv, err := tiletea.NewGeomViewFromWKT(
		"POLYGON((-74.02 40.70, -74.00 40.70, -74.00 40.72, -74.02 40.72, -74.02 40.70))",
	)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	p := tea.NewProgram(gv)
	if _, err := p.Run(); err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
}

Constructors: NewGeomViewFromGeoJSON([]byte), NewGeomViewFromWKT(string), NewGeomViewFromWKB([]byte), NewGeomViewFromGeometry(geom.Geometry), and the lower-level NewGeomView([]maprender.Overlay, ...).

Overlay colors default to a red stroke with no fill, overridable via maprender.Overlay{StrokeColor, FillColor} or GeoJSON feature properties (stroke/fill keys). A runnable example lives in cmd/geom:

go run ./cmd/geom -geojson feature.geojson
go run ./cmd/geom -wkt "LINESTRING(-74.02 40.70, -74.00 40.72)"

Controls

Keys / Mouse Action
Arrow keys / h j k l Pan
+ / = Zoom in
- Zoom out
Mouse wheel up/down Zoom in/out
q / ctrl+c Quit

Ultraviolet version caveat

This component renders the map by embedding raw Kitty graphics APC escape sequences (ESC _ G ... ESC \) into the Bubble Tea view string.

Starting with charm.land/bubbletea/v2 v2.0.8 (which pulls in github.com/charmbracelet/ultravioletv0.0.0-20260703014108), the renderer skips zero-width cells, and those APC sequences are silently dropped from the terminal output — so the map no longer appears.

Pin Bubble Tea to the newest version that still works:

go get charm.land/bubbletea/v2@v2.0.7

That resolves ultraviolet to v0.0.0-20260525132238 and github.com/charmbracelet/x/ansi to v0.11.7, both of which preserve the Kitty graphics sequences.

Documentation

Overview

Package tiletea provides a Bubble Tea component for rendering an interactive slippy map in the terminal using the Kitty graphics protocol.

Index

Constants

View Source
const (
	// DefaultStyleURL is the Mapbox GL style fetched when none is configured.
	DefaultStyleURL = "https://tiles.openfreemap.org/styles/liberty"

	// DefaultSourceURL is the TileJSON endpoint used to resolve the tile URL
	// template and source zoom range.
	DefaultSourceURL = "https://tiles.openfreemap.org/planet"

	// DefaultTileURLTemplate is used when the tile source cannot be resolved.
	DefaultTileURLTemplate = "https://tiles.openfreemap.org/planet/{z}/{x}/{y}.pbf"

	// MinZoom and MaxZoom bound the zoom level.
	MinZoom = 0
	MaxZoom = 18
)

Variables

This section is empty.

Functions

This section is empty.

Types

type GeomView

type GeomView struct {
	// contains filtered or unexported fields
}

GeomView is a Bubble Tea model that renders geometry on an interactive map, fitted to the geometry's bounds. It is a thin wrapper around Map that parses GeoJSON, WKT, WKB, or a geom.Geometry into overlays and centers the view on them.

func NewGeomView

func NewGeomView(overlays []maprender.Overlay, opts ...Option) *GeomView

NewGeomView creates a map view fitted to the given overlays. The map is centered and zoomed to the combined bounds of the overlays on first render.

func NewGeomViewFromGeoJSON

func NewGeomViewFromGeoJSON(data []byte, opts ...Option) (*GeomView, error)

NewGeomViewFromGeoJSON parses GeoJSON (a Geometry, Feature, or FeatureCollection) and creates a map view fitted to it.

func NewGeomViewFromGeometry

func NewGeomViewFromGeometry(g geom.Geometry, opts ...Option) *GeomView

NewGeomViewFromGeometry creates a map view fitted to a geom.Geometry.

func NewGeomViewFromWKB

func NewGeomViewFromWKB(wkb []byte, opts ...Option) (*GeomView, error)

NewGeomViewFromWKB parses a WKB byte slice and creates a map view fitted to it.

func NewGeomViewFromWKT

func NewGeomViewFromWKT(wkt string, opts ...Option) (*GeomView, error)

NewGeomViewFromWKT parses a WKT string and creates a map view fitted to it.

func (*GeomView) Init

func (g *GeomView) Init() tea.Cmd

Init implements tea.Model.

func (*GeomView) Map

func (g *GeomView) Map() *Map

Map returns the underlying map component.

func (*GeomView) Update

func (g *GeomView) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update implements tea.Model.

func (*GeomView) View

func (g *GeomView) View() tea.View

View implements tea.Model.

type Map

type Map struct {
	// contains filtered or unexported fields
}

Map is a Bubble Tea model that renders an interactive slippy map. It can be used standalone or embedded in a larger application.

The zero value is not usable; construct one with New.

func New

func New(lat, lng float64, zoom int, opts ...Option) *Map

New creates a map component centered at the given coordinates and zoom.

The map style and tile source are fetched synchronously on construction, falling back to built-in defaults when the network is unavailable.

func (*Map) Center

func (m *Map) Center() (lat, lng float64)

Center returns the current map center.

func (*Map) FitOverlays

func (m *Map) FitOverlays() tea.Cmd

FitOverlays recenters and rezooms the map to fit the current overlays. It returns a command that triggers the re-render, or nil when there are no overlays or the viewport size is not yet known.

func (*Map) Init

func (m *Map) Init() tea.Cmd

Init implements tea.Model.

func (*Map) Refresh

func (m *Map) Refresh() tea.Cmd

Refresh returns a command that re-renders the map with the current state (center, zoom, marker, overlays). Use it after mutating the map externally, e.g. from a click callback.

func (*Map) SetClickCallback

func (m *Map) SetClickCallback(fn func(lat, lng float64))

SetClickCallback sets or clears (nil) the click callback. See WithClickCallback.

func (*Map) SetIncremental

func (m *Map) SetIncremental(enabled bool)

SetIncremental enables or disables incremental rendering for pans. It is enabled by default.

func (*Map) SetMarker

func (m *Map) SetMarker(lat, lng *float64)

SetMarker sets the optional marker location. A nil lat or lng clears the marker.

func (*Map) SetOverlays

func (m *Map) SetOverlays(overlays ...maprender.Overlay)

SetOverlays replaces the geometry overlays drawn on top of the map. The overlays are applied on the next render.

func (*Map) SetStatusExtra

func (m *Map) SetStatusExtra(s string)

SetStatusExtra sets an optional extra segment displayed at the end of the status line. An empty string removes it.

func (*Map) Update

func (m *Map) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update implements tea.Model.

func (*Map) View

func (m *Map) View() tea.View

View implements tea.Model.

func (*Map) Zoom

func (m *Map) Zoom() int

Zoom returns the current zoom level.

type Option

type Option func(*Map)

Option configures a Map.

func WithAltScreen

func WithAltScreen(enabled bool) Option

WithAltScreen enables or disables the alternate screen buffer. It is enabled by default.

func WithClickCallback

func WithClickCallback(fn func(lat, lng float64)) Option

WithClickCallback registers fn to be invoked with the WGS84 latitude and longitude whenever the user clicks (left button) on the map.

fn is called synchronously during the Map's Update, so it should be quick and non-blocking.

func WithFitOverlays

func WithFitOverlays() Option

WithFitOverlays centers and zooms the map to fit the overlays on the first render. After the initial fit, the user can pan and zoom freely.

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger sets the logger used for render and debug output. A nil logger is ignored.

func WithMarker

func WithMarker(lat, lng float64) Option

WithMarker places a marker at the given coordinates.

func WithOverlays

func WithOverlays(overlays ...maprender.Overlay) Option

WithOverlays appends geometry overlays drawn on top of the map. Overlays are drawn in WGS84 (lon-lat) coordinates and accept GeoJSON/WKT/WKB-parsed geometry via the maprender.OverlayFrom* helpers.

func WithSourceMaxZoom

func WithSourceMaxZoom(maxZoom int) Option

WithSourceMaxZoom sets the source's maximum zoom, used for overzoom when a tile URL template is supplied directly.

func WithStyle

func WithStyle(style *maprender.MapStyle) Option

WithStyle supplies a pre-fetched map style, bypassing style fetching.

func WithStyleURL

func WithStyleURL(url string) Option

WithStyleURL overrides the URL used to fetch the map style.

func WithTileCache

func WithTileCache(dir string, ttl time.Duration) Option

WithTileCache configures the on-disk tile cache. An empty dir uses the default cache directory (~/.cache/maprender). A non-positive ttl disables expiry; a positive ttl expires entries older than ttl.

func WithTileSource

func WithTileSource(url string) Option

WithTileSource overrides the TileJSON endpoint used to resolve the tile URL template.

func WithTileURLTemplate

func WithTileURLTemplate(template string) Option

WithTileURLTemplate supplies the tile URL template directly, bypassing the TileJSON lookup. Use WithSourceMaxZoom to enable overzoom in this case.

func WithZIndex

func WithZIndex(z int) Option

WithZIndex sets the Kitty graphics z-index used to draw the map image. Negative values draw the image under text (the default); z=0 draws it at the text layer so it covers previously drawn text.

Directories

Path Synopsis
cmd
browse command
Command browse renders an interactive terminal map, demonstrating the tiletea component.
Command browse renders an interactive terminal map, demonstrating the tiletea component.
geom command
Command geom renders an interactive terminal map fitted to a geometry overlay, demonstrating the tiletea.GeomView component.
Command geom renders an interactive terminal map fitted to a geometry overlay, demonstrating the tiletea.GeomView component.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL