地图管理(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/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