唯杰地图 logo唯杰地图
VJMAP
EN

bounds-and-projection

vjmap/references/open-map/bounds-and-projection.md 5.3 KB123 行
原文

本文回答: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

fitBounds vs fitMapBounds:fitBounds(bounds) 只吃 LngLatLike(经纬度 bounds),把 CAD GeoBounds 直接传给它会抛 `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" 的回流过程中调用。

七、常见坑

  1. 忘了把 res.bounds 传给 GeoProjection → 后续 toLngLat 全是 NaN。
  2. 直接把 res.bounds 作为数组传(它是字符串 "[x1,y1,x2,y2]")→ 用 fromString,或直接传构造函数也支持字符串。
  3. map.getGeoBounds() 当作 CAD 真实范围 → 它返回的是全图范围 × initViewScale(缺省 0.7);要真实绘图范围用 svc.cmdGetDrawBounds(Memory 图返回 null,回退 currentMapParam().drawBounds)。
  4. 互联网底图用了 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