Cesium中高德、百度地图的偏移矫正
一、坐标系介绍
不同地图的坐标系
(1)WGS84(cesium GPS)
即GPS原始坐标系,也是当今三维开发中最普遍使用的坐标系。
(2)GCJ-02:火星坐标系(高德)
由国测局发布的加密坐标系,是国内互联网地图最广泛使用的坐标系,包括高德、腾讯。
(3)BD-09(百度地图)
百度地图所采用的坐标系,即在火星坐标系上进一步偏移得到,与高德地图相差不大。
(4)CGCS2000(天地图)
又称2000国家大地坐标系,由西安80坐标系发展而来,一般使用高斯克吕格投影,用于国内土地调查等坐标参考系。天地图采用此坐标系.
二、实现效果
底图为Cesium中默认的必应地图,上面是高德地图的注记地图。
二、实现方法
(1)添加坐标转换的js文件 CoordTransform.js;
const BD_FACTOR = (3.14159265358979324 * 3000.0) / 180.0; // 百度坐标系的修正因子
const PI = 3.1415926535897932384626; // 圆周率
const RADIUS = 6378245.0; // 地球半径
const EE = 0.00669342162296594323; // 椭球的偏心率平方
class CoordTransform {
/**
* BD-09(百度坐标系) To GCJ-02(火星坐标系) 的转换方法
* @param lng 经度
* @param lat 纬度
* @returns {number[]} 返回经纬度数组
*/
static BD09ToGCJ02(lng, lat) {
let x = +lng - 0.0065; // 经度修正
let y = +lat - 0.006; // 纬度修正
let z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * BD_FACTOR); // z 值修正
let theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * BD_FACTOR); // theta 值修正
let gg_lng = z * Math.cos(theta); // 修正后的经度
let gg_lat = z * Math.sin(theta); // 修正后的纬度
return [gg_lng, gg_lat];
}
/**
* GCJ-02(火星坐标系) To BD-09(百度坐标系) 的转换方法
* @param lng 经度
* @param lat 纬度
* @returns {number[]} 返回经纬度数组
*/
static GCJ02ToBD09(lng, lat) {
lat = +lat;
lng = +lng;
let z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * BD_FACTOR); // z 值修正
let theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * BD_FACTOR); // theta 值修正
let bd_lng = z * Math.cos(theta) + 0.0065; // 修正后的经度
let bd_lat = z * Math.sin(theta) + 0.006; // 修正后的纬度
return [bd_lng, bd_lat];
}
/**
* WGS-84(世界大地坐标系) To GCJ-02(火星坐标系) 的转换方法
* @param lng 经度
* @param lat 纬度
* @returns {number[]} 返回经纬度数组
*/
static WGS84ToGCJ02(lng, lat) {
lat = +lat;
lng = +lng;
if (this.out_of_china(lng, lat)) { // 如果在中国境内
return [lng, lat];
} else {
let d = this.delta(lng, lat); // 计算经纬度偏移值
return [lng + d[0], lat + d[1]]; // 返回修正后的经纬度
}
}
/**
* GCJ-02(火星坐标系) To WGS-84(世界大地坐标系) 的转换方法
* @param lng 经度
* @param lat 纬度
* @returns {number[]} 返回经纬度数组
*/
static GCJ02ToWGS84(lng, lat) {
lat = +lat;
lng = +lng;
if (this.out_of_china(lng, lat)) { // 如果在中国境内
return [lng, lat];
} else {
let d = this.delta(lng, lat); // 计算经纬度偏移值
let mgLng = lng + d[0];
let mgLat = lat + d[1];
return [lng * 2 - mgLng, lat * 2 - mgLat]; // 返回修正后的经纬度
}
}
/**
* 计算经纬度偏移值的方法
* @param lng 经度
* @param lat 纬度
* @returns {number[]} 返回经纬度偏移值数组
*/
static delta(lng, lat) {
let dLng = this.transformLng(lng - 105, lat - 35); // 经度偏移值
let dLat = this.transformLat(lng - 105, lat - 35); // 纬度偏移值
const radLat = (lat / 180) * PI;
let magic = Math.sin(radLat);
magic = 1 - EE * magic * magic;
const sqrtMagic = Math.sqrt(magic);
dLng = (dLng * 180) / ((RADIUS / sqrtMagic) * Math.cos(radLat) * PI);
dLat = (dLat * 180) / (((RADIUS * (1 - EE)) / (magic * sqrtMagic)) * PI);
return [dLng, dLat];
}
static transformLng(lng, lat) {
lat = +lat;
lng = +lng;
let ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
ret += ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0) / 3.0;
ret += ((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) * 2.0) / 3.0;
ret += ((150.0 * Math.sin((lng / 12.0) * PI) + 300.0 * Math.sin((lng / 30.0) * PI)) * 2.0) / 3.0;
return ret;
}
/**
*
* @param lng
* @param lat
* @returns {number}
*/
static transformLat(lng, lat) {
lat = +lat;
lng = +lng;
let ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
ret += ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0) / 3.0;
ret += ((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) * 2.0) / 3.0;
ret += ((160.0 * Math.sin((lat / 12.0) * PI) + 320 * Math.sin((lat * PI) / 30.0)) * 2.0) / 3.0;
return ret;
}
/**
* 判断是否在国内。不在国内不做偏移
* @param lng
* @param lat
* @returns {boolean}
*/
static out_of_china(lng,lat) {
lat = +lat;
lng = +lng;
return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55);
}
}
export default CoordTransform;
(2)重写 WebMercatorProjection 的 project 方法,实现从 WGS84 坐标系转换到 GCJ02 坐标系,再进行墨卡托投影,新建AmapMercatorTilingScheme.js
import { WebMercatorProjection, WebMercatorTilingScheme, Math, Cartographic, Cartesian2 } from 'cesium'; // 导入所需的 Cesium 模块
import CoordTransform from './CoordTransform'; // 导入坐标转换工具类
class AmapMercatorTilingScheme extends WebMercatorTilingScheme {
constructor() {
super();
let projection = new WebMercatorProjection(); // 创建 WebMercatorProjection 对象用于进行投影转换
// 重写 WebMercatorProjection 的 project 方法,实现从 WGS84 坐标系转换到 GCJ02 坐标系,再进行墨卡托投影
this._projection.project = function (cartographic, result) {
// 调用坐标转换工具类的 WGS84ToGCJ02 方法将经纬度转换为 GCJ02 坐标系下的经纬度
result = CoordTransform.WGS84ToGCJ02(
Math.toDegrees(cartographic.longitude),
Math.toDegrees(cartographic.latitude)
);
// 将转换后的 GCJ02 坐标系下的经纬度转换为弧度,并进行墨卡托投影
result = projection.project(new Cartographic(Math.toRadians(result[0]), Math.toRadians(result[1])));
return new Cartesian2(result.x, result.y); // 返回墨卡托投影后的结果
};
// 重写 WebMercatorProjection 的 unproject 方法,实现从墨卡托投影转换回 WGS84 坐标系
this._projection.unproject = function (cartesian, result) {
// 将墨卡托投影的坐标转换为经纬度
let cartographic = projection.unproject(cartesian);
// 调用坐标转换工具类的 GCJ02ToWGS84 方法将 GCJ02 坐标系下的经纬度转换为 WGS84 坐标系下的经纬度
result = CoordTransform.GCJ02ToWGS84(
Math.toDegrees(cartographic.longitude),
Math.toDegrees(cartographic.latitude)
);
return new Cartographic(Math.toRadians(result[0]), Math.toRadians(result[1])); // 返回 WGS84 坐标系下的结果
};
}
}
export default AmapMercatorTilingScheme; // 导出自定义的高德地图墨卡托投影切片方案
(3)在引入高德地图的地方
const tdtLayer = new Cesium.UrlTemplateImageryProvider({
url: "http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
minimumLevel: 3,
maximumLevel: 18,
tilingScheme: new AmapMercatorTilingScheme(),//坐标矫正
})
viewer.imageryLayers.addImageryProvider(tdtLayer);
三、总结
第一次写还有些生疏,以后也会多多记录,此篇博客也是借鉴前人的,并加以注释修改,地址如下:https://juejin.cn/post/7049681493685174286
百度、天地图转换方法也类似。