🚧 These docs are a work in progress — content is incomplete and may change without notice.
Skip to content

Billboard pins ​

Pins raise unit symbols above their map location. Symbols face the camera and keep a fixed pixel size. HQ staffs and labels remain visible. Direction arrows stay on the ground.

Pins need MapLibre GL JS 6 and WebGL2. They work in Mercator and globe mode and are off by default. Other map adapters and map-sheet exports show ordinary symbols. Pins apply only to point symbols, so configure a pointSymbols capability first.

For a complete, runnable example, see Toggle pins.

View settings ​

ts
const draw = new TacticalDraw(adapter, { pointSymbols: milsymbolPointSymbols() });
draw.setPinPresentation({
  enabled: true,
  mode: "screen", // "world" is a true vertical in world space
  liftPixels: 72, // screen mode: CSS pixels
  heightMeters: 250, // world mode: above terrain, including exaggeration
  sizePixels: 32,
  occlusion: "terrain", // default "none"
  depthSort: true, // far-to-near drawing; default true, false keeps layer order
  prioritizePins: true, // prefer visible pins over overlapping graphics
  stem: { color: "#172033", width: 2, opacity: 1 },
  dot: { color: "#172033", radius: 3, opacity: 1 },
  halo: { color: "#ffffff", width: 1.5, opacity: 1 }, // stems and ground dots
  symbolHalo: { color: "#ffffff", width: 2, opacity: 0.8 },
  plate: {
    enabled: true,
    color: "#ffffff",
    borderColor: "#64748b",
    borderWidth: 1,
    padding: 4,
    radius: 4,
    opacity: 0.95,
  },
});
  • Screen vertical raises symbols straight up on the screen, using liftPixels.
  • World vertical raises symbols above terrain, using heightMeters. On the globe, this follows the direction away from the Earth's center.
  • Terrain occlusion hides the parts of pins behind terrain. Units on the far side of the globe are always hidden.
  • Depth sorting draws whole pins far to near by their ground-anchor camera depth, keeping layer order for ties. It is enabled by default; set depthSort: false to fall back to layer order. Rendering and picking share the order. Camera movement reuses cached geometry and terrain elevations; unchanged order avoids GPU uploads.
  • Pin selection priority makes a visible pin win when its billboard overlaps an ordinary graphic. It is enabled by default and does not change render order.
  • Symbol halo adds an outline around the symbol, labels, and HQ staff. Set symbolHalo.width to 0 to turn it off.

Per-unit settings ​

In the preview, select one unit, open Pins, and choose Selected unit. Use Advanced styling for stems, dots, halos, and backing plates. Choose Reset unit to view defaults to remove its overrides.

In code:

ts
draw.setPinOverride("battalion-hq", {
  heightMeters: 400,
  sizePixels: 48,
  symbolHalo: { color: "#ffffff", width: 2 },
});

// Remove this unit's overrides.
draw.resetPinOverride("battalion-hq");

To change many units at once, for example when every unit's size follows a shared setting, use setPinOverrides. It applies all patches as one change and re-renders once; null removes that unit's overrides:

ts
draw.setPinOverrides({
  "battalion-hq": { sizePixels: 48 },
  "company-a": { sizePixels: 40 },
  "company-b": null,
});

A pin change re-renders only the units whose settings it changed, so a single setPinOverride stays cheap in a large document.

All settings can be set as view defaults. Per-unit overrides are supported as follows:

SettingPer-unit overridePurpose
enabledYesShow a pin or an ordinary symbol
modeNoScreen vertical or world vertical
occlusionNoHide parts behind terrain
depthSortNoDraw pins far to near
prioritizePinsNoPrefer visible pins during selection
liftPixelsYesLift in screen mode
heightMetersYesHeight in world mode
sizePixelsYesSymbol size
stemYesColor, width, opacity
dotYesColor, radius, opacity
haloYesStem and dot outline: color, width, opacity
symbolHaloYesSymbol outline: color, width, opacity
plateYesEnabled, color, border color/width, padding, radius, opacity

Per-unit mode, occlusion, depthSort, and prioritizePins overrides are rejected. The view's mode determines whether liftPixels or heightMeters is used.

Each setPinOverride call replaces the unit's previous patch. Omitted settings use the view defaults, then the library defaults.

Colors use hex RGB or RGBA. Dimensions must be finite and nonnegative; symbol size must be positive. Opacity ranges from 0 to 1. Invalid settings throw an error and leave the current settings unchanged.

Save settings ​

Pin settings are separate from unit data and undo history. Your app decides when to save them and when to remove overrides for deleted units.

ts
const unsubscribe = draw.pins.onChange((state) => saveViewPreferences(state));
const saved = JSON.stringify(draw.getPinPresentationState());
draw.pins.replace(JSON.parse(saved));

The state has the shape { defaults, overrides }, where overrides maps graphic IDs to their patches. Restore it after recreating the draw view. Per-unit overrides must not contain mode, occlusion, depthSort, or prioritizePins; put those in defaults.

Move units ​

draw.edit(unit) moves the unit's map location. Pin settings stay unchanged. Offset-drag shortcuts are disabled for pins.

Transform and offset editing show ordinary symbols until editing ends. Group transforms preserve the active pin presentation for selected units. Use session.setModes(...) to change the editing mode.

Hidden parts of pins cannot be picked. Selecting a unit does not reveal parts hidden by terrain. If terrain elevation is still loading, the pin waits to appear.

Check support ​

Your app can check support and show loading or error messages:

ts
const stop = draw.onPinPresentationStatusChange(({ graphicIds, status, reason }) => {
  showPresentationStatus(graphicIds, status, reason);
});
const capability = draw.getPinPresentationCapability("battalion-hq");

Status values are supported, unsupported, loading, waiting-for-elevation, and unavailable. Call stop() and unsubscribe() when disposing the view.

Rendering notes ​

Depth-sorted pins share a batch above ordinary graphics within their adapter layer. Separate adapter layers and the selection highlight surface retain their stacking order.

Pin symbols are rasterized at the map's pixel ratio, capped at 2. For high-resolution offscreen renders, raise the cap with the adapter's pinPixelRatio option; see Engines.