> **本文回答**：`vjmap.Draw.Tool` 控件用法 —— 选项 / 模式 / API / 事件 / 样式。

## 一、创建 Draw 控件

```js
const draw = new vjmap.Draw.Tool({
  keybindings: true,    // 键盘交互
  touchEnabled: true,
  boxSelect: true,      // shift+拖 框选
  snap: true,           // 捕捉
  guides: true,         // 对齐辅助线（经过已有顶点的水平/竖直参考线，不是网格）
  snapOptions: {
    snapPx: 15,
    snapToMidPoints: false,
    snapVertexPriorityDistance: 1.25,
    snapGridPx: 2000     // 辅助线的半长（像素），不是网格间距
  },
  controls: {
    point: true, line_string: true, polygon: true, trash: true
  },
  displayControlsDefault: true
});
map.addControl(draw, "top-right");
```

## 二、模式（mode）

| mode | 说明 |
|---|---|
| `static` | 非编辑态，可 hover / click 高亮 |
| `simple_select` | 选择、删除、拖整体 |
| `direct_select` | 选、删、拖顶点 |
| `draw_point` | 画点 |
| `draw_line_string` | 画线 |
| `draw_polygon` | 画多边形 |
| `draw_circle` | 画圆 |
| `draw_rectangle` | 画矩形 |
| `splitLineMode` | 切割线 |
| `cutPolygonMode` | 切割多边形 |

切换：

```js
draw.changeMode("draw_line_string");
draw.getMode();   // 当前模式
```

> 坑位：`changeMode("direct_select")` 必须带 `{ featureId }`，否则抛 `You must provide a featureId`；其余模式可裸名切换。

## 三、常用 API

```js
// 增/查
const ids = draw.add({ type: "Feature", geometry: { type: "Point", coordinates: [0,0] } });
draw.get(id);
draw.getAll();
draw.getSelected();
draw.getSelectedIds();

// 删
draw.delete(id);
draw.delete([id1, id2]);
draw.deleteAll();
draw.trash();      // 调当前模式的"删除"动作

// 批量替换
draw.set(featureCollection);

// 历史
draw.undo();
draw.redo();
draw.clearHistory();

// 刷新（改 geometry 后调）
draw.forceRefresh();

// 组合（⚠️ 只在 simple_select 模式下可用（组合需选中 ≥2 个）——其它模式下调用抛
// "onCombineFeatures is not a function"，组合 / 取消组合处理器只注册在 simple_select 上）
draw.combineFeatures();
draw.uncombineFeatures();
```

## 四、设置 Feature 属性

```js
draw.setFeatureProperty(id, "color", "#f00");
draw.setFeatureProperty(id, "line_width", 3);
draw.setFeatureProperty(id, "hoverPointer", true);  // 浏览模式高亮
draw.setFeatureProperty(id, "hover_color", "#ff0");
draw.setFeatureProperty(id, "isLocked", true);       // 锁定
draw.setFeatureProperty(id, "isOff", true);          // 隐藏
draw.setFeatureProperty(id, "noSnap", true);         // 禁捕捉
draw.setFeatureProperty(id, "disable_edit", true);   // 禁编辑
draw.setFeatureProperty(id, "minZoom", 5);
draw.setFeatureProperty(id, "maxZoom", 10);
draw.setFeatureProperty(id, "extrusionHeight", 100); // 多边形拉伸
```

点的额外属性：

```js
draw.setFeatureProperty(id, "icon_image", "my-icon");
draw.setFeatureProperty(id, "icon_size", 1.2);
draw.setFeatureProperty(id, "text", "A");
draw.setFeatureProperty(id, "text_size", 22);
draw.setFeatureProperty(id, "symbol", true);         // 带图标+文字
```

## 五、事件

```js
map.on("draw.create", e => console.log("新建", e.features));
map.on("draw.update", e => console.log("更新", e.action, e.features));
map.on("draw.delete", e => console.log("删除", e.features));
map.on("draw.selectionchange", e => console.log("选中", e.features));
map.on("draw.modechange", e => console.log("模式", e.mode));
map.on("draw.render", () => console.log("重绘"));
map.on("draw.actionable", e => console.log("可操作", e.actions));
map.on("draw.updatecoordinate", e => console.log("坐标", e.lnglat));
map.on("draw.simpleselect.contextmenu", e => { /* 右键 */ });
map.on("draw.directselect.contextmenu", e => { /* 右键 */ });
map.on("draw.custom", e => {
  if (e.customName === "clickedLocked") { /* 点了锁定图形 */ }
});

// static 模式
map.on("draw.static.mouseenter", e => { /* popup */ });
map.on("draw.static.mouseleave", e => { /* popup remove */ });
map.on("draw.static.click", e => { /* 点击 */ });
```

