# 图形分析

> 分类代号：C.4  本章共 **6 个接口**

这一组接口都是 vjmap 的"增值分析"能力，参数差异大、且对输入的 DWG/图像质量要求很高，因此 SDK 采用 **pass-through** 风格：你按下面文档组参数，SDK 负责 POST 与错误处理，**不做参数字段级强校验**。

全部走 `POST /api/v1/map/cmd/{cmd}/_null/v1`（`_null` 表示不绑定具体 mapid，参数里自带）。

---

## 快速索引

| 序号 | SDK 方法 | cmd |
|----:|---------|------|
| 30 | `cmd_map_diff` / `cmdMapDiff` | `mapDiff` |
| 31 | `cmd_map_compare` / `cmdMapCompare` | `mapCompare` |
| 32 | `cmd_extract_table` / `cmdExtractTable` | `extractTable` |
| 33 | `cmd_extract_center_lines` / `cmdExtractCenterLines` | `extractCenterLines` |
| 34 | `cmd_match_object` / `cmdMatchObject` | `objectMatch` |
| 35 | `cmd_image_svg_to_dwg` / `cmdImageSvgToDwg` | `imageSvgToDwg` |

---

## 30 · 像素差异 `mapDiff`

比较两张 DWG **渲染成图片后的像素差异**，返回增删改的像素块外包矩形中心点。

### 请求参数

| 字段 | 类型 | 默认 | 说明 |
|------|------|-----:|------|
| `mapid1` / `version1` | string | — | 旧图（version 缺省取该图最新版） |
| `mapid2` / `version2` | string | — | 新图 |
| `layer1` / `layer2` | string | `""` | 两张图各自参与对比的 style 名 |
| `size` | int | 4096 | 渲染网格边长（像素） |
| `cellsize` | int | 10 | 差异判定的单元格大小（像素） |
| `diffMinPixel` | int | 0 | 单元格内差异像素数阈值，低于则忽略 |
| `diffMinAlpha` | int | 25 | alpha 差异阈值 |
| `diffMinColor` | int | 6 | RGB 颜色差异阈值 |
| `noCompareNew` | bool | false | 不统计"新增"差异 |
| `noCompareDelete` | bool | false | 不统计"删除"差异 |

### 响应（实采：同图 vs 同图）

```json
{
  "del": "",
  "modify": "",
  "new": "",
  "status": true
}
```

有差异时 `del` / `modify` / `new` 是 `x1,y1,x2,y2;x3,y3,x4,y4` 格式的字符串；`svc.cmd_map_diff()` 不做 parse，调用方按需切分。

### Python

```python
res = svc.cmd_map_diff(
    mapid1="sys_zp", version1="v1",
    mapid2="sys_zp", version2="v2",
    cellsize=10, diffMinColor=6,
)
```

---

## 31 · 实体对比 `mapCompare`

和 `mapDiff` 不同：`mapCompare` 比较的是 **DWG 实体**，不是渲染像素。

### 请求参数

| 字段 | 类型 | 默认 | 说明 |
|------|------|-----:|------|
| `mapid1` / `version1`, `mapid2` / `version2` | string | — | 两张图 |
| ~~`layer1` / `layer2`~~ | string | — | **无效**：`cmdMapCompare` 读了 `layer1` 但从未使用，`layer2` 根本不读——实体对比总是整图，不能按 style 限定 |
| `noCompareProps` | bool | false | 不比较实体扩展属性，只比几何 |
| `comparePropMaxSize` | int | 5000 | 参与属性比较的实体数量上限 |

后端会先做一次快速 hash 去重。**两张图是同一文件时直接拒绝**（与 `mapDiff` 返回空差异不同）：

```json
{"error": "The files to be compared are the same<mapid>", "status": false}
```

> ⚠️ 报错文案里 `same` 与 mapid 之间缺空格（服务端原文如此）；HTTP 仍是 200，调用方须自查 `status`/`error` 字段。

真实对比时返回：

