iModel 地理坐标系统
坐标是用来确定具体的位置,如屏幕坐标、象限坐标等。地理坐标顾名思义是用来表达真实世界中的具体位置信息,例如 x, y, z 值能确定真实世界中的唯一位置。
文本从地理坐标划分内容开始,介绍地理位置在 iTwin 中的应用,描述不同坐标类型在 iTwin 中的表达,最后给出常用的坐标转换关键代码,供大家参考学习。
坐标系统类型
通常将地理坐标系统分为以下两大类
1. 全局坐标系或球坐标系
也称为 地理坐标系统(Geographic Coordinate System,GCS),生活中常用的 经纬度
坐标即属于此类坐标,即指在球体上的具体坐标值。
经度和纬度是从地心到球体表面上某点的测量角,测量角度的单位通常以度或百分度。下图将地球显示为具有经度和纬度值的地球。
由于地球是一个不规则的球体,且各地的引力值(G)也不同,人们为了方便测量、提高测量的精确值往往采用不同的椭球体完成地球的拟合。即采用不同的椭球体形状,改变椭球体参数实现地球拟合以满足自身需求。
常用的地理坐标主要有:
WGS84 -- EPSG:4326
世界大地测量系统(World Geodetic System, WGS)是一种用于地图学、大地测量学和導航(包括全球定位系统)的大地测量系统标准。WGS 的最新版本为 WGS 84(也称作 WGS 1984、EPSG:4326),1984 年定义、最后修订于 2004 年。
该坐标系统随着全球定位系统(GPS)的发展而广泛运用在各行业的定位与导航系统中。因此,人们也通常将此坐标系统作为坐标转换的中介参考。
CGCS2000 -- EPSG:4490
2000 国家大地坐标系,是我国当前最新的国家大地坐标系(China Geodetic Coordinate System 2000, CGCS2000)。
按照国务院要求,到 2016 年我国将完成现行国家大地坐标系向 CGCS2000 的过渡,2018 年将停止使用 1954 北京坐标系和 1980 西安坐标系。
2. 投影坐标系
投影指将三维球体投射到二维笛卡尔坐标的过程,而投影坐标是指在二维笛卡尔坐标系统的坐标值。在投影坐标系中,通过格网上的 x,y 坐标来标识位置,其原点位于格网中心。每个位置均具有两个值,这两个值是相对于该中心位置的坐标。一个指定其水平位置,另一个指定其垂直位置。这两个值称为 x 坐标和 y 坐标。采用此标记法,原点坐标是 x = 0 和 y = 0。
在将球体投影到二维平面时,由于弯曲的地球表面与平面不是等距的,因此,保留形状不可避免地会导致比例尺的变化,从而导致区域的非比例表现。
我国 CGCS2000 采用的投影类型选择的是 高斯-克吕格 投影。为减少投影变形,高斯-克吕格投影分为 3° 带和 6° 带投影。
MicroStation 软件中默认采用 EPSG:3152 坐标系统,坐标适用 瑞典区域
范围,非此区域的模型选择该坐标可能出现模型打开错误、模型丢失等异常情况。
常见三维笛卡尔坐标系
地心地固坐标系 (Earth-Centered, Earth-Fixed,ECEF) -- 简称地心坐标系
ECEF 坐标系是以地心为原点的 3D 右手笛卡尔坐标系,原点 O (0,0,0)为地球几何中心(地球质心),z 轴与地轴平行指向北极点,x 轴指向本初子午线与赤道的交点,y 轴垂直于 xOz 平面(即东经 90 度与赤道的交点)构成右手坐标系。有关以地心坐标系表示的目标位置 T 的示例,请参见下图。
当地东北天坐标系(Local east, north, up, ENU)
是定义在地表正切平面的局部坐标系。如下图所示,它以站心点的纬线方向(指东)为 X 轴,以站心点的经线方向(指北)为 Y 轴,以站心点的法线(指上)为 Z 轴。
ECEF 与 ENU 坐标相互转换
- ECEF 转 ENU 由于 ENU 坐标系定义在局部坐标系下,所以转换前需要知道 ENU 局部坐标系的原点的 ECEF 坐标(Xr,Yr,Zr),以及原点的经度 ϕ 和纬度 λ。计算式为:
- ENU 转 ECEF 是 ECEF 转 ENU 的逆方法,即二者矩阵互为逆矩阵。
iTwin 地理坐标
iTwin 中的地理坐标是关于将笛卡尔点坐标转换为地球上的制图坐标。转换的方法依据原始数据可以分为三种:
- 未知
- 线性
- 投影
未知
当原始数据(模型)没有包含地理位置信息时,则无法在 iTwin 中进行地理坐标的标识与转换。可通过 iModelConnection.isGeoLocated 方法进行判断。
线性转换
对于厂房、建筑物、变电站甚至校园等结构的 iModel,z 轴通常表示高于底层或某些其他基点的高度。那么,Z=0 是一个无限平面,不考虑地球的曲率(即 Z 方向的直线不一定指向地球的中心)。显然,这样的 iModel 只能包含一小部分区域,因此地球的曲率实际上并不重要——通常只有几公里。从笛卡尔{x,y,z}坐标到制图{lat,long,height}坐标的转换是通过一个单点的线性
变换完成的。源应用程序(如 Bentley 的 Open Building Designer、OpenPlant 和 Revit)使用线性地理定位创建 iModel。
投影转换
对于根据地图数据创建的 iModel,z 轴通常表示高于地球表面的某种形式的高度(例如,海平面、地形、椭球体等),因此 iModel 中的 z=0 平面投影地球的椭球体,z 方向的直线始终指向地球的中心(椭圆质心)。笛卡尔{x,y,z}值通过非线性
地图投影转换为制图{lat,long,height}坐标,其描述作为地理坐标系存储在 iModel 中。源应用程序,如 Bentley Map、OpenRoads、Civil 3D、GIS 应用程序等,使用投影地理位置创建 iModel。
常用坐标转换代码示例
1. 经纬度度分秒转为十进制
// 以 120°30'36.6" 为例
const degree = 120;
const minutes = 30;
const seconds = 36.6;
const decimalDegree = degree + minutes / 60 + seconds / 3600;
console.log("十进制结果为: ", decimalDegree);
2. iModel 在地面投影的项目范围
/** get a 5 point shape of Cartographic points that encloses the project on the ground plane. */
public async convertExtentsToCartographicShape(iModel: IModelConnection): Promise<Cartographic[]> {
const shape: Cartographic[] = [];
// convert extents to an 8 point array
const pts = Frustum.fromRange(iModel.projectExtents).points;
// the first 4 points are on the front plane
for (let i = 0; i < 4; ++i) {
shape[i] = await iModel.spatialToCartographic(pts[i]);
shape[i].height = 0; // set at ground level
}
shape[4] = shape[0]; // close shape
return shape;
}
3. iModel 的空间范围
/** get the low and high Cartographic range of this iModel */
public async getProjectMinMaxCartographic(iModel: IModelConnection) {
let low: Cartographic | undefined;
let high: Cartographic | undefined;
const pts = Frustum.fromRange(iModel.projectExtents).points;
for (const pt of pts) {
const geoPt = await iModel.spatialToCartographic(pt);
if (undefined === low || undefined === high) {
low = geoPt;
high = geoPt.clone();
continue;
}
low.latitude = Math.min(low.latitude, geoPt.latitude);
low.longitude = Math.min(low.longitude, geoPt.longitude);
low.height = Math.min(low.height, geoPt.height);
high.latitude = Math.max(high.latitude, geoPt.latitude);
high.longitude = Math.max(high.longitude, geoPt.longitude);
high.height = Math.max(high.height, geoPt.height);
}
return { min: low!, max: high! };
}
4. 获取当前相机的经纬度坐标
const cameraPoint = (vp!.view as ViewState3d).getEyePoint();
const cartLocation = await iModelConnection?.spatialToCartographic(cameraPoint);
// latitude of camera
console.log("latitude: ", cartLocation!.latitudeDegrees);
// longitude of camera
console.log("longitude: ", cartLocation!.longitudeDegrees);
5. 经纬度转世界坐标系
// 与参考椭球无关
// long:106.17960542126575 lat: 29.244371398709067 height: 258.2120626227443
const cart = Cartographic.fromRadians(
Angle.degreesToRadians(106.17960542126575),
Angle.degreesToRadians(29.244371398709067),
258.2120626227443
);
const ecef = cart.toEcef();
// ECEF result: x: -1552043.45826556 y: 5349274.991556412 z: 3097688.8570909603
console.log("ecef 坐标: ", ecef);
6. 世界坐标转经纬度
// x y z 世界坐标
const x = -1552153.962272866;
const y = 5349655.839715691;
const z = 3097910.8913075076;
const origin = new Point3d(x, y, z);
const cart = Cartographic.fromEcef(origin);
// Result: longitude: 106.17960546453158, latitude: 29.24437142620322 height: 712.6989097656179
console.log(
"经纬度坐标: ",
cart?.longitudeDegrees,
cart?.latitude,
cart?.height
);
7. 经纬度转当前模型空间坐标
// 输入经纬度,以常见的 WGS84 为例
const center = Cartographic.fromRadians(
Angle.degreesToRadians(106.17960542126573),
Angle.degreesToRadians(29.244371398681867),
258.21311713530514
);
// iModel 的坐标为 EPSG:3857, 不同的参考系算出的位置是不一样的
const spatial = await vp.iModel.cartographicToSpatial(center);
// Result: x: 11819859.608126009, y: 3406785.957360854, z: 258.21311713530514
console.log("iModel 空间坐标: ", spatial);
8. 从 iTwin 获取的坐标转换
// 坐标转换的结果依赖于当前 iTwin 采用的参考系
// 例如:鼠标拾取的坐标为 Point3d (x, y, z)
const iTwinPoint = {
x: 12685971.712373149,
y: 2576926.867955715,
z: 12.96646845936883,
};
// ecef 坐标
const ecef = iModelConnection.spatialToEcef(iTwinPoint);
// 经纬度坐标
const cartLocation = await iModelConnection.spatialToCartographic(iTwinPoint);
9. Cesium 坐标到 iTwin 平台
// 由于 Cesium 默认采用 WGS84 坐标,因此在进行坐标转换时需考虑 iModel 的坐标系统是否为采用 WGS84 椭球体
// 可查看 https://cesium.com/learn/cesiumjs/ref-doc/GeographicProjection.html
// 常见的 WGS84 坐标有:EPSG4326, EPSG3857, EPSG900913, WGS84 UTM 等
// 在进行坐标数据互操作时,推荐采用 经纬度 数据作为中间交换数据
/*--------- iTwin 坐标导出 ------------- */
// (1) 从 iTwin 获取坐标,为 iTwin 空间坐标
const spatial = { x: 0.1, y: 0.1, z: 0.1 };
// (2) 通过调用以下方法将空间坐标转换为经纬度
const exchange = await iModelConnection.spatialToCartographic(spatial);
/*--------- Cesium 坐标导出 ------------- */
// (1) 从 Cesium 获取坐标,通常为空间坐标
var cartesian3 = new Cesium.Cartesian3(0.1, 0.1, 0.1);
// (2) 通过调用以下方法将空间坐标转换为经纬度
var cartLocation =
viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian3);