Maps
@glyphcss/maps turns geographic data into a glyphcss scene. It projects
lon/lat to a 3D world, builds a real relief mesh from elevation tiles, mounts a
MapLibre-shaped layer vocabulary on top, and hands the whole thing to one
createGlyphScene — so a map is the same single <pre> write per frame as any
other glyphcss render.
npm install @glyphcss/mapsThere is no React or Vue binding. The package is imperative: you own the host
element, and createGlyphMap returns a handle.
Two entry points
Section titled “Two entry points”| Import | Contains | Safe in |
|---|---|---|
@glyphcss/maps | Everything — projections, tiles, the relief mesh, the widget, layers, vector providers | Browser, Node, a worker |
@glyphcss/maps/node | The root entry re-exported, plus loadGlyphMapSource | Node only |
The root is pure: no fs, no native modules, nothing that assumes a
filesystem. The /node subpath is the only place a filesystem reader lives, and
the root never imports it, so bundling the root can never pull node:fs into a
browser build.
gdal-async is deliberately not a dependency of either. The one reader that
ships reads Esri/Arc-Info ASCII Grid.
A first map
Section titled “A first map”Everything a map needs is a view (where you are looking) and a projection (how the world flattens). Layers are optional and can be added later.
import { createGlyphMap, glyphMapGlobe } from "@glyphcss/maps";
const host = document.querySelector<HTMLElement>("#map")!;
const map = createGlyphMap(host, { view: { center: [0, 20], span: 140, cols: 140, rows: 63 }, projection: glyphMapGlobe({ exaggeration: 24 }), layers: [{ type: "background", color: "#05070c" }],});
map.on("click", ({ lngLat }) => console.log(lngLat));map.fitBounds({ west: -74, east: -34, south: -56, north: 13 });Drag to pan or orbit, wheel to zoom, Ctrl+drag (or right-drag) to tilt and turn.
On touch: pinch to zoom, twist to turn, two fingers together to tilt.
The widget owns one requestAnimationFrame motion loop and issues at most one
render per displayed frame, so a gesture never queues renders it will throw
away.
Call map.destroy() when you are done — it removes the scene’s DOM, every host
listener, and any pending debounced tile fetch.
Everything public speaks lat/lng
Section titled “Everything public speaks lat/lng”GlyphMapView is the whole camera state a consumer ever sets:
interface GlyphMapView { center: readonly [lon: number, lat: number]; span: number; // degrees across the viewport cols: number; // output grid width, in character columns rows: number; // output grid height, in character rows bounds?: GlyphMapBounds; // only on a view built by glyphMapBounds()}The vertical extent is derived from span * (rows / cols) — the aspect lock
— so terrain can never shear. Grid and cell coordinates never appear in a public
parameter or return value on their own; project/unproject are the two places
they cross the boundary, and both are explicit about it:
const { col, row, visible } = map.project([-58.38, -34.6]);const lngLat = map.unproject([70, 20]); // null off any projected surfaceview.cols/view.rows are the authoritative grid shape. Pass autoSize: true
to let the host element’s pixel size drive them instead.
The handle
Section titled “The handle”createGlyphMap returns a GlyphMapHandle. The whole surface, grouped:
| Group | Methods |
|---|---|
| View | setView, getView, getMaxSpan, fitBounds, flyTo, resize |
| Camera | setTilt, getTilt, getMaxTilt, setBearing, getBearing, setProjection |
| Street level | setWalk, getWalk |
| Layers | addLayer, removeLayer, moveLayer, getContourFieldRange |
| Lighting | setSun, getSun, getSunDirection, getSubsolarPoint, setKeyLight, getKeyLight, getKeyLightDirection, setShadow, getShadow |
| Overlays | addMarker |
| Coordinates | project, unproject |
| Credit | getAttributions |
| Events | on, off |
| Teardown | destroy |
Plus two properties: host, and scene — the underlying GlyphSceneHandle.
scene is the deliberate escape hatch: anything glyphcss can do that the map
does not model (effect layers, charMode, the font atlas, direct camera reads)
you reach through it.
map.scene.setOptions({ charMode: "braille" });Scene options you want set from the start go through GlyphMapOptions.scene
instead — they are merged under the widget’s own camera/cols/rows, so
shading, colour encoding and shadows compose without fighting the map for the
grid:
createGlyphMap(host, { view, projection, scene: { useColors: true, colorEncoding: "atlas", ambientLight: { intensity: 0.25 } },});Events
Section titled “Events”map.on("load", () => {}); // construction-time layers finished their first loadmap.on("move", ({ view }) => {}); // pan / flyTo / setViewmap.on("zoom", ({ view }) => {});map.on("click", ({ lngLat, originalEvent }) => {}); // lngLat is null off-surfacemap.on("sun", ({ at, subsolar, direction }) => {}); // only while sun mode is onoff(type, handler) takes the same handler identity back.
Attribution is not optional
Section titled “Attribution is not optional”Real map data comes with real licence terms. Every source in this package declares its own provenance at the type level, and the handle aggregates whatever the currently mounted layers carry:
for (const credit of map.getAttributions()) { console.log(credit.name, credit.license, credit.url); // → OpenStreetMap contributors ODbL https://www.openstreetmap.org/copyright}interface GlyphMapAttribution { name: string; url?: string; license: string; date?: string;}The list is recomputed per call over the live layer order, so it grows when you mount an OSM layer and shrinks when you remove it. Render it somewhere the reader can see. OpenStreetMap data is ODbL and requires the credit; OpenMapTiles is CC-BY 4.0. Nothing in this package hardcodes a credit string on a page, precisely so the displayed credit cannot drift from the data actually on screen.
Where to go next
Section titled “Where to go next”- Projections — the lon/lat contract, the four projections, and capabilities.
- Layers — the full layer vocabulary and what each one takes.
- Camera & Navigation — tilt, bearing, their gestures, flights, and street-level walk mode.
- Terrain — elevation tiles, the relief mesh, draping, sun and shadows.
- OpenStreetMap — streaming the planet through OpenFreeMap.
The live maps workbench drives every option on this page against real
ETOPO1 terrain and OpenStreetMap tiles, and emits the createGlyphMap call for
whatever you tuned.