You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
492 lines
14 KiB
492 lines
14 KiB
11 months ago
|
/*
|
||
|
* @Author: hisense.wuhongjian
|
||
|
* @Date: 2021-03-09 16:59:18
|
||
|
* @LastEditors: hisense.wuhongjian
|
||
|
* @LastEditTime: 2022-03-14 16:37:49
|
||
|
* @Description: 坐标系转换通用方法 提供百度坐标(BD-09)、国测局坐标(火星坐标,GCJ-02)和WGS84坐标系之间的转换
|
||
|
*/
|
||
|
|
||
|
/** ******************************************************变量区*********************************************************/
|
||
|
|
||
|
const xPI = (3.14159265358979324 * 3000.0) / 180.0
|
||
|
const PI = 3.1415926535897932384626
|
||
|
const a = 6378245.0
|
||
|
const ee = 0.00669342162296594323
|
||
|
|
||
|
// let EARTHRADIUS = 6370996.81
|
||
|
const MCBAND = [12890594.86, 8362377.87, 5591021, 3481989.83, 1678043.12, 0]
|
||
|
const LLBAND = [75, 60, 45, 30, 15, 0]
|
||
|
|
||
|
const MC2LL = [
|
||
|
[
|
||
|
1.410526172116255e-8, 0.00000898305509648872, -1.9939833816331,
|
||
|
200.9824383106796, -187.2403703815547, 91.6087516669843, -23.38765649603339,
|
||
|
2.57121317296198, -0.03801003308653, 17337981.2,
|
||
|
],
|
||
|
[
|
||
|
-7.435856389565537e-9, 0.000008983055097726239, -0.78625201886289,
|
||
|
96.32687599759846, -1.85204757529826, -59.36935905485877, 47.40033549296737,
|
||
|
-16.50741931063887, 2.28786674699375, 10260144.86,
|
||
|
],
|
||
|
[
|
||
|
-3.030883460898826e-8, 0.00000898305509983578, 0.30071316287616,
|
||
|
59.74293618442277, 7.357984074871, -25.38371002664745, 13.45380521110908,
|
||
|
-3.29883767235584, 0.32710905363475, 6856817.37,
|
||
|
],
|
||
|
[
|
||
|
-1.981981304930552e-8, 0.000008983055099779535, 0.03278182852591,
|
||
|
40.31678527705744, 0.65659298677277, -4.44255534477492, 0.85341911805263,
|
||
|
0.12923347998204, -0.04625736007561, 4482777.06,
|
||
|
],
|
||
|
[
|
||
|
3.09191371068437e-9, 0.000008983055096812155, 0.00006995724062,
|
||
|
23.10934304144901, -0.00023663490511, -0.6321817810242, -0.00663494467273,
|
||
|
0.03430082397953, -0.00466043876332, 2555164.4,
|
||
|
],
|
||
|
[
|
||
|
2.890871144776878e-9, 0.000008983055095805407, -3.068298e-8,
|
||
|
7.47137025468032, -0.00000353937994, -0.02145144861037, -0.00001234426596,
|
||
|
0.00010322952773, -0.00000323890364, 826088.5,
|
||
|
],
|
||
|
]
|
||
|
|
||
|
const LL2MC = [
|
||
|
[
|
||
|
-0.0015702102444, 111320.7020616939, 1704480524535203, -10338987376042340,
|
||
|
26112667856603880, -35149669176653700, 26595700718403920,
|
||
|
-10725012454188240, 1800819912950474, 82.5,
|
||
|
],
|
||
|
[
|
||
|
0.0008277824516172526, 111320.7020463578, 647795574.6671607,
|
||
|
-4082003173.641316, 10774905663.51142, -15171875531.51559,
|
||
|
12053065338.62167, -5124939663.577472, 913311935.9512032, 67.5,
|
||
|
],
|
||
|
[
|
||
|
0.00337398766765, 111320.7020202162, 4481351.045890365, -23393751.19931662,
|
||
|
79682215.47186455, -115964993.2797253, 97236711.15602145,
|
||
|
-43661946.33752821, 8477230.501135234, 52.5,
|
||
|
],
|
||
|
[
|
||
|
0.00220636496208, 111320.7020209128, 51751.86112841131, 3796837.749470245,
|
||
|
992013.7397791013, -1221952.21711287, 1340652.697009075, -620943.6990984312,
|
||
|
144416.9293806241, 37.5,
|
||
|
],
|
||
|
[
|
||
|
-0.0003441963504368392, 111320.7020576856, 278.2353980772752,
|
||
|
2485758.690035394, 6070.750963243378, 54821.18345352118, 9540.606633304236,
|
||
|
-2710.55326746645, 1405.483844121726, 22.5,
|
||
|
],
|
||
|
[
|
||
|
-0.0003218135878613132, 111320.7020701615, 0.00369383431289,
|
||
|
823725.6402795718, 0.46104986909093, 2351.343141331292, 1.58060784298199,
|
||
|
8.77738589078284, 0.37238884252424, 7.45,
|
||
|
],
|
||
|
]
|
||
|
|
||
|
/** ******************************************************方法区*********************************************************/
|
||
|
|
||
|
/**
|
||
|
* @method bd09togcj02 百度坐标系(BD-09)转火星坐标系(GCJ-02)即百度地图 转成 谷歌地图、高德地图
|
||
|
* @param bdLon 百度坐标系下的经度
|
||
|
* @param bdLat 百度坐标系下的纬度
|
||
|
* @return [gcjLon,gcjLat] 火星坐标系下的经纬度坐标
|
||
|
*/
|
||
|
function bdLL2gcjLL(bdLon, bdLat) {
|
||
|
const lon = +bdLon
|
||
|
const lat = +bdLat
|
||
|
|
||
|
const x = lon - 0.0065
|
||
|
const y = lat - 0.006
|
||
|
const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * xPI)
|
||
|
|
||
|
const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * xPI)
|
||
|
const gcjLon = z * Math.cos(theta)
|
||
|
const gcjLat = z * Math.sin(theta)
|
||
|
|
||
|
return [gcjLon, gcjLat]
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method gcj02_to_bd09 火星坐标系(GCJ-02)转百度坐标系(BD-09) 即谷歌地图、高德地图 转成 百度地图
|
||
|
* @param gcjLon 火星坐标系下的经度
|
||
|
* @param gcjLat 火星坐标系下的纬度
|
||
|
* @returns [bdLon,bdLat] 百度坐标系下的经纬度坐标
|
||
|
*/
|
||
|
function gcjLL2bdLL(gcjLon, gcjLat) {
|
||
|
const lon = +gcjLon
|
||
|
const lat = +gcjLat
|
||
|
|
||
|
const z = Math.sqrt(lon * lon + lat * lat) + 0.00002 * Math.sin(lat * xPI)
|
||
|
|
||
|
const theta = Math.atan2(lat, lon) + 0.000003 * Math.cos(lon * xPI)
|
||
|
const bdLon = z * Math.cos(theta) + 0.0065
|
||
|
const bdLat = z * Math.sin(theta) + 0.006
|
||
|
|
||
|
return [bdLon, bdLat]
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method wgs84_to_gcj02 WGS84坐标系转火星坐标系(GCJ-02)
|
||
|
* @param wgsLon WGS84坐标系下的经度坐标
|
||
|
* @param wgsLat WGS84坐标系下的纬度坐标
|
||
|
* @returns [mglon,mglat] 转换后火星坐标系下的经纬度坐标
|
||
|
*/
|
||
|
function wgs84LL2gcjLL(wgsLon, wgsLat) {
|
||
|
const lon = +wgsLon
|
||
|
const lat = +wgsLat
|
||
|
|
||
|
// 如果坐标不在国内范围,则直接返回,不做偏移处理
|
||
|
if (out_of_china(lon, lat)) {
|
||
|
return [lon, lat]
|
||
|
} else {
|
||
|
let dlat = transform_lat(lon - 105.0, lat - 35.0)
|
||
|
let dlon = transform_lon(lon - 105.0, lat - 35.0)
|
||
|
|
||
|
const radlat = (lat / 180.0) * PI
|
||
|
let magic = Math.sin(radlat)
|
||
|
magic = 1 - ee * magic * magic
|
||
|
const sqrtmagic = Math.sqrt(magic)
|
||
|
|
||
|
dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI)
|
||
|
dlon = (dlon * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI)
|
||
|
|
||
|
const mglat = lat + dlat
|
||
|
const mglon = lon + dlon
|
||
|
|
||
|
return [mglon, mglat]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method gcj02_to_wgs84 火星坐标系(GCJ-02)转WGS84坐标系
|
||
|
* @param gcjLon 火星坐标系下的经度坐标
|
||
|
* @param gcjLat 火星坐标系下的纬度坐标
|
||
|
* @returns [] 转换后WGS84坐标系下的经纬度坐标
|
||
|
*/
|
||
|
export function gcjLL2wgs84LL(gcjLon, gcjLat) {
|
||
|
const lon = +gcjLon
|
||
|
const lat = +gcjLat
|
||
|
|
||
|
if (out_of_china(lon, lat)) {
|
||
|
return [lon, lat]
|
||
|
} else {
|
||
|
let dlat = transform_lat(lon - 105.0, lat - 35.0)
|
||
|
let dlon = transform_lon(lon - 105.0, lat - 35.0)
|
||
|
|
||
|
const radlat = (lat / 180.0) * PI
|
||
|
let magic = Math.sin(radlat)
|
||
|
magic = 1 - ee * magic * magic
|
||
|
const sqrtmagic = Math.sqrt(magic)
|
||
|
|
||
|
dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI)
|
||
|
dlon = (dlon * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI)
|
||
|
|
||
|
const mglat = lat + dlat
|
||
|
const mglon = lon + dlon
|
||
|
|
||
|
return [lon * 2 - mglon, lat * 2 - mglat]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method lonlat_to_webMercator_of_WGS84 经纬度坐标转墨卡托投影(WGS84)
|
||
|
* @param lon 待转换的经度坐标
|
||
|
* @param lat 待转换的纬度坐标
|
||
|
* @returns [x,y] 转换后的平面投影坐标
|
||
|
*/
|
||
|
function LL2ME_wgs84(lon, lat) {
|
||
|
// [114.32894, 30.585748]
|
||
|
const earthRad = 6378137.0
|
||
|
const x = ((lon * Math.PI) / 180) * earthRad
|
||
|
|
||
|
const a = (lat * Math.PI) / 180
|
||
|
const y = (earthRad / 2) * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a)))
|
||
|
|
||
|
return [x, y] // [12727039.383734727, 3579066.6894065146]
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method webMercator_to_lonlat_of_WGS84 墨卡托投影转经纬度坐标(WGS84)
|
||
|
* @param x 待转换的平面投影经度坐标
|
||
|
* @param y 待转换的平面投影纬度坐标
|
||
|
* @returns [lon,lat] 转换后的经纬度坐标
|
||
|
*/
|
||
|
function ME2LL_wgs84(x, y) {
|
||
|
// [12727039.383734727, 3579066.6894065146]
|
||
|
const lon = (x / 20037508.34) * 180
|
||
|
let lat = (y / 20037508.34) * 180
|
||
|
|
||
|
lat =
|
||
|
(180 / Math.PI) *
|
||
|
(2 * Math.atan(Math.exp((lat * Math.PI) / 180)) - Math.PI / 2)
|
||
|
|
||
|
return [lon, lat] // [114.32894001591471, 30.58574800385281]
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method wgs84_to_bd09 WGS84坐标系转百度坐标系(BD-09)
|
||
|
* @param wgsLon WGS84坐标系下的经度坐标
|
||
|
* @param wgsLat WGS84坐标系下的纬度坐标
|
||
|
* @returns bd_coordinate 转换后百度坐标系下的经纬度坐标
|
||
|
*/
|
||
|
export function wgs84LL2bdLL(wgsLon, wgsLat) {
|
||
|
const gcjCoordinate = wgs84LL2gcjLL(wgsLon, wgsLat) // WGS84坐标系 转 火星坐标系
|
||
|
const bdCoordinate = gcjLL2bdLL(gcjCoordinate[0], gcjCoordinate[1]) // 火星坐标系 转 百度坐标系
|
||
|
|
||
|
return bdCoordinate
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method bd09_to_wgs84 百度坐标系(BD-09)转WGS84坐标系
|
||
|
* @param bdLon 百度坐标系下的经度坐标
|
||
|
* @param bdLat 百度坐标系下的纬度坐标
|
||
|
* @returns wgs_coordinate 转换后WGS84坐标系下的经纬度坐标
|
||
|
*/
|
||
|
export function bdLL2wgs84LL(bdLon, bdLat) {
|
||
|
const gcjCoordinate = bdLL2gcjLL(bdLon, bdLat) // 百度坐标系 转 火星坐标系
|
||
|
const wgsCoordinate = gcjLL2wgs84LL(gcjCoordinate[0], gcjCoordinate[1]) // 火星坐标西 转 WGS84坐标系
|
||
|
|
||
|
return wgsCoordinate
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method webMercator_to_lonlat_of_bd09 墨卡托投影转经纬度坐标(bd09)
|
||
|
* @param x 待转换的墨卡托投影平面经度坐标
|
||
|
* @param y 待转换的墨卡托投影平面纬度坐标
|
||
|
* @returns location 转换后百度坐标系下的经纬度坐标
|
||
|
*/
|
||
|
function ME2LL_bd09(x, y) {
|
||
|
let cF = null
|
||
|
x = Math.abs(x)
|
||
|
y = Math.abs(y)
|
||
|
for (let cE = 0; cE < MCBAND.length; cE++) {
|
||
|
if (y >= MCBAND[cE]) {
|
||
|
cF = MC2LL[cE]
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if (cF !== null) {
|
||
|
const location = converter(x, y, cF)
|
||
|
return location
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method lonlat_to_webMercator_of_bd09 经纬度坐标转墨卡托投影(bd09)
|
||
|
* @param lon 待转换的百度坐标系下的经度坐标
|
||
|
* @param lat 待转换的百度坐标系下的纬度坐标
|
||
|
* @returns [] 转换后墨卡托投影平面经纬度坐标
|
||
|
*/
|
||
|
function LL2ME_bd09(lon, lat) {
|
||
|
let cE = ''
|
||
|
lon = getLoop(lon, -180, 180)
|
||
|
lat = getRange(lat, -74, 74)
|
||
|
|
||
|
for (let i = 0; i < LLBAND.length; i++) {
|
||
|
if (lat >= LLBAND[i]) {
|
||
|
cE = LL2MC[i]
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!cE) {
|
||
|
for (let i = LLBAND.length - 1; i >= 0; i--) {
|
||
|
if (lat <= -LLBAND[i]) {
|
||
|
cE = LL2MC[i]
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return converter(lon, lat, cE)
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method lonlat_of_wgs84_to_webMercator_of_bd09 WGS84坐标系转百度墨卡托
|
||
|
* @param lon 待转换的WGS84坐标系下的经度坐标
|
||
|
* @param lat 待转换的WGS84坐标系下的纬度坐标
|
||
|
* @returns [] 转换后百度坐标系下的墨卡托投影平面坐标
|
||
|
*/
|
||
|
export function wgs84LL2bdME(lon, lat) {
|
||
|
const bd = wgs84LL2bdLL(lon, lat)
|
||
|
return LL2ME_bd09(bd[0], bd[1])
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method webMercator_of_bd09_to_lonlat_of_wgs84 百度墨卡托转WGS84坐标系
|
||
|
* @param x 待转换的百度坐标系下墨卡托投影平面经度坐标
|
||
|
* @param y 待转换的百度坐标系下墨卡托投影平面纬度坐标
|
||
|
* @returns [] 转换后WGS84坐标系下的经纬度坐标
|
||
|
*/
|
||
|
function bdME2wgs84LL(x, y) {
|
||
|
if (!isNaN(x) && !isNaN(y)) {
|
||
|
const bd = ME2LL_bd09(x, y)
|
||
|
return bdLL2wgs84LL(bd[0], bd[1])
|
||
|
} else {
|
||
|
return [0, 0]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method 判断经纬度坐标是否在国内,如果不在国内返回true,否则返回false
|
||
|
* @param longitude 经度坐标
|
||
|
* @param latitude 纬度坐标
|
||
|
* @returns {boolean} 如果不在国内返回true,在国内返回false
|
||
|
*/
|
||
|
function out_of_china(longitude, latitude) {
|
||
|
const lon = +longitude
|
||
|
const lat = +latitude
|
||
|
|
||
|
// 纬度3.86~53.55,经度73.66~135.05
|
||
|
return !(lon > 73.66 && lon < 135.05 && lat > 3.86 && lat < 53.55)
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method transform_lat 转换纬度坐标
|
||
|
* @param longitude 待转换的经度坐标
|
||
|
* @param latitude 待转换的纬度坐标
|
||
|
* @returns {number} 返回转换后的纬度坐标
|
||
|
*/
|
||
|
function transform_lat(longitude, latitude) {
|
||
|
const lon = +longitude
|
||
|
const lat = +latitude
|
||
|
|
||
|
let ret =
|
||
|
-100.0 +
|
||
|
2.0 * lon +
|
||
|
3.0 * lat +
|
||
|
0.2 * lat * lat +
|
||
|
0.1 * lon * lat +
|
||
|
0.2 * Math.sqrt(Math.abs(lon))
|
||
|
ret +=
|
||
|
((20.0 * Math.sin(6.0 * lon * PI) + 20.0 * Math.sin(2.0 * lon * 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
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method transform_lon 转换经度坐标
|
||
|
* @param longitude 待转换的经度坐标
|
||
|
* @param latitude 待转换的纬度坐标
|
||
|
* @returns {number} 返回转换后的经度坐标
|
||
|
*/
|
||
|
function transform_lon(longitude, latitude) {
|
||
|
const lon = +longitude
|
||
|
const lat = +latitude
|
||
|
|
||
|
let ret =
|
||
|
300.0 +
|
||
|
lon +
|
||
|
2.0 * lat +
|
||
|
0.1 * lon * lon +
|
||
|
0.1 * lon * lat +
|
||
|
0.1 * Math.sqrt(Math.abs(lon))
|
||
|
ret +=
|
||
|
((20.0 * Math.sin(6.0 * lon * PI) + 20.0 * Math.sin(2.0 * lon * PI)) *
|
||
|
2.0) /
|
||
|
3.0
|
||
|
ret +=
|
||
|
((20.0 * Math.sin(lon * PI) + 40.0 * Math.sin((lon / 3.0) * PI)) * 2.0) /
|
||
|
3.0
|
||
|
ret +=
|
||
|
((150.0 * Math.sin((lon / 12.0) * PI) +
|
||
|
300.0 * Math.sin((lon / 30.0) * PI)) *
|
||
|
2.0) /
|
||
|
3.0
|
||
|
|
||
|
return ret
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method converter 墨卡托投影转百度坐标系经纬度坐标
|
||
|
* @param x 墨卡托投影平面经度坐标
|
||
|
* @param y 墨卡托投影平面纬度坐标
|
||
|
* @param cE 转换系数数组
|
||
|
* @returns [] 转换结果
|
||
|
*/
|
||
|
function converter(x, y, cE) {
|
||
|
let xTemp = cE[0] + cE[1] * Math.abs(x)
|
||
|
const cC = Math.abs(y) / cE[9]
|
||
|
let yTemp =
|
||
|
cE[2] +
|
||
|
cE[3] * cC +
|
||
|
cE[4] * cC * cC +
|
||
|
cE[5] * cC * cC * cC +
|
||
|
cE[6] * cC * cC * cC * cC +
|
||
|
cE[7] * cC * cC * cC * cC * cC +
|
||
|
cE[8] * cC * cC * cC * cC * cC * cC
|
||
|
xTemp *= x < 0 ? -1 : 1
|
||
|
yTemp *= y < 0 ? -1 : 1
|
||
|
|
||
|
return [xTemp, yTemp]
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method getLoop
|
||
|
* @param lng
|
||
|
* @param min
|
||
|
* @param max
|
||
|
* @returns {*}
|
||
|
*/
|
||
|
function getLoop(lng, min, max) {
|
||
|
while (lng > max) {
|
||
|
lng -= max - min
|
||
|
}
|
||
|
while (lng < min) {
|
||
|
lng += max - min
|
||
|
}
|
||
|
return lng
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @method getRange
|
||
|
* @param lat
|
||
|
* @param min
|
||
|
* @param max
|
||
|
* @returns {*}
|
||
|
*/
|
||
|
function getRange(lat, min, max) {
|
||
|
if (min != null) {
|
||
|
lat = Math.max(lat, min)
|
||
|
}
|
||
|
if (max != null) {
|
||
|
lat = Math.min(lat, max)
|
||
|
}
|
||
|
return lat
|
||
|
}
|
||
|
|
||
|
|
||
|
export function wgs84LL2gcjLL2(wgsLon, wgsLat) {
|
||
|
const lon = +wgsLon
|
||
|
const lat = +wgsLat
|
||
|
|
||
|
// 如果坐标不在国内范围,则直接返回,不做偏移处理
|
||
|
if (out_of_china(lon, lat)) {
|
||
|
return [lon, lat]
|
||
|
} else {
|
||
|
let dlat = transform_lat(lon - 105.0, lat - 35.0)
|
||
|
let dlon = transform_lon(lon - 105.0, lat - 35.0)
|
||
|
|
||
|
const radlat = (lat / 180.0) * PI
|
||
|
let magic = Math.sin(radlat)
|
||
|
magic = 1 - ee * magic * magic
|
||
|
const sqrtmagic = Math.sqrt(magic)
|
||
|
|
||
|
dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI)
|
||
|
dlon = (dlon * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI)
|
||
|
|
||
|
const mglat = lat + dlat
|
||
|
const mglon = lon + dlon
|
||
|
|
||
|
return [mglon, mglat]
|
||
|
}
|
||
|
}
|