# 中国地图坐标系转换详解:从WGS-84到GCJ-02再到BD-09
# 引言
在地理信息系统(GIS)和地图应用程序中,不同的地图服务提供商使用不同的坐标系。这意味着,如果您正在使用多个地图服务,您可能需要在不同的坐标系之间进行转换。本文将重点介绍中国地区常见的三种坐标系:WGS-84、GCJ-02(火星坐标)和BD-09(百度坐标)以及它们之间的转换。
# 坐标系简介
# WGS-84
WGS84:World Geodetic System 1984,是为GPS全球定位系统使用而建立的坐标系统。通过遍布世界的卫星观测站观测到的坐标建立,其初次WGS84的精度为1-2m,在1994年1月2号,通过10个观测站在GPS测量方法上改正,得到了WGS84(G730),G表示由GPS测量得到,730表示为GPS时间第730个周。1996年,National Imagery and Mapping Agency (NIMA) 为美国国防部 (U.S.Departemt of Defense, DoD)做了一个新的坐标系统。这样实现了新的WGS版本:WGS(G873)。其因为加入了USNO站和北京站的改正,其东部方向加入了31-39cm 的改正。所有的其他坐标都有在1分米之内的修正。第三次精化:WGS84(G1150),于2002年1月20日启用。
# GCJ-02
火星坐标系,也叫国测局坐标系(GCJ02),正式名称为「地形图非线性保密处理技术」。是由中国国家测绘局2002年制订的地理信息系统的坐标系统。我国规定国内出版的各种地图系统(包括电子形式),必须至少采用“GCJ02”对地理位置进行首次加密。所以在中国所有的地图必须使用“GCJ02”对地理位置进行首次加密。比如高德、腾讯都在用这个坐标系。它主要目的不是对GPS定位进行偏移,而是对高精度地图进行偏移,降低其精度。定位信息不是重中之重,高精度地图则是国家机密。而对定位进行偏移只不过是为了与脱密后的地图保持同步性,从而不影响导航等公开领域的应用。目标是通过保密处理,对高精度数据进行偏移,使其成为低精度数据,从而降低国外导弹精准打击的准度,以保护我国、我军的重要基础设施。
# BD-09
BD-09 是百度地图使用的坐标系。在GCJ02坐标系基础上再次加密。其中BD09LL表示百度经纬度坐标,BD09MC表示百度墨卡托米制坐标。
# 转换算法实现
核心代码如下:
# WGS84和火星坐标(GCJ02)互转
/**
* WGS-84 转 GCJ-02
*
* @param lng WGS-84经度
* @param lat WGS-84纬度
* @return GCJ-02 坐标系中的经度和纬度
*/
public static double[] wgs84togcj02(double lng, double lat) {
if (outOfChina(lng, lat)) {
return new double[]{lng, lat};
} else {
double dlat = transformlat(lng - 105.0, lat - 35.0);
double dlng = transformlng(lng - 105.0, lat - 35.0);
double radlat = lat / 180.0 * PI;
double magic = Math.sin(radlat);
magic = 1 - ee * magic * magic;
double sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
double mglat = lat + dlat;
double mglng = lng + dlng;
return new double[]{mglng, mglat};
}
}
/**
* GCJ-02 转换为 WGS-84
*
* @param lng GCJ-02经度
* @param lat GCJ-02纬度
* @return WGS-84 坐标系中的经度和纬度
*/
public static double[] gcj02towgs84(double lng, double lat) {
if (outOfChina(lng, lat)) {
return new double[]{lng, lat};
} else {
double dlat = transformlat(lng - 105.0, lat - 35.0);
double dlng = transformlng(lng - 105.0, lat - 35.0);
double radlat = lat / 180.0 * PI;
double magic = Math.sin(radlat);
magic = 1 - ee * magic * magic;
double sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
double mglat = lat + dlat;
double mglng = lng + dlng;
return new double[]{lng * 2 - mglng, lat * 2 - mglat};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 火星坐标(GCJ02)和百度坐标(BD09)互转
/**
* 百度坐标系 (BD-09) 转 火星坐标系 (GCJ-02)
* 即 百度 转 谷歌、高德
*
* @param bdLng 百度经度
* @param bdLat 百度纬度
* @return GCJ-02 坐标系中的经度和纬度
*/
public static double[] bd09togcj02(double bdLng, double bdLat) {
// 经纬度偏移量
double x = bdLng - 0.0065;
double y = bdLat - 0.006;
double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI);
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI);
double ggLng = z * Math.cos(theta);
double ggLat = z * Math.sin(theta);
return new double[]{ggLng, ggLat};
}
/**
* 火星坐标系 (GCJ-02) 转 百度坐标系 (BD-09)
* 即 谷歌、高德 转 百度
*
* @param lng GCJ-02经度
* @param lat GCJ-02纬度
* @return BD-09 坐标系中的经度和纬度
*/
public static double[] gcj02tobd09(double lng, double lat) {
double z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI);
double theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI);
double bdLng = z * Math.cos(theta) + 0.0065;
double bdLat = z * Math.sin(theta) + 0.006;
return new double[]{bdLng, bdLat};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 测试方法
这里在百度拾取坐标系统 (opens new window)中设置一个点

使用转换工具测试
public static void main(String[] args) {
double baiduLon = 121.699506;
double baiduLat = 31.316136;
double[] gcj02Point = bd09togcj02(baiduLon, baiduLat);
double[] wgs84Point = gcj02towgs84(gcj02Point[0], gcj02Point[1]);
System.out.println("百度原坐标:" + baiduLon + "---" + baiduLat);
System.out.println("百度转wgs84坐标:" + wgs84Point[0] + "---" + wgs84Point[1]);
}
2
3
4
5
6
7
8

叠加测试结果,位置基本上是一致的,偏差很小

# 仓库代码地址
请点个star关注一下,后面还会持续分享干货的。