```json
{
  "status": true,
  "addEntities": [...],     // 仅在新图存在的实体 [{name, objectid, bounds}]
  "deleteEntities": [...],  // 仅在旧图存在
  "modifyEntities": [...],  // 两边都有但有差异（含 differents 明细）
  "mapid1EntityCount": 123,
  "mapid2EntityCount": 130
}
```

### Python

```python
svc.cmd_map_compare(mapid1="a", version1="v1", mapid2="a", version2="v2",
                    noCompareProps=True)
```

---

## 32 · 表格提取 `extractTable`

自动从 DWG 里识别"线条围出来的表格"，返回单元格网格与每格文本。**最常用**于检图、清单导出。

### 请求参数

| 字段 | 说明 |
|------|------|
| `mapid` / `version` | 要识别的图 |
| `layer` | 可选，只在某 style / layer 上跑 |
| `bounds` | 可选，限定在地图坐标系下的 bbox（`[x1,y1,x2,y2]`） |
| `condition` | 可选，后端条件表达式（同 queryFeatures 的 `condition`） |
| `isContains` | 可选，bounds 内包含模式（默认相交） |
| `geom` | 可选，是否带几何参与识别（默认 true） |
| `pageLimit` | 可选，内部查询分页上限 |
| `findChildMapRects` | 可选，同时提取子图框范围 |

### 响应（实采：sys_zp/v1，无额外参数）

```json
{
  "tables": [{
    "attr": {
      "cellEmptyRatio": 45,
      "tableCellMaxCount": 70,
      "tableTextCount": 39,
      "unLinkLineRatio": 0
    },
    "colCount": 4,
    "cols": ["587693680.21", "587698180.22", "587717259.64", "..."],
    "datas": [["类目", "数量", "价格", "说明"], [...]],
    "rows": ["...", "..."]
  }]
}
```

- `cols` / `rows`：单元格网格线的坐标；
- `datas[i][j]`：每格文本（可能含编码乱码，视 DWG 字体而定）。

### Python

```python
res = svc.cmd_extract_table(mapid="sys_zp", version="v1")
for t in res.get("tables", []):
    print(t["colCount"], "x", len(t["rows"]))
```

### Node.js

```javascript
const res = await svc.cmdExtractTable({ mapid: 'sys_zp', version: 'v1' });
console.log(res.tables?.length);
```

---

## 33 · 中心线提取 `extractCenterLines`

对闭合多边形（如道路、管线）抽取中心线。

### 请求参数

| 字段 | 说明 |
|------|------|
| `mapid` / `version` | 源图 |
| `layer` | 可选，限定 style / layer |
| `bounds` | 可选，限定地图坐标 bbox |
| `condition` | 可选，后端条件表达式（同 queryFeatures 的 `condition`，**不是 `expr`**） |
| `isContains` | 可选，bounds 包含模式（默认 false 相交） |
| `pageLimit` | 可选，内部查询分页上限 |
| `bMergeLine` / `bMergeNode` / `bConnectGrapLine` 等 | 可选，中心线合并/连接调优开关（`b` 前缀 bool 系列） |

### 响应（实采：sys_zp/v1）

```json
{
  "centerlines": [
    [[587562497.79, 3103857885.77], [587556239.69, 3103860401.38]],
    [[587603886.22, 3103921932.46], [587602627.19, 3103925333.77]]
  ]
}
```

`centerlines` 是"多段折线数组"；每条线由若干 `[x, y]` 顶点构成。

### Python

```python
res = svc.cmd_extract_center_lines(mapid="sys_zp", version="v1")
for line in res["centerlines"]:
    print("line with", len(line), "verts")
```

---

## 34 · 目标匹配 `objectMatch`

在底图中找与模板图形相似的子图（样式 + 尺寸）。常用于批量识别图章、标注块、设备。

### 请求参数

