坐标转换(天地图、百度地图、高德地图)
首先清晰定义各个坐标系名称
(1)天地图
CGCS2000坐标,2000国家大地坐标系;我们其实很多时候直接用WGS84的坐标来代替CGCS2000坐标。因为CGCS2000的定义与WGS84实质一样。采用的参考椭球非常接近。扁率差异引起椭球面上的纬度和高度变化最大达0.1mm。当前测量精度范围内,可以忽略这点差异。可以说两者相容至cm级水平,但若一点的坐标精度达不到cm水平,则不认为CGCS2000和WGS84的坐标是相容的。
(2)百度地图
bd09II坐标。首先了解一下火星坐标,它是在国际标准坐标WGS-84上进行的一次加密,由于国内的电子地图都要至少使用火星坐标进行一次加密,百度直接就任性一些,直接自己又研究了一套加密算法,来了个二次加密,这就是我们所熟知的百度坐标(BD-09)。
(3)高德地图
gcj02坐标,也称为火星坐标。火星坐标是国家测绘局为了国家安全在原始坐标的基础上进行偏移得到的坐标,基本国内的电子地图、导航设备都是采用的这一坐标系或在这一坐标的基础上进行二次加密得到的。
各坐标系的转换
import java.util.ArrayList;
import java.util.List;
/**
* 生命不息,编程不止
*
* @author coco
* @date 2022/08/07 19:07
**/
public class MapUtils {
static Double x_PI = 3.14159265358979324 * 3000.0 / 180.0;
static Double PI = 3.1415926535897932384626;
static Double a = 6378245.0;
static Double ee = 0.00669342162296594323;
public static void main(String[] args) {
// 经度
double lon = 119.157064;
// 纬度
double lat = 28.066235;
System.out.println(" = = >>> 百度坐标 转 火星坐标" + bd09togcj02(lon, lat));
System.out.println(" = = >>> 火星坐标 转 百度坐标" + gcj02tobd09(lon, lat));
System.out.println(" = = >>> 地球坐标 转 火星坐标" + wgs84togcj02(lon, lat));
System.out.println(" = = >>> 火星坐标 转 地球坐标" + gcj02towgs84(lon, lat));
}
/**
* 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换
* 即 百度 转 谷歌、高德
*
* @param lon
* @param lat
* @returns
*/
public static List<Double> bd09togcj02(double lon, double lat) {
List<Double> list = new ArrayList<>();
double x_pi = 3.14159265358979324 * 3000.0 / 180.0;
double x = lon - 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 gg_lng = z * Math.cos(theta);
double gg_lat = z * Math.sin(theta);
list.add(gg_lng);
list.add(gg_lat);
return list;
}
/**
* 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换
* 即谷歌、高德 转 百度
*
* @param lon
* @param lat
* @returns {*[]}
*/
public static List<Double> gcj02tobd09(double lon, double lat) {
List<Double> list = new ArrayList<>();
double z = Math.sqrt(lon * lon + lat * lat) + 0.00002 * Math.sin(lat * x_PI);
double theta = Math.atan2(lat, lon) + 0.000003 * Math.cos(lon * x_PI);
double bd_lng = z * Math.cos(theta) + 0.0065;
double bd_lat = z * Math.sin(theta) + 0.006;
list.add(bd_lng);
list.add(bd_lat);
return list;
}
/**
* 地球坐标系(WGS84) 转 火星坐标系(GCj02)
*
* @param lon
* @param lat
*/
public static List<Double> wgs84togcj02(double lon, double lat) {
List<Double> list = new ArrayList<>();
if (out_of_china(lon, lat)) {
list.add(lon);
list.add(lat);
return list;
} else {
double dlat = transformlat(lon - 105.0, lat - 35.0);
double dlng = transformlng(lon - 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 = lon + dlng;
list.add(mglng);
list.add(mglat);
return list;
}
}
/**
* 火星坐标系(GCj02) 转 地球坐标系(WGS84)
*
* @param lon
* @param lat
* @return
*/
public static List<Double> gcj02towgs84(double lon, double lat) {
List<Double> list = new ArrayList<>();
if (out_of_china(lon, lat)) {
list.add(lon);
list.add(lat);
return list;
} else {
double dlat = transformlat(lon - 105.0, lat - 35.0);
double dlng = transformlng(lon - 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 = lon + dlng;
list.add(lon * 2 - mglng);
list.add(lat * 2 - mglat);
return list;
}
}
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;
}
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 lon
* @param lat
* @return
*/
private static boolean out_of_china(double lon, double lat) {
return (lon < 72.004 || lon > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false);
}
}