常见 GIS 坐标系的坐标转换

几种常见的 GIS 坐标系的简单介绍及坐标数据的相互转换方法。


一、坐标系介绍

1. WGS-84:大地坐标系

  • 国际通用坐标系,也叫地球坐标系。
  • 各 GPS 设备、境外地图(包括但不限于谷歌地图、高德地图、百度地图的境外版)等使用的坐标系。

2. GCJ-02:国测局坐标系

  • 国内通用坐标系,也叫火星坐标系。
  • 各境内地图(高德地图、腾讯地图等)等使用的坐标系。

3. BD-09:百度坐标系

  • GCJ-02 基础上的二次加密。
  • 百度标准,百度 SDK,百度地图,百度 GeoCoding 等使用的坐标系。

二、坐标数据转换方法 -Java 版

1. 准备工作

1.1 定义依赖的一些常量和转换方法

1
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
private static final double PI = Math.PI;
private static final double X_PI = PI * 3000.0 / 180.0;
private static final double AA = 6378245.0;
private static final double EE = 0.00669342162296594323;

/**
* 经纬度偏移转换 - 经度
*
* @param lng
* @param lat
* @return
*/
private static double transformLng(double lng, double lat) {
double 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
* @return
*/
private static double transformLat(double lng, double lat) {
double 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;
}

/**
* 判断坐标是否超出境内
*
* @param lng
* @param lat
* @return
*/
private static boolean outOfChina(double lng, double lat) {
// 纬度 3.86~53.55, 经度 73.66~135.05;非精确判断,可能误判
return !(73.66 < lng && lng < 135.05 && 3.86 < lat && lat < 53.55);
}

2. WGS-84 和 GCJ-02 的相互转换

2.1 WGS-84 转 GCJ-02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* WGS-84 转 GCJ-02
*
* @param lng
* @param lat
* @return
*/
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) / ((AA * (1 - EE)) / (magic * sqrtMagic) * PI);
dlng = (dlng * 180.0) / (AA / sqrtMagic * Math.cos(radlat) * PI);
double mglat = lat + dlat;
double mglng = lng + dlng;
return new double[]{mglng, mglat};
}
}

2.2 GCJ-02 转 WGS-84

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* GCJ-02 转 WGS-84
*
* @param lng
* @param lat
* @return
*/
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) / ((AA * (1 - EE)) / (magic * sqrtMagic) * PI);
dlng = (dlng * 180.0) / (AA / sqrtMagic * Math.cos(radlat) * PI);
double mglat = lat + dlat;
double mglng = lng + dlng;
return new double[]{lng * 2 - mglng, lat * 2 - mglat};
}
}

3. GCJ-02 和 BD-09 的相互转换

3.1 GCJ-02 转 BD-09

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* GCJ-02 转 BD-09
*
* @param lng
* @param lat
* @return
*/
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};
}

3.2 BD-09 转 GCJ-02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* BD-09 转 GCJ-02
*
* @param lng
* @param lat
* @return
*/
public static double[] bd09ToGcj02(double lng, double lat) {
double x = lng - 0.0065;
double y = lat - 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 gcjLng = z * Math.cos(theta);
double gcjLat = z * Math.sin(theta);
return new double[]{gcjLng, gcjLat};
}

4. WGS-84 和 BD-09 的相互转换

4.1 WGS-84 转 BD-09

1
2
3
4
5
6
7
8
9
10
11
/**
* WGS-84 转 BD-09
*
* @param lng
* @param lat
* @return
*/
public static double[] wgs84ToBd09(double lng, double lat) {
double[] gcjPoint = wgs84ToGcj02(lng, lat);
return gcj02ToBd09(gcjPoint[0], gcjPoint[1]);
}

4.2 BD-09 转 WGS-84

1
2
3
4
5
6
7
8
9
10
11
/**
* BD-09 转 WGS-84
*
* @param lng
* @param lat
* @return
*/
public static double[] bd09ToWgs84(double lng, double lat) {
double[] gcjPoint = bd09ToGcj02(lng, lat);
return gcj02ToWgs84(gcjPoint[0], gcjPoint[1]);
}

三、坐标数据转换方法 -Python 版

1. 准备工作

1.1 依赖的一些常量和转换方法

