唯杰地图 logo唯杰地图
VJMAP
EN

地图管理(Map Management)

vjmap-service/references/api/01-map-management.md 23.5 KB606 行
原文

分类 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 至少一个字段,否则后端回 500 param error。SDK 对无 body 的 cmd 统一走 GET。
  • openmap 通过 POST body 传 mapid/version/fileid/uploadname/geom;fileid 必须来自 upload_map 的返回,不要自己拼 md5。打开方式只看布尔 geom——后端不读 mapopenway(源码零消费点),SDK 把 mapopenway 换算成 geom。
  • 所有路径都带 ?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 是 c + md5[:11](CAD 文件)或 t + md5[:11](图片等其他文件),不要自己拼,应从 mapfile/uploads 响应里取
  • 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);两个 SDK 仍会带上,无害

⚠️ 表单里的其它字段(包括 secretKey)后端一律不读——上传时无法设密。加密只能在该图 v1 首次 openmap 时带 ≥10 位 secretKey(见 authentication.md §2.1)。

出参

{"fileid":"caf27de8a225","mapid":"caf27de8a225","uploadname":"caf27de8a225.dwg"}
  • 同内容文件已被某张图使用时,mapid 是那张图的 mapid,另带 version / geom;图片文件另带 maptype:"image";失败返回 {uploadname, error}(如 not support format xxx)
  • fileid:生成的 12 位标识(c+md5 前 11 位为 CAD 文件,t+md5 前 11 位为图片等其他文件;恰好都以 md5 首字符起头,容易误认为 md5[:12])。✅ .dwg 上传 → fileid==="c"+md5.slice(0,11)、.png → "t"+md5.slice(0,11) 精确吻合
  • mapid:默认等于 fileid;后续 open_map 可以给另一个业务名
  • uploadname:⚠️ 实际是回显上传时的原文件名,不是落盘名——服务端实际存为 fileid+扩展名(如 cc4d08f98148.dwg)。证据:上传后 delete_map_file(uploadname) 返回 notFound,而 delete_map_file(fileid+".dwg") 成功删除。清理上传产物要用 fileid 拼名

示例

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}')"
up = svc.upload_map("./my.dwg")
fileid, uploadname = up["fileid"], up["uploadname"]

# 也接受 (文件名, bytes) 元组——服务端照样按内容自算 md5 命名
up = svc.upload_map(("probe.dwg", dwg_bytes))
const up = await svc.uploadMap('./my.dwg');
const { fileid, uploadname } = up;

3. 打开/创建地图 open_map

URL:POST /api/v1/map/openmap/{mapid}?version={version} (SDK 默认走 POST)

⚠️ SDK 坑位:Node SDK 的 openMap({useMethod:'GET'}) 必崩——它把 jsonBody 传给 fetch,undici 禁止 GET/HEAD 带 body,抛 Request with GET/HEAD method cannot have body(包装成 VjmapError)。HTTP 层面的 GET 等价性在 SDK 里不可达,只用默认 POST。

这是整套 API 的中心接口。它同时承担:

  • 首次创建:mapid 未存在时结合 fileid + uploadname 新建
  • 再次打开:mapid 已存在时忽略 fileid/uploadname
  • 打开方式:body 布尔 geom(后端缺省 false)——true = GeomRender(几何渲染),false/缺省 = Memory(内存打开)。后端不读 mapopenway;SDK 的 mapopenway 参数(默认 GeomRender)按官方 JS SDK 的做法换算为 geom = mapopenway !== 'Memory'

入参(JSON body)

字段 类型 必填 说明
mapid string 是 业务 ID
version string 是 版本号,默认 v1
fileid string(12) 首次建图必填 upload_map 返回的 fileid
uploadname string 首次建图必填 upload_map 返回的 uploadname
geom bool 否 打开方式,后端缺省 false(Memory)。SDK 参数是 mapopenway: 'Memory' | 'GeomRender'(默认 GeomRender → geom:true)
style object 否 预设样式;详情见 § 2 create_map_style
secretKey string 否 图级密钥:仅在该图 v1 首次打开且 ≥10 位时设密(原样存);之后用于校验(见 authentication.md)
accessKey string 否 该图的 18 位 ak 开头 accessKey(仅浏览),或实例 super key
mapbounds array / string 否 限制的图面范围