| 字段 | 类型 | 默认值 | 说明 |
|------|------|-------|------|
| `mapid` / `version` | string | — | 底图（模板与搜索都在这张图内） |
| `objectBounds` | string | — | **必填**：模板图形所在 bbox `[x1,y1,x2,y2]`（地图坐标）。**必须是字符串形态**——直接传 JSON 数组 `[0,0,50,50]` 会被拒 `{"error":"param error, objectBounds is empty"}` |
| `objectIds` | string | — | 或用实体 id 列表指定模板（与 objectBounds 二选一） |
| `mapBounds` | string | 全图范围 | 搜索范围 bbox，缺省用图档 drawBounds/dbBounds |
| `layer` / `layeron` | string | — | 底图 style / 参与渲染的图层 |
| `method` | string | `matchTemplate` | 匹配算法 |
| `score` | number | 0.6 | 相似度阈值 |
| `maxCount` | int | 100 | 最大命中数 |
| `canOverlap` | bool | false | 命中框是否允许重叠 |
| `maxOverlap` | number | 0.2 | 允许的最大重叠比例 |
| `toleranceAngle` | number | 0 | 角度容差（度） |
| `useToleranceRange` | bool | false | 启用尺寸容差区间，配合 `tolerance1`~`tolerance4` |
| `minReduceArea` | number | 256 | 小于此面积的命中框被忽略 |
| `featureCount` | int | 128 | 特征点数量 |
| `size` | int | 16384 | 内部渲染分辨率 |

### 响应

成功时返回（注意字段名是 **`match`**）：

```json
{
  "status": true,
  "match": [
    { "bounds":"[x1,y1,x2,y2]", "angle": 0.0, "score": 0.92 },
    ...
  ]
}
```

### Python

```python
res = svc.cmd_match_object(
    mapid="sys_zp", version="v1",
    objectBounds="[587661000,3103886000,587661200,3103886200]",
    score=0.7, toleranceAngle=90,
)
```

---

## 35 · SVG / 图像转 DWG `imageSvgToDwg`

把 SVG 字符串或位图转换成 DWG 实体。对位图会先做线条检测 + 矢量化。

### 请求参数

| 字段 | 说明 |
|------|------|
| `svg` | SVG 文本 |
| `image` | 位图 base64 data-URL（`data:image/png;base64,...`，与 `svg` 二选一） |
| `layer` | 结果实体的默认图层名 |
| `flipY` | Y 轴翻转（默认 true，SVG/位图坐标系转 CAD） |
| `offsetX` / `offsetY` | 结果整体偏移 |
| `lineWidthScale` | 线宽缩放 |
| `whiteColorProcessingParam` / `blackColorProcessingParam` | 位图矢量化时的白/黑色处理参数（int） |
| `hasMapBounds` + `mapBoundLeftTopX/Y` + `mapBoundRightBtmX/Y` | 把结果对齐到指定地图坐标范围 |
| `isZoomExtents` | 结果 JSON 是否标记缩放到全图（默认 true） |
| `showLineWidth` | 结果 JSON 的 LWDISPLAY（默认 true） |
| `retDocument` | true 时额外返回 `doc`（DWG JSON 字符串） |
| `bDisbleDrawFill` | 禁用填充绘制（bool；后端拼写就是 `Disble`） |

### 响应（实采：svgData="`<svg></svg>`"，空内容）

```json
{
  "error": "image or svg content is empty",
  "status": false
}
```

合法输入时返回——`doc` 与 `fileid` **互斥**（`retDocument=true` 返回 `doc` DWG JSON 字符串；false 时落盘成临时文件返回 `fileid`）：

```json
// retDocument=true
{ "status": true, "doc": "<DWG JSON 字符串>" }
// retDocument=false（默认）
{ "status": true, "fileid": "processData/xxx_tmp_60.dwg" }
```

可以把 `doc` 作为 `mapdata` 传给 `create_entity_geo_data`（见 [03-query.md](./03-query.md)）渲染。

---

## 通用注意

1. 这组接口都可能 **耗时 >10s**，SDK 默认 timeout=60s，建议在业务层再做重试与超时兜底。
2. 后端遇到参数非法时常返回 `{"error": "...", "status": false}`——不是 HTTP 4xx/5xx。SDK 会把它当成 2xx 正常响应返回给调用方。
3. 返回 `"status": false` 时 `error` 字段的语言**依系统 locale 而定**（我们这里抓到了中/英混合的错误串）。

---
