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

/*
* @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]
}
}