Skip to content

labels

HTML label overlay. LabelLayer keeps geometry on the GPU and renders only the visible labels into the DOM, positioning anchors (reference-space) under a core!ViewTransform; cullLabels does the viewport + collision culling.

import { LabelLayer } from "@mapequation/d3gl/labels";
const labels = new LabelLayer(containerEl, (a) => a.text);
labels.update(anchors, transform, { width, height });
ClassDescription
LabelLayerAn HTML overlay of absolutely-positioned label elements. On each update it maps reference anchors through the view transform to screen pixels, culls to the viewport with collision resolution, and reconciles the DOM (reusing nodes by id). Geometry stays on the GPU; only the surviving labels are in the DOM.
InterfaceDescription
CullOptions-
LabelAnchorA label anchored in REFERENCE (projected, pre-transform) pixel space.
LabelBoxA label positioned in SCREEN pixels (after the view transform is applied).
LabelGeometryThe realised screen geometry of a label: its four corners, their AABB, whether it is axis-aligned (fast-path collision), and the CSS transform that reproduces the box.
Type AliasDescription
LabelStyleInline CSS for overlay label elements: camelCased CSS property → value (the string-valued subset of CSSStyleDeclaration), e.g. { color: "#1f2937", textShadow: "0 0 2px #fff" }. A plain, engine-agnostic shape, so it can lift as-is into other engines’ label overlays (#223).
TextAnchorWhere the anchor point sits along the label’s own text axis — like SVG text-anchor.
VariableDescription
DEFAULT_LABEL_STYLEDefault overlay label look (#224) — a compact dark sans-serif label with a white text-shadow halo, readable over busy geometry with zero user CSS. Applied by the engine label APIs (e.g. network.labels()) via resolveLabelStyle; a raw LabelLayer stays unstyled by default (the low-level primitive keeps today’s inherit-from-container behaviour).
DEFAULT_LABEL_TEXTThe same default look for BACKEND-NATIVE text (SVG <text> / Canvas fillText, incl. export), which CSS can’t reach: the font/color/halo equivalent of DEFAULT_LABEL_STYLE (canvas font shorthand takes no line-height; the 3px halo stroke ≈ the 3px shadow blur).
FunctionDescription
canvasFontStrip a /line-height token from a CSS font shorthand so it is valid for the canvas font property (which rejects line-height), e.g. "600 11px/1 system-ui""600 11px system-ui".
cullLabelsReduce label candidates to a renderable subset: drop anchors outside the viewport (+padding), then greedily place highest-priority first, skipping any that collide with an already-placed box. Collision uses each label’s true oriented footprint (see labelGeometry), so rotated labels pack by the space they actually occupy on screen rather than their un-rotated dimensions. This keeps the DOM at a few hundred nodes regardless of how many features exist (the “geometry on GPU, only visible labels in DOM” approach).
labelGeometryResolve a LabelBox to its on-screen geometry. Plain labels (no rotation) keep the historical top-left box and own their CSS transform. Oriented labels (rotation set) place the text along the rotated axis, vertically centred on the anchor, with text-anchor and the optional upright flip folded in — and emit the matching CSS transform. The collision corners and the transform come from the same computation, so render and culling stay consistent.
measureTextMeasure a label’s screen box (CSS px) for collision. font is a canvas font shorthand (no /line-height — strip it first). Width is measured; height is the font size × 1.25 (a consistent row box; 11px → 14, matching the estimates this replaces).
placeLabelsProject label anchors to screen px (screen = k·ref + (x,y) + the constant offset) and resolve collisions, returning the survivors as LabelBoxes (carrying text/opacity). Shared by the HTML overlay (LabelLayer.update) and the backend-native text path (#105 N7b-2) so both place and cull labels identically — they differ only in how they render the survivors.
resolveLabelStyleThe engine-label styling policy (#224): merge a caller’s inline LabelStyle over DEFAULT_LABEL_STYLE — a partial override keeps the rest — but when a className is given, return only the explicit style (possibly undefined): the class’s CSS keeps full control, since an inline default would beat it. Shared so other engines lift the exact same policy (#223); pass the result as LabelLayer’s style.