Host-backed Images ​
Images are first-class TacticalDraw graphics: they render in the same stack, can be picked, and support move, uniform scale, rotation, deletion, and mixed group transforms. TacticalDraw performs no file, network, decoding, hashing, sanitization, or storage work. The host supplies already-prepared resources synchronously.
Configure the capability ​
An Image persists only a resource ID and intrinsic dimensions. Keep the bytes in your application and resolve that ID to a prepared ground-overlay resource:
import {
TacticalDraw,
type ImageCapability,
type ImageGraphic,
type ImageResource,
} from "@orbat-mapper/tactical-draw";
const resources = new Map<string, ImageResource>();
let generation = 0;
const images: ImageCapability = {
resolve: (id) => resources.get(id),
get generation() {
return generation;
},
};
const td = new TacticalDraw(adapter, { images });ImageResource.overlayUrl must be a self-contained, browser-decodable raster data URL for native ground overlays. It has positive width and height. The returned id and dimensions must match the persisted Image. Resource IDs are an immutable-content contract: replacing bytes should create a new ID. Increment generation before the next render() whenever availability changes.
Host responsibility
Never pass untrusted SVG directly to a capability. Acquire, decode, validate, sanitize, size-limit, and persist media in the host before making it resolvable. TacticalDraw deliberately has no I/O API.
Render and place ​
const overlay: ImageGraphic = {
id: "overlay-1",
kind: "image",
resourceId: "image:sha256:…",
intrinsicSize: { width: 1600, height: 900 },
position: [10.75, 59.91],
rotation: 0,
size: { value: 5_000, unit: "meters" },
schemaVersion: 1,
name: "Operations overlay",
style: { opacity: 0.8 },
};
td.render([overlay]);
const placed = await td.draw({
kind: "image",
resourceId: overlay.resourceId,
intrinsicSize: overlay.intrinsicSize,
name: overlay.name,
});Image draw commits its geographic center on one click. Image width is always ground-anchored and persisted in meters. When omitted, the initial width is derived from 128 screen pixels at the placement zoom and immediately stored as meters. Images render as four-corner georeferenced overlays, so map engines scale them as part of the map throughout animated zooms.
Edit and recover missing resources ​
const edited = await td.edit(overlay, { modes: ["transform"] });
await td.editMany([edited.graphic, controlMeasure, pointSymbol]);Corner scaling preserves aspect ratio and the opposite corner. Rotation uses clockwise radians from geographic north. The complete rotated rectangle is the selection surface, including transparent pixels.
If resolve() returns undefined, the Image remains selectable and renders a built-in placeholder with its persisted footprint. A missing capability or a malformed/dimension-mismatched resource throws synchronously.