⚠️ cmd 默认按该图版本在 mapinfos ini 里的 geom 标记路由:从未以 geom:true 打开过的图走内存进程,queryFeatures 的 condition 查询会回 not support query type!(见 03-query)。旧版 SDK 因发送 mapopenway 而非 geom,实际一律按 Memory 打开——用它新建的图都是这种状态,用 GeomRender 重开一次即可。

GeomRender 首次打开是异步的

新图(或几何库尚未生成的图)以 geom:true 打开时立即返回 {"mapid","version","status":"working","error":""},后台子进程生成几何库。需轮询 GET cmd/listmaps/{mapid}/{version} 直到该版本 status=="finish"(error / runerror 为失败,原因在 errmsg),然后再 openmap 一次才拿到完整参数(bounds / layer / layers…)。官方 JS SDK openMap(param, isWaitFinish=true) 就是这么做的。

r = svc.open_map(mapid="demo_dwg", fileid=up["fileid"], uploadname=up["uploadname"])
if r.get("status") == "working":
    w = svc.wait_map_open_finish("demo_dwg", r["version"])   # {"status":"finish"|"error", ...}
    r = svc.open_map(mapid="demo_dwg", version=w.get("version") or r["version"])
let r = await svc.openMap({ mapid: 'demo_dwg', fileid: up.fileid, uploadname: up.uploadname });
if (r.status === 'working') {
  const w = await svc.waitMapOpenFinish('demo_dwg', r.version);
  r = await svc.openMap({ mapid: 'demo_dwg', version: w.version || r.version });
}

出参(节选,完整见 snapshot)

字段 含义
mapid / version / fileid / uploadname / filename 基本定位
bounds / dbBounds / drawBounds / initBounds / viewBounds 各种范围
layers (JSON string) 图层数组
styles (JSON string) 预置样式数组
format 瓦片格式(典型 png)
geom 实际打开方式,字符串 "true"(GeomRender)/ "false"(Memory)
layer 当前样式图层名(如 s6d04d896e)= 瓦片 URL 的 {layer} 段;SDK 存入 currentMapParam().layer
status GeomRender 首次打开时为 "working"(此时只有 mapid/version/status/error 四键,见上)
mapopenway 后端不读也不返回(下方片段来自早期版本;旧 SDK 发送它时本版响应里也没有)
pid 子进程 PID(⚠️ Memory 方式也返回——两种 openway 同值,非 GeomRender 专属)
renderAccuracy 渲染精度
isnewmap ⚠️ 实际是字符串 "true" 且恒真——对已存在图重复 openMap/updatemap 也回 "true",不能当"刚创建"判据

真实响应片段

注:片段来自早期版本实采。本版响应没有 mapopenway(实际打开方式看 geom 字段),isnewmap 是字符串 "true" 且恒真、pid 在 Memory 下也返回。

{
  "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,...}]"
}

示例

svc.open_map(
    mapid="demo_dwg", version="v1",
    mapopenway="Memory",
    fileid=up["fileid"], uploadname=up["uploadname"],
)
await svc.openMap({
  mapid: 'demo_dwg', version: 'v1',
  mapopenway: 'Memory',
  fileid: up.fileid, uploadname: up.uploadname,
});

4. 修改地图 update_map

URL:POST /api/v1/map/updatemap/{mapid}?version={version}

用法:给已存在的 mapid 换图档出新版本。服务端语义:

  • body 的 fileid 与当前最新版本的图档不同 → 新建版本 vN+1(内部打 updatemap:true),此时 deleteOldVersion:true 会在新版本打开后删除旧版本(只在这条路径生效)
  • fileid 为空或与当前相同 → 不出新版本,按当前版本原样重开
  • 打开方式与 openmap 相同,由 geom 决定(SDK 未给 geom 时按 mapopenway 换算,缺省 GeomRender);新版本同样走异步生成,需 waitMapOpenFinish

