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