概述
一:在进行地图开发过程中,我们一般能接触到以下三种类型的地图坐标系:
1.WGS-84 原始坐标系,一般用国际 GPS 纪录仪记录下来的经纬度,通过 GPS 定位拿到的原始经纬度,Google 和高德地图定位的的经纬度(国外)都是基于 WGS-84 坐标系的;但是在国内是不允许直接用 WGS84 坐标系标注的,必须经过加密后才能使用;
2.GCJ-02 坐标系,又名“火星坐标系”,是我国国测局独创的坐标体系,由 WGS-84 加密而成,在国内,必须至少使用 GCJ-02 坐标系,或者使用在 GCJ-02 加密后再进行加密的坐标系,如百度坐标系。高德和 Google 在国内都是使用 GCJ-02 坐标系或在此基础上面加密为直接的坐标系,可以说,GCJ-02 是国内最广泛使用的坐标系;
3. 百度坐标系:bd-09,百度坐标系是在 GCJ-02 坐标系的基础上再次加密偏移后形成的坐标系,只适用于百度地图。(目前百度 API 提供了从其它坐标系转换为百度坐标系的 API,但却没有从百度坐标系转为其他坐标系的 API)
二:为什么会发生偏移?
1. 由于坐标系之间不兼容,如在百度地图上定位的经纬度拿到高德地图上直接描点就肯定会发生偏移;只考虑国内的情况,高德地图和 Google 地图是可以不经过转换也能够准确显示的(在国内用的都是 GCJ-02 坐标系);下面是收录了网上的 WGS-84,GCJ-02,百度坐标系 (bd-09) 之间的相互转换的方法,经测试,是转换后相对准确可用的:
测试
工具类:
package com.topinfo.gps;
/**
* 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的互转
*
*/
public class GPSUtil {
public static double pi = 3.1415926535897932384626;
public static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;
public static double a = 6378245.0;
public static double ee = 0.00669342162296594323;
public static double transformLat(double x,double y){double 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 * pi) + 20.0 * Math.sin (2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin (y * pi) + 40.0 * Math.sin (y / 3.0 * pi)) * 2.0 / 3.0;
ret += (160.0 * Math.sin (y / 12.0 * pi) + 320 * Math.sin (y * pi / 30.0)) * 2.0 / 3.0;
return ret;
}
public static double transformLon(double x,double y){double 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 * pi) + 20.0 * Math.sin (2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin (x * pi) + 40.0 * Math.sin (x / 3.0 * pi)) * 2.0 / 3.0;
ret += (150.0 * Math.sin (x / 12.0 * pi) + 300.0 * Math.sin (x / 30.0 * pi)) * 2.0 / 3.0;
return ret;
}
public static double[] transform(double lat,double lon){if (outOfChina (lat, lon)) {return new double[] {lat, lon}; }
double dLat = transformLat (lon - 105.0, lat - 35.0);
double dLon = transformLon (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);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos (radLat) * pi);
double mgLat = lat + dLat;
double mgLon = lon + dLon;
return new double[] { mgLat, mgLon};
}
public static boolean outOfChina(double lat,double lon){if (lon < 72.004 || lon > 137.8347) return true;
if (lat < 0.8293 || lat > 55.8271) return true;
return false;
}
/**
* 84 to 火星坐标系 (GCJ-02) World Geodetic System ==> Mars Geodetic System
*
* @param lat
* @param lon
* @return
*/
public static double[] gps84_To_Gcj02(double lat,double lon){if (outOfChina (lat, lon)) {return new double[] {lat, lon}; }
double dLat = transformLat (lon - 105.0, lat - 35.0);
double dLon = transformLon (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);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos (radLat) * pi);
double mgLat = lat + dLat;
double mgLon = lon + dLon;
return new double[] { mgLat, mgLon};
}
/**
* * 火星坐标系 (GCJ-02) to 84 * * @param lon * @param lat * @return
* */
public static double[] gcj02_To_Gps84(double lat,double lon){double[] gps = transform (lat, lon);
double lontitude = lon * 2 - gps[1];
double latitude = lat * 2 - gps[0];
return new double[] { latitude, lontitude};
}
/**
* 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 将 GCJ-02 坐标转换成 BD-09 坐标
*
* @param lat
* @param lon
*/
public static double[] gcj02_To_Bd09(double lat,double lon){
double x = lon, y = lat;
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 tempLon = z * Math.cos (theta) + 0.0065;
double tempLat = z * Math.sin (theta) + 0.006;
double[] gps = { tempLat, tempLon};
return gps;
}
/**
* * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 * * 将 BD-09 坐标转换成 GCJ-02 坐标 * * @param
* bd_lat * @param bd_lon * @return
*/
public static double[] bd09_To_Gcj02(double lat,double lon){
double x = lon - 0.0065, 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 tempLon = z * Math.cos (theta);
double tempLat = z * Math.sin (theta);
double[] gps = { tempLat, tempLon};
return gps;
}
/** 将 gps84 转为 bd09
* @param lat
* @param lon
* @return
*/
public static double[] gps84_To_bd09(double lat,double lon){double[] gcj02 = gps84_To_Gcj02 (lat, lon);
double[] bd09 = gcj02_To_Bd09 (gcj02[0], gcj02[1]);
return bd09;
}
public static double[] bd09_To_gps84(double lat,double lon){double[] gcj02 = bd09_To_Gcj02 (lat, lon);
double[] gps84 = gcj02_To_Gps84 (gcj02[0], gcj02[1]);
// 保留小数点后六位
gps84[0] = retain6 (gps84[0]);
gps84[1] = retain6 (gps84[1]);
return gps84;
}
/** 保留小数点后六位
* @param num
* @return
*/
private static double retain6(double num){String result = String.format ("%.6f", num);
return Double.valueOf (result);
}
}
测试类:
package com.topinfo.gps;
public class Test2 {
/**
//GPS 坐标
116.32715863448607
39.990912172420714
// 百度
116.33993794567915
39.99790821949972
*/
public static void main(String[] args){double[] gps = GPSUtil.gps84_To_bd09 (39.990912172420714, 116.32715863448607);
System.out.println (gps[0]);
System.out.println (gps[1]);
}
}
输出:
39.997903976430734
116.33993249456921
经过比对:
39.997903976430734
39.99790821949972
116.33993249456921
116.33993794567915
然后在百度官网 API 测试;也几乎是重叠的,如果:
工具类我是在 https://blog.csdn.net/a135703… 找到的。