## 六、自定义控件按钮

⚠️ `addControls` 里与内置按钮**同 id** 的项会被丢弃（内置 id：`drawCircle / drawRectangle / splitLine / cutPolygon / undo / redo / snap_mode_snap / snap_mode_grid`），内置按钮照常保留——自定义按钮要用新 id；要隐藏内置按钮设 `opts.controls.<id> = false`（如 `opts.controls.splitLine = false`）。

```js
const opts = vjmap.cloneDeep(vjmap.Draw.defaultOptions());
opts.addControls = [
  {
    id: "myCircle", title: "画圆", className: "vjmap-map-draw_circle",
    onActivate: (ctx) => ctx.api.changeMode("draw_circle")
  },
  {
    id: "mySplitLine", title: "切线", className: "vjmap-map-draw-cut-line",
    onActivate: (ctx) => ctx.api.changeMode("splitLineMode", { spliter: "line_string" })
  },
  {
    id: "myUndo", title: "撤销", className: "vjmap-map-draw_undo",
    onActivate: (ctx) => ctx.api.undo()
  },
  {
    id: "mySnap", title: "捕捉", isCheckbox: true, checked: true,
    className: "snap_mode_snap",
    onActivate: (ctx, e) => ctx.options.snap = e.target.checked
  }
];
// 隐藏同功能的内置按钮
Object.assign(opts.controls, { drawCircle: false, splitLine: false, undo: false, snap_mode_snap: false });
const draw = new vjmap.Draw.Tool(opts);
```

## 七、绘制时禁用键盘

```js
map.setCustomKeyValue("_disable_drawkey_backspacekey", true);
map.setCustomKeyValue("_disable_drawkey_escapekey", true);
map.setCustomKeyValue("_disable_drawkey_enterkey", true);
```

## 八、默认样式修改

`Draw.defaultOptions()` 只是浅拷贝，`styles` / `controls` / `addControls` 是全局共享对象——直接改会污染之后所有 Draw 实例（含 `actionDraw*` 内部建的临时实例），先 `vjmap.cloneDeep`：

```js
const opts = vjmap.cloneDeep(vjmap.Draw.defaultOptions());
opts.styles[7].paint["line-width"] = 4;   // 线宽
opts.styles[7].paint["line-color"] = "#0f0";
opts.midpoints = false;                    // 不显示中点
const draw = new vjmap.Draw.Tool(opts);
```

## 九、仅 action 模式（不显示工具栏）

```js
const opts = vjmap.cloneDeep(vjmap.Draw.defaultOptions());
opts.isActionDrawMode = true;  // 不生成按钮
const draw = new vjmap.Draw.Tool(opts);
map.addControl(draw, "top-right");
// 用自己的实例驱动绘制：changeMode + 按 styleId 过滤 draw.create
map.on("draw.create", e => { if (e.styleId === draw.options.styleId) console.log(e.features); });
draw.changeMode("draw_line_string");
```

注意：`vjmap.Draw.actionDraw*` 每次都**自建并在结束后移除一个临时 Tool**，不会画进你的 `draw` 实例；要把结果放进自己的实例，拿 resolve 的 `features` 再 `draw.add(...)`。

## 十、常见坑

1. **`setFeatureProperty` 后不可见** → `draw.forceRefresh()`。
2. **触摸端体验差** → 调大 `touchBuffer`（默认 25）。
3. **多个 Draw 实例事件乱**：不同 draw 的 options.styleId 不同，在事件回调里比较 `e.styleId` 过滤。
4. **`draw.add(feature)` 但 id 已存在** → 实际是 update，不是新建；要唯一可以 `delete` + `add`。
5. **程序化增删不进历史、不发事件**：`add`/`delete`/`set`/`deleteAll` 只写 store + render（`api.add` 不发 `draw.create`，`api.delete` 走 `silent:true`）。历史记录只在 `draw.create/update/delete/combine` 事件的监听里生成——即 **`undo`/`redo` 只覆盖交互操作**，对程序化 `add` 是 no-op。需要可撤销的程序化改动要自己管快照，或走 `draw.set` + 自建历史。
