draw-action
本文回答:
vjmap.Draw.actionDraw*一次性绘制 API —— Promise 风格,无工具栏。
一、API 清单
| 函数 | 返回 |
|---|---|
actionDraw(map, mode, options) |
通用 |
actionDrawPoint(map, options) |
点 |
actionDrawLineSting(map, options) |
折线 |
actionDrawPolygon(map, options) |
多边形 |
actionDrawRectangle(map, options) |
矩形 |
actionDrawSlantRectangle(map, options) |
斜矩形 |
actionDrawCircle(map, options) |
圆 |
actionSelect(map, draw, options) |
选择实体 |
actionDraw* 的 Promise 从不 reject:画成时 resolve 为 draw.create 事件对象 { features: [Feature], styleId, state, ... }(不是带 type 的 FeatureCollection,但 res.features[0] 可用);未画成就退出(ESC、点数不足按 Enter、切换模式等)resolve 为 { cancel: true }(按 Enter 结束时另带 isEnterKey: true)。actionSelect resolve 为 draw.getSelected()(FeatureCollection),只在右键(draw.simpleselect.contextmenu)或开了 selectSingle 且选中后才结束。
二、通用 options
{
api: { getSnapFeatures: snapObj }, // 捕捉
updatecoordinate: (e) => { /* 实时更新 */ },
contextMenu: (e) => { /* 右键菜单 */ },
statusChange: (e) => {
if (e.isStart) { /* 开始绘制 */ }
if (e.isEnd) { /* 结束绘制 */ }
},
pointCount: N, // 画到 N 个点自动结束(线 / 多边形)
isClosed: true, // 自动闭合(线)
selectSingle: true, // actionSelect:选一个即返回
styles: [...] // 自定义样式
}
三、典型流程
// 画一条线
const res = await vjmap.Draw.actionDrawLineSting(map, {
api: { getSnapFeatures: snapObj },
updatecoordinate: (e) => {
if (!e.lnglat) return;
// 实时回显
}
});
if (res.cancel) return; // 用户 ESC
// 拿到点
const coords = res.features[0].geometry.coordinates; // [lng, lat]
const cadPts = map.fromLngLat(coords); // CAD 坐标
四、绘直线(两点)
const res = await vjmap.Draw.actionDrawLineSting(map, { pointCount: 2 });
五、绘闭合折线
const res = await vjmap.Draw.actionDrawLineSting(map, { isClosed: true });
六、自定义绘制时样式
const opts = vjmap.cloneDeep(vjmap.Draw.defaultOptions());
const styles = opts.styles;
styles[7].paint["line-width"] = ["case", ["has", "line_width"], ["get", "line_width"], 8];
styles[7].paint["line-color"] = "#0ff";
const res = await vjmap.Draw.actionDrawLineSting(map, { styles });
七、选择实体(不是 Draw 的实体,而是 CAD 实体)
两种不同的"选择":
actionSelect(map, draw, options)—— 选 Draw 里的要素。- 自定义 selectFeatures(map, ...) —— 选 CAD 图上的实体,需要
MapOpenWay.GeomRender。
CAD 实体框选 / 点选
async function selectFeatures(map, isPointSel) {
if (map.getService().currentMapParam()?.mapopenway == vjmap.MapOpenWay.Memory) {
console.error("内存打开模式不支持选择实体");
return;
}
const snapObj = {};
getMapSnapPoints(map, snapObj);
const selected = [];
while (true) {
let co;
if (isPointSel) {
const r = await vjmap.Draw.actionDrawPoint(map, {
api: { getSnapFeatures: snapObj },
contextMenu: () => map.fire("keyup", { keyCode: 13 }) // 右键 = 结束
});
if (r.cancel) break;
const pt = r.features[0].geometry.coordinates;
co = [pt, pt, pt, pt];
} else {
const r = await vjmap.Draw.actionDrawRectangle(map, {
api: { getSnapFeatures: snapObj },
contextMenu: () => map.fire("keyup", { keyCode: 13 })
});
if (r.cancel) break;
co = r.features[0].geometry.coordinates[0];
}
co = map.fromLngLat(co);
const bounds = [co[0].x, co[0].y, co[2].x, co[1].y];
const geom = await getHighlightEntities(map, bounds, true);
selected.push(...geom.features);
}
return selected;
}
getHighlightEntities 内部会调用 svc.conditionQueryFeature(条件查询 geom 字段=true)。
八、在 Action 模式里手动输入坐标
await vjmap.Draw.actionDrawPoint(map, {
statusChange: (e) => {
if (e.isStart) window.currentStyleId = e.styleId;
}
});
// 在其他地方(如键盘输入 x,y 回车)
map.fire("draw.input.click", {
lngLat: map.toLngLat([x, y]),
styleId: window.currentStyleId
});
九、旋转所选实体(复合动作)
async function selectRotate(map, draw) {
const sel = await vjmap.Draw.actionSelect(map, draw);
if (sel.features.length === 0) return;
const base = await vjmap.Draw.actionDrawPoint(map, {});
if (base.cancel) return;
const oldSel = vjmap.cloneDeep(sel.features);
const pt0 = map.fromLngLat(base.features[0].geometry.coordinates);
const endRes = await vjmap.Draw.actionDrawPoint(map, {
updatecoordinate: (e) => {
if (!e.lnglat) return;
const pt = map.fromLngLat(e.lnglat);
const angle = pt.angleTo(pt0);
// 把 oldSel 每个几何围绕 pt0 旋转 angle
const updated = vjmap.cloneDeep(oldSel);
for (const f of updated) {
const ng = vjmap.transform.convert(f, g => {
const p = map.fromLngLat(g);
return map.toLngLat(p.roateAround(angle, pt0));
});
draw.setFeatureProperty(f.id, "coordinates", ng);
}
draw.forceRefresh();
}
});
if (endRes.cancel) {
for (const f of oldSel) draw.setFeatureProperty(f.id, "coordinates", f);
draw.forceRefresh();
}
}
十、常见坑
updatecoordinate里修改业务状态 → 每帧都会触发,注意节流。- 连续绘制 →
while (true) { const r = await action...; if (r.cancel) break; }。 - "右键确认" 需要在 contextMenu 里 fire Enter:
map.fire("keyup", { keyCode: 13 })。 - Promise 无法 await 但 ctx 仍在绘制:可以保存状态并用 setTimeout 清理。
- 控件事件监听是延迟挂载的:
actionDraw*返回 Promise 时控件已创建,但其 mousemove/mouseup/keyup 监听要等map.loaded()才注册(内部connect()门控,瓦片未就绪时会拖很久)。程序化/合成事件驱动绘制前必须等连接完成——轮询map.loaded()或等本控件 draw 源出现(源名vjmap-map-draw-(hot|cold)_<styleId>,styleId 在opts.drawContext.options.styleId)。否则早期点击/按键静默丢失。 - 点吸附到上一个已提交顶点 = 结束绘制:
tr.onClick里snapped==lastVertex时直接changeMode(simple_select)(等同双击结束的语义)。同样,顶点不足(折线 <2 个提交点)按 Enter 会 resolve{cancel:true}而非 features。