# 地图服务后端SDK接口及下载
vjmap 后端 REST 接口文档 — 面向二次开发者的版本(
vjmapservice/)。
后端SDK下载python、nodejs、swagger服务。下载地址:http://vjmap.com/download/vjmapservice.zip (opens new window)

# 目录
- 00-overview.md
- 01-getting-started.md
- 02-conventions.md
- 03-authentication.md
- 04-workspace.md
- api/index.md
- api/01-map-management.md
- api/02-tiles-styles.md
- api/03-query.md
- api/04-analysis.md
- api/05-compose-export.md
- api/06-webcad.md
- api/07-workspace.md
- api/08-data-storage.md
- api/09-fullsearch.md
- api/10-symbols.md
- api/11-system.md
- api/12-service-misc.md
- api/13-ai.md
# 00-overview.md
# 概述(Overview)
# 1. 产品定位
vjmap 是一套以 CAD/GIS 图档为中心的服务端系统,核心能力包括:
- 上传 DWG / DXF / 图片等源文件,在服务端解析成数据库
- 以栅格瓦片或 MVT 矢量瓦片的形式提供 Web 地图访问
- 基于 cmd 指令完成元数据查询、样式切换、几何分析、表格/中心线提取、文件导出等
- 存储用户 KV 数据、工作区、符号库、全文索引等辅助能力
- 支持 Python / Node.js SDK 直接对接;
典型使用场景:
- 将单位内部 DWG 档案转成 Web 可浏览地图
- 需要把 CAD 图与 WMS / MVT 图层混合使用
- 针对 DWG 做图形分析(比较、表格提取、中心线)并导出 PDF / DXF
- 在业务系统里存储轻量 KV 配置或自定义属性
# 2. 系统架构
┌────────────────┐ JWT ┌──────────────────────┐ stdin/stdout ┌──────────────────────────┐
│ Client (Web/ │ ─────────► │ web服务 │ ───────────────► │ 图形引擎服务 │
│ Python / Node) │ │ :27660 │ │ geom / render / export │
└────────────────┘ ◄───────── │ router + handler │ ◄─────────────── │ appCmd / mapPsCmd / ... │
▲ │ + 缓存 / 限流 / 权限 │ │ 持久化 │
│ REST / HTTP └──────────────────────┘ └──────────────────────────┘
│ │
│ ├── Workspace(多租户命名空间)
│ ├── Full-text search
│ ├── 符号库
│ └── AI / 向量检索(可选)
│
└── Tile 通过 CDN / HTTP 缓存直接给前端
2
3
4
5
6
7
8
9
10
11
12
13
- web服务(:入口;做 JWT 鉴权、路由转发、缓存清理、工作区路由改写。
- **图形服务 **:实际的 CAD 解析/渲染/分析。
- 数据存储:每张图对应一个 数据库文件(默认
data/maps/{hash}.db),元数据、样式、缓存瓦片都放在里面;另有全局 KV、工作区、符号库等独立存储。 - 可选 AI服务:
/ai/*、/aimcp/*、/aiagent/*三套子服务通过反代挂载;不是所有部署都启用。
# 3. 常用接口分 13 大类
| # | 分类 | 典型接口 | 文档 |
|---|---|---|---|
| 1 | 地图管理 | upload / openmap / metadata / listmaps / thumbnail | api/01-map-management.md |
| 2 | 瓦片/样式 | tile / mvt / wms / createMapStyle / switchlayer | api/02-tiles-and-styles.md |
| 3 | 要素查询 | queryFeatures / createEntityGeoData | api/03-query.md |
| 4 | 图形分析 | mapDiff / mapCompare / extractTable / centerLines | api/04-analysis.md |
| 5 | 组合/导出 | composeNewMap / exportPdf / exportDxf | api/05-compose-and-export.md |
| 6 | WebCAD | listWebcadDraws / saveWebcadPatch(可选) | api/06-webcad.md |
| 7 | 工作区 | getWorkspace / workspaceCreate | api/07-workspace.md |
| 8 | KV 存储 | data/{key} / datas / datakeys | api/08-data-storage.md |
| 9 | 全文搜索 | fullsearch/search / add / delete | api/09-fullsearch.md |
| 10 | 符号库 | symbol/categories / symbol/list | api/10-symbols.md |
| 11 | 系统 | heart / version / runstatus / viewlogs | api/11-system.md |
| 12 | /service 杂项 | qrcode / fonts / uploadImg | api/12-service-misc.md |
| 13 | AI(可选) | vectorSearch / chatFaq / vectorDocs | api/13-ai.md |
# 4. 该怎么读这份文档?
按兴趣从下面挑一条路径:
- 第一次接触 vjmap:先过
01-getting-started.md把 Python 或 Node SDK 跑起来。 - 看得懂 curl 就够:直接翻
api/01-map-management.md,里面每个接口都有curl+ Python + Node 三列。
# 5. 命名与术语
| 术语 | 含义 |
|---|---|
mapid | 图唯一 ID;通常是业务名或 _ 占位 |
version | 图版本号;默认 v1 |
layer | 图层名;多数 cmd 通过请求体 layers 字段指定 |
fileid | 源文件 ID,一般等于 md5 前缀 |
secretKey / accessKey | 加密图的访问密钥(前者 16 字符,后者 ak 开头 18 字符) |
workspace | 命名工作区;默认工作区等价于 ""(空) |
geom render | 图形服务子进程,负责几何渲染/切片 |
# 6. 约定(Conventions)速览
- 所有
/api/v1/map/...接口需要token=<JWT>(query 或 header) - 95 % 的 cmd 接口既接受 GET 也接受 POST;SDK 统一走 POST
- 成功响应结构两类:
- 网关包装
{code:0, msg:"", data:{...}} - 图形服务 JSON(直接是
{...}或数组);SDK 自动识别并 unwrap
- 网关包装
- 错误:HTTP 非 2xx → 抛
VjmapError;网关{code≠0}也视为错误抛出 - 详细规则见
02-conventions.md
# 01-getting-started.md
# 快速上手(Getting Started)
在 5 分钟内用 Python 或 Node.js SDK 跑通 vjmap 的最小闭环。
# 1. 前置条件
- 已部署的 vjmap 服务,默认运行在
http://127.0.0.1:27660 - 一个有效 JWT Token(开发环境可直接从后端控制台获取;生产请走登录接口)
- Python ≥ 3.8 或 Node.js ≥ 18
# 2. 健康检查
服务是否活着:
curl -s http://127.0.0.1:27660/heart
# => {"status":"ok","timestamp":"..."}
curl -s http://127.0.0.1:27660/version
# => {"arch":"amd64","os":"windows","status":"ok","time":"...","version":"..."}
2
3
4
5
# 3. Python 一分钟体验
# 1. 拷贝 SDK
cp sdk/python/vjmap.py your-project/
# 2. 装依赖
pip install requests
# 3. hello world
python - <<'PY'
from vjmap import Vjmap
svc = Vjmap("http://127.0.0.1:27660/api/v1", "<paste-token>")
print("heart ->", svc.heart())
print("version ->", svc.version())
print("status ->", svc.run_status(detail=True)["application"])
PY
2
3
4
5
6
7
8
9
10
11
12
13
14
期望输出:
heart -> {'status': 'ok', 'timestamp': '...'}
version -> {'arch': 'amd64', 'os': 'windows', 'status': 'ok', 'time': '...', 'version': '2026041001'}
status -> {'pid': '...', 'runtime': ..., ...}
2
3
# 4. Node.js 一分钟体验
cp sdk/nodejs/vjmap.js your-project/
node - <<'JS'
const { Vjmap } = require('./vjmap');
(async () => {
const svc = new Vjmap('http://127.0.0.1:27660/api/v1', '<paste-token>');
console.log('heart ->', await svc.heart());
console.log('version ->', await svc.version());
console.log('status ->', (await svc.runStatus()).application);
})();
JS
2
3
4
5
6
7
8
9
10
# 5. 最常用的 4 步闭环
下面示例用 Python;Node 版本见
examples/nodejs/。
# 5.1 列出已有的图
maps = svc.list_maps() # 对应 /api/v1/map/cmd/listmaps/_/v1
for mapid, versions in maps.items():
print(mapid, [v["version"] for v in versions])
2
3
# 5.2 打开图并读元数据
svc.open_map(mapid="sys_zp", version="v1", mapopenway="GeomRender")
meta = svc.metadata()
print("layers:", [l["name"] for l in meta.get("layers", [])])
print("bounds:", meta.get("bounds"))
2
3
4
# 5.3 取栅格瓦片 URL( XYZ source)
url = svc.raster_tile_url(mapid="sys_zp", version="v1",
layer="0,1,2,3,4,5,6,7,8", z="{z}", x="{x}", y="{y}")
print(url)
# => http://127.0.0.1:27660/api/v1/map/tile/sys_zp/v1/0,1,.../{z}/{x}/{y}?token=...
2
3
4
把 URL 直接填到 vjmap 的 raster source 就能显示。
# 5.4 关闭图(可选,释放 图形服务 子进程)
svc.close_map(mapid="sys_zp", version="v1")
# 6. 下一步
- 想系统了解参数规则?→
02-conventions.md - 弄清 token / secretKey?→
03-authentication.md - 多租户分区?→
04-workspace.md - 跳到接口分类索引 →
api/index.md
# 02-conventions.md
# 通用约定(Conventions)
所有接口的共性规则集中在这里,具体接口文档不再重复。
# 1. Base URL 与前缀
- 默认:
http://127.0.0.1:27660/api/v1 - 文档里写成
{base};SDK 自动补/结尾 - 三种子前缀:
| 前缀 | 用途 |
|---|---|
{base}/map/... | 默认工作区的所有图相关接口 |
{base}/workspace/{name}/... | 命名工作区下的图相关接口(与 /map/ 等价但隔离存储) |
{base}/service/... | 与图无关的工作区/KV/符号库/全文搜索/AI 等接口 |
系统级根路径:
| 路径 | 说明 |
|---|---|
/heart | 健康检查 |
/version | 版本 |
# 2. HTTP 方法
- cmd 接口(路径形如
/api/v1/map/cmd/<cmd>/<mapid>/<version>)既接受 GET 也接受 POST;网关Router.Any统一挂载。SDK 默认走 POST,原因:- 请求体大时 GET 超长
- 语义上是"动作"而非"只读资源"
- 其它接口遵循标准 REST:GET 查、POST 创建、PUT 修改、DELETE 删除
# 3. 鉴权
详情见
03-authentication.md。
- 必带:
token=<JWT>(推荐放 query,也可放 header) - 加密图:请求头
secretKey: <key1>_;_<key2>...或 querysecret_key=... - 全局 access key(
ak开头 18 位):可放在accessKey位置用来一次性访问多张加密图
# 4. 路径占位符
| 占位 | 常用取值 | 含义 |
|---|---|---|
{mapid} | sys_zp、_、_null | 图 ID;_ / _null 用在不需要 mapid 的 cmd 上 |
{version} | v1、_ | 版本号;_ 表示与具体版本无关 |
{layer} | "0,1,2"、default、空 | 图层名/ID;多个用逗号 |
{z}/{x}/{y} | 10/854/402 | 标准 XYZ 瓦片坐标 |
{key} | 用户自定义 | KV 存储的 key(建议 URL-safe) |
重要:SDK 会对占位符做 encodeURIComponent,别再手动编码。
# 5. 请求体约定
| 场景 | Content-Type | 备注 |
|---|---|---|
| 绝大多数 cmd | application/json | SDK 默认 |
文件上传(/map/uploads、/service/uploadImg) | multipart/form-data | 字段名 file |
KV 存值 POST /data/{key} | application/x-www-form-urlencoded | 字段 data= |
# 6. 响应结构
两类并存,SDK 都能识别:
# 6.1 web服务 网关标准响应
{
"code": 0,
"msg": "",
"data": { /* 实际内容 */ }
}
2
3
4
5
code == 0→ SDK unwrap 成datacode != 0→ SDK 抛VjmapError(status=200, body={...})
# 6.2 图形服务
{
"bounds": [...],
"layers": [...],
...
}
2
3
4
5
直接返回,不包 {code,msg,data}。
# 6.3 错误响应
- HTTP 4xx / 5xx:SDK 抛
VjmapError(status=4xx|5xx, url, body) - cmd 执行失败但网关路由成功:多数 cmd 用
{error: "..."}或{result: {succeed: false}}表达;SDK 不会自动判断业务成功/失败,请业务侧自行校验
# 7. 分页 / 列表
大部分列出接口没有分页,一次返回全部:
listmaps:按 mapid 聚合,返回{mapid: [ {version, db, filename, ...} ]}symbol/list/{cat}:数组getWorkspace:数组
少数有 limit / offset / pageSize / pageNum(例如 fullsearch/search),每个接口文档单独说明。
# 8. 幂等性与重试
| 操作 | 幂等 | 可自动重试? |
|---|---|---|
openmap (GeomRender) | 否 | 否,会占用 图形服务 子进程 |
metadata / listmaps / runstatus | 是 | 是 |
updatemap / updateMetadata | 否 | 否 |
deletemap / deletestyle | 是 | 是,但业务需防误触 |
| 瓦片 GET | 是 | 是 |
SDK 不内置重试;
# 9. 超时
- SDK 默认 60 秒;超长 cmd(
composeNewMap、saveOfflineTileDb、extractTableon 大图)可能要 3-5 分钟,调用时显式timeout=300(秒)。 - 图形服务单请求默认
600000 ms = 10 min(runstatus.process.reqTimeout),超过会被强杀。
# 10. 文件下载 / 流式接口
导出 PDF、DXF、离线瓦片包、缩略图等接口返回 二进制流。SDK 里对应方法:
- Python:
svc._request("POST", url, stream=True)返回原始requests.Response,自己.iter_content()写文件 - Node:
svc._request('POST', url, { stream: true })返回原始Response,自己await res.arrayBuffer()或管道到文件
# 11. 命名风格(跨语言)
| 语言 | 风格 | 例 |
|---|---|---|
| Python SDK | snake_case | open_map, close_map, raster_tile_url |
| Node SDK | camelCase | openMap, closeMap, rasterTileUrl |
| 文档正文 | 二者并列列出 | 每个接口都给两种示例 |
# 12. 日志与可观测
runstatus?detail=true:看引擎 pid、活跃请求、子进程等viewlogs?record=100:看最近 100 条日志;含 cmd 耗时、错误
# 03-authentication.md
# 鉴权与加密图(Authentication)
# 1. JWT Token
- vjmap 后端用 标准 HS256 JWT 做鉴权
- Claims 里包含
ID/Username/AuthorityId/BufferTime/exp/iss=vjmap - 过期时间:默认
BufferTime=86400 秒;exp远期刷新由{UpdateTokenRoute}负责 - 生产环境拿 token 的方式:通过业务登录接口;开发环境可直接从 vjmap 后台复制
# 1.1 Token 放哪里
三种都接受,优先级从高到低:
- HTTP Header:
token: eyJ... - Query:
?token=eyJ... - Form:
token=eyJ...(仅个别接口)
SDK 默认走 Query(便于 curl 排障);如果你的反代会剥 query,可传 header=True(Python)/{headers:{token}} 客户端自行覆盖。
# 1.2 Token 的 5 分钟内测
TOKEN="eyJhbG..."
curl -s "http://127.0.0.1:27660/version?token=$TOKEN"
curl -s "http://127.0.0.1:27660/api/v1/map/cmd/runstatus/_/v1?token=$TOKEN&detail=true"
2
3
# 1.3 常见错误
| 现象 | 原因 |
|---|---|
HTTP 401 | token 过期 / 签名不匹配 |
{code: 10006, msg: "..."}(网关响应) | token 合法但权限不够(例如不是 root) |
HTTP 200 但业务字段异常 | token OK,但参数错误 |
# 2. 加密图(secretKey / accessKey)
vjmap 允许对图设置访问密钥,打开/瓦片/元数据请求都要带。
# 2.1 secretKey(每图一把,16 字符)
- 创建/上传图时可通过
secretKey字段设置 - 明文密码 →
secretKey算法(与 Service.tspwdToSecretKey一致):- 连续 4 次 md5 后取前 16 字符
- 空串 → 空串
- 长度 == 18 且以
ak开头 → 视为 accessKey,原样返回
- SDK:
compute_secret_key(pwd)/computeSecretKey(pwd)
# 2.2 accessKey(全局,18 字符 ak...)
- 跨图通用的"超级密钥",典型用途:服务端代为访问所有图
- 放在请求头/query 里的字段也叫
secretKey,但值是 18 位 ak 串 - SDK 识别:如果密钥串长度 == 18 且前缀
ak,不再 md5 拼装,直接透传
# 2.3 多密钥传递
一个请求可能同时携带多个 secretKey(覆盖多张图):
- Header:
secretKey: key1_;_key2_;_ak1234...abcd - Query: 用多个
secret_key=或一个secret_key=key1_;_key2
SDK Python:
svc._secret_keys = ["<16chars>", "<16chars>", "ak........"]
# 后续请求会自动拼 header
2
SDK Node:
svc._secretKeys = [ '16chars...', 'ak........' ];
# 2.4 加密图典型流程
svc = Vjmap(base, token)
svc._secret_keys = [compute_secret_key("myPass123")]
svc.open_map(mapid="finance_map", version="v1", mapopenway="GeomRender",
secretKey="myPass123") # 打开时也可带明文
meta = svc.metadata() # 会自动 carry secretKey
2
3
4
5
# 3. Super Key(运维视角)
.env 里的 VJMAP_MAP_SUPER_KEY 本质是一把 accessKey,含义是:
- 整个实例的所有加密图都接受它
- 二次开发一般不需要它;只有测试框架需要跑加密场景时才用
# 4. CSRF / 同源
- 后端不强制 CSRF token;依赖 JWT 签名
- 浏览器侧调用请注意 CORS:网关默认允许
Origin: *;如果在受限部署里碰到 CORS,需要运维修改ReqMiddlewareCors
# 5. 速率限制
- 默认无全局速率限制
- 单个 cmd 的并发由
runstatus.maxParallelProcessGeomNum(几何渲染)控制,典型值 2-4 - 超并发请求会被排队,不会报错但会"看起来卡住"
# 6. 过期 Token 的自动刷新
网关里有一个 {UpdateTokenRoute}(常见为 /api/v1/map/updateToken)。
# 04-workspace.md
# 工作区(Workspace)
工作区是 vjmap 的命名空间机制:不同工作区下的图、符号、KV 存储、全文索引互相隔离。
# 1. 概念
- 默认工作区:不带名字;URL 用
/api/v1/map/...前缀 - 命名工作区:人为创建;URL 变为
/api/v1/workspace/{name}/...前缀 - 工作区本身也有状态:
{name, description, createTime, ...},由/api/v1/service/getWorkspace列出 - 一个 token 理论上可访问所有工作区;实际权限由
AuthorityId决定
# 2. 切换工作区(SDK)
# Python
svc = Vjmap("http://127.0.0.1:27660/api/v1", token)
svc.switch_workspace("team_a") # 之后所有 map_url / cmd_url 都会自动带上 /workspace/team_a
svc.open_map("finance", "v1", mapopenway="Memory") # → /api/v1/workspace/team_a/cmd/openmap/finance/v1
svc.switch_workspace("") # 切回默认工作区
2
3
4
# Node
svc.switchWorkspace('team_a');
await svc.openMap({ mapid: 'finance', version: 'v1', mapopenway: 'Memory' });
svc.switchWorkspace('');
2
3
# 3. 生命周期接口
| # | 操作 | URL | 方法 |
|---|---|---|---|
| 1 | 列出 | /api/v1/service/getWorkspace | GET |
| 2 | 创建 | /api/v1/service/workspaceCreate | POST |
| 3 | 修改 | /api/v1/service/workspaceModify | POST |
| 4 | 删除 | /api/v1/service/workspaceDelete/{name} | DELETE |
| 5 | 状态 | /api/v1/service/workspaceStatus | GET |
详细字段、响应示例请看 api/07-workspace.md 。
# 4. 底层 URL 改写规则
- web服务网关把
/api/v1/workspace/{name}/*改写成 带 workspace 参数的/api/v1/map/*;图形服务不感知名字,由网关保证隔离 - 因此凡是
/api/v1/map/下的路径都能在/workspace/{name}/下一模一样使用 /api/v1/service/*(全局 KV、符号库、全文搜索)不受工作区前缀影响;它们的隔离通过各自的workspace参数实现(大部分接口接受workspace=query)
# 5 反模式
- 不要在热路径里频繁调用
switch_workspace()再立刻发请求;SDK 内部只是改前缀,没 IO,但业务代码可能会混乱。建议一个 svc 实例绑一个 workspace。 - 不要把
workspace和mapid拼到一起当复合键用;保持每图一个 mapid,workspace 单独字段。
# api/index.md
# 接口文档总索引(API Index)
本页把 13 个分类、73 个 接口分别映射到具体文档、SDK 方法。
# 0. 阅读顺序建议
- 先读
../00-overview.md、../02-conventions.md - 再按需要翻下面分类文档
- 每个接口文档包含 4 块:URL & 方法 / 入参 / 出参 / 示例(curl + Python + Node + 真实响应)
# 1. 分类一览
| # | 分类 | 接口数 | 文档 | Phase |
|---|---|---|---|---|
| 01 | 地图管理(Map Management) | 15 | 01-map-management.md | C.1 |
| 02 | 瓦片 & 样式(Tiles & Styles) | 10 | 02-tiles-and-styles.md | C.2 |
| 03 | 要素查询(Query) | 4 | 03-query.md | C.3 |
| 04 | 图形分析(Analysis) | 6 | 04-analysis.md | C.4 |
| 05 | 组合 / 导出(Compose & Export) | 6 | 05-compose-and-export.md | C.5 |
| 06 | WebCAD(可选) | 6 | 06-webcad.md | C.6 |
| 07 | 工作区(Workspace) | 5 | 07-workspace.md | C.7 |
| 08 | KV 存储(Data Storage) | 5 | 08-data-storage.md | C.8 |
| 09 | 全文搜索(Full-text Search) | 3 | 09-fullsearch.md | C.9 |
| 10 | 符号库(Symbols) | 3 | 10-symbols.md | C.10 |
| 11 | 系统(System) | 4 | 11-system.md | C.11 |
| 12 | /service 杂项(Service Misc) | 3 | 12-service-misc.md | C.12 |
| 13 | AI(可选) | 3 | 13-ai.md | C.13 |
| - | 合计 | 73 | - | - |
# 2. 快速找接口(常用任务 → 文档)
| 我要做…… | 看这里 |
|---|---|
| 上传一个 DWG 文件并在后端渲染 | 01-map-management.md § uploads / openmap |
| 读元数据(图层列表、范围) | 01-map-management.md § metadata |
| 生成 XYZ 瓦片 URL | 02-tiles-and-styles.md § tile / mvt |
| 切换显隐图层并应用样式 | 02-tiles-and-styles.md § createMapStyle / switchlayer |
| 找几何要素 | 03-query.md § queryFeatures |
| 两图对比 | 04-analysis.md § mapDiff / mapCompare |
| 导出 PDF / DXF | 05-compose-and-export.md |
| WebCAD 修改落盘 | 06-webcad.md |
| 存一段用户配置 JSON | 08-data-storage.md § data/{key} |
| 全文搜索 | 09-fullsearch.md |
| 看服务是否在运行 | 11-system.md § heart / runstatus |
# 3. SDK 方法名速查
Python 与 Node 的方法名一一对应(snake_case ↔ camelCase):
| Python | Node | 对应 URL |
|---|---|---|
heart() | heart() | GET /heart |
version() | version() | GET /version |
run_status(detail) | runStatus(detail) | GET /api/v1/map/cmd/runstatus/_/v1 |
upload_map(...) | uploadMap(...) | POST /api/v1/map/uploads |
check_map_file(md5) | checkMapFile(md5) | GET /api/v1/map/mapfile |
open_map(mapid, version, ...) | openMap({mapid, version, ...}) | GET/POST /api/v1/map/openmap/{mapid} |
update_map(...) | updateMap(...) | POST /api/v1/map/updatemap/{mapid} |
close_map(mapid, version) | closeMap(mapid, version) | POST /api/v1/map/cmd/closemap/{mapid}/{version} |
delete_map(mapid, version) | deleteMap(mapid, version) | POST /api/v1/map/cmd/deletemap/{mapid}/{version} |
rename_map(old, new) | renameMap(old, new) | POST /api/v1/map/cmd/renamemap/{old}/_ |
list_maps() | listMaps() | POST /api/v1/map/cmd/listmaps/_/v1 |
metadata(mapid, version) | metadata(mapid, version) | POST /api/v1/map/cmd/metadata/{mapid}/{version} |
update_metadata(...) | updateMetadata(...) | POST /api/v1/map/cmd/updateMetadata/{mapid}/{version} |
thumbnail_url(...) | thumbnailUrl(...) | GET /api/v1/map/cmd/thumbnail/{mapid}/{version} |
support_format() | supportFormat() | GET /api/v1/map/cmd/supportFormat/_/v1 |
const_data() | constData() | GET /api/v1/map/cmd/constData/_/v1 |
get_data_bounds(mapid, version) | getDataBounds(mapid, version) | POST /api/v1/map/cmd/getDataBounds/{mapid}/{version} |
raster_tile_url(...) | rasterTileUrl(...) | GET /api/v1/map/tile/... |
vector_tile_url(...) | vectorTileUrl(...) | GET /api/v1/map/tile/.../{z}/{x}/{y}.mvt |
wms_tile_url(...) | wmsTileUrl(...) | GET /api/v1/map/cmd/wms/... |
create_map_style(...) | createMapStyle(...) | POST /api/v1/map/cmd/createMapStyle/... |
cmd_switch_layers(...) | cmdSwitchLayers(...) | POST /api/v1/map/cmd/switchlayer/... |
cmd_update_style(...) | cmdUpdateStyle(...) | POST /api/v1/map/cmd/updateStyle/... |
slice_layer(...) | sliceLayer(...) | POST /api/v1/map/cmd/slicelayer/... |
get_slice_cache_zoom(...) | getSliceCacheZoom(...) | GET /api/v1/map/cmd/getSliceCacheZoom/... |
delete_style(...) | deleteStyle(...) | POST /api/v1/map/cmd/deletestyle/... |
query_features(...) | queryFeatures(...) | POST /api/v1/map/cmd/queryFeatures/... |
create_entity_geo_data(...) | createEntityGeoData(...) | POST /api/v1/map/cmd/createEntityGeoData/_null/v1 |
coord_transform(...) | coordTransform(...) | POST /api/v1/map/cmd/coordtransform/_/_ |
prj_wkt_to_prj4_str(...) | prjWktToPrj4Str(...) | POST /api/v1/map/cmd/prjWktToPrj4Str/_/_ |
cmd_map_diff(...) | cmdMapDiff(...) | POST /api/v1/map/cmd/mapDiff/_null/v1 |
cmd_map_compare(...) | cmdMapCompare(...) | POST /api/v1/map/cmd/mapCompare/_null/v1 |
cmd_extract_table(...) | cmdExtractTable(...) | POST /api/v1/map/cmd/extractTable/_null/v1 |
cmd_extract_center_lines(...) | cmdExtractCenterLines(...) | POST /api/v1/map/cmd/extractCenterLines/_null/v1 |
cmd_match_object(...) | cmdMatchObject(...) | POST /api/v1/map/cmd/objectMatch/_null/v1 |
cmd_image_svg_to_dwg(...) | cmdImageSvgToDwg(...) | POST /api/v1/map/cmd/imageSvgToDwg/_null/v1 |
cmd_compose_new_map(...) | cmdComposeNewMap(...) | POST /api/v1/map/cmd/composeNewMap/_null/v1 |
cmd_split_map(...) | cmdSplitMap(...) | POST /api/v1/map/cmd/cmdSplitMap/_null/v1 |
cmd_export_layout(...) | cmdExportLayout(...) | POST /api/v1/map/cmd/exportLayout/_null/v1 |
cmd_export_pdf(...) | cmdExportPdf(...) | POST /api/v1/map/cmd/exportPdf/{mapid}/{version} |
cmd_export_dxf(...) | cmdExportDxf(...) | POST /api/v1/map/cmd/exportDxf/{mapid}/{version} |
cmd_save_offline_tile_db(...) | cmdSaveOfflineTileDb(...) | POST /api/v1/map/cmd/saveOfflineTileDb/_null/v1 |
list_webcad_draws() | listWebcadDraws() | GET /api/v1/map/cmd/listWebcadDraws/_/v1 |
get_webcad_data(...) | getWebcadData(...) | POST /api/v1/map/cmd/getWebcadData/... |
save_webcad_patch(...) | saveWebcadPatch(...) | POST /api/v1/map/cmd/saveWebcadPatch/... |
delete_webcad_draw(...) | deleteWebcadDraw(...) | POST /api/v1/map/cmd/deleteWebcadDraw/... |
convert_webcad(...) | convertWebcad(...) | POST /api/v1/map/cmd/convertWebCAD/... |
export_webcad(...) | exportWebcad(...) | POST /api/v1/map/cmd/exportWebCAD/_null/v1 |
get_workspaces() | getWorkspaces() | GET /api/v1/service/getWorkspace |
workspace_create(...) | workspaceCreate(...) | POST /api/v1/service/workspaceCreate |
workspace_modify(...) | workspaceModify(...) | POST /api/v1/service/workspaceModify |
workspace_delete(name) | workspaceDelete(name) | DELETE /api/v1/service/workspaceDelete/{name} |
workspace_status() | workspaceStatus() | GET /api/v1/service/workspaceStatus |
get_custom_data(key) | getCustomData(key) | GET /api/v1/service/data/{key} |
save_custom_data(key, val) | saveCustomData(key, val) | POST /api/v1/service/data/{key} |
delete_custom_data(key) | deleteCustomData(key) | DELETE /api/v1/service/data/{key} |
save_multiple_custom_data(...) | saveMultipleCustomData(...) | POST /api/v1/service/datas |
get_custom_data_keys(prefix) | getCustomDataKeys(prefix) | GET /api/v1/service/datakeys |
full_search(...) | fullSearch(...) | GET/POST /api/v1/service/fullsearch/search |
full_search_add(...) | fullSearchAdd(...) | POST /api/v1/service/fullsearch/add |
full_search_delete(...) | fullSearchDelete(...) | DELETE /api/v1/service/fullsearch/delete |
symbol_categories() | symbolCategories() | GET /api/v1/service/symbol/categories |
symbol_list(cat) | symbolList(cat) | GET /api/v1/service/symbol/list/{cat} |
symbol_get(id) | symbolGet(id) | GET /api/v1/service/symbol/{id} |
view_logs(record) | viewLogs(record) | GET /api/v1/map/cmd/viewlogs/_/v1 |
qrcode_url(content, size) | qrcodeUrl(content, size) | GET /api/v1/service/qrcode |
glyphs_url(fontstack, range) | glyphsUrl(fontstack, range) | GET /api/v1/service/fonts/... |
upload_img(file) | uploadImg(file) | POST /api/v1/service/uploadImg |
vector_search(...) | vectorSearch(...) | POST /api/v1/service/vectorSearch |
chat_faq(q) | chatFaq(q) | GET /api/v1/service/chatFaq |
vector_docs(...) | vectorDocs(...) | GET /api/v1/service/vectorDocs |
# api/01-map-management.md
# 地图管理(Map Management)
分类 1 / 13 · 接口 15 个 ·
快速目录
| # | 接口 | 方法 | URL |
|---|---|---|---|
| 1 | 秒传检测 | GET | /api/v1/map/mapfile |
| 2 | 上传源文件 | POST | /api/v1/map/uploads |
| 3 | 打开/创建地图 | POST | /api/v1/map/openmap/{mapid} |
| 4 | 修改地图 | POST | /api/v1/map/updatemap/{mapid} |
| 5 | 关闭地图 | GET | /api/v1/map/cmd/closemap/{mapid}/{version} |
| 6 | 删除地图 | GET | /api/v1/map/cmd/deletemap/{mapid}/{version} |
| 7 | 重命名地图 | GET | /api/v1/map/cmd/renamemap/{old}/_ |
| 8 | 删除源文件 | GET | /api/v1/map/cmd/deletemapfile/_/v1 |
| 9 | 列出地图 | GET | /api/v1/map/cmd/listmaps/_/v1 |
| 10 | 读元数据 | GET | /api/v1/map/cmd/metadata/{mapid}/{version} |
| 11 | 修改元数据 | POST | /api/v1/map/cmd/updateMetadata/{mapid}/{version} |
| 12 | 缩略图 | GET | /api/v1/map/cmd/thumbnail/{mapid}/{version} |
| 13 | 支持的格式 | GET | /api/v1/map/cmd/supportFormat/_/v1 |
| 14 | 系统常量 | GET | /api/v1/map/cmd/constData/_/v1 |
| 15 | 数据范围 | GET | /api/v1/map/cmd/getDataBounds/{mapid}/{version} |
共性约定(重要)
- 所有
cmd/*路径:GET不需要 body,POST要求 body 至少一个字段,否则后端回 500param error。SDK 对无 body 的 cmd 统一走 GET。 openmap通过 POST body 传mapid/version/fileid/uploadname/mapopenway;fileid必须来自upload_map的返回,不要自己拼 md5。- 所有路径都带
?token=<JWT>(SDK 自动加)。
# 1. 秒传检测 check_map_file
URL:GET /api/v1/map/mapfile?md5={md5}
用途:客户端上传前先算文件 md5,若 result:true 则复用已有文件,跳过上传。
# 入参(Query)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
md5 | string(32) | 是 | 源文件 MD5(小写 hex) |
token | string | 是 | JWT |
# 出参
{"result": false}
result=true:已有同 md5 文件;可直接跳过上传用fileid = md5[:12]result=false:需要走upload_map
# 示例
curl -s "http://127.0.0.1:27660/api/v1/map/mapfile?token=$T&md5=caf27de8a2259b2c4e04f2afde2f5c42"
svc.check_map_file("caf27de8a2259b2c4e04f2afde2f5c42")
await svc.checkMapFile('caf27de8a2259b2c4e04f2afde2f5c42');
# 2. 上传源文件 upload_map
URL:POST /api/v1/map/uploads
Content-Type:multipart/form-data
# 入参(Form)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
file | binary | 是 | 源文件 |
md5 | string(32) | 是 | 预算的 MD5 |
secretKey | string | 否 | 设置加密图的 16 位密钥;SDK 中默认空 |
# 出参
{"fileid":"caf27de8a225","mapid":"caf27de8a225","uploadname":"caf27de8a225.dwg"}
fileid:生成的12 位十六进制(= md5 前 12 位)mapid:默认等于fileid;后续open_map可以给另一个业务名uploadname:服务端实际落盘的文件名
# 示例
curl -s -X POST "http://127.0.0.1:27660/api/v1/map/uploads?token=$T" \
-F "file=@./my.dwg" \
-F "md5=$(md5sum my.dwg | awk '{print $1}')"
2
3
up = svc.upload_map("./my.dwg")
fileid, uploadname = up["fileid"], up["uploadname"]
2
const up = await svc.uploadMap('./my.dwg');
const { fileid, uploadname } = up;
2
# 3. 打开/创建地图 open_map
URL:POST /api/v1/map/openmap/{mapid}?version={version} (SDK 默认走 POST;GET 等价)
这是整套 API 的中心接口。它同时承担:
- 首次创建:
mapid未存在时结合fileid + uploadname新建 - 再次打开:
mapid已存在时忽略fileid/uploadname - 切换模式:
mapopenway决定是否拉起 渲染子进程
# 入参(JSON body)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
mapid | string | 是 | 业务 ID |
version | string | 是 | 版本号,默认 v1 |
fileid | string(12) | 首次建图必填 | upload_map 返回的 fileid |
uploadname | string | 首次建图必填 | upload_map 返回的 uploadname |
mapopenway | Memory / GeomRender | 是 | 见 ../02-conventions.md |
style | object | 否 | 预设样式;详情见 § 2 create_map_style |
secretKey | string | 否 | 图级密钥 |
accessKey | string | 否 | 全局 ak |
mapbounds | array / string | 否 | 限制的图面范围 |
# 出参(节选,完整见 snapshot)
| 字段 | 含义 |
|---|---|
mapid / version / fileid / uploadname / filename | 基本定位 |
bounds / dbBounds / drawBounds / initBounds / viewBounds | 各种范围 |
layers (JSON string) | 图层数组 |
styles (JSON string) | 预置样式数组 |
format | 瓦片格式(典型 png) |
mapopenway | 实际使用的打开方式 |
pid | 子进程 PID(GeomRender 时) |
renderAccuracy | 渲染精度 |
isnewmap | 是否刚创建 |
# 真实响应片段
{
"mapid": "nb_roundtrip_1776829368",
"version": "v1",
"fileid": "caf27de8a225",
"uploadname": "caf27de8a225.dwg",
"filename": "caf27de8a225.dwg",
"mapopenway": "Memory",
"bounds": "[67108.08576259,-10253.62403246,73355.58576721,-4006.12402784]",
"isnewmap": true,
"format": "png",
"layers": "[{\"color\":\"#ffffff\",\"index\":0,...}]",
"styles": "[{\"backcolor\":0,\"isgeom\":false,...}]"
}
2
3
4
5
6
7
8
9
10
11
12
13
# 示例
svc.open_map(
mapid="demo_dwg", version="v1",
mapopenway="Memory",
fileid=up["fileid"], uploadname=up["uploadname"],
)
2
3
4
5
await svc.openMap({
mapid: 'demo_dwg', version: 'v1',
mapopenway: 'Memory',
fileid: up.fileid, uploadname: up.uploadname,
});
2
3
4
5
# 4. 修改地图 update_map
URL:POST /api/v1/map/updatemap/{mapid}?version={version}
用法:修改 已存在图的属性(描述、可见性、部分字段等)。实现上等价于"带新参数再打开一次",因此 body 通常和 open_map 类似,加上要改的字段。
# 入参(JSON body)
| 字段 | 必填 | 说明 |
|---|---|---|
mapid | 是 | 要更新的图 |
fileid | 是 | 必须和打开时一致 |
uploadname | 是 | 同上 |
mapopenway | 是 | 同上 |
description | 否 | 描述 |
deleteOldVersion | 否 | 删除旧版本再建 |
| …… | 其余字段参考 ServiceInterface.ts::IUpdateMapParam |
# 出参
结构与 open_map 返回一致。
# 示例
svc.update_map("demo_dwg", "v1", body={
"mapid": "demo_dwg",
"fileid": up["fileid"],
"uploadname": up["uploadname"],
"mapopenway": "Memory",
"description": "添加二次开发示例描述",
})
2
3
4
5
6
7
# 5. 关闭地图 close_map
URL:GET /api/v1/map/cmd/closemap/{mapid}/{version}
仅断开 图形服务 中的内存/渲染引用;不删除数据库文件,也不删图。
# 出参
{"status": true}
# 注意:POST 无 body 会 500
# ✗ 500 param error
curl -X POST ".../closemap/sys_zp/v1?token=$T"
# ✓ 200
curl ".../closemap/sys_zp/v1?token=$T"
# ✓ 200(POST 可以,但 body 必须有至少一个字段)
curl -X POST ".../closemap/sys_zp/v1?token=$T" -d '{"fileid":"caf27de8a225"}' -H 'content-type: application/json'
2
3
4
5
6
SDK 内部统一使用 GET 规避该陷阱。
# 6. 删除地图 delete_map
URL:GET /api/v1/map/cmd/deletemap/{mapid}/{version}?keepSource={bool}
| 参数 | 默认 | 说明 |
|---|---|---|
keepSource | true | true 保留上传的源文件(只清元数据/DB),false 同时删源文件 |
# 出参
{"status": true}
# 示例
svc.delete_map("demo_dwg", "v1", keep_source=True)
# 7. 重命名地图 rename_map(已知不稳定)
URL:GET /api/v1/map/cmd/renamemap/{old}/_?newmapid={new}
# 响应样例(实测)
{
"status": false,
"error": true,
"reason": "mapid is empty",
"newmapid": "xxx_new",
"AuthorityId": "root"
}
2
3
4
5
6
7
# 8. 删除源文件 delete_map_file
URL:GET /api/v1/map/cmd/deletemapfile/_/v1?files=a.dwg;b.dwg
删除上传到 data/mapfiles/ 目录下的物理文件。被删的文件必须没有图在引用。
# 出参(实测)
{
"deleted": [],
"deletedCount": 0,
"notFound": ["__nb_nonexistent__.dwg"],
"status": false
}
2
3
4
5
6
# 示例
svc.delete_map_file(["caf27de8a225.dwg"])
# 或字符串
svc.delete_map_file("a.dwg;b.dwg")
2
3
# 9. 列出地图 list_maps
URL:GET /api/v1/map/cmd/listmaps/_/v1
# 入参
| Query | 说明 |
|---|---|
keyword | 按 mapid 过滤(可选) |
page / pageSize | 分页(可选,多数版本支持) |
# 出参(实测)
{
"sys_zp": [
{
"db": "c47be46a2ad2.db",
"filename": "c47be46a2ad2.dwg",
"geom": true,
"mtime": "2026-04-10 11:31:00",
"status": "finish",
"uploadname": "sys_zp.dwg",
"version": "v1"
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
- 顶层 key 是
mapid,value 是该图所有版本的数组 geom:true表示该图已有切片缓存status:"finish"表示打开流程完成
# 示例
maps = svc.list_maps()
for mapid, versions in maps.items():
print(mapid, [v["version"] for v in versions])
2
3
const maps = await svc.listMaps();
# 10. 读元数据 metadata
URL:GET /api/v1/map/cmd/metadata/{mapid}/{version}
图必须先 open_map 打开才能读到完整元数据。
# 入参(Query)
| 字段 | 说明 |
|---|---|
enableEditMeta | true 时返回可编辑字段;默认 false |
fileid | 罕见用法:读源文件级元数据 |
# 出参(节选)
字段非常多,以下是二次开发最常用的子集:
| 字段 | 类型 | 含义 |
|---|---|---|
mapid / name / version / fileid / uploadname / filename | string | 定位 |
bounds | string("[minx,miny,maxx,maxy]") | 整图范围 |
dbBounds / drawBounds / initBounds / viewBounds | string | 各阶段范围 |
createTime / time / mtime | string | 时间戳 |
layers | JSON 字符串(数组) | 图层列表 |
styles | JSON 字符串(数组) | 样式列表 |
layouts | string,逗号分隔 | 布局名 |
findFonts / preferableFonts | JSON/字符串 | 字体字典 |
geomRecordCount | string | 几何记录总数(估值) |
ucsorg | string | UCS 原点矩阵 |
pid | string | 进程 PID(仅 GeomRender) |
⚠ 注意:
layers/styles/findFonts等被序列化成字符串存在顶层;SDK 不会自动解嵌套 JSON,请按需json.loads()。
# 示例
svc.open_map("sys_zp", "v1", mapopenway="Memory",
fileid="c47be46a2ad2", uploadname="sys_zp.dwg")
meta = svc.metadata("sys_zp", "v1")
layers = json.loads(meta["layers"])
2
3
4
# 11. 修改元数据 update_metadata
URL:POST /api/v1/map/cmd/updateMetadata/{mapid}/{version}
通过 POST body 修改图的"用户可编辑字段"(通常是 description、layerOn 状态、业务标签等)。
# 入参(JSON body)
{
"fields": [
{ "name": "description", "value": "我是新的描述" },
{ "name": "tags", "value": "building,sample" }
]
}
2
3
4
5
6
具体可编辑字段清单取决于后端版本;二次开发前可用 metadata?enableEditMeta=true 先探测。
# 出参
{"status": true}
# 示例
svc.update_metadata("demo_dwg", "v1", body={
"fields": [{"name": "description", "value": "hello"}]
})
2
3
# 12. 缩略图 thumbnail
URL:GET /api/v1/map/cmd/thumbnail/{mapid}/{version}?width=W&height=H[&layers=...][&backcolor=...]
返回 PNG 图片字节流。
# 常用参数
| Query | 说明 |
|---|---|
width / height | 缩略图像素大小 |
layers | 仅渲染指定图层;逗号分隔 |
backcolor | 背景色(0xRRGGBB 数字或 #RRGGBB) |
# SDK
svc.open_map("sys_zp", "v1", mapopenway="GeomRender",
fileid="c47be46a2ad2", uploadname="sys_zp.dwg")
png = svc.thumbnail_bytes("sys_zp", "v1", width=256, height=160)
open("thumb.png","wb").write(png)
2
3
4
const buf = await svc.thumbnailBytes('sys_zp', 'v1', { width: 256, height: 160 });
fs.writeFileSync('thumb.png', buf);
2
也可以只构造 URL 直接嵌到 <img src>:
url = svc.thumbnail_url("sys_zp", "v1", width=128, height=96)
# => http://127.0.0.1:27660/api/v1/map/cmd/thumbnail/sys_zp/v1?width=128&height=96&token=...
2
# 13. 支持的格式 support_format
URL:GET /api/v1/map/cmd/supportFormat/_/v1
# 实测响应
{
"cad": "dwg;dxf",
"image": "webp;heic;zip;png;pdf;jpg;jpe;jpc;heif;exif;avif;bmp;dib;tiff;hif;hdr;jfif;gif;tif;jpeg"
}
2
3
4
# 14. 系统常量 const_data
URL:GET /api/v1/map/cmd/constData/_/v1
返回系统级字典,最常用的是 CAD 实体类型号 → AcDb 类名 的映射。
# 实测响应(节选)
{
"entTypeIdMap": {
"1": "AcDbLine",
"2": "AcDbPolyline",
"5": "AcDbSpline",
"6": "AcDbArc",
"7": "AcDbCircle",
"10": "AcDbBlockReference",
"11": "AcDbHatch",
"12": "AcDbMText",
"13": "AcDbText",
"34": "AcDb3dSolid"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
搭配 query_features 的 entType 过滤参数使用。
# 15. 数据范围 get_data_bounds
URL:GET /api/v1/map/cmd/getDataBounds/{mapid}/{version}
返回图上所有 几何实体的紧凑包围盒,比 metadata.bounds(可能含空图框)更贴合可见数据。
# 实测响应
{
"bounds": [
587464241.0047014,
3103794303.1750364,
587769759.9828022,
3104004303.209077
],
"status": true
}
2
3
4
5
6
7
8
9
# 示例
b = svc.get_data_bounds("sys_zp", "v1")["bounds"]
# 用来设置 vjmap 初始 fitBounds
2
# api/02-tiles-styles.md
# 瓦片 / 样式
分类代号:C.2 本章共 10 个接口(3 个纯 URL 拼装 + 7 个 REST)
vjmap 的瓦片出图有两套机制:
- 按地图内置默认 layer 出图
open_map之后,可直接用raster_tile_url()/vector_tile_url()拿到{z}/{x}/{y}占位符 URL,喂给 vjmap。 - 按自定义 style 出图
先create_map_style()拿到stylename,把它作为layer段传给 tile URL,即可得到"按该 style 渲染"的瓦片。
后续可用cmd_update_style()原地修改;用cmd_switch_layers()临时切换可见图层;用slice_layer()触发预切片缓存;用get_slice_cache_zoom()查询当前已缓存到几级;用delete_style()清理。
WMS 出图走的是独立管线,用 wms_tile_url() 构造即可,单图走 cmd/wms/{mapid}/{version},多图叠加走 /service/wms。
# 快速索引
| 序号 | SDK 方法 | HTTP | URL |
|---|---|---|---|
| 16 | raster_tile_url / rasterTileUrl | GET | /api/v1/map/tile/{mapid}/{version}/{layer}/{z}/{x}/{y} |
| 17 | vector_tile_url / vectorTileUrl | GET | /api/v1/map/tile/{mapid}/{version}/{layer}/{z}/{x}/{y}.mvt |
| 18 | wms_tile_url / wmsTileUrl(单图) | GET | /api/v1/map/cmd/wms/{mapid}/{version} |
| 19 | wms_tile_url / wmsTileUrl(多图) | GET | /api/v1/service/wms |
| 20 | create_map_style / createMapStyle | POST | /api/v1/map/cmd/createMapStyle/{mapid}/{version} |
| 21 | cmd_switch_layers / cmdSwitchLayers | POST | /api/v1/map/cmd/switchlayer/{mapid}/{version} |
| 22 | cmd_update_style / cmdUpdateStyle | POST | /api/v1/map/cmd/updateStyle/{mapid}/{version} |
| 23 | slice_layer / sliceLayer | POST | /api/v1/map/cmd/slicelayer/{mapid}/{version} |
| 24 | get_slice_cache_zoom / getSliceCacheZoom | POST | /api/v1/map/cmd/getSliceCacheZoom/{mapid}/{version} |
| 25 | delete_style / deleteStyle | POST | /api/v1/map/cmd/deletestyle/{mapid}/{version} |
注意:10/21/22/23/24/25 这 6 个是
cmd路由,POST 必须带非空 JSON body(即使是{"noop":true}),否则会返回500 param error。SDK 已经替你处理好这点,直接调方法即可。
# 16 · 栅格瓦片 URL
SDK:svc.raster_tile_url(mapid?, version?, layer?, fileid?)
# 请求参数
| 字段 | 类型 | 说明 |
|---|---|---|
mapid | string | 可省,默认为当前 _cur_map.mapid |
version | string | 可省,默认 v1 |
layer | string | 可省,默认 _(即图档内置默认 style)。若先调用 create_map_style,把返回的 stylename 传进来即可 |
fileid | string | 可省。主要用于 tile 缓存破坏。后端不校验,只要 {mapid,version,layer} 对,tile 就出得来 |
# 实采示例
"http://127.0.0.1:27660/api/v1/map/tile/sys_zp/v1/_/{z}/{x}/{y}?tag=c47be46a2ad2&token=<jwt>"
# Python
from vjmap import Vjmap, MapOpenWay
svc = Vjmap("http://127.0.0.1:27660/api/v1", TOKEN)
svc.open_map(mapid="sys_zp", version="v1", mapopenway=MapOpenWay.GeomRender)
print(svc.raster_tile_url())
2
3
4
5
# Node.js
import { Vjmap, MapOpenWay } from './sdk/nodejs/vjmap.js';
const svc = new Vjmap('http://127.0.0.1:27660/api/v1', TOKEN);
await svc.openMap({ mapid: 'sys_zp', version: 'v1', mapopenway: MapOpenWay.GeomRender });
console.log(svc.rasterTileUrl());
2
3
4
5
# 17 · 矢量瓦片 URL(MVT)
SDK:svc.vector_tile_url(...),参数与 raster_tile_url 完全一致,区别是 URL 末尾多了 .mvt 后缀。
# 实采示例
"http://127.0.0.1:27660/api/v1/map/tile/sys_zp/v1/main/{z}/{x}/{y}.mvt?tag=c47be46a2ad2&token=<jwt>"
{layer} 默认是 main(内置 MVT 默认名),不是 _。
# 用法
MVT 非常适合作为 vjmap vector source:
map.addSource('vj', {
type: 'vector',
tiles: [svc.vectorTileUrl()],
minzoom: 0,
maxzoom: 22,
});
map.addLayer({ id: 'vj-line', source: 'vj', 'source-layer': 'default', type: 'line' });
2
3
4
5
6
7
# 18 / 19 · WMS 瓦片 URL
SDK:svc.wms_tile_url(mapid, version?, *, layer?, width?, height?, bbox?, srs?, backcolor?, mvt?)
# 参数规则
mapid: 可以是字符串,也可以是list[str]- 单图 → 走
/api/v1/map/cmd/wms/{mapid}/{version} - 多图 → 走
/api/v1/service/wms?mapid=a;b;c&version=v1;v1;v1
- 单图 → 走
bbox: 默认{bbox-epsg-3857}占位符。如果你直接用 如Leaflet,就保留占位符交给前端去替换。SDK 刻意不对bbox做 percent-encode。srs: 坐标系,默认EPSG:3857。mvt=True: URL 末尾加.mvt,输出矢量。backcolor: 背景色,十进制 RGB(0 = 黑/透明)。layer: 可选,指定某个 stylename 渲染。
# 实采示例
单图:
"http://127.0.0.1:27660/api/v1/map/cmd/wms/sys_zp/v1?bbox={bbox-epsg-3857}&srs=EPSG%3A3857&width=512&height=512&token=<jwt>"
多图:
"http://127.0.0.1:27660/api/v1/service/wms?bbox={bbox-epsg-3857}&mapid=sys_zp&version=v1&srs=EPSG%3A3857&width=256&height=256&token=<jwt>"
# Python
url = svc.wms_tile_url("sys_zp", "v1", width=512, height=512)
url_multi = svc.wms_tile_url(["sys_zp", "other"], ["v1", "v1"])
2
# Node.js
const url = svc.wmsTileUrl({ mapid: 'sys_zp', version: 'v1', width: 512, height: 512 });
const urlMulti = svc.wmsTileUrl({ mapid: ['sys_zp', 'other'], version: ['v1', 'v1'] });
2
# 20 · 创建地图样式 createMapStyle
URL:POST /api/v1/map/cmd/createMapStyle/{mapid}/{version}
SDK:svc.create_map_style(mapid, version, *, layeron, layeroff, backcolor, clipbounds, expression, lineweight, is_geom_layer=True, name)
# 请求参数
| 字段 | 类型 | 说明 |
|---|---|---|
geom | bool | true 几何渲染(默认);false 栅格渲染 |
name | string | style 名字,可自动生成 |
layeron | string / 数组 | 可见图层;数组传入时 SDK 会自动转成 (0,1,2) 格式 |
layeroff | string / 数组 | 隐藏图层 |
backcolor | int | 背景色(0=黑色,常用于暗色底图) |
clipbounds | string | 裁剪范围 JSON 字符串 |
expression | string | Cesium 风格的颜色表达式 |
lineweight | int[] | 线宽缩放 [默认,最小,最大] |
# 响应(实采)
{
"stylename": "sfbd35705c"
}
2
3
返回的 stylename 就是后续作为瓦片 URL 中 {layer} 段的值。
# Python
style = svc.create_map_style("sys_zp", "v1", layeron="*", backcolor=0)
tile_url = svc.raster_tile_url(layer=style["stylename"])
2
# Node.js
const style = await svc.createMapStyle({ mapid: 'sys_zp', version: 'v1', layeron: '*', backcolor: 0 });
const tileUrl = svc.rasterTileUrl({ layer: style.stylename });
2
# curl
curl -X POST "http://127.0.0.1:27660/api/v1/map/cmd/createMapStyle/sys_zp/v1?token=$TOKEN" \
-H 'Content-Type: application/json' \
-d '{"geom":true,"layeron":"*","backcolor":0}'
2
3
# 21 · 切换可见图层 switchlayer
URL:POST /api/v1/map/cmd/switchlayer/{mapid}/{version}
SDK:svc.cmd_switch_layers(visible_layers, *, expression?, dark_mode?, mapid?, version?)
# 请求参数
| 字段 | 类型 | 说明 |
|---|---|---|
visibleLayer | string[] | 要可见的图层 id 列表 |
expression | string | 颜色表达式(与 createMapStyle 同) |
darkMode | bool | 是否暗色(可在 openMap 时也指定) |
# 响应(实采)
{
"layerid": "s24d720029"
}
2
3
与 createMapStyle 的 stylename 相似,layerid 指向新的内部 style(后端按参数 hash 复用,不会无限增长)。
# Python
res = svc.cmd_switch_layers(["0"])
print(res["layerid"])
2
# Node.js
const res = await svc.cmdSwitchLayers(["0"]);
console.log(res.layerid);
2
# 22 · 原地修改样式 updateStyle
URL:POST /api/v1/map/cmd/updateStyle/{mapid}/{version}
SDK:svc.cmd_update_style(*, name, layeron, layeroff, backcolor, clipbounds, expression, lineweight, mapid?, version?)
与 createMapStyle 最大的区别:它可以修改已有 style(传同名 name),而不是每次新建。
# 响应(实采)
{
"bounds": "[587227463.27, 3103509765.63, 588006538.39, 3104288840.75]",
"boundsChange": false,
"layerid": "m765111cb4"
}
2
3
4
5
boundsChange = false 表示裁剪范围没变;若 clipbounds 修改了,bounds 与 boundsChange 会反映出来。
# Python
svc.cmd_update_style(
mapid="sys_zp", version="v1",
name="nb_style_1",
layeron="*",
backcolor=0,
)
2
3
4
5
6
# Node.js
await svc.cmdUpdateStyle({
mapid: 'sys_zp', version: 'v1',
name: 'nb_style_1',
layeron: '*',
backcolor: 0,
});
2
3
4
5
6
# 23 · 触发预切片缓存 slicelayer
URL:POST /api/v1/map/cmd/slicelayer/{mapid}/{version}
SDK:svc.slice_layer(*, layer, zoom, ismvt, iscancel, is_all_cancel, batch_num?, idle_batch_sleep_ms?, busy_batch_sleep_ms?, mapid?, version?)
# 请求参数
| 字段 | 类型 | 说明 |
|---|---|---|
layer | string | 要预切的 layer id(即 stylename) |
zoom | int / int[] / string | 要切的 zoom 层级。单个或数组 |
ismvt | bool | true 切 MVT,false 切栅格 |
iscancel | bool | 取消本次任务 |
isAllCancel | bool | 取消该地图所有正在进行的切片任务 |
batchnum | int | 批处理大小 |
idle_batch_sleepms | int | 空闲批间隔毫秒 |
busy_batch_sleepms | int | 忙时批间隔毫秒 |
# 响应(实采)
取消切片:
{}
即空对象。真实触发切片时后端会流式推进度({progress:0.5,total:...}),这里我们的 SDK 只取第一响应。
# Python
svc.slice_layer(layer="s24d720029", zoom=[0, 1, 2, 3], ismvt=False)
# Node.js
await svc.sliceLayer({ layer: 's24d720029', zoom: [0,1,2,3], ismvt: false });
# 24 · 查询已缓存的切片级别 getSliceCacheZoom
URL:POST /api/v1/map/cmd/getSliceCacheZoom/{mapid}/{version}
SDK:svc.get_slice_cache_zoom(*, layer, ismvt, mapid?, version?)
# 响应(实采)
{
"sliceCacheZoom": 0
}
2
3
sliceCacheZoom = 当前已稳定缓存到的最大 zoom。若 = 0 通常表示还没跑过预切片。
# Python / Node
print(svc.get_slice_cache_zoom(layer="s24d720029", ismvt=False))
console.log(await svc.getSliceCacheZoom({ layer: 's24d720029', ismvt: false }));
# 25 · 删除自定义样式 deletestyle
URL:POST /api/v1/map/cmd/deletestyle/{mapid}/{version}
SDK:svc.delete_style(stylename, *, mapid?, version?)
# 请求参数
| 字段 | 类型 | 说明 |
|---|---|---|
stylename | string | 要删除的 style 名(即 create_map_style 返回的 stylename) |
# 响应(实采)
即便 stylename 不存在也返回空对象(幂等):
{}
# Python / Node
svc.delete_style("sfbd35705c", mapid="sys_zp", version="v1")
await svc.deleteStyle('sfbd35705c', { mapid: 'sys_zp', version: 'v1' });
# 组合示例:从开图到看地图
from vjmap import Vjmap, MapOpenWay
svc = Vjmap("http://127.0.0.1:27660/api/v1", TOKEN)
svc.open_map(mapid="sys_zp", version="v1", mapopenway=MapOpenWay.GeomRender)
# 1. 自定义一个黑底白线风格
style = svc.create_map_style(
"sys_zp", "v1",
layeron="*",
backcolor=0,
expression="gOutColorRed:=255;gOutColorGreen:=255;gOutColorBlue:=255;gOutColorAlpha:=255;",
)
# 2. 拿 tile URL 给 vjmap
raster = svc.raster_tile_url(layer=style["stylename"])
# 3. 预切前 4 级,加快首屏
svc.slice_layer(layer=style["stylename"], zoom=[0,1,2,3], ismvt=False)
# 4. 不用了,清掉
svc.delete_style(style["stylename"], mapid="sys_zp", version="v1")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# api/03-query.md
# 要素查询 / 几何
分类代号:C.3 本章共 4 个接口
本章汇集所有"拿到几何 / 属性 / 投影"的基础操作。其中 queryFeatures 是最常用的接口,根据 querytype 可覆盖点/矩形/表达式/条件 4 种查询场景,因此 SDK 只暴露一个 query_features(querytype=...) 统一入口。
# 快速索引
| 序号 | SDK 方法 | HTTP | URL |
|---|---|---|---|
| 26 | query_features / queryFeatures | POST | /api/v1/map/cmd/queryFeatures/{mapid}/{version} |
| 27 | create_entity_geo_data / createEntityGeoData | POST | /api/v1/map/cmd/createEntityGeoData/_null/v1 |
| 28 | coord_transform / coordTransform | POST | /api/v1/map/cmd/coordtransform/_/_ |
| 29 | prj_wkt_to_prj4_str / prjWktToPrj4Str | POST | /api/v1/map/cmd/prjWktToPrj4Str/_/_ |
# 26 · 要素查询 queryFeatures
URL:POST /api/v1/map/cmd/queryFeatures/{mapid}/{version}
SDK:svc.query_features(querytype, ...) / svc.queryFeatures({ querytype, ... })
# querytype 枚举
| 值 | 含义 | 必填参数 |
|---|---|---|
point | 点查询:在屏幕像素范围内找要素 | x, y, pixelsize, zoom, pixelToGeoLength |
rect | 矩形查询:几何 bounds 内找要素 | x1, y1, x2, y2 |
expresion | 表达式查询:按 DWG 属性字段过滤 | expr |
condition | 条件查询:按后端预定义条件 | condition(可空),可选 bounds |
⚠️
expresion是后端拼写,不是expression。
# 可选参数(所有 querytype 通用)
| 字段 | 说明 |
|---|---|
limit | 最大返回条数(同时会写入 maxReturnCount 和 limit 两个字段) |
fields | 要返回的属性字段,逗号分隔,空串 = 全部 |
geom | 是否返回几何(默认 true,false 则只返回 envelop/attrs) |
simplifyTolerance | 几何简化容差 |
useCache | 开启查询缓存(condition/expresion 常用) |
toMapCoordinate | 是否把返回坐标转成地图坐标系 |
maxGeomBytesSize | 单条几何字节大小上限(超过则降级为 envelop) |
# 响应结构
{
"recordCount": 5,
"result": [
{
"id": 1,
"objectid": "EA_ED__EE_#",
"layerindex": 13,
"name": "AcDbArc",
"geojson": "...",
"envelop": "POLYGON(...)",
"bounds": "[minx,miny,maxx,maxy]",
"isEnvelop": true,
"color": -65281,
"linetype": "BYLAYER",
"xdata": "",
"center": "x,y,z",
"points": "x1,y1,z1;x2,y2,z2;...",
"colorIndex": 6
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
recordCount:后端实际扫到的全量数量,不是本次返回条数。想分页时根据recordCount+beginpos自行控制。geojson是字符串而非对象,调用方需要再JSON.parse()。isEnvelop=true说明 geojson 被替换成了 envelop(单条太大或命中maxGeomBytesSize)。
# 实采(rect 查询 sys_zp/v1,limit=5)
{
"recordCount": 5,
"result": [{
"id": 1,
"objectid": "EA_ED__EE_#",
"layerindex": 13,
"name": "AcDbArc",
"geojson": "{\"type\":\"GeometryCollection\",\"geometries\":[{\"type\":\"Polygon\",\"coordinates\":[[[2271440.45,-685837.25],[2271440.45,-679783.61],[2276451.33,-679783.61],[2276451.33,-685837.25],[2271440.45,-685837.25]]]}]}",
"envelop": "POLYGON((2271440.45 -685837.25, ... ))",
"bounds": "[587661158.58,3103885970.22,587661256.00,3103886087.91]",
"isEnvelop": true,
"color": -65281,
"linetype": "BYLAYER"
}]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 实采(condition 查询,limit=1)
{
"recordCount": 6982,
"result": [{
"id": 1,
"name": "AcDbArc",
"geojson": "",
"envelop": "POLYGON(...)",
"bounds": "[...]",
"isEnvelop": false,
"color": -65281,
"center": "587661197.39,3103886029.19,-152738.04",
"points": "x1,y1,z1;x2,y2,z2;x3,y3,z3",
"colorIndex": 6
}]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
recordCount=6982 表示该地图共 6982 个要素满足条件;limit=1 只取第一条。
# Python
# 矩形查询:sys_zp/v1 全图前 5 条
meta = svc.open_map(mapid="sys_zp", version="v1", mapopenway=MapOpenWay.GeomRender)
x1, y1, x2, y2 = [float(x) for x in meta["bounds"][1:-1].split(",")][:4]
res = svc.query_features(
querytype="rect",
mapid="sys_zp", version="v1",
x1=x1, y1=y1, x2=x2, y2=y2,
limit=5,
)
for item in res["result"]:
print(item["id"], item["name"])
# 点查询:屏幕 5 像素范围
res = svc.query_features(
querytype="point",
x=587661197.39, y=3103886029.19,
zoom=10,
pixelsize=5,
pixel_to_geo_length=100.0,
limit=3,
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Node.js
const res = await svc.queryFeatures({
querytype: 'rect',
mapid: 'sys_zp', version: 'v1',
x1, y1, x2, y2, limit: 5,
});
const byExpr = await svc.queryFeatures({
querytype: 'expresion',
expr: "layerindex=13",
limit: 10,
});
2
3
4
5
6
7
8
9
10
11
# curl
curl -X POST "http://127.0.0.1:27660/api/v1/map/cmd/queryFeatures/sys_zp/v1?token=$TOKEN" \
-H 'Content-Type: application/json' \
-d '{"querytype":"condition","layername":"","zoom":1,"limit":1}'
2
3
# 27 · 构造实体几何 createEntityGeoData
URL:POST /api/v1/map/cmd/createEntityGeoData/_null/v1
SDK:svc.create_entity_geo_data(mapdata=..., map_extent=..., render_accuracy=..., exclude_attribute=..., use_zip=...)
mapdata是前端@vj/draw构造的 DWG 结构体(JSON 字符串)。独立使用场景较少,通常和前端绘图配合。
# 请求参数
| 字段 | 类型 | 说明 |
|---|---|---|
mapdata | string | 前端传入的 DWG JSON 字符串 |
mapExtent | string | 地图范围 JSON:[minx,miny,maxx,maxy] |
renderAccuracy | number | 渲染精度(像素) |
excludeAttribute | bool | 是否排除属性(默认 true) |
useZip | bool | 是否对 mapdata 做 gzip 压缩(默认 true) |
# 响应
- 合法输入:
{ "result": [...], "bounds": "...", ... }(与 queryFeatures 类似)
# 28 · 坐标转换 coordtransform
URL:POST /api/v1/map/cmd/coordtransform/_/_
SDK:svc.coord_transform(srs=..., crs=..., points=..., four_parameter=..., is_inverse_four_parameter=...)
# 请求参数
| 字段 | 类型 | 说明 |
|---|---|---|
srs | string | 源坐标系,例如 EPSG:4326 |
crs | string | 目标坐标系,例如 EPSG:3857 |
points | string | 多点串:x1,y1;x2,y2 |
fourParameter | string | 可选四参数:xOff,yOff,scale,rotRad |
isInverseFourParamter | bool | 是否反向应用四参数(注意后端拼写是 Paramter) |
SDK 的 points 接受:
- 单点
(x,y)或{"x":1,"y":2} - 多点
[(x1,y1),(x2,y2)] fourParameter支持"x,y,s,r"或[x,y,s,r]
# 响应(实采:4326 → 3857 两点)
{
"points": [
[12957254.769864663, 4852582.082864688],
[13522423.824622115, 3662655.1833045874]
]
}
2
3
4
5
6
# Python
res = svc.coord_transform(
srs="EPSG:4326",
crs="EPSG:3857",
points=[(116.397, 39.908), (121.474, 31.23)],
)
print(res["points"]) # [[12957254.77, 4852582.08], [13522423.82, 3662655.18]]
2
3
4
5
6
# Node.js
const res = await svc.coordTransform({
srs: 'EPSG:4326',
crs: 'EPSG:3857',
points: [[116.397, 39.908], [121.474, 31.23]],
});
console.log(res.points);
2
3
4
5
6
# 29 · WKT → PROJ4 prjWktToPrj4Str
URL:POST /api/v1/map/cmd/prjWktToPrj4Str/_/_
SDK:svc.prj_wkt_to_prj4_str(wkt, from_="")
# 请求参数
| 字段 | 类型 | 说明 |
|---|---|---|
wkt | string | ESRI prj 文件内容或 WKT 字符串 |
from | string | 数据来源。空串 = WKT;其他可选 wmsauto / xml / urn / crsurl / url / micoordsys / pci |
# 响应(实采:WGS84 WKT)
{ "proj4": "+proj=longlat +datum=WGS84 +no_defs" }
# Python
wkt = open("sample.prj", encoding="utf-8").read()
res = svc.prj_wkt_to_prj4_str(wkt)
print(res["proj4"])
2
3
# Node.js
import { readFileSync } from 'node:fs';
const wkt = readFileSync('sample.prj', 'utf8');
const res = await svc.prjWktToPrj4Str(wkt);
console.log(res.proj4);
2
3
4
# api/04-analysis.md
# 图形分析
分类代号: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 | 旧图 |
mapid2 / version2 | string | 新图 |
darkMode | bool | 是否使用暗色底渲染 |
bufferSize | int | 像素容差,相同像素在此距离内不判为"改" |
mergeClose | bool | 合并靠得很近的差异块 |
# 响应(实采:同图 vs 同图)
{
"del": "",
"modify": "",
"new": "",
"status": true
}
2
3
4
5
6
有差异时 del / modify / new 是 x1,y1,x2,y2;x3,y3,x4,y4 格式的字符串;svc.cmd_map_diff() 不做 parse,调用方按需切分。
# Python
res = svc.cmd_map_diff(
mapid1="sys_zp", version1="v1",
mapid2="sys_zp", version2="v2",
bufferSize=5,
)
2
3
4
5
# 31 · 实体对比 mapCompare
和 mapDiff 不同:mapCompare 比较的是 DWG 实体,不是渲染像素。
# 请求参数
| 字段 | 类型 | 说明 |
|---|---|---|
mapid1 / version1, mapid2 / version2 | string | 两张图 |
tolerancePos | number | 坐标容差 |
toleranceAngle | number | 角度容差(度) |
后端会先做一次快速 hash 去重。真实对比时返回:
{
"status": true,
"add": [...], // 仅在新图存在的实体 id
"del": [...], // 仅在旧图存在
"modify": [...] // 两边都有但有差异
}
2
3
4
5
6
# Python
svc.cmd_map_compare(mapid1="a", version1="v1", mapid2="a", version2="v2")
# 32 · 表格提取 extractTable
自动从 DWG 里识别"线条围出来的表格",返回单元格网格与每格文本。最常用于检图、清单导出。
# 请求参数
| 字段 | 说明 |
|---|---|
mapid / version | 要识别的图 |
layer | 可选,只在某 style / layer 上跑 |
bbox | 可选,限定在地图坐标系下的 bbox |
minCellWidth / minCellHeight | 最小单元格尺寸阈值 |
# 响应(实采:sys_zp/v1,无额外参数)
{
"tables": [{
"attr": {
"cellEmptyRatio": 45,
"tableCellMaxCount": 70,
"tableTextCount": 39,
"unLinkLineRatio": 0
},
"colCount": 4,
"cols": ["587693680.21", "587698180.22", "587717259.64", "..."],
"datas": [["类目", "数量", "价格", "说明"], [...]],
"rows": ["...", "..."]
}]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
cols/rows:单元格网格线的坐标;datas[i][j]:每格文本(可能含编码乱码,视 DWG 字体而定)。
# Python
res = svc.cmd_extract_table(mapid="sys_zp", version="v1")
for t in res.get("tables", []):
print(t["colCount"], "x", len(t["rows"]))
2
3
# Node.js
const res = await svc.cmdExtractTable({ mapid: 'sys_zp', version: 'v1' });
console.log(res.tables?.length);
2
# 33 · 中心线提取 extractCenterLines
对闭合多边形(如道路、管线)抽取中心线。
# 请求参数
| 字段 | 说明 |
|---|---|
mapid / version | 源图 |
layer | 可选,限定 layer |
expr | 可选,按属性过滤实体(与 queryFeatures 的 expr 同义) |
# 响应(实采:sys_zp/v1)
{
"centerlines": [
[[587562497.79, 3103857885.77], [587556239.69, 3103860401.38]],
[[587603886.22, 3103921932.46], [587602627.19, 3103925333.77]]
]
}
2
3
4
5
6
centerlines 是"多段折线数组";每条线由若干 [x, y] 顶点构成。
# Python
res = svc.cmd_extract_center_lines(mapid="sys_zp", version="v1")
for line in res["centerlines"]:
print("line with", len(line), "verts")
2
3
# 34 · 目标匹配 objectMatch
在底图中找与模板图形相似的子图(样式 + 尺寸)。常用于批量识别图章、标注块、设备。
# 请求参数
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
mapid / version | string | — | 底图 |
templateMapid / templateVersion | string | — | 模板图 |
objectBounds | string / number[] | — | 必填:模板所在的 bbox(地图坐标) [x1,y1,x2,y2] |
score | number | 0.6 | 相似度阈值 |
canOverlap | bool | false | 命中框是否允许重叠 |
maxOverlap | number | 0.3 | 允许的最大重叠比例 |
toleranceAngle | number | 180 | 角度容差(度),180 = 任意旋转 |
minReduceArea | number | 256 | 小于此面积的命中框被忽略 |
# 响应
成功时返回:
{
"status": true,
"matches": [
{ "bounds":"[x1,y1,x2,y2]", "angle": 0.0, "score": 0.92 },
...
]
}
2
3
4
5
6
7
# Python
res = svc.cmd_match_object(
mapid="sys_zp", version="v1",
templateMapid="sys_zp", templateVersion="v1",
objectBounds="[587661000,3103886000,587661200,3103886200]",
score=0.7, toleranceAngle=90,
)
2
3
4
5
6
# 35 · SVG / 图像转 DWG imageSvgToDwg
把 SVG 字符串或位图转换成 DWG 实体。对位图会先做线条检测 + 矢量化。
# 请求参数
| 字段 | 说明 |
|---|---|
svgData | SVG 文本 |
imageUrl | 远程图片 URL(与 svgData 二选一) |
threshold | 位图二值化阈值(0–255) |
mapid / version | 可选:把结果直接写入该地图;为空则只返回 DWG JSON |
# 响应(实采:svgData="<svg></svg>",空内容)
{
"error": "image or svg content is empty",
"status": false
}
2
3
4
合法输入时返回:
{
"status": true,
"mapdata": "<DWG JSON 字符串>"
}
2
3
4
可以把 mapdata 传给 create_entity_geo_data(见 03-query.md)渲染。
# 通用注意
- 这组接口都可能 耗时 >10s,SDK 默认 timeout=60s,建议在业务层再做重试与超时兜底。
- 后端遇到参数非法时常返回
{"error": "...", "status": false}——不是 HTTP 4xx/5xx。SDK 会把它当成 2xx 正常响应返回给调用方。 - 返回
"status": false时error字段的语言依系统 locale 而定(我们这里抓到了中/英混合的错误串)。
# api/05-compose-export.md
# 组合 / 拆分 / 导出
分类代号:C.5 本章共 6 个接口
把多张 DWG 组合成一张、把一张大图按图框 / 图层拆成多张、把图纸导出为 PDF / DXF 或离线瓦片包——这一章就是"图纸经纪人"。
共同特点:这 6 个接口都相对重,后端会真实创建新图档 / 新文件。测试环境请用临时 mapid,不要对生产图做实验。
# 快速索引
| 序号 | SDK 方法 | URL |
|---|---|---|
| 36 | cmd_compose_new_map / cmdComposeNewMap | POST /api/v1/map/cmd/composeNewMap/_null/v1 |
| 37 | cmd_split_map / cmdSplitMap | POST /api/v1/map/cmd/cmdSplitMap/_null/v1 |
| 38 | cmd_export_layout / cmdExportLayout | POST /api/v1/map/cmd/exportLayout/_null/v1 |
| 39 | cmd_export_pdf / cmdExportPdf | POST /api/v1/map/cmd/exportPdf/{mapid}/{version} |
| 40 | cmd_export_dxf / cmdExportDxf | POST /api/v1/map/cmd/exportDxf/{mapid}/{version} |
| 41 | cmd_save_offline_tile_db / cmdSaveOfflineTileDb | POST /api/v1/map/cmd/saveOfflineTileDb/_null/v1 |
# 36 · 组合新地图 composeNewMap
把多张已有 DWG 按几何变换(偏移 + 旋转)拼成一张。
# 请求参数
| 字段 | 类型 | 说明 |
|---|---|---|
maps | array | 组合清单:[{mapid, version, dx?, dy?, angle?}] |
newMapid | string | 新图 mapid |
newVersion | string | 新图 version(可省) |
bounds | string | 可选:组合后的 bbox 限定 |
# 响应
成功:{ "status": true, "mapid": "...", "fileid": "...", "version": "v1" }
失败(实采:源文件不存在):
{
"error": "file is not exist, ",
"status": false
}
2
3
4
# Python
svc.cmd_compose_new_map(
maps=[
{"mapid": "part_a", "version": "v1", "dx": 0, "dy": 0},
{"mapid": "part_b", "version": "v1", "dx": 1000.0, "dy": 0, "angle": 0},
],
newMapid="assembled_map",
)
2
3
4
5
6
7
# Node.js
await svc.cmdComposeNewMap({
maps: [
{ mapid: 'part_a', version: 'v1', dx: 0, dy: 0 },
{ mapid: 'part_b', version: 'v1', dx: 1000, dy: 0, angle: 0 },
],
newMapid: 'assembled_map',
});
2
3
4
5
6
7
# 37 · 拆分子图 cmdSplitMap
按图框线 / 按图层把一张大图拆成多张小图。
# 请求参数
| 字段 | 类型 | 说明 |
|---|---|---|
mapid / version | string | 源图 |
splitType | string | frame(按图框)/ layer(按图层) |
framelayer | string | 仅 splitType=frame 时:图框线所在图层 |
clipBounds | string | 也可以直接给一系列 bbox 列表拆 |
outNamePrefix | string | 新图 mapid 前缀,默认用源 mapid + 序号 |
# 响应
成功:{ "status": true, "maps": [{mapid, version, bounds}, ...] }
失败(实采:frame 拆分未给 clipBounds):
{ "error": "clipBounds is empty" }
# 38 · 导出布局为 DWG exportLayout
从 DWG 的布局空间(paper space)单独导出一张新 DWG。CAD 里经常一个文件多个布局(A3、A2、首页),这个接口能一次性把某个布局变成独立图档。
# 请求参数
| 字段 | 类型 | 说明 |
|---|---|---|
mapid / version | string | 源图 |
layoutIndex | int | 布局序号(0 起) |
layoutName | string | 或直接用布局名,优先级高于 layoutIndex |
newMapid | string | 可选:直接导入为新地图 |
newVersion | string | 可选:新地图 version |
# 响应(实采:sys_zp/v1, layoutIndex=0)
{
"fileid": "pf68817b9655",
"status": true
}
2
3
4
fileid 指向后端缓存里的新 DWG 文件,可以接着调用 open_map(mapid=...) 打开,或用 exportDxf / exportPdf 进一步出图。
# Python
res = svc.cmd_export_layout(mapid="sys_zp", version="v1", layoutIndex=0)
assert res["status"]
print(res["fileid"]) # 'pf68817b9655'
2
3
# 39 · 导出 PDF exportPdf
把指定图纸输出为 PDF。支持按地图 bbox 截图 + 指定纸张。
# 请求参数
| 字段 | 类型 | 说明 |
|---|---|---|
bounds / bbox | string | 要导出的 bbox:[minx,miny,maxx,maxy] |
width / height | number | 纸张尺寸(mm),常用 A4=297×210 |
scale | number | 比例尺(可选) |
darkMode | bool | 是否暗色渲染 |
outName | string | 自定义输出文件名 |
layer | string | 仅导出某 style / layer |
# 响应(实采:sys_zp/v1, 297×210 mm)
{
"path": "download/d5f939154c.pdf",
"status": true
}
2
3
4
完整下载 URL 是 http://127.0.0.1:27660/{path}?token=<jwt>。
# Python
res = svc.cmd_export_pdf(
mapid="sys_zp", version="v1",
width=297, height=210,
)
url = svc.base_gateway() + res["path"] + f"?token={svc.token}"
2
3
4
5
# Node.js
const res = await svc.cmdExportPdf({
mapid: 'sys_zp', version: 'v1',
width: 297, height: 210,
});
const url = `${svc.baseGateway()}${res.path}?token=${svc.token}`;
2
3
4
5
# 40 · 导出 DXF exportDxf
把当前图档(或其子集)导出为 ASCII DXF 文件。
# 请求参数
| 字段 | 类型 | 说明 |
|---|---|---|
bounds / bbox | string | 裁剪 bbox |
layer | string | 限定 style / layer |
dxfVersion | string | DXF 版本 tag:AC1009 (R12), AC1018 (R2004) 等 |
outName | string | 自定义输出文件名 |
# 响应
成功:{ "status": true, "path": "download/xxx.dxf" }
失败(实采:当前地图没有可导出图元):
{
"error": "Invalid Symbol Table name",
"status": false
}
2
3
4
# Python
res = svc.cmd_export_dxf(
mapid="sys_zp", version="v1",
dxfVersion="AC1018",
bounds="[587661158,3103885970,587661256,3103886087]",
)
2
3
4
5
# 41 · 离线瓦片包 saveOfflineTileDb
把指定地图范围预切并打成 .mbtiles / .sqlitedb 离线包,方便前端离线展示。
# 请求参数
| 字段 | 类型 | 说明 |
|---|---|---|
mapid / version | string | 源图 |
minzoom / maxzoom | int | zoom 范围 |
bounds | string | 要切的 bbox |
ismvt | bool | true 切 MVT(.mbtiles),false 切栅格 PNG |
outName | string | 输出文件名 |
# 响应
成功:{ "status": true, "path": "download/xxx.mbtiles" }
失败(实采:缺 bounds):
{ "error": "param error" }
# Python
res = svc.cmd_save_offline_tile_db(
mapid="sys_zp", version="v1",
minzoom=0, maxzoom=6,
bounds="[587661158,3103885970,587661256,3103886087]",
ismvt=True,
outName="sys_zp_offline.mbtiles",
)
2
3
4
5
6
7
# 组合示例:DWG → PDF 一把梭
# 1. 上传并打开 DWG
up = svc.upload_map("project.dwg", mapid="proj", version="v1")
svc.open_map(
mapid="proj", version="v1",
fileid=up["fileid"], uploadname=up["uploadname"],
mapopenway=vjmap.MapOpenWay.GeomRender,
)
# 2. 导出第 0 号布局
lay = svc.cmd_export_layout(mapid="proj", version="v1", layoutIndex=0,
newMapid="proj_layout0", newVersion="v1")
# 3. 打开刚生成的布局图并导 PDF
svc.open_map(mapid="proj_layout0", version="v1",
fileid=lay["fileid"], uploadname="proj_layout0.dwg",
mapopenway=vjmap.MapOpenWay.GeomRender)
pdf = svc.cmd_export_pdf(mapid="proj_layout0", version="v1",
width=297, height=210)
print(svc.base_gateway() + pdf["path"])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# api/06-webcad.md
# WebCAD(条件启用)
分类代号:C.6 本章共 6 个接口
# 启用判定
最推荐的方式:
svc = vjmap.Vjmap(API, TOKEN)
if svc.webcad_enabled():
draws = svc.list_webcad_draws()
else:
print("本服务未启用 WebCAD")
2
3
4
5
svc.webcad_enabled() 的判定规则:
GET /api/v1/service/webcad若返回 2xx 且 没有enabled=false/error字段,视为"可用";- 若返回 4xx/5xx 或业务错误(如
Token is invalid),视为"不可用"。
# 本仓库实采
// /api/v1/service/webcad?token=...
{
"status": 403,
"body": {
"code": 7,
"data": { "reload": true },
"msg": "Token is invalid"
}
}
2
3
4
5
6
7
8
9
# 快速索引
| 序号 | SDK 方法 | URL |
|---|---|---|
| 42 | list_webcad_draws / listWebcadDraws | GET /api/v1/map/cmd/listWebcadDraws/_/v1 |
| 43 | get_webcad_data / getWebcadData | POST /api/v1/map/cmd/getWebcadData/{mapid}/{version} |
| 44 | save_webcad_patch / saveWebcadPatch | POST /api/v1/map/cmd/saveWebcadPatch/{mapid}/{version} |
| 45 | delete_webcad_draw / deleteWebcadDraw | POST /api/v1/map/cmd/deleteWebcadDraw/{mapid}/{version} |
| 46 | convert_webcad / convertWebcad | POST /api/v1/map/cmd/convertWebCAD/{mapid}/{version} |
| 47 | export_webcad / exportWebcad | POST /api/v1/map/cmd/exportWebCAD/_null/v1 |
# 42 · 列出 WebCAD 图 listWebcadDraws
URL:GET /api/v1/map/cmd/listWebcadDraws/_/v1
响应(启用时):
{
"draws": [
{ "mapid": "proj_wc", "version": "v1", "updatedAt": "2024-..." },
...
]
}
2
3
4
5
6
# Python / Node
svc.list_webcad_draws()
await svc.listWebcadDraws();
# 43 · 读取 WebCAD 数据 getWebcadData
URL:POST /api/v1/map/cmd/getWebcadData/{mapid}/{version}
# 请求参数
| 字段 | 说明 |
|---|---|
branch | 分支名 |
includePatches | 是否把历次补丁一并返回 |
# 响应(启用时)
{
"mapdata": "<DWG JSON 字符串>",
"branch": "main",
"headVersion": 12,
"patches": []
}
2
3
4
5
6
# Python
svc.get_webcad_data("proj_wc", "v1", branch="main")
# 44 · 保存 WebCAD 补丁 saveWebcadPatch
URL:POST /api/v1/map/cmd/saveWebcadPatch/{mapid}/{version}
# 请求参数
| 字段 | 说明 |
|---|---|
patch | 一段 JSON/字符串形式的差异描述 |
branch | 分支名 |
author | 作者标识(可选) |
comment | 提交说明 |
baseVersion | 基于哪个补丁号提交,用于冲突检测 |
# 响应(启用时)
{ "status": true, "version": 13 }
# Python
svc.save_webcad_patch(
"proj_wc", "v1",
patch='[{"op":"add","entity":{...}}]',
branch="main",
author="alice",
comment="add hatch",
baseVersion=12,
)
2
3
4
5
6
7
8
# 45 · 删除 WebCAD 图 deleteWebcadDraw
URL:POST /api/v1/map/cmd/deleteWebcadDraw/{mapid}/{version}
# 请求参数
| 字段 | 说明 |
|---|---|
branch | 只删某个分支;为空则删整图 |
# 响应(启用时)
{ "status": true }
# 46 · 转为 WebCAD convertWebCAD
把一张已上传的 DWG 转成 WebCAD 可用的结构(初始化分支、预计算可编辑实体)。
URL:POST /api/v1/map/cmd/convertWebCAD/{mapid}/{version}
# 响应(启用时)
{
"status": true,
"branch": "main",
"entityCount": 12345
}
2
3
4
5
# 47 · 导出 WebCAD 为 DWG exportWebCAD
URL:POST /api/v1/map/cmd/exportWebCAD/_null/v1
# 请求参数
| 字段 | 说明 |
|---|---|
mapid / version | 要导出的 WebCAD 图 |
branch | 导出哪个分支(默认 main) |
newMapid / newVersion | 新 DWG 的 mapid / version |
# 响应(启用时)
{ "status": true, "path": "download/xxx.dwg" }
# api/07-workspace.md
# 工作区
分类代号:C.7 本章共 5 个接口
如果还没读过,建议先看 入门 · 工作区,了解 vjmap 的命名空间模型。
工作区用来把地图 / KV / 全文搜索按"项目"隔开。默认所有地图都在"匿名工作区";创建命名工作区后,URL 前缀由 /api/v1/map/... 变成 /api/v1/workspace/{name}/...。
# 快速索引
| 序号 | SDK 方法 | URL |
|---|---|---|
| 48 | get_workspaces / getWorkspaces | GET /api/v1/service/getWorkspace |
| 49 | workspace_create / workspaceCreate | POST /api/v1/service/workspaceCreate |
| 50 | workspace_modify / workspaceModify | POST /api/v1/service/workspaceModify |
| 51 | workspace_delete / workspaceDelete | DELETE /api/v1/service/workspaceDelete/{name} |
| 52 | workspace_status / workspaceStatus | GET /api/v1/service/workspaceStatus |
# 48 · 列出工作区 getWorkspace
URL:GET /api/v1/service/getWorkspace
- 非 root 账户仅看到 自己创建的 + isPublic=true 的工作区
- root(
AuthorityId:root)账户看全部
# 响应(实采:创建 1 个后)
[
{
"name": "nb_ws_1776831982",
"alias": "nb 临时测试",
"isPublic": false,
"workDir": "nb_ws_1776831982",
"idleCloseTime": 0,
"disableFullSearch": false,
"createTime": "2026-04-22 12:26:22"
}
]
2
3
4
5
6
7
8
9
10
11
一个工作区都没有时返回 null(而不是空数组 [])。
# Python
ws_list = svc.get_workspaces() or []
for w in ws_list:
print(w["name"], w["alias"])
2
3
# Node.js
const ws = (await svc.getWorkspaces()) || [];
for (const w of ws) console.log(w.name, w.alias);
2
# 49 · 创建工作区 workspaceCreate
URL:POST /api/v1/service/workspaceCreate
# 请求 body
| 字段 | 类型 | 说明 |
|---|---|---|
name | string | 唯一工作区名(会出现在 URL 路径里,注意字符集) |
alias | string | 显示名,任意字符 |
workDir | string | 工作区数据目录名;空串表示与 name 一致 |
isPublic | bool | 是否对所有登录用户可见 |
disableFullSearch | bool | 是否禁用该工作区的全文搜索索引 |
# 响应(实采)
{
"data": "nb_ws_1776831982",
"status": "ok"
}
2
3
4
# Python
svc.workspace_create(
name="myproj",
alias="我的项目",
is_public=False,
)
2
3
4
5
# Node.js
await svc.workspaceCreate({ name: 'myproj', alias: '我的项目' });
# 50 · 修改工作区 workspaceModify
URL:POST /api/v1/service/workspaceModify
body 必须是 getWorkspaces() 返回的完整 obj(包含 createTime 在内所有字段)。常见用法:先拿再改再 POST。
# 响应(实采)
{
"name": "nb_ws_1776831982",
"status": "success"
}
2
3
4
# Python
ws_list = svc.get_workspaces() or []
me = next(w for w in ws_list if w["name"] == "myproj")
me["alias"] = "新别名"
me["disableFullSearch"] = True
svc.workspace_modify(me)
2
3
4
5
# Node.js
const ws = (await svc.getWorkspaces()) || [];
const me = ws.find(w => w.name === 'myproj');
me.alias = '新别名';
await svc.workspaceModify(me);
2
3
4
# 51 · 删除工作区 workspaceDelete
URL:DELETE /api/v1/service/workspaceDelete/{name}
# 响应(实采)
{
"name": "nb_ws_1776831982",
"status": "success"
}
2
3
4
⚠️ 删除不会自动清理工作区内的地图数据;如需彻底清除,先在该工作区内删完地图再删工作区。SDK 切换到目标工作区调
delete_map / delete_map_file即可(见 01-map-management)。
# Python
svc.workspace_delete("myproj")
# Node.js
await svc.workspaceDelete('myproj');
# 52 · 工作区状态 workspaceStatus
URL:GET /api/v1/service/workspaceStatus
查询当前正在服务中的工作区(已启用 fork 进程 / 加载了地图)。若无工作区处于活跃状态,返回 null。
# 响应(实采:无活跃工作区)
null
活跃时形如:
[
{ "name": "myproj", "status": "running", "pid": 12345, "memory": 128_000_000 }
]
2
3
# 在 SDK 中切换工作区
SDK 构造后,可以用 svc.use_workspace("myproj") 把后续所有 map/... / cmd/... 调用都前缀到 /workspace/myproj/:
svc = vjmap.Vjmap(API, TOKEN)
svc.use_workspace("myproj")
svc.list_maps() # -> /api/v1/workspace/myproj/cmd/listmaps/...
svc.use_workspace(None) # 切回匿名
2
3
4
Node.js 同名方法 svc.useWorkspace("myproj")。
# api/08-data-storage.md
# 键值数据存储 (KV)
分类代号:C.8 本章共 5 个接口
vjmap 内置了一个按工作区隔离的 KV 数据表(SQLite),用来存用户配置、缩略图、缓存数据等零碎东西。"零碎"并不代表"不好用"——二开里非常常用,比如存每个用户的图层可见性、自定义地图标注等。
存储位置:后端 data/customData.db。
自动前缀:所有用户可见 key 都加了隐形 data_ 前缀,本 SDK 已屏蔽此细节。
TTL:可选过期时间(秒),到期自动清理。
批量 key 分隔符:路径里 ;;(双分号)分隔多个 key。
# 快速索引
| 序号 | SDK 方法 | URL |
|---|---|---|
| 53 | get_custom_data / getCustomData | GET /api/v1/service/data/{key} |
| 54 | save_custom_data / saveCustomData | POST /api/v1/service/data/{key} |
| 55 | delete_custom_data / deleteCustomData | DELETE /api/v1/service/data/{key} |
| 56 | save_multiple_custom_data / saveMultipleCustomData | POST /api/v1/service/datas |
| 57 | get_custom_data_keys / getCustomDataKeys | GET /api/v1/service/datakeys?prefix= |
# 53 · 获取值 data/{key} GET
# 查询参数
| 字段 | 取值 | 说明 |
|---|---|---|
retDataType | "" / "value" / "prop" | 默认返回 {data, prop};value 只要 data;prop 只要 prop |
contentType | "" / "image" | image 时后端把 base64 dataURL 解出,直接返回 PNG 二进制(不是 JSON) |
批量:key 路径段用 ;; 拼接多 key,SDK 传字符串列表即可。
# 响应
- 存在:
{ "status": "ok", "data": <value>, "prop": <prop> } - 不存在:
{ "status": "keyNoExist" }(不是 404) - 批量:
{ "values": [...], "props": [...], "status": [...] }
# 实采
// get_custom_data.json
{
"data": "hello-vjmap",
"prop": "text/plain",
"status": "ok"
}
2
3
4
5
6
// get_missing.json —— 一定要判断 status
{ "status": "keyNoExist" }
2
# Python
d = svc.get_custom_data("myproj/theme")
if d["status"] == "keyNoExist":
...
else:
theme = d["data"]
2
3
4
5
# Node.js
const d = await svc.getCustomData('myproj/theme');
if (d.status === 'keyNoExist') { /* ... */ }
2
# 批量读
svc.get_custom_data(["a", "b"])
# { "values": ["va","vb"], "props":["pa","pb"], "status":["ok","ok"] }
2
# 54 · 保存值 data/{key} POST
# 查询参数
| 字段 | 说明 |
|---|---|
ttl | 秒;到点清除。省略或 0 = 长期 |
# Body 两种写法
- JSON:
{"value": "...", "prop": "..."};prop可存元数据(MIME、扩展字段) - Raw:直接把文件二进制 POST 上去,作为 value 字段存(SDK 里叫
raw_body/rawBody)
# 响应(实采)
{
"data": "nb_kv_a_1776832185",
"status": "ok"
}
2
3
4
# Python
svc.save_custom_data("myproj/theme",
value='{"mode":"dark"}',
prop="application/json")
# 带 TTL
svc.save_custom_data("tmp_token",
value="eyJ...", ttl=3600)
# Raw bytes
svc.save_custom_data("user_avatar_42",
raw_body=open("avatar.png","rb").read())
2
3
4
5
6
7
8
9
10
11
# Node.js
await svc.saveCustomData('myproj/theme', {
value: JSON.stringify({ mode: 'dark' }),
prop: 'application/json',
});
await svc.saveCustomData('tmp_token', { value: tokenStr, ttl: 3600 });
2
3
4
5
6
# 55 · 删除值 data/{key} DELETE
# 查询参数
| 字段 | 说明 |
|---|---|
isPrefix | "true" 时把 key 当前缀,批量清除;否则只删单条 |
# 响应
- 单条(实采):
{"data":"nb_kv_a_...","status":"ok"} - 前缀(实采):
{"data":"nb_prefix_...","isPrefix":true,"status":"ok"}
# Python
svc.delete_custom_data("myproj/theme")
# 前缀批量
svc.delete_custom_data("myproj/user42/", is_prefix=True)
# 列表批量(走 ;; 分隔)
svc.delete_custom_data(["a", "b", "c"])
2
3
4
5
6
7
# 56 · 批量存值 datas POST
URL:POST /api/v1/service/datas
# Body
[{"key":..,"value":..,"prop":..,"ttl":"60"}],注意 ttl 是字符串秒数。
# 响应(实采)
{ "status": ["ok"] }
# Python
svc.save_multiple_custom_data([
{"key": "cfg/a", "value": "1"},
{"key": "cfg/b", "value": "2", "ttl": "3600"},
])
2
3
4
# Node.js
await svc.saveMultipleCustomData([
{ key: 'cfg/a', value: '1' },
{ key: 'cfg/b', value: '2', ttl: '3600' },
]);
2
3
4
# 57 · 按前缀列键 datakeys GET
# 查询参数
| 字段 | 说明 |
|---|---|
prefix | 前缀(自动加 data_) |
retDataType | value / prop / valueprop —— 同时取出值,省一次往返 |
isSys | true 时返回带 data_ 内部前缀的系统键(仅 root 场景) |
# 响应
- 仅 keys(实采):
{ "keys": ["nb_kv_a_1776832185", "nb_kv_b_1776832185"] }
- 带值(
retDataType=valueprop,实采):
{
"keys": ["nb_kv_a_1776832185", "nb_kv_b_1776832185"],
"props": ["text/plain", "bp-1"],
"values": ["hello-vjmap", "batch-1"],
"status": ["ok", "ok"]
}
2
3
4
5
6
# Python
keys = svc.get_custom_data_keys(prefix="myproj/")["keys"]
# 同时取值
bundle = svc.get_custom_data_keys(prefix="myproj/", ret_data_type="valueprop")
for k, v, p in zip(bundle["keys"], bundle["values"], bundle["props"]):
print(k, v, p)
2
3
4
5
6
# 最佳实践
- 前缀分层:用
//:建立命名空间,如myproj/user42/tool_state。这样前缀批量删除、列出都方便。 - 大文件不要存 KV:value 单条一般 < 1 MB;大图/DWG 用
/map/uploadmap或独立对象存储。 - TTL 清理:用
ttl让后端帮忙过期;别自己写 crontab。 keyNoExist≠ 错误:200 正常响应里status == "keyNoExist"表示没值,而不是失败。
# api/09-fullsearch.md
# 全文搜索 (Full-text Search)
分类代号:C.9 本章共 3 个接口
可关闭特性:config.yaml里fullsearch.disable: true会让这组路由完全不注册;当full_search返回VjmapError(404)时即判定为"关闭"。
vjmap 内置 bleve (opens new window) 作为 CAD 图纸全文索引,主要用于搜"图上某段文字在哪儿"。典型使用场景:
- 用户在前端搜索框里输入"消防栓",后端返回所有匹配到的文字 + bbox,前端画框高亮
- 按图元类型(单行/多行/属性注记/图层名/块名…)过滤
- 按工作区 / map_ver 隔离
# 快速索引
| 序号 | SDK 方法 | URL |
|---|---|---|
| 58 | full_search / fullSearch | POST /api/v1/service/fullsearch/search(也支持 GET) |
| 59 | full_search_add / fullSearchAdd | POST /api/v1/service/fullsearch/add |
| 60 | full_search_delete / fullSearchDelete | DELETE /api/v1/service/fullsearch/delete |
# Document 数据结构
{
"id": "unique-id", // 必填,全库唯一
"map_ver": "sys_zp_v1", // 建议填,便于按图过滤
"content": "消防栓", // 被索引的文本
"type": 1, // 见下方 type 表
"min_x": 0.0, // bbox
"min_y": 0.0,
"max_x": 1.0,
"max_y": 1.0,
"props": {...}, // 任意附加属性
"workspace":"default", // 为空 = 匿名工作区
"data": "..." // 原始业务载荷(由你自己定义)
}
2
3
4
5
6
7
8
9
10
11
12
13
# type 常用值
| type | 说明 |
|---|---|
| 1 | 单行文字 |
| 2 | 多行文本 MText |
| 3 | 属性注记 Attribute |
| 4 | 块属性注记 |
| 5 | 图层名 |
| 6 | 块名 |
| 7 | 线型名 |
| 8 | 填充符号名 |
| 9 | 用户自定义 |
# 58 · 搜索 fullsearch/search
两种调用方式:
- GET:所有参数走 query,适合简单关键词。
- POST:所有参数走 JSON body,支持复杂结构(尤其
bounding_box)。
# 请求字段
| 字段 | 类型 | 说明 |
|---|---|---|
query | string | 搜索关键词 |
map_ver | string | 多个用 , 分隔;纯 mapid 自动取最新版本 |
type | string | 类型过滤:"1" / "1,2,5" / "1-3" / "1-3,6" |
workspace | string | 工作区,多个逗号分隔 |
limit | int | 每页条数,默认 10 |
offset | int | 分页偏移 |
fields | string | 返回字段过滤,逗号分隔 |
facet | bool | 是否按 map_ver 聚合 facet |
bounding_box | string | "minx,miny,maxx,maxy" 或 "[..]" |
time_range | string | "2023-01-01~2023-12-31" 或 "7"(最近 N 天) |
# 响应(实采)
{
"total": 0,
"hits": [],
"took": 2
}
2
3
4
5
total 是命中数量,hits 是文档数组(含 id / content / score / fragments 等 bleve 字段),took 是耗时毫秒。
# Python
res = svc.full_search(
query="消防栓",
map_ver="proj_v1",
type_="1,2",
bounding_box="[0,0,1000,1000]",
limit=20,
)
for hit in res.get("hits", []):
print(hit["id"], hit.get("content"))
2
3
4
5
6
7
8
9
用 GET 走查询串:
svc.full_search(query="消防栓", limit=5, use_get=True)
# Node.js
const res = await svc.fullSearch('消防栓', {
mapVer: 'proj_v1',
type: '1,2',
boundingBox: '[0,0,1000,1000]',
limit: 20,
});
2
3
4
5
6
# 59 · 新增文档 fullsearch/add
# Body
{
"document": { "id":"...","map_ver":"...","content":"..." }
}
2
3
# 响应(实采)
{
"id": "nb_fs_1776832372",
"message": "Document added successfully"
}
2
3
4
# Python
svc.full_search_add({
"id": "firehydrant_01",
"map_ver": "proj_v1",
"content": "消防栓",
"type": 1,
"min_x": 100, "min_y": 200, "max_x": 110, "max_y": 210,
})
2
3
4
5
6
7
批量导入:本 SDK 只封装了单条
full_search_add。接口/fullsearch/batchAdd用同样的 POST JSON({"documents":[...]});本次不做默认封装,可按需自行svc._request("POST", svc.service_url("fullsearch/batchAdd"), json_body={"documents":[...]})。
# 60 · 删除文档 fullsearch/delete
注意是 DELETE 带 JSON body,而不是 ?id=(gin 用 ShouldBindJSON)。
# Body
{ "id": "firehydrant_01" }
# 响应(实采)
{
"id": "nb_fs_1776832372",
"message": "Document deleted successfully"
}
2
3
4
# Python
svc.full_search_delete("firehydrant_01")
# 常见错误
| HTTP 状态 | body 片段 | 含义 |
|---|---|---|
| 400 | "error":"Invalid request parameters" | JSON 反序列化失败 |
| 400 | "error":"Document ID cannot be empty" | 未填 id |
| 400 | "error":"Invalid bounding box format" | bounding_box 解析失败 |
| 404 | 整条路由不存在 | fullsearch.disable:true,功能关闭 |
| 500 | "error":"Failed to initialize service" | 后端索引损坏,看服务器日志 |
# 使用建议
- 用
map_ver隔离:别把所有图纸的文字都塞一个索引里查全局,会拖慢。 - 先 bbox 后全文:多数场景只需搜当前视口,给
bounding_box可大幅缩小候选集。 - 批量构建走后台 job:把"从 DWG 抽文字 → 批量 add"放到离线任务里;前端只做搜索。
- 关闭索引也没关系:SDK 的
full_search会在 404 时抛VjmapError(404),应用层 catch 一下退化到普通查询即可。
# api/10-symbols.md
# 符号库
分类代号:C.10 本章共 3 个接口(只读)
vjmap 自带一个"常用图块/图符"库,按分类组织。二开场景里最常见的用法是:
- 前端工具条展示"符号面板",用户选一个图符,拖到图上
- 拖放后前端本地生成图元并调后端
updatemap持久化 - 导出 DWG 时把符号实例替换为 DXF BlockRef
本章只覆盖只读三接口(分类列表 / 符号列表 / 符号详情)。写操作(增 / 改 / 删分类与符号)SDK 暂不封装;如果你用 root token,可按
POST /symbol/category、PUT /symbol/:id等直接svc._request(...)调。
# 快速索引
| 序号 | SDK 方法 | URL |
|---|---|---|
| 61 | symbol_categories / symbolCategories | GET /api/v1/service/symbol/categories |
| 62 | symbol_list / symbolList | GET /api/v1/service/symbol/list/{categoryId} |
| 63 | symbol_get / symbolGet | GET /api/v1/service/symbol/{id} |
# 61 · 分类列表 symbol/categories
# 响应(实采)
{
"data": [
{ "id": "default", "name": "默认分类", "createTime": "2026-04-10 11:29:15" },
{ "id": "user_custom", "name": "用户自定义", "createTime": "2026-04-10 11:29:15" }
],
"status": "ok"
}
2
3
4
5
6
7
# Python
cats = svc.symbol_categories()["data"]
for c in cats:
print(c["id"], c["name"])
2
3
# Node.js
const cats = (await svc.symbolCategories()).data;
# 62 · 符号列表 symbol/list/{categoryId}
按分类分页列出符号。
# 查询参数
| 字段 | 默认 | 说明 |
|---|---|---|
page | 1 | 分页起始(1-based) |
pageSize | 20 | 每页 |
keyword | - | 名称模糊匹配 |
# 响应(实采:分类为空)
{
"data": {
"list": [],
"total": 0,
"page": 1,
"pageSize": 3
},
"status": "ok"
}
2
3
4
5
6
7
8
9
有数据时 list 元素形如:
{
"id": "symbol_abc",
"categoryId": "default",
"name": "消防栓",
"basePoint": [0, 0],
"thumbnail": "data:image/png;base64,...",
"data": "...DWG/SVG 原始数据..."
}
2
3
4
5
6
7
8
# Python
page = svc.symbol_list("default", page=1, page_size=20, keyword="栓")
for it in page["data"]["list"]:
print(it["id"], it["name"])
2
3
# Node.js
const page = await svc.symbolList('default', { page: 1, pageSize: 20 });
# 63 · 符号详情 symbol/{id}
# 响应
- 成功:
{ "status": "ok", "data": { ...同上"有数据时"结构... } } - 未找到:HTTP 404 +
{ "status": "error", "message": "not found" }
# Python
try:
sym = svc.symbol_get("symbol_abc")["data"]
except vjmap.VjmapError as e:
if e.status == 404:
... # 符号不存在
else:
raise
2
3
4
5
6
7
# 使用范式
下面是一个"前端面板"级别的整合:
def list_all_symbols():
for cat in svc.symbol_categories()["data"]:
page = 1
while True:
bundle = svc.symbol_list(cat["id"], page=page, page_size=50)["data"]
for sym in bundle.get("list", []):
yield cat, sym
if page * bundle["pageSize"] >= bundle["total"]:
break
page += 1
2
3
4
5
6
7
8
9
10
# api/11-system.md
# 系统
分类代号:C.11 本章共 4 个接口
这几个接口是运维和监控最常看的:
- heart:心跳
- version:版本与构建信息
- runstatus:当前进程 / 连接 / 切片状态
- viewlogs:读最近 N 条日志
生产环境完全可以周期性采集 runstatus 做监控告警,不需要登进后端机器。
# 快速索引
| 序号 | SDK 方法 | URL |
|---|---|---|
| 64 | heart / heart | GET /heart |
| 65 | version / version | GET /version |
| 66 | run_status / runStatus | GET /api/v1/map/cmd/runstatus/_/v1?detail=true |
| 67 | view_logs / viewLogs | GET /api/v1/map/cmd/viewlogs/_/v1?record=N |
# 64 · 心跳 /heart
直接挂 Gateway 上(不走 /api/v1/... 前缀),用来判断进程是否活着。
# 响应(实采)
{
"status": "ok",
"timestamp": "2026-04-22T12:37:54.501381+08:00"
}
2
3
4
# Python / Node.js
svc.heart()
await svc.heart();
# 用作健康检查
# docker-compose healthcheck 片段
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:27660/heart"]
interval: 30s
timeout: 3s
retries: 3
2
3
4
5
6
# 65 · 版本 /version
# 响应(实采)
{
"arch": "amd64",
"os": "windows",
"status": "ok",
"time": "2026-04-22 12:37:54",
"version": "2026041001"
}
2
3
4
5
6
7
version是 service 的 build id(yyyyMMDD + 计数)time是服务器当前时间,可用来校时- 图形服务的版本在
runstatus → application.version里(本例是"20260305")
# 66 · 运行状态 runstatus
# 查询参数
| 字段 | 默认 | 说明 |
|---|---|---|
detail | false | true 时额外返回 activeRequests 列表 |
# 响应(实采 detail=true)
{
"application": {
"pid": "11996",
"runtime": 24063,
"taskActiveThreadCount": 0,
"threadId": "12000",
"version": "20260305"
},
"process": {
"activeProcesss": {},
"activeRequestCount": 0,
"activeRequests": [],
"curParallelProcessGeomNum": 0,
"maxParallelProcessGeomNum": 2,
"recycleProcesss": [],
"reqTimeout": 600000,
"waitWorkMapIds": []
},
"slice": {},
"store": {
"connectionNames": "_cache_map",
"storeLastAccessTime": [],
"storeNoActiveCloseTime": 60,
"storeStatus": []
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 字段速查
| 字段 | 含义 |
|---|---|
application.runtime | 进程启动毫秒数 |
process.activeRequestCount | 当前在处理的请求数 |
process.activeRequests | detail=true 时的 URL 列表,排查"卡在什么请求上" |
process.curParallelProcessGeomNum | 当前并行几何任务 |
process.maxParallelProcessGeomNum | 并发上限 |
process.recycleProcesss | 被回收的子进程 |
slice | 切片缓存状态(按 layer) |
store | KV 存储连接 |
# Python
status = svc.run_status(detail=True)
assert status["application"]["pid"]
print(status["process"]["activeRequestCount"], "个并发请求")
2
3
# Node.js
const st = await svc.runStatus(true);
# 67 · 查看日志 viewlogs
# 查询参数
| 字段 | 默认 | 说明 |
|---|---|---|
record | 100 | 读取最近多少条记录 |
# 响应(实采,部分片段)
{
"logs": [
" *** log260422_0x115f4_2026-04-22.log *** ",
"[2026-04-22 11:58:01.625] [daily_logger] [info] File:() Line:(0) Msg: (\"process app started..._null_@>v1_1571__||__11996__||__28011__||__D:/Program Files/vjmapserver/data\")\n",
"[2026-04-22 11:58:01.697] [daily_logger] [info] File:() Line:(0) Msg: (openMap begin... \"_null_@>v1\")\n",
...
]
}
2
3
4
5
6
7
8
每一条都是一行日志(带行尾 \n);多子进程时会用 *** log....log *** 分隔不同文件。
# Python
lines = svc.view_logs(record=200)["logs"]
for line in lines[-20:]:
print(line.rstrip())
2
3
# Node.js
const { logs } = await svc.viewLogs(200);
⚠️ 这是最近 N 条,不是整个日志文件;想做审计请定时拉并本地存档。
# 监控样例:Python 拉取 → Prometheus
from time import sleep
def poll():
while True:
try:
s = svc.run_status(detail=False)
metrics = {
"vjmap_up": 1,
"vjmap_active_reqs": s["process"]["activeRequestCount"],
"vjmap_runtime_ms": s["application"]["runtime"],
"vjmap_store_count": len(s["store"].get("storeStatus") or []),
}
except Exception:
metrics = {"vjmap_up": 0}
push_to_prometheus(metrics)
sleep(15)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# api/12-service-misc.md
# /service 杂项
分类代号:C.12 本章共 3 个接口
这里汇集了"不方便归到其它分类、但二开很常用"的三个能力:
- QR Code 生成:把字符串直接渲染成 PNG 二维码
- 字体字形 (PBF):vjmap地图渲染依赖的字体文件
- 图片上传:把任意图片文件塞到服务器指定路径(配合前端水印/图床)
# 快速索引
| 序号 | SDK 方法 | URL |
|---|---|---|
| 68 | qrcode_url / qrcode_bytes / qrcodeUrl / qrcodeBytes | GET /api/v1/service/qrcode?content=...&size= |
| 69 | glyphs_url / glyphs_bytes / glyphsUrl / glyphsBytes | GET /api/v1/service/fonts/{fontstack}/{range}.pbf |
| 70 | upload_img / uploadImg | POST /api/v1/service/uploadImg (multipart) |
# 68 · 二维码 qrcode
# 查询参数
| 字段 | 默认 | 说明 |
|---|---|---|
content | -(必填) | 要编码的字符串;后端会 decodeURIComponent,所以不用双重编码 |
size | 256 | 图片边长(像素) |
# 响应
直接返回 image/png 字节,不是 JSON。
# SDK 两种用法
- URL 构造(适合前端直接
<img src=...>):
url = svc.qrcode_url("https://example.com/project/42", size=512)
# http://127.0.0.1:27660/api/v1/service/qrcode?content=https%3A%2F%2Fexample.com%2Fproject%2F42&size=512&token=<jwt>
2
- 下载字节(适合服务端生成后转发/保存):
png = svc.qrcode_bytes("VJMAP", size=128)
open("qr.png", "wb").write(png)
2
Node.js:
const url = svc.qrcodeUrl('VJMAP', 512);
const buf = await svc.qrcodeBytes('VJMAP', 128);
2
# 实采信息
// qrcode_bytes_info.json
{ "len": 284, "md5": "2577b1157cf93feefce8c6b180a0ff42", "magic_ok": true }
2
# 69 · 字体 fonts/{fontstack}/{range}.pbf
vjmap 矢量渲染在画文字时,需要按需下载对应字体、对应 Unicode 区间的 PBF 字形文件。这个接口就是字形的分发端。
# 路径参数
| 字段 | 说明 |
|---|---|
fontstack | 字体名,可用 , 分隔多个回退栈。后端会依次尝试,并自动在末尾追加 Arial Unicode MS Regular 作为兜底 |
range | Unicode 字形区间,形如 0-255、256-511。通常是 256 个字形一包 |
# 响应
application/x-protobuf 字节(PBF)。
# SDK
url = svc.glyphs_url("Arial Unicode MS Regular", "0-255")
pbf = svc.glyphs_bytes("Arial Unicode MS Regular", "0-255")
2
const url = svc.glyphsUrl('Arial Unicode MS Regular', '0-255');
# 实采
// glyphs_bytes_info.json
{
"len": 75282,
"md5": "638faf3bf9a90424d23513cfab7e9ce1"
}
2
3
4
5
# 用在 vjmap 里
map.setStyle({
glyphs: 'http://127.0.0.1:27660/api/v1/service/fonts/{fontstack}/{range}.pbf?token=' + TOKEN,
// ...其它
});
2
3
4
注意 {fontstack}/{range} 是 vjmap 自己替换的占位符,别让 URL 编码器把 { / } percent-encode 掉。
# 70 · 上传图片 uploadImg
# 特殊鉴权
这个接口不走普通 token,而是检查 HTTP header:
Authorization: Basic X3ZqbWFwQF8=
即 Basic base64("_vjmap@_")。后端硬编码。SDK 默认已经带上;你也能传自定义 authorization 参数覆盖。
# Headers
| Header | 说明 |
|---|---|
Authorization | 上述固定值(SDK 默认) |
Uri | 必填:服务器端保存目录(以 / 结尾)。SDK 参数名 uri |
watermark | "1" 时后端自动叠加水印(utils.NewWatermarkBase64 实现) |
# multipart 字段
| 字段 | 说明 |
|---|---|
upload | 文件内容。注意 field name 是 upload 不是 file |
# 响应
成功(实采):
{
"code": 0,
"path": "./data/test_upload/nb_upload_smoke.png",
"url": "nb_upload_smoke.png"
}
2
3
4
5
失败(实采:错误 Authorization):
{
"code": 7,
"data": {},
"msg": "no Authorization"
}
2
3
4
5
# Python
# 从本地文件上传
svc.upload_img(
"./avatar.png",
uri="./data/upload/avatars/",
filename="user_42.png",
watermark=True,
)
# 从字节上传
svc.upload_img(
("user_42.png", png_bytes),
uri="./data/upload/avatars/",
)
2
3
4
5
6
7
8
9
10
11
12
13
# Node.js
// Node 18+
await svc.uploadImg('./avatar.png', {
uri: './data/upload/avatars/',
filename: 'user_42.png',
watermark: true,
});
// Buffer
await svc.uploadImg(buf, {
uri: './data/upload/avatars/',
filename: 'user_42.png',
});
2
3
4
5
6
7
8
9
10
11
12
# api/13-ai.md
# 13. AI(向量检索 / FAQ / 文档)
条件启用:这一组接口依赖
vjmap服务端的嵌入(embedding)模型与向量库。 若未正确配置,调用会返回500(例如缺少bge-large-zh-v1.5-f16.gguf模型文件)。 在你的环境若未开启 AI 子服务,直接跳过本章。
本章覆盖 3 个接口:
| No. | 接口 | URL | Method | 说明 |
|---|---|---|---|---|
| 13.1 | vectorSearch | /api/v1/service/vectorSearch | GET / POST | 相似性检索 |
| 13.2 | chatFaq | /api/v1/service/chatFaq | GET | 获取 FAQ 知识库 |
| 13.3 | vectorDocs | /api/v1/service/vectorDocs | GET | 获取向量库已有文档元数据 |
# 13.0 启用探针
SDK 提供 aiEnabled() / ai_enabled()(内部调用 chatFaq)判断 AI 子服务是否可用;
失败(抛 VjmapError)则视为未启用,跳过后续调用即可。
if not svc.ai_enabled():
raise SystemExit("AI 未启用,略")
2
# 13.1 vectorSearch — 相似性检索
按给定自然语言 query 在向量库中检索最相似的若干条文档。
# 请求
两种请求方式:
| 方式 | URL | Body | 备注 |
|---|---|---|---|
| GET | GET /api/v1/service/vectorSearch?query=...&numDocuments=... | 无 | 简单场景用 |
| POST(推荐) | POST /api/v1/service/vectorSearch | JSON | 支持 documents 等复杂字段 |
# 主要参数
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
query | string | — | 必填 查询文本 |
numDocuments | int | 20 | 返回条数 |
collection | string | "vj" | 向量库集合名 |
where | string | — | metadata 过滤表达式(chromem-go (opens new window) 语法) |
whereDocument | string | — | document 内容过滤表达式 |
searchDescription | string | — | 附加搜索上下文,帮助模型更精准 |
disableSplitSentence | bool | false | 关闭"先分句再分别 embed" |
disableSearchKnowledge | bool | false | 仅搜 documents,不搜知识库 |
documents | array | — | 临时附带的用户文档(不会持久化)。每项:{content:"...", metadata:{...}} |
# 响应
{
"status": "ok",
"data": [
{
"id": "__createPolygon",
"content": "根据给定的点坐标数组直接绘制一个多边形",
"metadata": { "...": "..." },
"similarity_score": 0.8273
},
{ "...": "..." }
]
}
2
3
4
5
6
7
8
9
10
11
12
# SDK 示例
res = svc.vector_search("如何画多边形", num_documents=5)
for doc in res.get("data", []):
print(doc["id"], doc.get("similarity_score"))
2
3
const res = await svc.vectorSearch('如何画多边形', { numDocuments: 5 });
res.data.forEach(d => console.log(d.id, d.similarity_score));
2
# 本地实测(失败样例)
当前测试环境缺少 embedding 模型文件,真实请求返回:
{
"status": 500,
"body": {
"code": 8,
"msg": "D:/Program Files/vjmapserver/data/ai/models/bge-large-zh-v1.5-f16.gguf is not exist"
}
}
2
3
4
5
6
7
部署提示:部署方需根据官方文档下载 embedding 模型文件,放置于 服务端的
data/ai/models/目录下,并在配置中指定模型名。未配置时,vectorSearch会失败但不影响其他子系统运行。
# 13.2 chatFaq — FAQ 知识库
# 请求
GET /api/v1/service/chatFaq
# 响应
返回一个按"主题"分组的字符串数组字典。
{
"status": "ok",
"data": {
"UI交互": [
"弹出学生表单输入对话框,包括姓名、年龄(默认18)、性别",
"获取地图的当前中心点坐标X和Y、地图级别,生成json对象,根据这数据弹出数据信息框..."
],
"信息窗口": [
"在坐标[30,50]处添加一个信息窗口,内容为\"Hello,World\""
],
"几何算法": [ "..." ]
}
}
2
3
4
5
6
7
8
9
10
11
12
13
数据源来自服务端 ./data/ai/faq/*.md,并开启 5 分钟 HTTP 缓存。
# SDK 示例
faq = svc.chat_faq()
for topic, items in faq["data"].items():
print(f"- {topic}: {len(items)} 项")
2
3
const { data } = await svc.chatFaq();
for (const [topic, items] of Object.entries(data)) {
console.log(`- ${topic}: ${items.length} 项`);
}
2
3
4
# 13.3 vectorDocs — 向量库文档
# 请求
GET /api/v1/service/vectorDocs
# 响应
返回一个数组,每项是知识库里的一条文档元数据:
{
"status": "ok",
"data": [
{
"id": "__createPolygon",
"content": "根据给定的点坐标数组直接绘制一个多边形",
"metadata": {
"category": "draw",
"code": "...",
"comments": "...",
"function": "__createPolygon",
"param": "..."
},
"similarity_score": 0
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
similarity_score 恒为 0(此接口不做相似度计算,仅导出文档)。
# SDK 示例
docs = svc.vector_docs()["data"]
print("知识库条目数:", len(docs))
print("分类统计:", {
c: sum(1 for d in docs if d["metadata"].get("category") == c)
for c in sorted({d["metadata"].get("category", "") for d in docs})
})
2
3
4
5
6
const { data: docs } = await svc.vectorDocs();
console.log('条目数:', docs.length);
2
# 使用建议
- 部署前先跑探针:初始化客户端后先调
chat_faq(),失败则关闭 AI UI 入口。 - 前端搜索:优先用 POST
vectorSearch,把searchDescription带上业务上下文能显著提升命中率。 - 冷启动:第一次调用
vectorSearch会加载 embedding 模型,响应可能秒级; 第二次起基本在毫秒级。 - 安全:
vectorDocs直接返回所有文档内容(含code字段),不要把它暴露给未鉴权用户。
← 地图服务Rest接口 全文搜索 →