入参(JSON body)

字段 必填 说明
mapid 否(实采) 要更新的图;路径里的 {mapid} 已生效,body 里可省
fileid 出新版本时必填 新上传图档的 fileid;为空/与当前相同 = 原样重开
uploadname 否 上传原文件名
geom 否 打开方式;SDK 参数 mapopenway 自动换算
deleteOldVersion 否 true 时出新版本后删除旧版本
…… 其余字段参考 vjmap 发布包 d.ts 里的 IUpdateMapParam

📌 实采(2026-09-20):POST updatemap/{mapid}?version=v1 带空 body 也成功,返回该图当前参数快照(等价"原样重开")。

出参

结构与 open_map 返回一致。

示例

up2 = svc.upload_map("./demo_rev2.dwg")          # 新图档
r = svc.update_map("demo_dwg", "v1", body={
    "fileid": up2["fileid"],
    "uploadname": up2["uploadname"],
    "deleteOldVersion": True,                      # 可选:出 v2 后删 v1
})
# r["version"] == "v2";GeomRender 下 r["status"] 可能是 "working",同 open_map 等待

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'

SDK 内部统一使用 GET 规避该陷阱。


6. 删除地图 delete_map

URL:GET /api/v1/map/cmd/deletemap/{mapid}/{version}?deleteDwgFile={bool}

参数 默认 说明
deleteDwgFile false 后端实际读取的参数:true 时强制删除源文件
keepSource - openapi/旧 SDK 记载的参数,后端没有消费点,传了无效
retainVersionMaxCount 0 配合 version=* 删除全部版本时可保留最新 N 个版本

⚠️ 后端行为:删除源文件的判断是 deleteDwgFile==true || !noAutoDeleteDwgFile(noAutoDeleteDwgFile 是服务端 config.json 配置项)。keepSource 本身在后端没有消费点——要可靠保留源文件,部署方需把 noAutoDeleteDwgFile 设为 true,否则默认行为是连源文件一起删。

出参

{"status": true}

⚠️ mapid 不存在时仍返回 {"status": true}——该接口对"删除不存在的图"静默成功,不能靠返回值判断是否真删了图。

示例

svc.delete_map("demo_dwg", "v1", delete_dwg_file=False)

7. 重命名地图 rename_map

URL:GET /api/v1/map/cmd/renamemap/{old}/_?newMapId={new}

⚠️ 查询键是驼峰 newMapId(小写 newmapid 后端读不到,会报 mapid is empty);且要求对该图有 admin 权限(isAdminPermInMap),否则报 no admin permisson。

响应样例(权限/参数不满足时)

{
  "status": false,
  "error": true,
  "reason": "no admin permisson to delete this map, please use password or superKey"
}

⚠️ mapid 不存在时返回 {"status": false, "error": true, "reason": "mapid ... is not exist"}——HTTP 仍是 200,SDK 只在 HTTP 层非 2xx 时抛 VjmapError,业务错误要调用方自查 status/error/reason 字段。

⚠️ 该接口(及同族 cmd 接口)的响应体会回显完整 token 字段——把响应写日志/上报前务必剔除,否则会泄露凭据。


8. 删除源文件 delete_map_file

URL:GET /api/v1/map/cmd/deletemapfile/_/v1?files=a.dwg;b.dwg

删除上传到 data/mapfiles/ 目录下的物理文件。后端不检查是否有图在引用(找到即删),删前自己确认;不带扩展名时自动补 .dwg 再找。

出参

{
  "deleted": [],
  "deletedCount": 0,
  "notFound": ["__nb_nonexistent__.dwg"],
  "status": false
}

示例

svc.delete_map_file(["caf27de8a225.dwg"])
# 或字符串
svc.delete_map_file("a.dwg;b.dwg")

9. 列出地图 list_maps

URL:GET /api/v1/map/cmd/listmaps/_/v1

入参

参数 说明
curPage 页码(1 起,配合 pageCount 生效;不传/为 0 则返回全部)
pageCount 每页条数
mapIds 限定只返回这些 mapid(字符串列表)