1
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
import math

# 常量定义
PI = math.pi
x_PI = float(PI * float(3000.0) / float(180.0))
aa = float(6378245.0)
ee = 0.00669342162296594323

# 经纬度偏移转换
def transform_lat(lng, lat):
ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * math.sqrt(math.fabs(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

def transform_lng(lng, lat):
ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * math.sqrt(math.fabs(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

# 判断坐标是否超出境内
def out_of_china(lng, lat):
# 纬度 3.86~53.55, 经度 73.66~135.05;非精确判断,可能误判
if 73.66 < lng < 135.05 and 3.86 < lat < 53.55:
return False

2. WGS-84 和 GCJ-02 的相互转换

2.1 WGS-84 转 GCJ-02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# WGS-84 转 GCJ-02
def wgs84_to_gcj02(lng, lat):
if out_of_china(lng, lat):
return [lng, lat]
else:
dlat = transform_lat(lng - 105.0, lat - 35.0)
dlng = transform_lng(lng - 105.0, lat - 35.0)
radlat = lat / 180.0 * PI
magic = math.sin(radlat)
magic = 1 - ee * magic * magic
sqrtmagic = math.sqrt(magic)
dlat = (dlat * 180.0) / ((aa * (1 - ee)) / (magic * sqrtmagic) * PI)
dlng = (dlng * 180.0) / (aa / sqrtmagic * math.cos(radlat) * PI)
mglat = lat + dlat
mglng = lng + dlng
return [mglng, mglat]

2.2 GCJ-02 转 WGS-84

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# GCJ-02 转 WGS-84
def gcj02_to_wgs84(lng, lat):
if out_of_china(lng, lat):
return [lng, lat]
else:
dlat = transform_lat(lng - 105.0, lat - 35.0)
dlng = transform_lng(lng - 105.0, lat - 35.0)
radlat = lat / 180.0 * PI
magic = math.sin(radlat)
magic = 1 - ee * magic * magic
sqrtmagic = math.sqrt(magic)
dlat = (dlat * 180.0) / ((aa * (1 - ee)) / (magic * sqrtmagic) * PI)
dlng = (dlng * 180.0) / (aa / sqrtmagic * math.cos(radlat) * PI)
mglat = lat + dlat
mglng = lng + dlng
return [lng * 2 - mglng, lat * 2 - mglat]

3. GCJ-02 和 BD-09 的相互转换

3.1 GCJ-02 转 BD-09

1
2
3
4
5
6
7
# GCJ-02 转 BD-09
def gcj02_to_bd09(lng, lat):
z = math.sqrt(lng * lng + lat * lat) + 0.00002 * math.sin(lat * x_PI)
theta = math.atan2(lat, lng) + 0.000003 * math.cos(lng * x_PI)
bd_lng = z * math.cos(theta) + 0.0065
bd_lat = z * math.sin(theta) + 0.006
return [bd_lng, bd_lat]

3.2 BD-09 转 GCJ-02

1
2
3
4
5
6
7
8
9
# BD-09 转 GCJ-02
def bd09_to_gcj02(bd_lon, bd_lat):
x = bd_lon - 0.0065
y = bd_lat - 0.006
z = math.sqrt(x * x + y * y) - 0.00002 * math.sin(y * x_PI)
theta = math.atan2(y, x) - 0.000003 * math.cos(x * x_PI)
gg_lng = z * math.cos(theta)
gg_lat = z * math.sin(theta)
return [gg_lng, gg_lat]

4. WGS-84 和 BD-09 的相互转换

4.1 WGS-84 转 BD-09

1
2
3
4
5
# WGS-84 转 BD-09
def wgs84_to_bd09(lng, lat):
gcj_point = wgs84_to_gcj02(lng, lat)
bd_point = gcj02_to_bd09(gcj_point[0], gcj_point[1])
return [bd_point[0], bd_point[1]]

4.2 BD-09 转 WGS-84

1
2
3
4
5
# BD-09 转 WGS-84
def bd09_to_wgs84(lng, lat):
gcj_point = bd09_to_gcj02(lng, lat)
wgs_point = gcj02_to_wgs84(gcj_point[0], gcj_point[1])
return [wgs_point[0], wgs_point[1]]