bounds-and-projection
本文回答:
res.bounds→GeoBounds→GeoProjection/LnglatProjection的链路,以及map.fitMapBounds、map.getGeoBounds、updateMapExtent的用法。
一、数据流
svc.openMap(...) --> res.bounds (string)
|
v
vjmap.GeoBounds.fromString(res.bounds) // 得到 GeoBounds 实例
|
v
new vjmap.GeoProjection(mapExtent) // 几何投影:CAD → 渲染经纬度
|
v
new vjmap.Map({center: prj.toLngLat(...), zoom, ...})
map.attach(svc, prj)
二、GeoBounds 常用方法
const extent = vjmap.GeoBounds.fromString(res.bounds);
// 或者自定义范围:
const extent2 = vjmap.GeoBounds.fromArray([x1, y1, x2, y2]);
extent.center(); // 中心点(CAD 坐标)
extent.width(); // 宽度
extent.height(); // 高度
extent.toPointArray(); // 四个角点
extent.randomPoint(); // 随机取一个点(常用于批量生成演示数据)
extent.randomPoints(minCount, maxCount); // ⚠️ 两参必填:随机取 min~max 个点(d.ts 签名核实)
extent.square(); // 按较长边补齐成正方形(新建图前常用)
extent.toString(); // 序列化为字符串
三、两种投影对象
GeoProjection(CAD 图为底图)
const prj = new vjmap.GeoProjection(mapExtent);
const lngLat = prj.toLngLat([cadX, cadY]);
const cadPt = prj.fromLngLat(lngLat);
prj.getMapExtent(); // 返回 GeoBounds
LnglatProjection(互联网地图为底图)
const prj = new vjmap.LnglatProjection();
// 经纬度与"渲染经纬度"是一一对应:
const lngLat = prj.toLngLat([116.391, 39.907]); // 输出仍是 [116.391, 39.907]
四、Map 上的便捷 API
await map.onLoad();
map.fitMapBounds(); // 缩放至图范围
map.getGeoBounds(); // 全图范围 × initViewScale(缺省 0.7),要 1 倍传 getGeoBounds(1)
map.getGeoBounds(0.4); // 0.4 倍范围(CAD 有效内容大致范围)
map.getGeoBounds(0.6); // 0.6 倍(取点时常用)
// CAD <-> 渲染经纬度(attach 了 prj 后,map 与 prj 等价)
map.toLngLat([cadX, cadY]);
map.fromLngLat([lng, lat]);
// 屏幕像素 <-> 经纬度
map.project(lngLat); // 返回 {x, y}
map.unproject([x, y]); // 返回 LngLat
fitBoundsvsfitMapBounds:fitBounds(bounds)只吃 LngLatLike(经纬度 bounds),把 CADGeoBounds直接传给它会抛`LngLatLike` argument must be specified as ...类型错误。CAD 坐标系的 bounds 一律用fitMapBounds(GeoBounds | number),内部会自动toLngLat转换再调fitBounds。
五、精确获取有效图形范围
res.bounds 往往是整张 drawing 的扩展范围,可能包含空白。两种方式取"有实体的区域":
// 方式 1:范围准确、速度稍慢(发一次后端请求;Memory 方式打开的图恒返回 null,须判空回退方式 2)
const dataBounds = await svc.cmdGetDrawBounds();
// 方式 2:范围大致准确、速度快(本地缓存)
const drawBounds = svc.currentMapParam().drawBounds;
六、动态改变图范围
生成图形过程中范围会扩大(如 vjmapext 绘图),需要让 prj + map 重新计算:
map.updateMapExtent(newBoundsString); // 内部会重建 GeoProjection
map.fitMapBounds();
updateMapExtent 常在 "前端数据 -> 后端 cmdCreateEntitiesGeomData -> 返回新 mapBounds" 的回流过程中调用。
七、常见坑
- 忘了把
res.bounds传给GeoProjection→ 后续toLngLat全是 NaN。 - 直接把
res.bounds作为数组传(它是字符串"[x1,y1,x2,y2]")→ 用fromString,或直接传构造函数也支持字符串。 map.getGeoBounds()当作 CAD 真实范围 → 它返回的是全图范围 ×initViewScale(缺省 0.7);要真实绘图范围用svc.cmdGetDrawBounds(Memory 图返回 null,回退currentMapParam().drawBounds)。- 互联网底图用了
GeoProjection→ 应该用LnglatProjection。否则坐标会错位。
八、GeoBounds 工具方法与同名函数辨析
vjmap.getGeoBounds(data)(顶层函数)与 map.getGeoBounds(ratio)(地图方法)同名不同物:
vjmap.getGeoBounds(data):计算 GeoJSON / 点数组 / JSON 字符串的地理范围 →GeoBounds。字符串入参走JSON.parse,纯文本"x,y,x,y"抛Invalid input coordinate——文本格式用GeoBounds.fromString("x,y,x,y")(自动补[])。map.getGeoBounds(ratio?):全图范围 × ratio(见上文)。
GeoBounds 的语义:
| 方法 | 行为 |
|---|---|
closestPoint(pt, includeInterior?) |
界外点→最近边界点;内部点默认返回自身,includeInterior=true 也返回自身(不投影到边) |
square() |
返回包容正方形 |
scale(r) |
以中心缩放,scale(2) → 各边外扩一倍 |
translate(dx,dy) |
原地平移(改 this,不 clone) |
isIntersect(b) / isContains(b) / contains(pt) |
边界相交/包含关系 |
randomPoint() / randomGeoJsonPointCollection(n) / LineCollection / PolygonCollection |
界内随机生成 GeoJSON |