multi-map
本文回答:一个页面同时显示多张 vjmap 地图,视图同步;两张图卷帘比较;CAD 新旧图差异。
一、两张独立地图
<div id="map1" style="width: 50%; height: 100%; float:left"></div>
<div id="map2" style="width: 50%; height: 100%; float:left"></div>
async function initMap(containerId, mapid) {
const svc = new vjmap.Service(URL, TOKEN);
const res = await svc.openMap({ mapid, mapopenway: vjmap.MapOpenWay.GeomRender });
const prj = new vjmap.GeoProjection(vjmap.GeoBounds.fromString(res.bounds));
const map = new vjmap.Map({
container: containerId,
style: svc.rasterStyle(),
center: prj.toLngLat(prj.getMapExtent().center()),
zoom: 2,
renderWorldCopies: false
});
map.attach(svc, prj);
await map.onLoad();
return map;
}
const map1 = await initMap("map1", "sys_zp");
const map2 = await initMap("map2", "sys_hello");
二、视图同步(zoom / center)
直接用内置 vjmap.syncMaps(任意张数,互相同步 center / zoom / bearing / pitch,内部同步时先摘掉监听防递归;返回解绑函数):
const unsync = vjmap.syncMaps(map1, map2); // 也可 vjmap.syncMaps([map1, map2, map3])
// 不再同步时
unsync();
⚠️ 不要两边各挂一个无 guard 的 move → jumpTo:jumpTo 每次都同步 fire move,双向绑定会无限递归直至栈溢出(手写写法见 sync-view)。
三、卷帘 / swipe
内置 vjmap.Compare(两张地图叠放在同一容器内,自带滑块并自动 syncMaps;官方示例 map/mapcompare.js):
const compare = new vjmap.Compare(map1, map2, "#map", { mousemove: false, orientation: "vertical" });
compare.on("slideend", e => console.log("滑块停止"));
compare.setSlider(300); // 程序设置滑块位置(像素)
compare.remove(); // 移除滑块并解除同步
或自建 DOM 遮罩 + 容器滑块方案:
<div id="container" style="position:relative;width:100%;height:100%">
<div id="before" style="position:absolute;inset:0"></div>
<div id="after" style="position:absolute;inset:0;clip-path:inset(0 50% 0 0)"></div>
<div id="handle" style="position:absolute;top:0;bottom:0;left:50%;width:4px;background:#000;cursor:ew-resize"></div>
</div>
const handle = document.getElementById("handle");
const after = document.getElementById("after");
let dragging = false;
handle.onmousedown = () => dragging = true;
window.onmouseup = () => dragging = false;
window.onmousemove = e => {
if (!dragging) return;
const rect = container.getBoundingClientRect();
const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
handle.style.left = ratio * 100 + "%";
after.style.clipPath = `inset(0 ${(1 - ratio) * 100}% 0 0)`;
};
四、CAD 图版本比对(两张相同 mapid 的不同 version)
同上思路:svc.openMap({ mapid, version: "v1" }) 和 { version: "v2" }) 各打一张。
五、服务端图差分析
两张图的像素差可以直接在服务端算:
const diff = await svc.cmdMapDiff({
mapid1, version1, layer1,
mapid2, version2, layer2
});
// diff = { modify: [[pt,pt,..]], new: [...], del: [...], error? }
前端可视化差异:
function drawPolygons(map, points, color) {
if (points.length === 0) return;
points.forEach(p => p.push(p[0]));
const polys = points.map(pts => ({
points: map.toLngLat(pts),
properties: { color }
}));
vjmap.createAntPathAnimateLineLayer(map, polys, {
fillColor1: color,
fillColor2: "#0ff",
lineWidth: 4,
lineOpacity: 0.8
});
}
drawPolygons(map2, diff.modify, "#f00"); // 修改
drawPolygons(map2, diff.new, "#0f0"); // 新增
drawPolygons(map1, diff.del, "#00f"); // 删除
六、基于 mapCompare 命令(实体级比对)
mapCompare 比较两个图版本的实体数据,返回新增 / 删除 / 修改的实体;上面的 cmdMapDiff(mapDiff)才是像素比较(快,但只能定位差异区域、定位不到具体实体)。
const result = await svc.execCommand("mapCompare", {
mapid1: "a", version1: "v1",
mapid2: "b", version2: "v1"
}, "_null", "v1");
七、常见坑
- 同步后抖动 / 栈溢出:手写双向
move → jumpTo会无限递归;用内置vjmap.syncMaps,或加 guard(见 sync-view)。 - 两张图容器大小不一致:zoom 会看似对齐,实际投影像素不同;建议两张图等宽等高。
- 卷帘后 mouseover 穿透:
pointer-events: none在遮罩部分。 mapCompare在大图上超时 → 它逐实体比对数据,图大运算量大;只需定位差异区域时改用像素比较cmdMapDiff。