/** * Annotations: the editorial layer. * * A map with data on it says what is where. An annotation says what the reader * is supposed to notice, and that is usually the actual point of publishing the * map. Three anchoring modes, because there are three things authors point at: * * - **points** at a coordinate: "epicentre", "the new plant". * - **features** by key: anchored at the same point the label engine picks, so * it tracks the geometry through a projection change instead of drifting off * the shape it belongs to. * - **areas** over a box or any geometry: "the drought region". * * Four decisions worth stating, because each is a place a naive implementation * behaves worse in a way that is hard to notice: * * 1. **Annotations never lose a collision; generated labels yield to them.** * An author placed the annotation deliberately, and a feature label is * produced by a rule. Dropping the deliberate one because the generated one * got there first is exactly backwards, so annotations lay out first and * hand their boxes to the label engine as already-occupied space. * 2. **Inert to the pointer.** An annotation sits over data by definition, and * a chip that swallows the hover of the country it is explaining would make * the map worse than an unannotated one. * 3. **Anchors live in world space, text in screen space.** Same split as * labels: the anchor tracks the geography through pan and zoom, while the * text keeps its size, because editorial type that grows with the camera * stops being type and becomes decoration. * 4. **Areas go through the projection**, so a "box" over the Arctic bows the * way the graticule does rather than staying a screen-space rectangle that * claims the projection is flat. * * @module components/Annotations */ import type { SvgRenderer } from '../renderers/SvgRenderer'; import type { Box } from './Labels'; import type { Viewport } from '../geo/Viewport'; import type { AnnotationOptions, WorldPoint } from '../types'; /** * A screen-space box an annotation occupies, for the label engine to avoid. * The same unit label collision uses, so reservation is just a seeded list. */ export type ReservedBox = Box; /** * How the chart exposes what annotations need to look up, so this component * holds no geometry of its own and cannot go stale against a drilldown. */ export interface AnnotationAccess { /** World-space anchor for a feature key, from the same map labels use. */ anchorFor(key: string): WorldPoint | undefined; /** The feature itself, for tracing an outline. */ featureFor(key: string): { geometry?: unknown; } | undefined; } export declare class Annotations { readonly renderer: SvgRenderer; readonly viewport: Viewport; options: AnnotationOptions; warnings: string[]; /** Screen-space boxes occupied by the last layout, for label collision. */ reserved: ReservedBox[]; count: number; private readonly access; private overlayGroup; private regionGroup; private resolved; constructor({ renderer, viewport, options, access, }: { renderer: SvgRenderer; viewport: Viewport; options?: AnnotationOptions; access: AnnotationAccess; }); /** * Resolve anchors to world space. Called once per draw, and again on a * projection change, but never per camera frame: projecting is the expensive * half and the camera does not change it. */ resolve(): void; /** * Draw. World-space shapes are written once per resolve; screen-space parts * are rebuilt per camera change, exactly as labels are, because the candidate * set is small and pre-resolved. */ layout(): void; private drawRegions; private drawOverlay; /** * Draw the chip and its connector, and report the box it occupies. * * The box is what the label engine avoids, so it is measured the same * approximate way labels measure themselves. Measuring properly would need a * reflow per annotation, which is the per-frame cost the whole layout is * built to avoid, and being a few pixels generous only makes labels yield * slightly more. */ private appendLabel; private outlinePath; destroy(): void; } //# sourceMappingURL=Annotations.d.ts.map