坐标转换
坐标转换原因
CAD二维图形世界坐标系是笛卡尔直角坐标系,图形的范围是指绘制出的图形所占的范围。
如用户在坐标 [1000,1000]和[2000,2000]绘制了一条直线,那么图形的范围 [左上角x, 左下角y,右上角x,右下角y] 就是[1000,1000,2000,2000]。
但有时候用户制图时坐标用的是大地坐标,坐标数值会比较大,如在坐标[600000000,30000000],[620000000,50000000]绘制了一条直线,那图形的范围就是[600000000,30000000,700000000,70000000]。
同时在一些基于互联网地图开发时,经纬度数值都很小。
为了解决大坐标渲染的问题和统一互联网底图和CAD底图坐标的需要,我们渲染底层采用了经纬度进行渲染,绘制时需要把实际坐标转换成经纬度进行绘制。经纬度范围为 经度范围为(-180,180), 纬度范围为(-85,85)
坐标转换关系
| 名称 | 说明 |
|---|---|
| 几何坐标 | CAD图中实际的坐标 |
| 经纬度坐标 | 经纬度坐标(在渲染时必须要转成经纬度坐标) |
| 墨卡托坐标 | 投影坐标 |
如果底图为CAD图为坐标系时
转换关系如下图
通过调用 toLngLat 把CAD图转成渲染用的经纬度坐标, 如会把 [CAD范围左下角x1,左下角y1]转成 经纬度[-180,-85]。
所以这时候所转成的经纬度只是底层渲染用的经纬度坐标数值,不是实际互联网地图中所用到的经纬度坐标,这是两个不同的概念。
如果底图是互联网地图如天地图时
转换关系如下图
这时候因为互联网地图的范围和渲染用到的经纬度范围完全一样, 所以这时候所转成的经纬度就是实际互联网地图中所用到的经纬度坐标,这是同一个概念。
有地理坐标系的CAD图与互联网地图经纬度转换
如果CAD图有地理坐标系系,如是2000坐标系时,需要转换为互联网地图经纬度时。这时候需要用第三方坐标转换库如proj4进行转换。转换原理如下图:
在CAD中使用UCS坐标绘制的图坐标与WCS坐标转换
在云端管理平台打开图后,右侧属性栏中 ucsorg 的值不为零的话表示有ucs,或者用map的hasUcs方法来判断是否有ucs坐标。
在唯杰地图获取的是CAD的WCS坐标。
WCS坐标转UCS坐标用 map.wcsToUcs
UCS坐标转WCS坐标用 map.ucsToWcs
总结
总结1
唯杰地图渲染用的经纬度坐标只有在底图为互联网底图(如天地图、高德地图)时,才是真正的经纬度坐标。
否则为CAD底图时,经纬度坐标值只是一个用于底层渲染的坐标值,没有实际的地理意义,只是为了解决大坐标渲染的问题和统一互联网底图和CAD底图坐标的需要。
如果CAD底图是有真正地理坐标系,如北京54坐标、西安80坐标、2000坐标系时,需要转换成wgs84经纬度时,可以用SDK提供的vjmap.transform.convert方法或者自己用第三方库如proj4进行转换,具体可以参考演示示例中的互联网地图
或者使用四参数对不同的坐标系进行坐标转换如何把另外一个坐标系的数据叠加到当前cad图上
总结2
如果map关联了投影对象 map.attach(svc, prj) 则
map.toLngLat 等价于 prj.toLngLat
map.fromLngLat 等价于 prj.fromLngLat
CAD实际几何坐标 转 底层渲染的经纬度坐标 一律请调用 toLngLat (与底层绘制有关的,都得调用toLngLat转经纬度)
由底层渲染的经纬度坐标 转 CAD实际几何坐标 一律请调用 fromLngLat (得到CAD图上面的实际几何坐标,都得调用fromLngLat转CAD实际几何坐标)
总结3
因为CAD图坐标转渲染经纬度时,转换过程是非线性的,在进行几何计算如相交、插值等请使用 CAD几何坐标, 渲染时再转成 经纬度坐标
总结4
因为CAD图坐标转渲染经纬度时,和CAD当前绘制范围有关。绘图范围变化所转换的渲染经纬度的值也会变化。所以如果要保存坐标至后台下次打开再加载显示的时候,一定要保存CAD图坐标, 不要保存经纬度。因为如果CAD图版本更新了,如果绘图范围有变化的话,加载经纬度坐标就会和原图对应不上了。而加载CAD图坐标能完全对应上。
例如 自定义高亮区域 这个示例
当点击保存数据时会获取绘图对象的渲染经纬度,通过调用 map.fromLngLat 转为CAD几何坐标再保存至后台
// 保存数据至后台过程
// 获取获取绘图对象的渲染经纬度
let lngLatJson = draw.getAll();
// 渲染经纬度转成CAD几何坐标
let cadJson = map.fromLngLat(lngLatJson);
而在加载后台数据显示至地图中,则需要获取 map.toLngLat 把CAD几何坐标转为绘图对象的渲染经纬度进行绘制。
// 前端加载后台数据进行显示
let cadJson = {......}; // 后台获取至的CAD几何坐标
// CAD几何坐标转成渲染经纬度
let lngLatJson = map.toLngLat(cadJson);
// 设置绘图对象坐标
draw.set(lngLatJson)
如何对CAD图进行坐标转换处理
1、获取要转换的四参数
在CAD图上拾取三组及以上的对应点坐标(公共点尽量分布在图的四周)
let pts = [
{
cadX: 1, // CAD图上对应点一的坐标X
cadY: 1, // CAD图上对应点一的坐标Y
destX: 101, // 对应点一目标图上的坐标x
destY: 101, // 对应点一目标图上的坐标y
},
{
cadX: 1, // CAD图上对应点二的坐标X
cadY: 5, // CAD图上对应点二的坐标Y
destX: 101, // 对应点二目标图上的坐标x
destY: 105, // 对应点二目标图上的坐标y
},
{
cadX: 5, // CAD图上对应点三的坐标X
cadY: 5, // CAD图上对应点二的坐标Y
destX: 105, // 对应点三目标图上的坐标x
destY: 105, // 对应点三目标图上的坐标y
}
]
let cadPoints = [];
let destPoints = [];
for(let n = 0; n < pts.length; n++) {
cadPoints.push(vjmap.geoPoint([pts[n].cadX, pts[n].cadY]))
destPoints.push(vjmap.geoPoint([pts[n].destX, pts[n].destY]))
}
// 通过坐标参数求出四参数
let fourparam = vjmap.coordTransfromGetFourParamter(cadPoints, destPoints, false);
console.log([fourparam.dx, fourparam.dy, fourparam.scale, fourparam.rotate])
// 计算误差
for(let i = 0; i< cadPoints.length; i++) {
const destPt = vjmap.coordTransfromByFourParamter(cadPoints[i], fourparam);
console.log("原始点:" + cadPoints[i], "计算结果:" + destPt, "误差:" + destPoints[i].distanceTo(destPt))
}
例如把ucs的图转成wcs坐标的图
用如下代码可自动获取原图wcs到ucs转换的四参数
let cadPoints = [];
let destPoints = []
// 获取地图范围的四个点算出对应的ucs坐标,做为公共点
let mapExtent = map.getMapExtent()
let pts = mapExtent.toPointArray()
for (let n = 0; n < pts.length; n++) {
let wcs = vjmap.geoPoint(pts[n]);
let ucs = map.wcsToUcs(wcs);
cadPoints.push(wcs)
destPoints.push(ucs);
}
// 通过坐标参数求出四参数
let fourparam = vjmap.coordTransfromGetFourParamter(cadPoints, destPoints, false);
console.log([fourparam.dx, fourparam.dy, fourparam.scale, fourparam.rotate])
2、在云端管理平台里面打开此图,点击更多功能里面的在线处理图形
3、在在线处理图形中直接输入四参数格式输入上面获取到fourparam.dx, fourparam.dy, fourparam.scale, fourparam.rotate值
4、点击确定,选择新建图形。即可。
坐标控件
CAD为底图坐标系
// --打开地图[栅格瓦片]--以栅格瓦片方式打开已上传的CAD的DWG格式的图
// js代码
// 新建地图服务对象,传入服务地址和token
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// 打开地图
let res = await svc.openMap({
mapid: env.exampleMapId, // 地图ID,(请确保此ID已存在,可上传新图形新建ID)
mapopenway: vjmap.MapOpenWay.GeomRender, // 以几何数据渲染方式打开
style: vjmap.openMapDarkStyle() // div为深色背景颜色时,这里也传深色背景样式
})
if (res.error) {
message.error(res.error)
}
// 获取地图的范围
let mapExtent = vjmap.GeoBounds.fromString(res.bounds);
// 建立坐标系
let prj = new vjmap.GeoProjection(mapExtent);
// 新建地图对象
let map = new vjmap.Map({
container: 'map', // container ID
style: svc.rasterStyle(), // 栅格瓦片样式
center: prj.toLngLat(mapExtent.center()), // 中心点
zoom: 2,
renderWorldCopies: false
});
// 地图关联服务对象和坐标系
map.attach(svc, prj);
// 使地图全部可见
map.fitMapBounds();
await map.onLoad();
let mousePositionControl = new vjmap.MousePositionControl({
showLatLng: true
});
map.addControl(mousePositionControl, "top-left");互联网地图为底图坐标系
// --天地图地图--
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// 根据地图范围建立经纬度投影坐标系
let prj = new vjmap.LnglatProjection();
// 地图对象
let map = new vjmap.Map({
container: 'map', // DIV容器ID
style: {
version: svc.styleVersion(),
glyphs: svc.glyphsUrl(),
sources: {
tdt1: {
type: 'raster',
// 影像
//tiles: ["https://t3.tianditu.gov.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}&tk=7baeffb96bf61861b302d0f963cfda66"],
// 道路
tiles: ["https://t3.tianditu.gov.cn/DataServer?T=vec_w&X={x}&Y={y}&L={z}&tk=7baeffb96bf61861b302d0f963cfda66"],
},
tdt2: {
type: 'raster',
tiles: ["https://t3.tianditu.gov.cn/DataServer?T=cva_w&X={x}&Y={y}&L={z}&tk=7baeffb96bf61861b302d0f963cfda66"],
}
},
layers: [{
id: 'tdt1',
type: 'raster',
source: 'tdt1',
},{
id: 'tdt2',
type: 'raster',
source: 'tdt2',
}]
},
center: prj.toLngLat([116.3912, 39.9073]),
zoom: 10,
pitch: 0,
renderWorldCopies: false // 不显示多屏地图
});
// 关联服务对象和投影对象
map.attach(svc, prj);
// 根据地图本身范围缩放地图至全图显示
//map.fitMapBounds();
await map.onLoad();
let mousePositionControl = new vjmap.MousePositionControl({
showLatLng: true
});
map.addControl(mousePositionControl, "top-left");