Engines ​
TacticalDraw talks to a map through MapAdapter. Choose the adapter package for your engine; the rest of your TacticalDraw code stays the same.
Switch engines in the demo below. Each library loads only when selected.
Wrap your map with the matching adapter:
new MapLibreAdapter(map); // maplibre-gl Map
new OpenLayersAdapter(map); // ol/Map
new LeafletAdapter(map); // leaflet MapFrom there, new TacticalDraw(adapter), td.draw(...), and td.render(...) work identically. See the Quick start for a complete example.
Supported engines ​
| Engine | Adapter package | Supported versions |
|---|---|---|
| MapLibre GL JS | @orbat-mapper/tactical-draw-adapter-maplibre | 5.8 or later, 6.x |
| OpenLayers | @orbat-mapper/tactical-draw-adapter-openlayers | 7 or later |
| Leaflet | @orbat-mapper/tactical-draw-adapter-leaflet | 1.9 |
Each engine is a peer dependency, so your app controls its version.
All three adapters render the same styles, labels, fill patterns, point symbols, and Images. Billboard pins are the exception: they require MapLibre GL JS 6 with WebGL2, and other engines show ordinary symbols.
Install ​
Install TacticalDraw, one adapter, and its peer map engine. No other engine packages are needed.
MapLibre GL ​
pnpm add @orbat-mapper/tactical-draw @orbat-mapper/tactical-draw-adapter-maplibre maplibre-glnpm install @orbat-mapper/tactical-draw @orbat-mapper/tactical-draw-adapter-maplibre maplibre-glimport { TacticalDraw } from "@orbat-mapper/tactical-draw";
import { MapLibreAdapter } from "@orbat-mapper/tactical-draw-adapter-maplibre";
import * as maplibregl from "maplibre-gl";
import workerUrl from "maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url";
import "maplibre-gl/dist/maplibre-gl.css";
maplibregl.setWorkerUrl(workerUrl);
const map = new maplibregl.Map({ container, style, center, zoom });
map.on("load", () => {
const td = new TacticalDraw(new MapLibreAdapter(map));
});Create the adapter after the map's load event. MapLibre 6 needs its worker configured as shown; follow MapLibre's v5-to-v6 migration guide for your bundler. With MapLibre 5, omit the setWorkerUrl lines.
MapLibreAdapter accepts options as a second argument:
| Option | Default | Description |
|---|---|---|
viewChangeMode | "continuous" | "continuous" refits screen-sized graphics, handles, and offset symbols on every frame of an animated zoom, rotate, or pitch. "settle" refits once when the animation ends, which is cheaper for large scenes. |
terrainBanding | true | With terrain active, paints all fills and lines in one band below points, icons, text, and pins, so terrain draping takes few passes. false keeps exact per-graphic paint order at one draping pass per graphic. |
pinPixelRatio | map ratio, ≤ 2 | Device pixels per CSS pixel used to rasterize pin symbols. Raise it for sharp offscreen renders above 2×. |
new MapLibreAdapter(map, { viewChangeMode: "settle" });OpenLayers ​
pnpm add @orbat-mapper/tactical-draw @orbat-mapper/tactical-draw-adapter-openlayers olnpm install @orbat-mapper/tactical-draw @orbat-mapper/tactical-draw-adapter-openlayers olimport { TacticalDraw } from "@orbat-mapper/tactical-draw";
import { OpenLayersAdapter } from "@orbat-mapper/tactical-draw-adapter-openlayers";
import OLMap from "ol/Map";
import "ol/ol.css";
const map = new OLMap({ target, layers, view });
const td = new TacticalDraw(new OpenLayersAdapter(map));OpenLayersAdapter accepts { viewChangeMode: "continuous" | "settle" } as a second argument, with the same meaning as for MapLibre. OpenLayers dispatches clicks on singleclick, so a click fires after a short delay that distinguishes it from a double-click.
Leaflet ​
pnpm add @orbat-mapper/tactical-draw @orbat-mapper/tactical-draw-adapter-leaflet leafletnpm install @orbat-mapper/tactical-draw @orbat-mapper/tactical-draw-adapter-leaflet leafletimport { TacticalDraw } from "@orbat-mapper/tactical-draw";
import { LeafletAdapter } from "@orbat-mapper/tactical-draw-adapter-leaflet";
import L from "leaflet";
import "leaflet/dist/leaflet.css";
const map = L.map(target, { center, zoom });
const td = new TacticalDraw(new LeafletAdapter(map));LeafletAdapter takes no options. Screen-sized graphics refit after Leaflet's zoom animation finishes rather than during it.
Use a different map engine ​
For another engine, extend BaseMapAdapter and implement the remaining MapAdapter methods. The adapter surface lists them, and the conformance suites in @orbat-mapper/tactical-draw/testing check your implementation against the same tests as the built-in adapters.