⚠️ mapIds 由后端 toStringList() 读取——GET query 里的字符串不会拆分,必须用 POST + JSON body {"mapIds":["a","b"]}(官方 JS SDK 也是这样发的)。curPage/pageCount 走 query 或 body 均可。

⚠️ openapi/旧文档写的 keyword、page、pageSize 在本版后端没有消费点,传了也不会过滤。另外:mapid 路径段写具体图名时返回 {mapid:[该图各版本]}(不走分页;version 写 _ = 最新版);非 root 用户默认只隐藏 ns_ 前缀的图(服务端 map_list_exclude_prefix 可配,; 分隔,* 全隐藏),root 只隐藏 ns_temp_。temp_ 前缀的图不隐藏,只是会被登记进临时图自动清理队列。

出参

{
  "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"
    }
  ]
}
  • 顶层 key 是 mapid,value 是该图所有版本的数组
  • geom:true 表示该图已有切片缓存
  • status:"finish" 表示打开流程完成
  • ⚠️ 响应形态随参数分叉:不传分页参数 → 返回 {mapid: [版本…]} 字典;带 curPage/pageCount(都 >0)→ 返回分页信封 {count, curPage, pageCount, mapIds:[本页 mapid…], mapInfos:{mapid:[版本…]}, allMapIds:[…]}——mapIds 是数组,版本详情在 mapInfos 对象里;count 是过滤后总数;allMapIds 是实例全部 mapid(未做前缀隐藏过滤);带 mapIds 过滤(POST body)→ 仍回 {mapid:[…]} 形态但只含命中图

示例

maps = svc.list_maps()
for mapid, versions in maps.items():
    print(mapid, [v["version"] for v in versions])
const maps = await svc.listMaps();

10. 读元数据 metadata

URL:GET /api/v1/map/cmd/metadata/{mapid}/{version}

图必须先 open_map 打开才能读到完整元数据。

入参(Query)

本接口在本版后端不读业务参数(只解析路径里的 mapid/version,version="_" 自动取最新版本)。官方 openapi 记载的 enableEditMeta、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"])

11. 修改元数据 update_metadata

URL:POST /api/v1/map/cmd/updateMetadata/{mapid}/{version}

通过 POST body 修改图的元数据字段(如 description、业务标签等)。

入参(JSON body)

⚠️ 后端行为:body 的顶层键会被逐个直接合并进元数据(meta[key]=value),不是 {fields:[{name,value}]} 数组形式——传 fields 数组会原样写进一个名为 fields 的元数据键。

{
  "description": "我是新的描述",
  "tags": "building,sample"
}

image 类型图另有特判:同时传 imageLeft/imageTop/imageResolution 时会重算 bounds/dbBounds/drawBounds。

出参

⚠️ 返回 {} 空对象,不是文档早期声称的"合并后完整元数据";且写入的 description/自定义键在随后 metadata() 回读中不可见(疑似仅对 image 类型图的 imageLeft/imageTop/imageResolution 特判路径或受打开方式约束)。另注意:这是 cmd 路由,空 JSON body {} 直接 HTTP 500(SDK 不做 noop 兜底),必须至少带一个键。

示例

svc.update_metadata("demo_dwg", "v1", body={
    "description": "hello"
})

12. 缩略图 thumbnail

URL:GET /api/v1/map/cmd/thumbnail/{mapid}/{version}?width=W&height=H

返回 PNG 图片字节流。后端行为:返回的是该图切片目录下预生成的 .png 缓存,缩放后输出——不是实时渲染,也不支持 layers/backcolor(这两个参数没有消费点)。

常用参数

Query 说明
width / height 缩略图像素大小(按此缩放缓存图)
darkTheme true 时把近黑像素反白,适配深色主题

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)
const buf = await svc.thumbnailBytes('sys_zp', 'v1', { width: 256, height: 160 });
fs.writeFileSync('thumb.png', buf);

也可以只构造 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=...

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"
}

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"
  }
}

搭配 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
}

示例

b = svc.get_data_bounds("sys_zp", "v1")["bounds"]
# 用来设置 vjmap 初始 fitBounds

下一分类:02-tiles-styles.md