唯杰地图 logo唯杰地图
VJMAP
EN

annotation

vjmap/references/overlay/annotation.md 3.6 KB123 行
原文

本文回答:图纸批注 —— 箭头 / 框线 / 文字写在 CAD 图上保存(SVG + DivOverlay)。

一、方案

使用 Fabric.js 绘制 SVG 批注 → 导出 SVG 字符串 → 用 vjmap.DivOverlay 把 SVG 叠加到地图一个范围。

二、DivOverlay 类

new vjmap.DivOverlay({
  bounds: [[minX, minY], [minX, maxY], [maxX, maxY], [maxX, minY]]  // 四个 CAD 点
       | new vjmap.GeoBounds(...),
  element: HTMLElement,        // 要附加的 div(含 svg)
  width, height,                // 自身 width / height
  updateDivSize: true,          // svg 需要放大显示时开启
  minZoom, maxZoom,
  maxDivSize                    // 超过该大小后改像素缩放
})
.addTo(map);

三、流程

步骤 1:在独立 Fabric.js canvas 里绘制

const canvas = new fabric.Canvas("draw-canvas");
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = "#f00";
canvas.freeDrawingBrush.width = 3;
// 或用 fabric.Line / Circle / Rectangle / Textbox ...

步骤 2:导出 SVG

const svgText = canvas.toSVG();

步骤 3:包装 div + DivOverlay

function createDivSvg(map, eleId, p1, p2, w, h, svg) {
  svg = svg.substring(svg.indexOf("<desc>"));
  const div = document.createElement("div");
  div.id = eleId;
  div.innerHTML = `
    <svg viewBox="0 0 ${w} ${h}" preserveAspectRatio="xMinYMin meet" version="1.1" xmlns="http://www.w3.org/2000/svg">
      ${svg}
    </svg>
  `;
  Object.assign(div.style, {
    position: "absolute",
    pointerEvents: "none",
    width: w + "px",
    height: h + "px"
  });

  const overlay = new vjmap.DivOverlay({
    bounds: [[p1.x, p2.y], [p1.x, p1.y], [p2.x, p1.y], [p2.x, p2.y]],
    element: div,
    width: w,
    height: h,
    updateDivSize: true
  });
  overlay.addTo(map);
  return overlay;
}

步骤 4:保存到后端

const anno = {
  mapId, version,
  name: "批注-1",
  imgSrc: canvas.toDataURL(),
  zoom: map.getZoom(),
  centerX: map.getCenter().lng,
  centerY: map.getCenter().lat,
  bearing: map.getBearing(),
  pitch: map.getPitch(),
  pt1: p1, pt2: p2,
  width: w, height: h,
  svg: canvas.toSVG(),
  json: canvas.toJSON()
};

// 自己的 API 存数据库
await api.save(anno);

步骤 5:恢复批注(从后端读出并渲染)

for (const a of await api.load(mapId, version)) {
  createDivSvg(map, a.id, a.pt1, a.pt2, a.width, a.height, a.svg);
}

四、常见批注元素(Fabric.js)

  • 箭头:fabric.Path + drawArrow(x1, y1, x2, y2, theta, headLen)
  • 直线 / 虚线:fabric.Line + strokeDashArray
  • 圆:fabric.Circle(半径=距离一半)
  • 矩形:fabric.Rect 或 fabric.Path(4 点)
  • 三角形:fabric.Triangle 或 fabric.Path
  • 文字:fabric.Textbox + enterEditing()
  • 自由画笔:canvas.isDrawingMode = true

五、导出为图片保存(缩略图)

const imgData = canvas.toDataURL({ format: "png", quality: 0.8 });

六、常见坑

  1. SVG 不随缩放放大 → updateDivSize: true + preserveAspectRatio="xMinYMin meet"。
  2. SVG 里有 <desc> 标签导致截取错误 → 从 <desc> 开始截避免 <?xml 头的命名空间。
  3. pointer-events: none:SVG 批注不应干扰地图点击;若需要交互,则改 auto。
  4. 坐标系:p1/p2 用 CAD 坐标。DivOverlay 内部对 bounds 的每个点无条件 map.toLngLat 再投影,没有自动判定——bounds 只能传 CAD 坐标(GeoBounds 或四角点数组),传经纬度会被当成 CAD 坐标再转一次而错位。