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 ​
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: falseto 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.widthto0to 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:
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:
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:
| Setting | Per-unit override | Purpose |
|---|---|---|
enabled | Yes | Show a pin or an ordinary symbol |
mode | No | Screen vertical or world vertical |
occlusion | No | Hide parts behind terrain |
depthSort | No | Draw pins far to near |
prioritizePins | No | Prefer visible pins during selection |
liftPixels | Yes | Lift in screen mode |
heightMeters | Yes | Height in world mode |
sizePixels | Yes | Symbol size |
stem | Yes | Color, width, opacity |
dot | Yes | Color, radius, opacity |
halo | Yes | Stem and dot outline: color, width, opacity |
symbolHalo | Yes | Symbol outline: color, width, opacity |
plate | Yes | Enabled, 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.
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:
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.