WGS84与GCJ02、BD09经纬度坐标转换介绍

news/2024/11/25 4:39:16/

在涉及到经纬度的地方,我们在编码的时候,经常需要进行转换。而且转换代码似乎非常复杂,真正理解,其实需要做一些工作,这里我将了解的经纬度坐标相关的知识做一个梳理。

首先了解经纬度坐标系统。

因为地球并不是一个规则的球体,而是一个椭球体。如何对椭球体进行坐标划定,这里有以下几种:

这里面提到的WGS84,也叫大地坐标系,它是原始坐标系统,为了数据安全和保密,通过地形图非线性保密处理算法(俗称火星加密)加密过的WGS84坐标系,俗称国测局坐标系,或火星坐标系就是我们今天所要提到的GCJ02,目前谷歌地图(中国cn)、腾讯地图、高德地图,使用的都是GCJ02,只有百度地图没有使用这种加密算法,而是使用的是BD09,从名字上可以看出,GCJ02是2002年提出来的算法,BD09则是2009年提出来的,虽然百度地图没有使用GCJ02加密算法,但是他却是在GCJ02基础上做了一个二次加密,所以说,从WGS84坐标系不能直接转BD09,中间需要跨越一个GCJ02,反过来,需要将GCJ02或者BD09转为WGS84就是纠偏算法,相当于逆向解密,同样的DB09直接到不了WGS84,中间还需要转为GCJ02,所以现在的很多算法,如果你看到有百度坐标转大地坐标,基本上需要借助火星坐标来计算。

下面是js转换代码

var GPS = {PI: 3.14159265358979324,x_pi: 3.14159265358979324 * 3000.0 / 180.0,delta: function (lat, lon) {// Krasovsky 1940//// a = 6378245.0, 1/f = 298.3// b = a * (1 - f)// ee = (a^2 - b^2) / a^2;var a = 6378245.0; //  a: 卫星椭球坐标投影到平面地图坐标系的投影因子。var ee = 0.00669342162296594323; //  ee: 椭球的偏心率。var dLat = this.transformLat(lon - 105.0, lat - 35.0);var dLon = this.transformLon(lon - 105.0, lat - 35.0);var radLat = lat / 180.0 * this.PI;var magic = Math.sin(radLat);magic = 1 - ee * magic * magic;var sqrtMagic = Math.sqrt(magic);dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * this.PI);dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * this.PI);return { 'lat': dLat, 'lon': dLon };},//WGS-84 to GCJ-02gcj_encrypt: function (wgsLat, wgsLon) {if (this.outOfChina(wgsLat, wgsLon))return { 'lat': wgsLat, 'lon': wgsLon };var d = this.delta(wgsLat, wgsLon);return { 'lat': wgsLat + d.lat, 'lon': wgsLon + d.lon };},//GCJ-02 to WGS-84 (粗略)gcj_decrypt: function (gcjLat, gcjLon) {if (this.outOfChina(gcjLat, gcjLon))return { 'lat': gcjLat, 'lon': gcjLon };var d = this.delta(gcjLat, gcjLon);return { 'lat': gcjLat - d.lat, 'lon': gcjLon - d.lon };},//GCJ-02 to WGS-84 exactly (精确(二分极限法))gcj_decrypt_exact: function (gcjLat, gcjLon) {var initDelta = 0.01;var threshold = 0.000000001;var dLat = initDelta, dLon = initDelta;var mLat = gcjLat - dLat, mLon = gcjLon - dLon;var pLat = gcjLat + dLat, pLon = gcjLon + dLon;var wgsLat, wgsLon, i = 0;while (1) {wgsLat = (mLat + pLat) / 2;wgsLon = (mLon + pLon) / 2;var tmp = this.gcj_encrypt(wgsLat, wgsLon)dLat = tmp.lat - gcjLat;dLon = tmp.lon - gcjLon;if ((Math.abs(dLat) < threshold) && (Math.abs(dLon) < threshold))break;if (dLat > 0) pLat = wgsLat; else mLat = wgsLat;if (dLon > 0) pLon = wgsLon; else mLon = wgsLon;if (++i > 10000) break;}//console.log(i);return { 'lat': wgsLat, 'lon': wgsLon };},//GCJ-02 to BD-09bd_encrypt: function (gcjLat, gcjLon) {var x = gcjLon, y = gcjLat;var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * this.x_pi);var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * this.x_pi);bdLon = z * Math.cos(theta) + 0.0065;bdLat = z * Math.sin(theta) + 0.006;return { 'lat': bdLat, 'lon': bdLon };},//BD-09 to GCJ-02bd_decrypt: function (bdLat, bdLon) {var x = bdLon - 0.0065, y = bdLat - 0.006;var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * this.x_pi);var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * this.x_pi);var gcjLon = z * Math.cos(theta);var gcjLat = z * Math.sin(theta);return { 'lat': gcjLat, 'lon': gcjLon };},//WGS-84 to Web mercator//mercatorLat -> y mercatorLon -> xmercator_encrypt: function (wgsLat, wgsLon) {var x = wgsLon * 20037508.34 / 180.;var y = Math.log(Math.tan((90. + wgsLat) * this.PI / 360.)) / (this.PI / 180.);y = y * 20037508.34 / 180.;return { 'lat': y, 'lon': x };/*if ((Math.abs(wgsLon) > 180 || Math.abs(wgsLat) > 90))return null;var x = 6378137.0 * wgsLon * 0.017453292519943295;var a = wgsLat * 0.017453292519943295;var y = 3189068.5 * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a)));return {'lat' : y, 'lon' : x};//*/},// Web mercator to WGS-84// mercatorLat -> y mercatorLon -> xmercator_decrypt: function (mercatorLat, mercatorLon) {var x = mercatorLon / 20037508.34 * 180.;var y = mercatorLat / 20037508.34 * 180.;y = 180 / this.PI * (2 * Math.atan(Math.exp(y * this.PI / 180.)) - this.PI / 2);return { 'lat': y, 'lon': x };/*if (Math.abs(mercatorLon) < 180 && Math.abs(mercatorLat) < 90)return null;if ((Math.abs(mercatorLon) > 20037508.3427892) || (Math.abs(mercatorLat) > 20037508.3427892))return null;var a = mercatorLon / 6378137.0 * 57.295779513082323;var x = a - (Math.floor(((a + 180.0) / 360.0)) * 360.0);var y = (1.5707963267948966 - (2.0 * Math.atan(Math.exp((-1.0 * mercatorLat) / 6378137.0)))) * 57.295779513082323;return {'lat' : y, 'lon' : x};//*/},//两点的距离distance: function (latA, lonA, latB, lonB) {var earthR = 6371000.;var x = Math.cos(latA * this.PI / 180.) * Math.cos(latB * this.PI / 180.) * Math.cos((lonA - lonB) * this.PI / 180);var y = Math.sin(latA * this.PI / 180.) * Math.sin(latB * this.PI / 180.);var s = x + y;if (s > 1) s = 1;if (s < -1) s = -1;var alpha = Math.acos(s);var distance = alpha * earthR;return distance;},outOfChina: function (lat, lon) {if (lon < 72.004 || lon > 137.8347)return true;if (lat < 0.8293 || lat > 55.8271)return true;return false;},transformLat: function (x, y) {var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;ret += (20.0 * Math.sin(y * this.PI) + 40.0 * Math.sin(y / 3.0 * this.PI)) * 2.0 / 3.0;ret += (160.0 * Math.sin(y / 12.0 * this.PI) + 320 * Math.sin(y * this.PI / 30.0)) * 2.0 / 3.0;return ret;},transformLon: function (x, y) {var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;ret += (20.0 * Math.sin(x * this.PI) + 40.0 * Math.sin(x / 3.0 * this.PI)) * 2.0 / 3.0;ret += (150.0 * Math.sin(x / 12.0 * this.PI) + 300.0 * Math.sin(x / 30.0 * this.PI)) * 2.0 / 3.0;return ret;}};

上面这段代码,没有对百度坐标bd09转大地坐标wgs84、大地坐标wgs84转百度坐标bd09进行书写,如果你了解了前面所说的坐标之间的关系,你大概就能猜到,只需要借助wgs84->gcj02 gcj02->bd09就能实现wgs84->bd09的实现。相反,借助bd09->gcj02 gcj02->wgs84就能实现bd09->wgs84的转换了。

我以前不了解这些坐标之间的关系,以为他们之间的转换非常复杂,其实当你了解了坐标之间的关系,就很容易理解了。

总结一下就是:

WGS84是大地坐标系,这是一种国际通用的坐标,中国的大地坐标是CGCS2000,也是原始坐标系,一般的终端设备,如果自己有定位功能,比如车载系统,他们发出的gps坐标就是原始坐标,我们在一些地图应用中使用的定位,比如打车软件,其他自带地图的软件,他们一般都是火星坐标系GCJ02,需要进行一个坐标转换。而百度地图相关的应用,基本都是使用的百度坐标,这个是在火星坐标基础上进行的再次加密,所以它还是与火星坐标有关系。


2023/04/25 又发现一个不错的坐标转换,贴上来

/*** Created by Wandergis on 2015/7/8.* 提供了百度坐标(BD-09)、国测局坐标(火星坐标,GCJ-02)、和 WGS-84 坐标系之间的转换*/
// UMD 魔法代码
// if the module has no dependencies, the above pattern can be simplified to
(function (root, factory) {if (typeof define === 'function' && define.amd) {// AMD. Register as an anonymous module.define([], factory);} else if (typeof module === 'object' && module.exports) {// Node. Does not work with strict CommonJS, but// only CommonJS-like environments that support module.exports,// like Node.module.exports = factory();} else {// Browser globals (root is window)root.coordtransform = factory();}
}(this, function () {// 定义一些常量var x_PI = 3.14159265358979324 * 3000.0 / 180.0;var PI = 3.1415926535897932384626;var a = 6378245.0;var ee = 0.00669342162296594323;/*** 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02) 的转换* 即 百度 转 谷歌、高德* @param bd_lng* @param bd_lat* @returns {*[]}*/var bd09togcj02 = function bd09togcj02(bd_lng, bd_lat) {var bd_lng = +bd_lng;var bd_lat = +bd_lat;var x = bd_lng - 0.0065;var y = bd_lat - 0.006;var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI);var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI);var gg_lng = z * Math.cos(theta);var gg_lat = z * Math.sin(theta);return [gg_lng, gg_lat]};/*** 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换* 即 谷歌、高德 转 百度* @param lng* @param lat* @returns {*[]}*/var gcj02tobd09 = function gcj02tobd09(lng, lat) {var lat = +lat;var lng = +lng;var z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI);var theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI);var bd_lng = z * Math.cos(theta) + 0.0065;var bd_lat = z * Math.sin(theta) + 0.006;return [bd_lng, bd_lat]};/*** WGS-84 转 GCJ-02* @param lng* @param lat* @returns {*[]}*/var wgs84togcj02 = function wgs84togcj02(lng, lat) {var lat = +lat;var lng = +lng;if (out_of_china(lng, lat)) {return [lng, lat]} else {var dlat = transformlat(lng - 105.0, lat - 35.0);var dlng = transformlng(lng - 105.0, lat - 35.0);var radlat = lat / 180.0 * PI;var magic = Math.sin(radlat);magic = 1 - ee * magic * magic;var sqrtmagic = Math.sqrt(magic);dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);var mglat = lat + dlat;var mglng = lng + dlng;return [mglng, mglat]}};/*** GCJ-02 转换为 WGS-84* @param lng* @param lat* @returns {*[]}*/var gcj02towgs84 = function gcj02towgs84(lng, lat) {var lat = +lat;var lng = +lng;if (out_of_china(lng, lat)) {return [lng, lat]} else {var dlat = transformlat(lng - 105.0, lat - 35.0);var dlng = transformlng(lng - 105.0, lat - 35.0);var radlat = lat / 180.0 * PI;var magic = Math.sin(radlat);magic = 1 - ee * magic * magic;var sqrtmagic = Math.sqrt(magic);dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);var mglat = lat + dlat;var mglng = lng + dlng;return [lng * 2 - mglng, lat * 2 - mglat]}};var transformlat = function transformlat(lng, lat) {var lat = +lat;var lng = +lng;var 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};var transformlng = function transformlng(lng, lat) {var lat = +lat;var lng = +lng;var 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 {boolean}*/var out_of_china = function out_of_china(lng, lat) {var lat = +lat;var lng = +lng;// 纬度 3.86~53.55, 经度 73.66~135.05 return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55);};return {bd09togcj02: bd09togcj02,gcj02tobd09: gcj02tobd09,wgs84togcj02: wgs84togcj02,gcj02towgs84: gcj02towgs84}
}));

使用方法:

/*** Created by wandergis on 15/11/17.*/
//国测局坐标(火星坐标,比如高德地图在用),百度坐标,wgs84坐标(谷歌国外以及绝大部分国外在线地图使用的坐标)
var coordtransform = require('../index');
//百度经纬度坐标转国测局坐标
var bd09togcj02 = coordtransform.bd09togcj02(116.404, 39.915);
//国测局坐标转百度经纬度坐标
var gcj02tobd09 = coordtransform.gcj02tobd09(116.404, 39.915);
//wgs84转国测局坐标
var wgs84togcj02 = coordtransform.wgs84togcj02(116.404, 39.915);
//国测局坐标转wgs84坐标
var gcj02towgs84 = coordtransform.gcj02towgs84(116.404, 39.915);
console.log(bd09togcj02);
console.log(gcj02tobd09);
console.log(wgs84togcj02);
console.log(gcj02towgs84);
//result
//bd09togcj02:   [ 116.39762729119315, 39.90865673957631 ]
//gcj02tobd09:   [ 116.41036949371029, 39.92133699351021 ]
//wgs84togcj02:  [ 116.41024449916938, 39.91640428150164 ]
//gcj02towgs84:  [ 116.39775550083061, 39.91359571849836 ]

http://www.ppmy.cn/news/299314.html

相关文章

SQLyog安装教程

SQLyog安装教程 文章目录 前言1.安装准备2.安装教程3.踩过的坑参考跳转 前言 知识目录这是平时学习总结的地方&#xff0c;用做知识库平时看到其他文章的相关知识&#xff0c;也会增加到这里随着学习深入&#xff0c;会进行知识拆分和汇总&#xff0c;所以文章会随时更新参考的…

U-blox F9P、天宝BD982、UB4B0M三家主流GNSS板卡性能哪家强

一、GNSS板卡性能测试目的及环境 通过定点测试和道路测试的方法对比出三家主流GNSS板卡经过RTK解算后的定位性能和精准度。 1.测试产品 天宝BD982 多频GNSS板卡 U-blox ZED-F9P双模块GNSS板卡 和芯星通UB4B0M全频GNSS板卡 2.测试方法 &#xff08;1&#xff09;定点测…

SQLyog的安装及使用

SQLyog是业界著名的Webyog公司出品的一款简洁高效、功能强大的图形化MySQL数据库管理工具。 特点&#xff1a; 1.基于C和MySQLAPI编程&#xff1b; 2.方便快捷的数据库同步与数据库结构同步工具&#xff1b; 3.易用的数据库、数据表备份与还原功能&#xff1b; 4.支持导入与…

SQLyog安装使用教程

SQLyog安装使用教程 SQLyog操作简单&#xff0c;功能强大&#xff0c;能够帮助用户轻松管理自己的MYSQL数据库&#xff0c; 文章目录 SQLyog安装使用教程前言一、安装SQLyog总结 前言 我这里使用的是SQLyog-13.1.7-0.x64Community&#xff0c;这是目前最新的版本了&#xff0…

GPS数据格式:NMEA-0183协议

常用nmea协议格式 NMEA协议是为了在不同的GPS&#xff08;全球定位系统&#xff09;导航设备中建立统一的BTCM&#xff08;海事无线电技术委员会&#xff09;标准&#xff0c;由美国国家海洋电子协会&#xff08;NMEA-The National Marine Electronics Associa-tion&#xff09…

详细解决:SQLyog连接MySQL8报错#1251

报错信息&#xff1a;Error No. 1251 client does not support authentication protocol requested by server;consider upgrading Mysql client 主要原因 主要是由于mysql8以前的加密规则与mysql8以后的存在差异 解决方法 确认mysql服务已启动 [rootinfluxdb252 ~]# systemctl…

OGG(oracle golden date)简单部署ORACLE 11g-ORACLE 11g

摘要&#xff1a;本文档为本人学习goldendate时的相关实验过程&#xff0c;仅供参考。 1.环境介绍 本文主要分介绍OGG的安装部署&#xff0c;下面是实验环境介绍。 实验规划&#xff1a; 项目源环境目标环境 操作系统 OEL 5.7 X86_64 OEL 5.7 X86_64 …

XDG0008

问题描述 在写C# WPF 应用程序的时候&#xff0c;尝试数据绑定(Data Binding)的时候使用了如下代码&#xff1a; <Canvas.Resources><local:NumberOfPlayers x:Key"numberOfPlayersData" /> </Canvas.Resources>但是奇怪的是&#xff0c;在命名空…